Rails.cache in different threads not caching?

2019-09-06 13:56发布

I have a problem with caching method results in rails project.

I came to this code that simulates two different requests:

[  
   Thread.new { puts Rails.cache.fetch('a', expires_in: 2.seconds) { rand } }, 
   Thread.new { puts Rails.cache.fetch('a', expires_in: 2.seconds) { rand } } 
]

Try to execute it in a rails console. If I try to run it in a console as output I get different numbers. How is that possible?

But for example this code (with sleep) works as intended:

[  
  Thread.new { sleep(1); puts Rails.cache.fetch('a', expires_in: 2.seconds) { rand } }, 
  Thread.new { puts Rails.cache.fetch('a', expires_in: 2.seconds) { rand } } 
]

2条回答
Viruses.
2楼-- · 2019-09-06 14:30

You could use mutex to prevent running some code at the same time:

mutex = Mutex.new
[  Thread.new { puts mutex.synchronize { Rails.cache.fetch('a', expires_in: 2.seconds) { rand } } }, Thread.new { puts mutex.synchronize { Rails.cache.fetch('a', expires_in: 2.seconds) { rand } } } ]
查看更多
可以哭但决不认输i
3楼-- · 2019-09-06 14:55

In the 1st block of code

[  
  Thread.new { puts Rails.cache.fetch('a', expires_in: 2.seconds) { rand } }, 
  Thread.new { puts Rails.cache.fetch('a', expires_in: 2.seconds) { rand } } 
]

I think your rails configuration is using file for caching (to ensure that, pls check the folder /tmp/cache in your rails app).

Because 'Rails.cache' needs to perform reading cache from file, it is time-consuming, and the action 'fetch' in 2 threads mostly run at the same time, so 'a' value doesn't exist or is expired already.

In the 2nd block of code

[  
  Thread.new { sleep(1); puts Rails.cache.fetch('a', expires_in: 2.seconds) { rand } }, 
  Thread.new { puts Rails.cache.fetch('a', expires_in: 2.seconds) { rand } } 
]

When the first thread tries to fetch 'a', 'a' is in cache file already. So that is why it works.

Let make another simulation, instead of using 'Rails.cache', we will use MemoryStore cache like this:

cache = ActiveSupport::Cache::MemoryStore.new(expires_in: 5.minutes)
[
  Thread.new { puts cache.fetch('a', expires_in: 2.seconds) { rand } },
  Thread.new { puts cache.fetch('a', expires_in: 2.seconds) { rand } }
]

It works as our expectation as well!

查看更多
登录 后发表回答