Skip to content

Commit fe824aa

Browse files
committed
Enable auth retry on clock skew error.
If a request gets an authentication error that indicates a clock skew problem, clear the current authentication cached values and retry the operation once. Also, use an internal datetime that is one minute ahead, to help align cached signature values across the valid time skew range.
1 parent a64318f commit fe824aa

File tree

4 files changed

+59
-28
lines changed

4 files changed

+59
-28
lines changed

driver/src/main/java/oracle/nosql/driver/AuthorizationProvider.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,4 +71,10 @@ public default void setRequiredHeaders(String authString,
7171
headers.set(AUTHORIZATION, authString);
7272
}
7373
}
74+
75+
/**
76+
* Invalidate any cached authorization strings.
77+
*/
78+
public default void flushCache() {
79+
}
7480
}

driver/src/main/java/oracle/nosql/driver/http/Client.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949

5050
import oracle.nosql.driver.AuthorizationProvider;
5151
import oracle.nosql.driver.DefaultRetryHandler;
52+
import oracle.nosql.driver.InvalidAuthorizationException;
5253
import oracle.nosql.driver.NoSQLException;
5354
import oracle.nosql.driver.NoSQLHandleConfig;
5455
import oracle.nosql.driver.RateLimiter;
@@ -678,6 +679,26 @@ public Result execute(Request kvRequest) {
678679
rae);
679680
throw new NoSQLException("Unexpected exception: " +
680681
rae.getMessage(), rae);
682+
} catch (InvalidAuthorizationException iae) {
683+
/* allow a single retry on clock skew errors */
684+
if (iae.getMessage().contains("clock skew") == false ||
685+
kvRequest.getNumRetries() > 0) {
686+
/* same as NoSQLException below */
687+
kvRequest.setRateLimitDelayedMs(rateDelayedMs);
688+
statsControl.observeError(kvRequest);
689+
logFine(logger, "Client execute NoSQLException: " +
690+
iae.getMessage());
691+
throw iae;
692+
}
693+
/* flush auth cache and do one retry */
694+
authProvider.flushCache();
695+
kvRequest.addRetryException(iae.getClass());
696+
kvRequest.incrementRetries();
697+
exception = iae;
698+
logFine(logger,
699+
"Client retrying on InvalidAuthorizationException: " +
700+
iae.getMessage());
701+
continue;
681702
} catch (SecurityInfoNotReadyException sinre) {
682703
kvRequest.addRetryException(sinre.getClass());
683704
exception = sinre;

driver/src/main/java/oracle/nosql/driver/iam/SignatureProvider.java

Lines changed: 30 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@
3535
import oracle.nosql.driver.SecurityInfoNotReadyException;
3636
import oracle.nosql.driver.iam.SecurityTokenSupplier.SecurityTokenBasedProvider;
3737
import oracle.nosql.driver.ops.Request;
38-
import oracle.nosql.driver.util.LruCache;
3938

4039
import io.netty.handler.codec.http.HttpHeaders;
4140

@@ -121,11 +120,6 @@ public class SignatureProvider
121120
"(request-target) host date opc-obo-token";
122121
private static final String OBO_TOKEN_HEADER = "opc-obo-token";
123122

124-
/* Cache key name */
125-
private static final String CACHE_KEY = "signature";
126-
/* Refresh key name */
127-
private static final String REFRESH_CACHE_KEY = "refresh_signature";
128-
129123
/* Maximum lifetime of signature 240 seconds */
130124
protected static final int MAX_ENTRY_LIFE_TIME = 240;
131125

@@ -139,16 +133,21 @@ public class SignatureProvider
139133
/* Delegation token specified for signing */
140134
private String delegationToken;
141135

142-
private final LruCache<String, SignatureDetails> signatureCache;
136+
/* the currently cached signature */
137+
private SignatureDetails currentSigDetails;
138+
139+
/* new signature, in process of warmup */
140+
private SignatureDetails refreshSigDetails;
143141

144142
/* Refresh timer */
145143
private volatile Timer refresher;
146144

147-
/* Refresh time before signature expired from cache */
145+
/* Refresh time before signature expires */
148146
private long refreshAheadMs = DEFAULT_REFRESH_AHEAD;
149147

150148
/* Refresh interval, if zero, no refresh will be scheduled */
151149
private long refreshIntervalMs = 0;
150+
152151
private String serviceHost;
153152
private Region region;
154153
private Logger logger;
@@ -675,8 +674,6 @@ protected SignatureProvider(AuthenticationProfileProvider profileProvider,
675674
"Signature cannot be cached longer than " +
676675
MAX_ENTRY_LIFE_TIME + " seconds");
677676
}
678-
this.signatureCache =
679-
new LruCache<String, SignatureDetails>(2, durationSeconds * 1000);
680677

681678
this.refreshAheadMs = refreshAhead;
682679
long durationMS = durationSeconds * 1000;
@@ -739,6 +736,12 @@ public void setRequiredHeaders(String authString,
739736
}
740737
}
741738

