Module: Retriable
- Defined in:
- lib/retriable.rb,
lib/retriable/config.rb,
lib/retriable/version.rb,
lib/retriable/validation.rb,
lib/retriable/exponential_backoff.rb,
sig/retriable.rbs
Defined Under Namespace
Modules: Validation Classes: Config, ExponentialBackoff
Constant Summary collapse
- OVERRIDE_THREAD_KEY =
Thread-local storage key for the active #with_override block. We deliberately use Thread#thread_variable_set/get (true thread-local) rather than Thread.current (fiber-local) so that fibers within a thread share the same override. Changing this to Thread.current would silently break callers that use fiber-based concurrency.
:retriable_override- VERSION =
"5.0.0"
Class Method Summary collapse
-
.config ⇒ Config
The configuring thread sees its own candidate, still mutable and mid-build.
-
.configure ⇒ void
Copy-on-write: dup the published config, let the caller mutate the copy, then atomically publish it, deeply frozen.
- .retriable(opts = {}) {|arg0| ... } ⇒ Object
- .with_context(context_key, options = {}) {|arg0| ... } ⇒ Object
- .with_override(opts = {}) { ... } ⇒ Object
Class Method Details
.config ⇒ Config
The configuring thread sees its own candidate, still mutable and mid-build. Every other reader sees the last fully published snapshot, which is deeply frozen: mutating it raises FrozenError instead of silently corrupting the config other threads are reading. Use #configure to change configuration.
82 83 84 |
# File 'lib/retriable.rb', line 82 def config configuring_config || published_config end |
.configure ⇒ void
This method returns an undefined value.
Copy-on-write: dup the published config, let the caller mutate the copy, then atomically publish it, deeply frozen. Readers therefore always observe a consistent, fully-applied snapshot that nothing can mutate underneath them, and a failed/raising block leaves the old config intact. Does NOT validate (validation stays lazy at #retriable time). Nested calls on the configuring thread share the outer candidate. Only the outermost call takes CONFIG_MUTEX and publishes, so nesting remains safe even though the mutex is not reentrant.
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
# File 'lib/retriable.rb', line 61 def configure candidate = configuring_config return yield(candidate) if candidate CONFIG_MUTEX.synchronize do candidate = config.dup Thread.current.thread_variable_set(CONFIGURING_THREAD_KEY, candidate) begin result = yield(candidate) publish_config(candidate) result ensure Thread.current.thread_variable_set(CONFIGURING_THREAD_KEY, nil) end end end |
.retriable(opts = {}) {|arg0| ... } ⇒ Object
120 121 122 |
# File 'lib/retriable.rb', line 120 def retriable(opts = {}, &) retriable_with_config(config, opts, &) end |
.with_context(context_key, options = {}) {|arg0| ... } ⇒ Object
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/retriable.rb', line 101 def with_context(context_key, = {}, &) raise ArgumentError, "with_context requires a block" unless block_given? # Resolve the whole call against one snapshot and one traversal of its # contexts. Re-reading `config` here would let a concurrent #configure pass # the existence check on the old snapshot while options resolve against the # new one, silently dropping the context's retry options. config_snapshot = config configured_contexts = config_contexts(config_snapshot) contexts = configured_contexts.merge(override_contexts) if !contexts.key?(context_key) raise ArgumentError, "#{context_key} not found in Retriable contexts (including overrides). Available contexts: #{contexts.keys}" end retriable_with_config(config_snapshot, (context_key, configured_contexts, ), &) end |
.with_override(opts = {}) { ... } ⇒ Object
86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
# File 'lib/retriable.rb', line 86 def with_override(opts = {}) raise ArgumentError, "empty override options are not allowed" if opts.empty? raise ArgumentError, "with_override requires a block" unless block_given? (opts) previous = Thread.current.thread_variable_get(OVERRIDE_THREAD_KEY) Thread.current.thread_variable_set(OVERRIDE_THREAD_KEY, opts) begin yield ensure Thread.current.thread_variable_set(OVERRIDE_THREAD_KEY, previous) end end |