Improve the example of ActiveSupport::Cache::Store#fetch

The original example has race condition issue that the output of the example isn't conistent, see https://github.com/rails/rails/issues/43588.

The change improves the example by removing unnecessary thread and add detailed comments.

In the new example, there will be race condition only if `ActiveSupport::Cache::MemoryStore` took longer than 0.1 second to extend expiry but that's unlikely to happen.

Close #43588
This commit is contained in:
Jian Weihang 2024-02-20 11:56:16 +08:00
parent 554e71af0b
commit a8bc63af54
No known key found for this signature in database
GPG Key ID: 294AEB9F5C889A31
1 changed files with 31 additions and 15 deletions

View File

@ -405,31 +405,47 @@ module ActiveSupport
# has elapsed.
#
# # Set all values to expire after one minute.
# cache = ActiveSupport::Cache::MemoryStore.new(expires_in: 1.minute)
# cache = ActiveSupport::Cache::MemoryStore.new(expires_in: 1)
#
# cache.write('foo', 'original value')
# cache.write("foo", "original value")
# val_1 = nil
# val_2 = nil
# sleep 60
# p cache.read("foo") # => "original value"
#
# Thread.new do
# val_1 = cache.fetch('foo', race_condition_ttl: 10.seconds) do
# sleep 1 # wait until the cache expires
#
# t1 = Thread.new do
# # fetch does the following:
# # 1. gets an recent expired entry
# # 2. extends the expiry by 2 seconds (race_condition_ttl)
# # 3. regenerates the new value
# val_1 = cache.fetch("foo", race_condition_ttl: 2) do
# sleep 1
# 'new value 1'
# "new value 1"
# end
# end
#
# Thread.new do
# val_2 = cache.fetch('foo', race_condition_ttl: 10.seconds) do
# 'new value 2'
# end
# # Wait until t1 extends the expiry of the entry
# # but before generating the new value
# sleep 0.1
#
# val_2 = cache.fetch("foo", race_condition_ttl: 2) do
# # This block won't be executed because t1 extended the expiry
# "new value 2"
# end
#
# cache.fetch('foo') # => "original value"
# sleep 10 # First thread extended the life of cache by another 10 seconds
# cache.fetch('foo') # => "new value 1"
# val_1 # => "new value 1"
# val_2 # => "original value"
# t1.join
#
# p val_1 # => "new value 1"
# p val_2 # => "oritinal value"
# p cache.fetch("foo") # => "new value 1"
#
# # The entry requires 3 seconds to expire (expires_in + race_condition_ttl)
# # We have waited 2 seconds already (sleep(1) + t1.join) thus we need to wait 1
# # more second to see the entry expire.
# sleep 1
#
# p cache.fetch("foo") # => nil
#
# ==== Dynamic Options
#