Module: IdempotencyLock
- Defined in:
- lib/idempotency_lock.rb,
lib/idempotency_lock/lock.rb,
lib/idempotency_lock/result.rb,
lib/idempotency_lock/railtie.rb,
lib/idempotency_lock/version.rb,
lib/generators/idempotency_lock/install_generator.rb
Overview
Database-backed idempotency locks for Ruby on Rails.
Ensures operations run exactly once using database-backed locks with support for TTL expiration, multiple error handling strategies, and clean return value handling.
Defined Under Namespace
Modules: Generators Classes: Error, Lock, Railtie, Result
Constant Summary collapse
- ON_ERROR_UNLOCK =
Error handling strategies
:unlock- ON_ERROR_KEEP_LOCKED =
Remove lock so operation can be retried
:keep- ON_ERROR_RAISE =
Keep lock in place (default)
:raise- VERSION =
"0.1.3"
Class Attribute Summary collapse
Class Method Summary collapse
-
.cleanup_expired ⇒ Integer
Clean up all expired locks.
-
.locked?(name) ⇒ Boolean
Check if an operation is currently locked.
-
.once(name, ttl: nil, on_error: ON_ERROR_KEEP_LOCKED) { ... } ⇒ Result
(also: wrap)
Execute a block exactly once for the given operation name.
-
.release(name) ⇒ Boolean
Release a lock manually (useful for testing or manual intervention).
Class Attribute Details
.logger ⇒ Object
83 84 85 |
# File 'lib/idempotency_lock.rb', line 83 def logger @logger ||= defined?(Rails) ? Rails.logger : nil end |
Class Method Details
.cleanup_expired ⇒ Integer
Clean up all expired locks
76 77 78 |
# File 'lib/idempotency_lock.rb', line 76 def cleanup_expired Lock.cleanup_expired end |
.locked?(name) ⇒ Boolean
Check if an operation is currently locked
69 70 71 |
# File 'lib/idempotency_lock.rb', line 69 def locked?(name) Lock.locked?(name) end |
.once(name, ttl: nil, on_error: ON_ERROR_KEEP_LOCKED) { ... } ⇒ Result Also known as: wrap
Execute a block exactly once for the given operation name.
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/idempotency_lock.rb', line 38 def once(name, ttl: nil, on_error: ON_ERROR_KEEP_LOCKED, &) raise ArgumentError, "Block required" unless block_given? now = Time.current expires_at = calculate_expires_at(ttl, now) acquired = Lock.acquire(name, expires_at: expires_at, now: now) unless acquired log_debug("Lock already exists for '#{name}', skipping execution") return Result.new(executed: false, skipped: true) end log_debug("Lock acquired for '#{name}', executing block") execute_with_lock(name, on_error, &) end |
.release(name) ⇒ Boolean
Release a lock manually (useful for testing or manual intervention)
61 62 63 |
# File 'lib/idempotency_lock.rb', line 61 def release(name) Lock.release(name) end |