You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pulsar.apache.org by "lhotari (via GitHub)" <gi...@apache.org> on 2026/03/19 19:19:17 UTC
[PR] [OAuth2] Enable configurable preemptive token refresh in Java Client [pulsar]
lhotari opened a new pull request, #25363:
URL: https://github.com/apache/pulsar/pull/25363
Continuation of #13951 (originally authored by @michaeljmarshall). Rebased onto master, merge conflicts resolved, and all reviewer feedback addressed.
### Motivation
In some client use cases it is helpful to start refreshing the OAuth2 token in the background before it expires. This reduces latency spikes caused by blocking token-fetch calls and improves resilience when the Identity Provider is temporarily unavailable — the client can tolerate an outage lasting up to `(1 - earlyTokenRefreshPercent) * expires_in` seconds.
### Modifications
**Core feature (`AuthenticationOAuth2`)**
* Add optional preemptive background token refresh, scheduled at `earlyTokenRefreshPercent * expires_in` milliseconds after a successful fetch.
* Default value is `1` (100 %), which **disables** the feature and preserves existing behaviour.
* On refresh failure the scheduler retries with exponential backoff; a final synchronous attempt is made on the next `getAuthData()` call if the token has since expired.
* Replace the per-instance `ScheduledThreadPoolExecutor` (raised as a reviewer concern) with a **shared static `INTERNAL_SCHEDULER`** that uses `io.netty.util.concurrent.DefaultThreadFactory` (daemon threads, named `oauth2-token-refresher`) and allows the core thread to time out after 10 s of idle time — so no OS thread is held when no instances are actively refreshing.
* Callers may supply their own `ScheduledExecutorService`; this class never shuts it down.
* `close()` cancels the pending refresh task via the scheduler thread to avoid a race where a running refresh re-schedules itself; neither the shared nor the caller-supplied scheduler is shut down.
**Configuration via JSON string (`configure()`)**
* Add `earlyTokenRefreshPercent` parameter, enabling the feature for broker-side / reflection-created instances.
* Accepts a **decimal** (`"0.8"` → 0.8) or an **integer percentage** (`"80"` → 0.8, `"100"` → 1.0 = disabled).
**`AuthenticationOAuth2Builder`** (new)
* Fluent builder exposing `setEarlyTokenRefreshPercent(double)` and `setEarlyTokenRefreshExecutor(ScheduledExecutorService)` for programmatic construction with full control.
**`ClientCredentialsConfiguration`** (new)
* Immutable value-object for client-credentials parameters, used by the builder.
**`AuthenticationFactoryOAuth2`** (updated)
* `clientCredentials()` overloads delegate to `ClientCredentialsBuilder`; uses the default (early-refresh disabled) `AuthenticationOAuth2` constructor.
**`AuthenticationOAuth2StandardAuthzServer`** (updated)
* Overrides `parseAuthParameters()` to inject the RFC 8414 well-known metadata path, so the `configure()` refactor is transparent to subclasses.
### Verifying this change
New and updated unit tests in `AuthenticationOAuth2Test`:
* Token caching and expiry-driven refresh (no early refresh).
* Early refresh schedules background calls before expiry.
* External scheduler is used when provided, and is **not** shut down on `close()`.
* `configure()` via default constructor: decimal and integer `earlyTokenRefreshPercent` values, disabled (`100`), zero and non-numeric values throw `IllegalArgumentException`.
* `parseEarlyRefreshPercent` unit tests for all branches.
* Background scheduler actually triggers `authenticate()` when enabled via `configure()`.
### Does this pull request potentially affect one of the following parts?
- **Default behaviour is unchanged** — `earlyTokenRefreshPercent` defaults to `1` (disabled).
- Adds new configuration surface for OAuth2 users wanting proactive token management.
- Thread-resource concern (one thread per client) addressed by shared daemon executor that releases the thread when idle.
### Documentation
- [x] `doc` — configuration parameter `earlyTokenRefreshPercent` documented in javadoc and builder javadoc.
Closes #13951
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@pulsar.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [improve][client] Enable configurable preemptive OAuth2 token refresh [pulsar]
Posted by "nodece (via GitHub)" <gi...@apache.org>.
nodece commented on code in PR #25363:
URL: https://github.com/apache/pulsar/pull/25363#discussion_r2963798587
##########
pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/oauth2/AuthenticationOAuth2.java:
##########
@@ -96,21 +228,111 @@ public void start() throws PulsarClientException {
flow.initialize();
}
+ /**
+ * The first time that this method is called, it retrieves a token. All subsequent
+ * calls should get a cached value. However, if there is an issue with the Identity
+ * Provider, there is a chance that the background thread responsible for keeping
+ * the refresh token hot will
+ * @return The authentication data identifying this client that will be sent to the broker
+ * @throws PulsarClientException
+ */
@Override
public synchronized AuthenticationDataProvider getAuthData() throws PulsarClientException {
+ if (isClosed) {
+ throw new PulsarClientException.AlreadyClosedException("Authentication already closed.");
+ }
if (this.cachedToken == null || this.cachedToken.isExpired()) {
- TokenResult tr = this.flow.authenticate();
- this.cachedToken = new CachedToken(tr);
+ this.authenticate();
}
return this.cachedToken.getAuthData();
}
+ /**
+ * Retrieve the token (synchronously), and then schedule refresh runnable.
+ */
+ private void authenticate() throws PulsarClientException {
+ if (log.isDebugEnabled()) {
+ log.debug("Attempting to retrieve OAuth2 token now.");
+ }
+ TokenResult tr = this.flow.authenticate();
+ this.cachedToken = new CachedToken(tr);
+ handleSuccessfulTokenRefresh();
+ }
+
+ /**
+ * When we successfully get a token, we need to schedule the next attempt to refresh it.
+ * This is done completely based on the "expires_in" value returned by the identity provider.
+ * The code is run on the single scheduler thread in order to ensure that the backoff and the nextRefreshAttempt are
+ * updated safely.
+ */
+ private void handleSuccessfulTokenRefresh() {
+ if (scheduler != null) {
+ scheduler.execute(() -> {
+ if (earlyTokenRefreshPercent < 1) {
+ backoff = buildBackoff(cachedToken.latest.getExpiresIn());
+ long expiresInMillis = TimeUnit.SECONDS.toMillis(cachedToken.latest.getExpiresIn());
+ scheduleRefresh((long) (expiresInMillis * earlyTokenRefreshPercent));
+ }
+ });
+ }
Review Comment:
```suggestion
if (scheduler != null && earlyTokenRefreshPercent < 1) {
scheduler.execute(() -> {
backoff = buildBackoff(cachedToken.latest.getExpiresIn());
long expiresInMillis = TimeUnit.SECONDS.toMillis(cachedToken.latest.getExpiresIn());
scheduleRefresh((long) (expiresInMillis * earlyTokenRefreshPercent));
});
}
```
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@pulsar.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [improve][client] Enable configurable preemptive OAuth2 token refresh [pulsar]
Posted by "lhotari (via GitHub)" <gi...@apache.org>.
lhotari merged PR #25363:
URL: https://github.com/apache/pulsar/pull/25363
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@pulsar.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] [improve][client] Enable configurable preemptive OAuth2 token refresh [pulsar]
Posted by "codecov-commenter (via GitHub)" <gi...@apache.org>.
codecov-commenter commented on PR #25363:
URL: https://github.com/apache/pulsar/pull/25363#issuecomment-4093091631
## [Codecov](https://app.codecov.io/gh/apache/pulsar/pull/25363?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) Report
:x: Patch coverage is `62.03704%` with `41 lines` in your changes missing coverage. Please review.
:white_check_mark: Project coverage is 72.74%. Comparing base ([`74f4e5a`](https://app.codecov.io/gh/apache/pulsar/commit/74f4e5a123be1224ea5e9c81be117c057e400850?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache)) to head ([`7174a1f`](https://app.codecov.io/gh/apache/pulsar/commit/7174a1fbdfe0661b362d426c5768cf1c77cfa7db?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache)).
:warning: Report is 1 commits behind head on master.
| [Files with missing lines](https://app.codecov.io/gh/apache/pulsar/pull/25363?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | Patch % | Lines |
|---|---|---|
| [.../impl/auth/oauth2/AuthenticationOAuth2Builder.java](https://app.codecov.io/gh/apache/pulsar/pull/25363?src=pr&el=tree&filepath=pulsar-client%2Fsrc%2Fmain%2Fjava%2Forg%2Fapache%2Fpulsar%2Fclient%2Fimpl%2Fauth%2Foauth2%2FAuthenticationOAuth2Builder.java&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache#diff-cHVsc2FyLWNsaWVudC9zcmMvbWFpbi9qYXZhL29yZy9hcGFjaGUvcHVsc2FyL2NsaWVudC9pbXBsL2F1dGgvb2F1dGgyL0F1dGhlbnRpY2F0aW9uT0F1dGgyQnVpbGRlci5qYXZh) | 0.00% | [23 Missing :warning: ](https://app.codecov.io/gh/apache/pulsar/pull/25363?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) |
| [.../client/impl/auth/oauth2/AuthenticationOAuth2.java](https://app.codecov.io/gh/apache/pulsar/pull/25363?src=pr&el=tree&filepath=pulsar-client%2Fsrc%2Fmain%2Fjava%2Forg%2Fapache%2Fpulsar%2Fclient%2Fimpl%2Fauth%2Foauth2%2FAuthenticationOAuth2.java&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache#diff-cHVsc2FyLWNsaWVudC9zcmMvbWFpbi9qYXZhL29yZy9hcGFjaGUvcHVsc2FyL2NsaWVudC9pbXBsL2F1dGgvb2F1dGgyL0F1dGhlbnRpY2F0aW9uT0F1dGgyLmphdmE=) | 78.04% | [11 Missing and 7 partials :warning: ](https://app.codecov.io/gh/apache/pulsar/pull/25363?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) |
<details><summary>Additional details and impacted files</summary>
[](https://app.codecov.io/gh/apache/pulsar/pull/25363?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache)
```diff
@@ Coverage Diff @@
## master #25363 +/- ##
=========================================
Coverage 72.73% 72.74%
- Complexity 34264 34279 +15
=========================================
Files 1954 1955 +1
Lines 154792 154872 +80
Branches 17731 17739 +8
=========================================
+ Hits 112586 112654 +68
- Misses 33170 33177 +7
- Partials 9036 9041 +5
```
| [Flag](https://app.codecov.io/gh/apache/pulsar/pull/25363/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | Coverage Δ | |
|---|---|---|
| [inttests](https://app.codecov.io/gh/apache/pulsar/pull/25363/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | `25.79% <0.00%> (-0.12%)` | :arrow_down: |
| [systests](https://app.codecov.io/gh/apache/pulsar/pull/25363/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | `22.52% <0.00%> (+0.04%)` | :arrow_up: |
| [unittests](https://app.codecov.io/gh/apache/pulsar/pull/25363/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | `73.71% <62.03%> (-0.02%)` | :arrow_down: |
Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache#carryforward-flags-in-the-pull-request-comment) to find out more.
| [Files with missing lines](https://app.codecov.io/gh/apache/pulsar/pull/25363?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | Coverage Δ | |
|---|---|---|
| [.../impl/auth/oauth2/AuthenticationFactoryOAuth2.java](https://app.codecov.io/gh/apache/pulsar/pull/25363?src=pr&el=tree&filepath=pulsar-client%2Fsrc%2Fmain%2Fjava%2Forg%2Fapache%2Fpulsar%2Fclient%2Fimpl%2Fauth%2Foauth2%2FAuthenticationFactoryOAuth2.java&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache#diff-cHVsc2FyLWNsaWVudC9zcmMvbWFpbi9qYXZhL29yZy9hcGFjaGUvcHVsc2FyL2NsaWVudC9pbXBsL2F1dGgvb2F1dGgyL0F1dGhlbnRpY2F0aW9uRmFjdG9yeU9BdXRoMi5qYXZh) | `94.11% <100.00%> (ø)` | |
| [...auth2/AuthenticationOAuth2StandardAuthzServer.java](https://app.codecov.io/gh/apache/pulsar/pull/25363?src=pr&el=tree&filepath=pulsar-client%2Fsrc%2Fmain%2Fjava%2Forg%2Fapache%2Fpulsar%2Fclient%2Fimpl%2Fauth%2Foauth2%2FAuthenticationOAuth2StandardAuthzServer.java&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache#diff-cHVsc2FyLWNsaWVudC9zcmMvbWFpbi9qYXZhL29yZy9hcGFjaGUvcHVsc2FyL2NsaWVudC9pbXBsL2F1dGgvb2F1dGgyL0F1dGhlbnRpY2F0aW9uT0F1dGgyU3RhbmRhcmRBdXRoelNlcnZlci5qYXZh) | `71.42% <100.00%> (+18.48%)` | :arrow_up: |
| [...client/impl/auth/oauth2/ClientCredentialsFlow.java](https://app.codecov.io/gh/apache/pulsar/pull/25363?src=pr&el=tree&filepath=pulsar-client%2Fsrc%2Fmain%2Fjava%2Forg%2Fapache%2Fpulsar%2Fclient%2Fimpl%2Fauth%2Foauth2%2FClientCredentialsFlow.java&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache#diff-cHVsc2FyLWNsaWVudC9zcmMvbWFpbi9qYXZhL29yZy9hcGFjaGUvcHVsc2FyL2NsaWVudC9pbXBsL2F1dGgvb2F1dGgyL0NsaWVudENyZWRlbnRpYWxzRmxvdy5qYXZh) | `80.00% <ø> (ø)` | |
| [.../client/impl/auth/oauth2/AuthenticationOAuth2.java](https://app.codecov.io/gh/apache/pulsar/pull/25363?src=pr&el=tree&filepath=pulsar-client%2Fsrc%2Fmain%2Fjava%2Forg%2Fapache%2Fpulsar%2Fclient%2Fimpl%2Fauth%2Foauth2%2FAuthenticationOAuth2.java&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache#diff-cHVsc2FyLWNsaWVudC9zcmMvbWFpbi9qYXZhL29yZy9hcGFjaGUvcHVsc2FyL2NsaWVudC9pbXBsL2F1dGgvb2F1dGgyL0F1dGhlbnRpY2F0aW9uT0F1dGgyLmphdmE=) | `80.37% <78.04%> (-7.13%)` | :arrow_down: |
| [.../impl/auth/oauth2/AuthenticationOAuth2Builder.java](https://app.codecov.io/gh/apache/pulsar/pull/25363?src=pr&el=tree&filepath=pulsar-client%2Fsrc%2Fmain%2Fjava%2Forg%2Fapache%2Fpulsar%2Fclient%2Fimpl%2Fauth%2Foauth2%2FAuthenticationOAuth2Builder.java&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache#diff-cHVsc2FyLWNsaWVudC9zcmMvbWFpbi9qYXZhL29yZy9hcGFjaGUvcHVsc2FyL2NsaWVudC9pbXBsL2F1dGgvb2F1dGgyL0F1dGhlbnRpY2F0aW9uT0F1dGgyQnVpbGRlci5qYXZh) | `0.00% <0.00%> (ø)` | |
... and [76 files with indirect coverage changes](https://app.codecov.io/gh/apache/pulsar/pull/25363/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache)
</details>
<details><summary> :rocket: New features to boost your workflow: </summary>
- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
- :package: [JS Bundle Analysis](https://docs.codecov.com/docs/javascript-bundle-analysis): Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
</details>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@pulsar.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org