Class: RackJwtVerifier::InProcessCache

Inherits:
Object
  • Object
show all
Defined in:
lib/rack_jwt_verifier/in_process_cache.rb

Overview

A simple, thread-safe, in-memory cache implementation designed to mimic a real cache store (like Rails.cache or Redis) but without external dependencies. This is the default cache store used if the user doesn't configure one.

Constant Summary collapse

DEFAULT_EXPIRY =

The cache lifespan in seconds (5 minutes)

300
MONOTONIC_CLOCK =

Expiry is measured on the monotonic clock, so a wall-clock jump (NTP correction, DST) cannot extend or cut short an entry's life.

-> { Process.clock_gettime(Process::CLOCK_MONOTONIC) }
SWEEP_THRESHOLD =

Expired entries are swept whenever the store grows past this many entries, and the threshold then doubles from whatever survived. Amortised O(1) per write, so a jti-per-request workload cannot grow the store without bound while a single-key workload never pays for a sweep.

1024

Instance Method Summary collapse

Constructor Details

#initialize(clock: MONOTONIC_CLOCK) ⇒ InProcessCache

Returns a new instance of InProcessCache.

Parameters:

  • clock (#call) (defaults to: MONOTONIC_CLOCK)

    Returns the current time in seconds; injectable for tests.



22
23
24
25
26
27
# File 'lib/rack_jwt_verifier/in_process_cache.rb', line 22

def initialize(clock: MONOTONIC_CLOCK)
  @store = {}
  @lock = Mutex.new # Ensure thread safety for multi-threaded environments
  @clock = clock
  @sweep_at = SWEEP_THRESHOLD
end

Instance Method Details

#clearObject

Removes every entry.



65
66
67
68
69
70
71
# File 'lib/rack_jwt_verifier/in_process_cache.rb', line 65

def clear
  @lock.synchronize do
    @store.clear
    @sweep_at = SWEEP_THRESHOLD
  end
  nil
end

#delete(key) ⇒ Object?

Deletes an entry from the cache.

Parameters:

  • key (String)

    The cache key.

Returns:

  • (Object, nil)

    The deleted entry value or nil.



57
58
59
60
61
62
# File 'lib/rack_jwt_verifier/in_process_cache.rb', line 57

def delete(key)
  @lock.synchronize do
    entry = @store.delete(key)
    entry&.first
  end
end

#read(key) ⇒ Object?

Reads the value for a given key. Automatically checks for expiry.

Parameters:

  • key (String)

    The cache key.

Returns:

  • (Object, nil)

    The cached value or nil if expired or not found.



32
33
34
# File 'lib/rack_jwt_verifier/in_process_cache.rb', line 32

def read(key)
  @lock.synchronize { live_value(key) }
end

#sizeObject

Number of entries held, expired ones included until the next sweep.



74
75
76
# File 'lib/rack_jwt_verifier/in_process_cache.rb', line 74

def size
  @lock.synchronize { @store.size }
end

#write(key, value, options = {}) ⇒ Object, false

Writes a value to the cache with an optional expiration time.

Parameters:

  • key (String)

    The cache key.

  • value (Object)

    The value to store.

  • options (Hash) (defaults to: {})

    :expires_in (seconds); :unless_exist (true to keep an existing live entry, as ActiveSupport stores do).

Returns:

  • (Object, false)

    The stored value, or false when :unless_exist was given and a live entry already existed.



43
44
45
46
47
48
49
50
51
52
# File 'lib/rack_jwt_verifier/in_process_cache.rb', line 43

def write(key, value, options = {})
  @lock.synchronize do
    return false if options[:unless_exist] && !live_value(key).nil?

    expiry = options[:expires_in] || DEFAULT_EXPIRY
    @store[key] = [value, @clock.call + expiry]
    sweep_if_needed
    value
  end
end