Skip to content
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

Add AsyncLoader to load and update value periodically #5590

Open
wants to merge 14 commits into
base: main
Choose a base branch
from

Conversation

injae-kim
Copy link
Contributor

Fixes #5506.

Motivation:

AsyncLoader can be useful in the following situations.

  • When it is necessary to periodically read and update information from a file such as resolv.conf .
  • When data is not valid after a certain period of time, such as an OAuth 2.0 access token.

We already have an implementation for that on AbstractOAuth2AuthorizationGrant.java.
However, I hope to generalize it and add new features to use it in various cases.

Modifications:

  • Add AsyncLoader to load and update value periodically

Result:

@ikhoon ikhoon added new feature sprint Issues for OSS Sprint participants labels Apr 12, 2024
Comment on lines +109 to +113
if (token == null && fallbackTokenProvider != null) {
CompletableFuture<? extends GrantedOAuth2AccessToken> fallbackTokenFuture = null;
try {
fallbackTokenFuture = requireNonNull(
fallbackTokenProvider.get(), "fallbackTokenProvider.get() returned null");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Side note: It is not related to this PR but fallbackTokenProvider is a name that we should only use when a token acquisition fails since it is a fallback.

I think it would be better to remove fallback from the API method. tokenProvider seems clearer.

Copy link

codecov bot commented Apr 24, 2024

Codecov Report

Attention: Patch coverage is 84.31373% with 24 lines in your changes are missing coverage. Please review.

Project coverage is 74.07%. Comparing base (b8eb810) to head (cca5962).
Report is 208 commits behind head on main.

Current head cca5962 differs from pull request most recent head af5fea9

Please upload reports for the commit af5fea9 to get more accurate results.

Files Patch % Lines
...t/auth/oauth2/DefaultOAuth2AuthorizationGrant.java 42.30% 13 Missing and 2 partials ⚠️
...necorp/armeria/common/util/DefaultAsyncLoader.java 94.33% 2 Missing and 4 partials ⚠️
...necorp/armeria/common/util/AsyncLoaderBuilder.java 85.00% 3 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##               main    #5590      +/-   ##
============================================
+ Coverage     73.95%   74.07%   +0.11%     
- Complexity    20115    21252    +1137     
============================================
  Files          1730     1848     +118     
  Lines         74161    78567    +4406     
  Branches       9465    10024     +559     
============================================
+ Hits          54847    58199    +3352     
- Misses        14837    15669     +832     
- Partials       4477     4699     +222     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Contributor

@ikhoon ikhoon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall looks good.

Comment on lines +205 to +215
private static final RefreshingFuture<?> COMPLETED;

static {
COMPLETED = new RefreshingFuture<>(null);
COMPLETED.complete(null);
}

@SuppressWarnings("unchecked")
static <T> RefreshingFuture<T> completedFuture() {
return (RefreshingFuture<T>) COMPLETED;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

Inspired by UnmodifiableFuture.completedFuture implementation!


final List<AggregatedHttpResponse> responses3 =
getConcurrently(client, "/resource-read-write-update/", count).join();
validateResponses(responses3, HttpStatus.FORBIDDEN);
verify(grant, times(1)).obtainAccessToken(any());
verify(grant, times(0)).refreshAccessToken(any(), any());
assertThat(newTokenCounter.get()).isOne();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

    DefaultOAuth2AuthorizationGrant(..) {
        tokenLoader = tokenLoader();
    }

FAILED: wanted but not invoked obtainAccessToken, only invoked getAccessToken();

We make tokenLoader in constructor, then mockito can't verify obtainAccessToken, refreshAccessToken invocation well.

So I replace it with newTokenCounter :)

import com.linecorp.armeria.common.annotation.Nullable;

/**
* An {@link AsyncLoader} to atomically load, cache and update value.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it doesn't make much sense to refer to itself when defining something, especially in the first sentence of the definition. Can we rephrase this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ updated!

Comment on lines 86 to 88
if (!needsRefresh(cacheEntry) || loadFuture.refreshing) {
return loadFuture;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe log here about why and why not refreshing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ updated! add logger.debug(..)

}
}

private boolean isValid(@Nullable CacheEntry<T> cacheEntry) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add more logging in this method to make it easy to figure why isValid() returned false?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ updated! add logger.debug(..)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
new feature sprint Issues for OSS Sprint participants
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Provide a way to load a value periodically or when it expires
3 participants