Skip to content

Commit

Permalink
feat: allow redis store to receive a connection pool (#13)
Browse files Browse the repository at this point in the history
Co-authored-by: Matteo Rossi <[email protected]>
  • Loading branch information
matteoredz and Matteo Rossi authored Jan 29, 2025
1 parent 2463b55 commit adafc66
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 7 deletions.
2 changes: 1 addition & 1 deletion lib/rack/idempotency_key/memory_store.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
module Rack
class IdempotencyKey
class MemoryStore < Store
def initialize(store = {}, expires_in: 86_400)
def initialize(store = {}, expires_in: Store::DEFAULT_EXPIRATION)
super(store, expires_in: expires_in)
@mutex = Mutex.new
end
Expand Down
32 changes: 26 additions & 6 deletions lib/rack/idempotency_key/redis_store.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,46 @@ class IdempotencyKey
class RedisStore < Store
KEY_NAMESPACE = "idempotency_key"

def initialize(store, expires_in: 86_400)
super(store, expires_in: expires_in)
end

def get(key)
value = store.get(namespaced_key(key))
value = with_redis { |redis| redis.get(namespaced_key(key)) }
JSON.parse(value) unless value.nil?
rescue Redis::BaseError => e
raise Rack::IdempotencyKey::Store::Error, "#{self.class}: #{e.message}"
end

def set(key, value)
store.set(namespaced_key(key), value, nx: true, ex: expires_in)
with_redis { |redis| redis.set(namespaced_key(key), value, nx: true, ex: expires_in) }
get(key)
rescue Redis::BaseError => e
raise Rack::IdempotencyKey::Store::Error, "#{self.class}: #{e.message}"
end

private

# Executes the given block with a Redis connection, supporting both direct
# Redis instances and connection pools (https://github.com/mperham/connection_pool).
#
# If a `ConnectionPool` is detected (by responding to `with`), it will yield a Redis
# connection from the pool. Otherwise, it will yield the direct Redis instance.
#
# @yieldparam redis [Redis] A Redis connection instance.
# @return [Object] The result of the block execution.
#
# @example Using a direct Redis instance
# store = RedisStore.new(Redis.new)
# store.with_redis { |redis| redis.set("key", "value") }
#
# @example Using a Redis connection pool
# store = RedisStore.new(ConnectionPool.new(size: 5, timeout: 5) { Redis.new })
# store.with_redis { |redis| redis.set("key", "value") }
def with_redis(&block)
if store.respond_to?(:with)
store.with(&block)
else
yield store
end
end

def namespaced_key(key)
"#{KEY_NAMESPACE}:#{key.split.join}"
end
Expand Down

0 comments on commit adafc66

Please sign in to comment.