Topic: Async view fragment rendering

Hi all, I am trying to render a part of view in separate thread. In view I have something like:

- cache "key_name", :expires_delta => 30.minutes do
  %h1 Some haml code here

Then I created cache store class (SmarterMemcacheStore) that inherits from MemCacheStore and patched rails fragment_for and read_fragment methods to also pass &block to cache.read method (originally it pass &block only to write mothod). As result, in my SmarterMemcacheStore class I have 2 methods: read and write and both receieve a block.

In method read I am trying to generate new cache if existing one is expired (call the block). So the flow is:
1. Return nil if cache does not exists (fragment_for method will cal the block and create cache)
2. Return cache if it exists and not expired
3. If cache exists and is expired we still should return it, run block to update the cache.

Last part makes no sense if I will run haml block in main thread because it will not be async and ActionView::Helpers::CacheHelper#fragment_for already will do that if method read will return nil.

So, I am trying to run the block in separate thread, like:

if Time.now > expires_at
  return data if exist?("lock_#{key}")
  return nil if !block_given?
 
  orig_write("lock_#{key}", true, :expires_in => lock_expires_in)

  Thread.new do
    eval("output_buffer = ActionView::OutputBuffer.new", block.binding) #otherwise getting error from haml
    context = eval("self", block.binding)
    out = context.capture(&block)
    write(key, out, options)
  end
end

This works good except one moment that captured html output also contains some server logs and could look like this:
<h1Started GET "/assets/fontawesome-webfont.woff" for 127.0.0.1 at [...] >Some haml code here</h1>

I am understanding the resaon for that — 2 threads trying to access the same output, but can't find how to avoid this. And I am actually not understand why logs appears in output_buffer.

The reason for all this work is that some views could took about 1 minute to built, so default rails behaviour is not good for me. The best I can do now without all this ugly hacks — simply create a lock in memcahed when cache is expired and return nil (or return outdated cache if lock for requested key already exists). In this case fragment_for method will run the block in main thread and user who requested that page with outdated cache will be forced to wait untill new cache will be ready. All other users will get the outdated cache at this time so only one user will suffer from slowdown. But the app I built is for company internal usage and there are not too many users thus very often same users suffering from slowdown.

I am also understand that idea with threads is not too good and many threads will lock main app because of GIL, but it's ok for application where it will be used.

Thanks in advance,
Alex