cowboyd / handlebars.rb

Ruby Bindings for Handlebars.js
http://www.handlebarsjs.com
161 stars 67 forks source link

Threadsafe usage #51

Open tombeynon opened 4 years ago

tombeynon commented 4 years ago

We were having a lot of issues with this gem causing hanging behaviour in a multi-threaded environment. V8 is the downstream cause of this behaviour.

We looked at #46 to try to solve it, but that PR exhibited similar issues.

We ended up solving by using a Singleton class to manage a mutex whenever you want to compile a template. An example below in case anyone else hits the same problem:

HandlebarsCompiler.instance.compile(layout_path, content_path, data)
class HandlebarsCompiler
  include Singleton

  def initialize
    @mutex = Mutex.new
  end

  def compile(layout_path, content_path, data)
    @mutex.synchronize do
      content_template = File.read(content_path)
      handlebars = Handlebars::Context.new
      handlebars.register_partial('content', content_template)
      html = handlebars.compile(File.read(layout_path))
      html.call(data)
    end
  end
end
omenking commented 4 years ago

Attempting this implementation I get:

V8::Error (deadlock; recursive locking):

I suppose I'll have to read up more on the ruby threading. 🤷‍♂️

I've been impacted by this issue for a few years now.

omenking commented 4 years ago

askcharlie has your implementation. I'll give it a go.

https://github.com/askcharlie/handlebars.rb/commit/f8259b4770f74fdcd3e3d21b7fc492b6b1380130

omenking commented 4 years ago

Revisiting this problem, still, hangs. Its a bummer a pure ruby implementation doesn't match the syntax found in this gem.

katpadi commented 3 years ago

We were having a lot of issues with this gem causing hanging behaviour in a multi-threaded environment. V8 is the downstream cause of this behaviour.

We looked at #46 to try to solve it, but that PR exhibited similar issues.

We ended up solving by using a Singleton class to manage a mutex whenever you want to compile a template. An example below in case anyone else hits the same problem:

HandlebarsCompiler.instance.compile(layout_path, content_path, data)
class HandlebarsCompiler
  include Singleton

  def initialize
    @mutex = Mutex.new
  end

  def compile(layout_path, content_path, data)
    @mutex.synchronize do
      content_template = File.read(content_path)
      handlebars = Handlebars::Context.new
      handlebars.register_partial('content', content_template)
      html = handlebars.compile(File.read(layout_path))
      html.call(data)
    end
  end
end

This looks like it's working. However, it seems like there's some memory leak when we're doing it this way as we are registering some handlebar methods