-
Notifications
You must be signed in to change notification settings - Fork 166
LG-7918: Use Rails.cache for USPS IPPaaS auth tokens #7294
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
cad73a4
5cffcf4
f5497a6
c5d2a96
64b9707
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,6 @@ | ||
| module UspsInPersonProofing | ||
| class Proofer | ||
| mattr_reader :token, :token_expires_at | ||
| API_TOKEN_CACHE_KEY = :usps_ippaas_api_token | ||
|
|
||
| # Makes HTTP request to get nearby in-person proofing facilities | ||
| # Requires address, city, state and zip code. | ||
|
|
@@ -102,17 +102,28 @@ def request_enrollment_code(unique_id) | |
| end | ||
|
|
||
| # Makes a request to retrieve a new OAuth token | ||
| # and modifies self to store the token and when | ||
| # and updates the cached token value | ||
| # it expires (15 minutes). | ||
| # @return [String] the token | ||
| # @return [String] Auth token | ||
| def retrieve_token! | ||
| body = request_token | ||
| @@token_expires_at = Time.zone.now + body['expires_in'] | ||
| @@token = "#{body['token_type']} #{body['access_token']}" | ||
| token, expires_in, token_type = request_token.fetch_values( | ||
| 'access_token', | ||
| 'expires_in', | ||
| 'token_type', | ||
| ) | ||
| authz = "#{token_type} #{token}" | ||
| Rails.cache.write( | ||
| API_TOKEN_CACHE_KEY, authz, | ||
| expires_at: Time.zone.now + expires_in | ||
| ) | ||
| authz | ||
| end | ||
|
|
||
| def token_valid? | ||
| token.present? && token_expires_at.present? && token_expires_at.future? | ||
| # Checks the cache for an unexpired token and returns it. If the cache has expired, retrieves | ||
| # a new token and returns it | ||
| # @return [String] Auth token | ||
| def token | ||
| Rails.cache.read(API_TOKEN_CACHE_KEY) || retrieve_token! | ||
| end | ||
|
Comment on lines
+125
to
127
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does the Rails cache include an in-memory cache or does it go straight to Redis? If it's the latter, then I would suggest keeping the class/module field. e.g. in the use case of a large volume in the the USPS proofing job, this could mean hundreds of unnecessary trips to Redis.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You would still need to be mindful of the TTL though, as well as the possibility of another server writing to the Redis cache.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it'd be interesting to see how load testing goes with all of the requests to redis. I agree it could end up being an issue under very high load and doing two layers of caching (eg class variable and Rails.cache) may make a lot of sense here
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My understanding is that it was a primary goal of this work to have it be stored in a Redis cache, so that the same USPS token would be reused across IdP instances. The Redis cost is definitely an interesting factor I hadn't given much thought to previously, but then we'd still want to weigh it against the additional cost of roundtrips to USPS for IdP-instance-specific tokens.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The intention would be to use the same token across instances, but that token could be cached in-memory - i.e. in a manner that respects the original TTL of the token.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, I think I see what you're saying now -- still use Redis, but also having a local copy to avoid repeatedly calling to Redis while the token is assumed to be valid? (what @sheldon-b was referring to in "two layers of caching") Sounds like it could be a good idea, but also likely to be tricky to implement 😅
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's what I'm talking about. Pseudocode would look like: The TTL should be identical between the local cache and Redis, but both should be very slightly before the real expiry of the token. |
||
|
|
||
| private | ||
|
|
@@ -144,8 +155,6 @@ def faraday | |
| # | ||
| # Returns the same value returned by that block of code. | ||
| def dynamic_headers | ||
| retrieve_token! unless token_valid? | ||
|
|
||
| { | ||
| 'Authorization' => token, | ||
| 'RequestID' => request_id, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is there an average/expected expiration for these tokens? Wondering if we could pick a fixed duration and use the block
Rails.cache.fetch(..., expires_in) { }syntax and simplify a bunch of this codeThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Expected is 15 minutes but it feels weird to hardcode it when we get the actual value in the API response. Agreed it'd be more concise. Mixed feelings about it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would make our version of the token expire slightly before the value provided in the response, maybe by a few seconds.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i.e. to reduce the probability that the token will expire between fetching it from cache & attempting to use it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I had that idea but Andrew convinced me that it's a half-measure and we should just fix it properly by adding in a retry if the token has expired. I created this ticket for it. Original PR thread
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not both?
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because it removes the urgency of fixing it better. I think we'd want to do both, just not the one right now