Class: Anthropic::Credentials::TokenCache
- Inherits:
-
Object
- Object
- Anthropic::Credentials::TokenCache
- Defined in:
- lib/anthropic/credentials/token_cache.rb
Overview
Thread-safe cache wrapping an access token provider with two-tier proactive refresh and single-flight semantics.
Refresh policy on each #get_token call:
-
No cached token → call provider (blocking), cache, return.
-
Cached with expires_at=nil → return cached forever (never refresh).
-
More than
advisory_refresh_secondsremaining → return cached. -
Between
mandatory_refresh_secondsandadvisory_refresh_secondsremaining (advisory window) → try provider; on success swap cache; on failure log a warning and return the stale cached token. If another caller is already refreshing, the advisory caller just returns the cached token — no second refresh, no waiting. -
Less than
mandatory_refresh_secondsremaining or already expired (mandatory window) → call provider; on failure RAISE. Concurrent mandatory callers wait on a shared condition so exactly one provider call is in flight.
The lock is released before the provider call so a 30-second HTTP POST doesn’t serialize unrelated callers through a single thread.
Constant Summary collapse
- ADVISORY_REFRESH_BACKOFF_SECONDS =
Seconds to skip advisory refreshes after a failure to avoid hammering a failing token endpoint.
5
Instance Method Summary collapse
-
#get_token ⇒ String
Returns a valid bearer token, refreshing if necessary.
-
#initialize(provider, advisory_refresh_seconds: ADVISORY_REFRESH_SECONDS, mandatory_refresh_seconds: MANDATORY_REFRESH_SECONDS, time_source: -> { Time.now.to_f }) ⇒ TokenCache
constructor
A new instance of TokenCache.
-
#invalidate ⇒ void
Clears the cached token so the next #get_token re-invokes the provider.
Constructor Details
#initialize(provider, advisory_refresh_seconds: ADVISORY_REFRESH_SECONDS, mandatory_refresh_seconds: MANDATORY_REFRESH_SECONDS, time_source: -> { Time.now.to_f }) ⇒ TokenCache
Returns a new instance of TokenCache.
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
# File 'lib/anthropic/credentials/token_cache.rb', line 37 def initialize( provider, advisory_refresh_seconds: ADVISORY_REFRESH_SECONDS, mandatory_refresh_seconds: MANDATORY_REFRESH_SECONDS, time_source: -> { Time.now.to_f } ) @provider = provider @advisory = advisory_refresh_seconds @mandatory = mandatory_refresh_seconds @time_source = time_source @lock = Monitor.new @cached = nil @refresh_in_progress = false @refresh_condition = @lock.new_cond @next_force = false @last_advisory_failure_time = 0.0 @provider_accepts_force_refresh = detect_force_refresh_support end |
Instance Method Details
#get_token ⇒ String
Returns a valid bearer token, refreshing if necessary.
This method is thread-safe. Multiple threads calling get_token simultaneously will coordinate to avoid redundant provider calls.
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
# File 'lib/anthropic/credentials/token_cache.rb', line 63 def get_token loop do # rubocop:disable Metrics/BlockLength advisory_fallback = nil should_refresh = false @lock.synchronize do if @cached if @cached.expires_at.nil? return @cached.token end remaining = @cached.expires_at - @time_source.call if remaining > @advisory return @cached.token end if remaining > @mandatory if @refresh_in_progress return @cached.token end if @time_source.call - @last_advisory_failure_time < ADVISORY_REFRESH_BACKOFF_SECONDS return @cached.token end advisory_fallback = @cached end end if @refresh_in_progress @refresh_condition.wait next end @refresh_in_progress = true should_refresh = true end next unless should_refresh begin force = @lock.synchronize { @next_force } fresh = invoke_provider(force: force) @lock.synchronize do @next_force = false @cached = fresh end return fresh.token rescue Anthropic::Errors::Error, IOError raise if advisory_fallback.nil? @lock.synchronize { @last_advisory_failure_time = @time_source.call } return advisory_fallback.token ensure @lock.synchronize do @refresh_in_progress = false @refresh_condition.broadcast end end end end |
#invalidate ⇒ void
This method returns an undefined value.
Clears the cached token so the next #get_token re-invokes the provider.
Also sets a one-shot force_refresh flag so on-disk providers skip their freshness short-circuit instead of re-serving the revoked token.
129 130 131 132 133 134 |
# File 'lib/anthropic/credentials/token_cache.rb', line 129 def invalidate @lock.synchronize do @cached = nil @next_force = true end end |