class StatelyDB::Common::Auth::AuthTokenProvider::Actor

def refresh_token_impl

Returns:
  • (String) - The new access token
def refresh_token_impl
  Sync do
    token_result = @token_fetcher.fetch
    new_expires_in_secs = token_result.expires_in_secs
    new_expires_at_unix_secs = Time.now.to_i + new_expires_in_secs
    # only update the token state if the new expiry is later than the current one
    if @token_state.nil? || new_expires_at_unix_secs > @token_state.expires_at_unix_secs
      @token_state = TokenState.new(token: token_result.token, expires_at_unix_secs: new_expires_at_unix_secs)
    else
      # otherwise use the existing expiry time for scheduling the refresh
      new_expires_in_secs = @token_state.expires_at_unix_secs - Time.now.to_i
    end
    # Schedule a refresh of the token ahead of the expiry time
    # Calculate a random multiplier between 0.9 and 0.95 to to apply to the expiry
    # so that we refresh in the background ahead of expiration, but avoid
    # multiple processes hammering the service at the same time.
    jitter = (Random.rand * 0.05) + 0.9
    delay_secs = new_expires_in_secs * jitter
    # do this on the fiber scheduler (the root scheduler) to avoid infinite recursion
    @scheduled ||= Fiber.scheduler.async do
      # Kernel.sleep is non-blocking if Ruby 3.1+ and Async 2+
      # https://github.com/socketry/async/issues/305#issuecomment-1945188193
      sleep(delay_secs)
      refresh_token
      @scheduled = nil
    end
    @token_state.token
  end
end