feat: Regional Access Boundary Update#1880
feat: Regional Access Boundary Update#1880vverman wants to merge 4 commits intogoogleapis:feat-tb-safrom
Conversation
…pe. Updated tests.
…rors are now retryable.
| return false; | ||
| } | ||
|
|
||
| private boolean handleStaleRegionalAccessBoundaryError( |
There was a problem hiding this comment.
IIUC, this method looks like it's doing two things:
- Checking if there we should check for stale RAB Error
- And if so, checking if there is a stable RAB error message
Can this be broken into two helper methods to simply this? e.g. perhaps somehting like shouldhandleStableRAB... and handleStaleRAB...
There was a problem hiding this comment.
Done, thanks for the suggestion.
| try { | ||
| // Check for the stale regional access boundary error message in the response body. | ||
| // Note: This consumes the response stream. | ||
| String content = response.parseAsString(); |
There was a problem hiding this comment.
I think this should be fine since this is part of the flow to initialize this request. But if it consumes the content and closes the stream, I wonder if this may break anything that tries to consume it later.
There was a problem hiding this comment.
The reason is that there is no easy way to create a copy of an HTTP response or peek into its contents without consuming the response stream.
I decided to implement it for the reason that this flow makes a few assumptions before consuming the error:
- The HTTP code is a 406 which is relatively uncommon
- The request actually carried RAB headers
This should ensure that for the majority of scenarios this downstream error won't be a problem.
| * @param token The access token to use for the refresh. | ||
| * @throws IOException If getting the universe domain fails. | ||
| */ | ||
| public final void reactiveRefreshRegionalAccessBoundary(@Nullable URI uri, AccessToken token) |
There was a problem hiding this comment.
are the uri and accesstoken parameters used here?
There was a problem hiding this comment.
Got rid of uri thanks for the catch.
| // this thread "won the race" and is responsible for starting the background task. | ||
| // All other concurrent threads will return false and exit immediately. | ||
| if (refreshFuture.compareAndSet(null, future)) { | ||
| CompletableFuture.runAsync( |
There was a problem hiding this comment.
Can you use Guava's ListeningFuture to keep with the rest of the auth-library?
Since this is called from both getReqestMetadata(...) methods (one of which takes in an executor), I think we'll also need to respect that.
I think this method should take in the executor as a param
There was a problem hiding this comment.
The way I understand: the executor in getRequestMetadataAsync is used to provide a pool of threads to execute the token refresh in the background.
My initial assumption with doing this via CompletableFuture was that the user might want the background thread to exclusively focus on token refresh and not the RAB refresh.
If we are okay with the user's background thread waiting longer for the RAB to refresh then I am okay with making this change.
| if (accessToken.getExpirationTime() != null | ||
| && accessToken.getExpirationTime().before(new Date())) { |
There was a problem hiding this comment.
Since we can inject the Clock object for tests, can we do comparisons using the clock object instead of using new Date()
e.g. something like accessToken.getExpirationTime().getInstant().isBefore(clock.getInstant())?
There was a problem hiding this comment.
used getExpirationTimeMilis in the clock object.
| } | ||
|
|
||
| /** | ||
| * Refreshes the Regional Access Boundary if it is expired or not yet fetched. |
There was a problem hiding this comment.
nit: the method name and docs isn't fully accurate as it does more than just checking if the boundary has expired.
If possible, can this be broken into two methods:
- shouldRefresh...()
- refresh...()
I think it might be easier to test as well. Let me know what you think
| metadata.put( | ||
| RegionalAccessBoundary.HEADER_KEY, Collections.singletonList(rab.getEncodedLocations())); | ||
| } | ||
| refreshRegionalAccessBoundaryIfExpired(uri, getAccessToken()); |
There was a problem hiding this comment.
IIUC this correctly, refreshRegionalAccessBoundaryIfExpired is not blocking right?
Shouldn't we have a blocking variant to ensure this gets the proper headers?
There was a problem hiding this comment.
We don't wish to block the main request's flow for RAB refresh. The request will still succeed without RAB headers they could face some added latency due to being routed to non-mandatory regions.
| currentCooldownMillis = INITIAL_COOLDOWN_MILLIS; | ||
| } | ||
|
|
||
| synchronized boolean isCooldownActive() { |
There was a problem hiding this comment.
This method is synchronized but is going to be called often due it being called from getRequestMetadata() call. Logic doesn't run that long but it is a possibility for contention.
I'll need think about this a bit more, but I wonder if we can format this so it doesn't need a lock.
There was a problem hiding this comment.
One consideration could be to have the cooldown variable as an atomic reference reducing the need for the synchronized function. I too can explore this a bit.
Contains changes for the feature Regional Access Boundary (Previously Called Trust Boundaries).
The following are salient changes:
Calls to refresh RAB are now all async and in a separate thread.
Logic for refreshing RAB now exists in its own class for cleaner maintenance.
Self-signed jwts are within scope.
Changes to how we trigger RAB refresh and deal with refresh errors.