739+
@Override
740+
public synchronized void flushCache() {
741+
currentSigDetails = null;
742+
refreshSigDetails = null;
743+
}
744+
742745
/**
743746
* Get tenant OCID if using user principal.
744747
* @return tenant OCID of user
@@ -753,7 +756,6 @@ private String getTenantOCID() {
753756

754757
@Override
755758
public void close() {
756-
signatureCache.stop(false);
757759
if (refresher != null) {
758760
refresher.cancel();
759761
refresher = null;
@@ -858,28 +860,32 @@ private void logMessage(Level level, String msg) {
858860
}
859861

860862
private SignatureDetails getSignatureDetails(Request request) {
861-
String key = request.getIsRefresh() ? REFRESH_CACHE_KEY : CACHE_KEY;
862-
SignatureDetails sigDetails = signatureCache.get(key);
863+
SignatureDetails sigDetails =
864+
(request.getIsRefresh() ? refreshSigDetails : currentSigDetails);
863865
if (sigDetails != null) {
864866
return sigDetails;
865867
}
866868

867869
if (request.getIsRefresh()) {
868-
/* try normal key before failing */
869-
sigDetails = signatureCache.get(CACHE_KEY);
870+
/* try current details before failing */
871+
sigDetails = currentSigDetails;
870872
if (sigDetails != null) {
871873
return sigDetails;
872874
}
873875
}
874876

875-
logMessage(Level.WARNING, "No signature in cache");
876877
return getSignatureDetailsInternal(false);
877878
}
878879

879880
/* visible for testing */
880881
synchronized SignatureDetails getSignatureDetailsInternal(boolean isRefresh)
881882
{
882-
String date = createFormatter().format(new Date());
883+
/*
884+
* add one minute to the current time, so that any caching is
885+
* effective over a more valid time period.
886+
*/
887+
long nowPlus = System.currentTimeMillis() + 60_000L;
888+
String date = createFormatter().format(new Date(nowPlus));
883889
String keyId = provider.getKeyId();
884890
if (provider instanceof InstancePrincipalsProvider) {
885891
privateKeyProvider.reload(provider.getPrivateKey(),
@@ -907,7 +913,7 @@ synchronized SignatureDetails getSignatureDetailsInternal(boolean isRefresh)
907913
* if this is not a refresh, use the normal key and schedule a
908914
* refresh
909915
*/
910-
signatureCache.put(CACHE_KEY, sigDetails);
916+
currentSigDetails = sigDetails;
911917
scheduleRefresh();
912918
} else {
913919
/*
@@ -916,16 +922,15 @@ synchronized SignatureDetails getSignatureDetailsInternal(boolean isRefresh)
916922
* 1. perform callbacks if needed and when done,
917923
* 2. move the object to the normal key and schedule a refresh
918924
*/
919-
signatureCache.put(REFRESH_CACHE_KEY, sigDetails);
925+
refreshSigDetails = sigDetails;
920926
}
921927
return sigDetails;
922928
}
923929

924930
private synchronized void setRefreshKey() {
925-
SignatureDetails sigDetails =
926-
signatureCache.remove(REFRESH_CACHE_KEY);
927-
if (sigDetails != null) {
928-
signatureCache.put(CACHE_KEY, sigDetails);
931+
if (refreshSigDetails != null) {
932+
currentSigDetails = refreshSigDetails;
933+
refreshSigDetails = null;
929934
}
930935
}
931936

@@ -978,10 +983,9 @@ private class RefreshTask extends TimerTask {
978983
private static final int DELAY_MS = 500;
979984

980985
private void handleRefreshCallback(long refreshMs) {
981-
SignatureDetails sigDetails = signatureCache.get(REFRESH_CACHE_KEY);
982-
if (sigDetails == null) {
986+
if (refreshSigDetails == null) {
983987
logMessage(Level.FINE,
984-
"Refresh didn't find refresh cache key");
988+
"Refresh didn't find cached refresh key");
985989
return;
986990
}
987991

driver/src/main/java/oracle/nosql/driver/ops/Request.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ public int getTimeoutInternal() {
8484
/**
8585
* @hidden
8686
* this is public to allow access from Client during refresh
87-
* @param timeoutMs timeout in milliseconds
87+
* @param timeoutMs the request timeout, in milliseconds
8888
*/
8989
public void setTimeoutInternal(int timeoutMs) {
9090
if (timeoutMs <= 0) {
@@ -455,7 +455,7 @@ public boolean getIsRefresh() {
455455
/**
456456
* @hidden
457457
* Copy internal fields to another Request object.
458-
* @param other the request to copy
458+
* @param other the Request object to copy to.
459459
*/
460460
public void copyTo(Request other) {
461461
other.setTimeoutInternal(this.timeoutMs);

0 commit comments

Comments
 (0)