Class: SafeMemoize::Adapters::ConcurrentRuby
- Inherits:
-
Stores::Base
- Object
- Stores::Base
- SafeMemoize::Adapters::ConcurrentRuby
- Defined in:
- lib/safe_memoize/adapters/concurrent_ruby.rb
Overview
Optional store adapter backed by +concurrent-ruby+.
Replaces the default +Mutex+-guarded +Hash+ with +Concurrent::Map+ and +Concurrent::ReentrantReadWriteLock+. Multiple readers proceed in parallel; writers still get exclusive access. For hot paths with many concurrent readers this can meaningfully reduce lock contention compared to Stores::Memory.
Opt in per class:
class MyService prepend SafeMemoize self.safe_memoize_store = SafeMemoize::Adapters::ConcurrentRuby.new end
Or globally via SafeMemoize.configure:
SafeMemoize.configure do |c| c.default_store = SafeMemoize::Adapters::ConcurrentRuby.new end
Requires the +concurrent-ruby+ gem, which is not a runtime dependency of +safe_memoize+. Add it to your own Gemfile or gemspec:
gem "concurrent-ruby"
A LoadError with an actionable message is raised at instantiation time if the gem is not available.
Constant Summary
Constants inherited from Stores::Base
Instance Method Summary collapse
- #clear ⇒ void
- #delete(key) ⇒ void
-
#initialize ⇒ ConcurrentRuby
constructor
A new instance of ConcurrentRuby.
-
#keys ⇒ Array<Object>
Returns all live (non-expired) keys.
-
#read(key) ⇒ Object
The stored value, or Stores::Base::MISS if absent or expired.
- #write(key, value, expires_in: nil) ⇒ void
Methods inherited from Stores::Base
Constructor Details
#initialize ⇒ ConcurrentRuby
Returns a new instance of ConcurrentRuby.
33 34 35 36 37 38 39 40 41 42 |
# File 'lib/safe_memoize/adapters/concurrent_ruby.rb', line 33 def initialize require "concurrent/map" require "concurrent/atomic/reentrant_read_write_lock" @data = Concurrent::Map.new @lock = Concurrent::ReentrantReadWriteLock.new rescue LoadError raise LoadError, "SafeMemoize::Adapters::ConcurrentRuby requires the concurrent-ruby gem. " \ "Add `gem 'concurrent-ruby'` to your Gemfile." end |
Instance Method Details
#clear ⇒ void
This method returns an undefined value.
75 76 77 |
# File 'lib/safe_memoize/adapters/concurrent_ruby.rb', line 75 def clear @lock.with_write_lock { @data.clear } end |
#delete(key) ⇒ void
This method returns an undefined value.
70 71 72 |
# File 'lib/safe_memoize/adapters/concurrent_ruby.rb', line 70 def delete(key) @lock.with_write_lock { @data.delete(key) } end |
#keys ⇒ Array<Object>
Returns all live (non-expired) keys.
81 82 83 84 85 86 87 88 |
# File 'lib/safe_memoize/adapters/concurrent_ruby.rb', line 81 def keys now = Process.clock_gettime(Process::CLOCK_MONOTONIC) @lock.with_read_lock do result = [] @data.each_pair { |k, entry| result << k unless entry[:expires_at] && entry[:expires_at] <= now } result end end |
#read(key) ⇒ Object
Returns the stored value, or Stores::Base::MISS if absent or expired.
46 47 48 49 50 51 52 53 54 |
# File 'lib/safe_memoize/adapters/concurrent_ruby.rb', line 46 def read(key) @lock.with_read_lock do entry = @data[key] return MISS unless entry return MISS if expired?(entry) entry[:value] end end |
#write(key, value, expires_in: nil) ⇒ void
This method returns an undefined value.
60 61 62 63 64 65 66 |
# File 'lib/safe_memoize/adapters/concurrent_ruby.rb', line 60 def write(key, value, expires_in: nil) now = Process.clock_gettime(Process::CLOCK_MONOTONIC) expires_at = expires_in ? now + expires_in.to_f : nil @lock.with_write_lock do @data[key] = {value: value, expires_at: expires_at, cached_at: now} end end |