class OpenUI::Rails::Actions::Idempotency::MemoryStore
Default, single-process idempotency store. Hosts that run multiple processes should inject a shared store (e.g. Redis or the database) that implements the same contract.
Contract:
claim(key, ttl:, digest:) -> [status, result, digest, claim_token] status is :claimed, :pending or :replay (even for a nil result). record(key, result, claim_token:) ignores an expired/replaced claim.
Public Class Methods
new(clock: -> { Process.clock_gettime(Process::CLOCK_MONOTONIC) }
click to toggle source
# File lib/openui/rails/actions/idempotency/memory_store.rb, line 18 def initialize(clock: -> { Process.clock_gettime(Process::CLOCK_MONOTONIC) }) @entries = {} @mutex = Mutex.new @clock = clock end
Public Instance Methods
claim(key, ttl:, digest: nil)
click to toggle source
# File lib/openui/rails/actions/idempotency/memory_store.rb, line 24 def claim(key, ttl:, digest: nil) raise ConfigurationError, "idempotency ttl must be positive" unless ttl.is_a?(Numeric) && ttl.finite? && ttl > 0 @mutex.synchronize do now = @clock.call purge(now) if (entry = @entries[key]) return [ entry[:completed] ? :replay : :pending, entry[:result], entry[:digest], entry[:token] ] end token = SecureRandom.hex(16) @entries[key] = { digest: digest, result: nil, completed: false, token: token, expires_at: now + ttl } [ :claimed, nil, digest, token ] end end
record(key, result, claim_token:)
click to toggle source
# File lib/openui/rails/actions/idempotency/memory_store.rb, line 41 def record(key, result, claim_token:) @mutex.synchronize do entry = @entries[key] if entry && entry[:token] == claim_token && entry[:expires_at] > @clock.call entry[:result] = result entry[:completed] = true end end end
size()
click to toggle source
# File lib/openui/rails/actions/idempotency/memory_store.rb, line 51 def size @mutex.synchronize { @entries.size } end