-
Couldn't load subscription status.
- Fork 5
Fetch fill lock #46
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Fetch fill lock #46
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,6 +10,8 @@ module Dalli | |
| ## | ||
| # rubocop:disable Metrics/ClassLength | ||
| class Client | ||
| LOCK_TTL = 5 # seconds, default lock TTL | ||
| FILL_LOCK_INTERVAL = 0.01 # seconds, default fill lock interval | ||
| ## | ||
| # Dalli::Client is the main class which developers will use to interact with | ||
| # the memcached server. Usage: | ||
|
|
@@ -148,6 +150,47 @@ def fetch(key, ttl = nil, req_options = nil) | |
| new_val | ||
| end | ||
|
|
||
| # Fetch the value associated with the key, along with a lock. | ||
| # If a value is found, then it is returned. | ||
| # | ||
| # If a value is not found and no block is given, then nil is returned. | ||
| # | ||
| # If a value is not found (or if the found value is nil and :cache_nils is false) | ||
| # and a block is given, the block will be invoked and its return value | ||
| # written to the cache and returned. | ||
| # rubocop:disable Metrics/AbcSize | ||
| # rubocop:disable Metrics/CyclomaticComplexity | ||
| # rubocop:disable Metrics/PerceivedComplexity | ||
| def fetch_with_lock(key, ttl = nil, req_options = nil) | ||
| req_options = {} if req_options.nil? | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need an early return here since there is a default lock TTL and interval? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this just sets to an empty hash opposed to nil as it is annoying to keep checking for if the value is in the hash or if the options don't exist at all. |
||
| clean_req_options = cache_nils ? req_options.merge(CACHE_NILS) : req_options | ||
| lock_ttl, fill_lock_interval, lock_wait_end_time = get_lock_options(req_options) | ||
|
|
||
| req_options = clean_req_options.dup | ||
| req_options[:meta_flags] ||= [] | ||
| req_options[:meta_flags] << "N#{lock_ttl}" | ||
|
|
||
| loop do | ||
| val, meta_flags = get(key, req_options) | ||
|
|
||
| if val && val != '' | ||
| return val | ||
| elsif meta_flags[:w] | ||
| new_val = yield | ||
| set(key, new_val, ttl_or_default(ttl), clean_req_options) | ||
| return new_val | ||
| elsif meta_flags[:z] | ||
| break if Time.now.to_f >= lock_wait_end_time | ||
| end | ||
|
|
||
| sleep(fill_lock_interval) | ||
| end | ||
| yield # fails to read value in wait time, yield back the value | ||
| end | ||
| # rubocop:enable Metrics/AbcSize | ||
| # rubocop:enable Metrics/CyclomaticComplexity | ||
| # rubocop:enable Metrics/PerceivedComplexity | ||
|
|
||
| ## | ||
| # compare and swap values using optimistic locking. | ||
| # Fetch the existing value for key. | ||
|
|
@@ -390,6 +433,22 @@ def with | |
|
|
||
| private | ||
|
|
||
| def get_lock_options(req_options) | ||
| lock_ttl = req_options.delete(:lock_ttl) || LOCK_TTL | ||
| fill_lock_interval = req_options.delete(:fill_lock_interval) || FILL_LOCK_INTERVAL | ||
|
|
||
| raise ArgumentError, 'lock_ttl must be a positive integer' if !lock_ttl.is_a?(Integer) && lock_ttl < 1 | ||
|
|
||
| if fill_lock_interval.is_a?(Numeric) && fill_lock_interval <= 0 | ||
| raise ArgumentError, | ||
| 'fill_lock_interval must be a positive number' | ||
| end | ||
|
|
||
| lock_wait_end_time = Time.now.to_f + lock_ttl | ||
|
|
||
| [lock_ttl, fill_lock_interval, lock_wait_end_time] | ||
| end | ||
|
|
||
| def check_positive!(amt) | ||
| raise ArgumentError, "Positive values only: #{amt}" if amt.negative? | ||
| end | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -150,10 +150,10 @@ def quiet? | |
| alias multi? quiet? | ||
|
|
||
| # NOTE: Additional public methods should be overridden in Dalli::Threadsafe | ||
| ALLOWED_QUIET_OPS = %i[add replace set delete incr decr append prepend flush noop].freeze | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. rubocop update required moving this outside of private |
||
|
|
||
| private | ||
|
|
||
| ALLOWED_QUIET_OPS = %i[add replace set delete incr decr append prepend flush noop].freeze | ||
| def verify_allowed_quiet!(opkey) | ||
| return if ALLOWED_QUIET_OPS.include?(opkey) | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
github actions needed this update to run