diff --git a/core/src/main/java/com/google/cloud/sql/core/CloudSqlInstance.java b/core/src/main/java/com/google/cloud/sql/core/CloudSqlInstance.java index 0a1b77225..fd6313711 100644 --- a/core/src/main/java/com/google/cloud/sql/core/CloudSqlInstance.java +++ b/core/src/main/java/com/google/cloud/sql/core/CloudSqlInstance.java @@ -301,9 +301,6 @@ String getPreferredIp(List preferredTypes) { * @return {@code true} if successfully scheduled, or {@code false} otherwise. */ boolean forceRefresh() { - if (!forcedRenewRateLimiter.tryAcquire()) { - return false; - } synchronized (instanceDataGuard) { // If a scheduled refresh hasn't started, perform one immediately if (nextInstanceData.cancel(false)) { @@ -323,6 +320,8 @@ boolean forceRefresh() { * would expire. */ private ListenableFuture performRefresh() { + // To avoid unreasonable SQL Admin API usage, use a rate limit to throttle our usage. + forcedRenewRateLimiter.acquire(1); // Use the Cloud SQL Admin API to return the Metadata and Certificate ListenableFuture metadataFuture = executor.submit(this::fetchMetadata); ListenableFuture ephemeralCertificateFuture = @@ -364,18 +363,40 @@ private ListenableFuture performRefresh() { executor, metadataFuture, sslContextFuture); - refreshFuture.addListener( - () -> { - synchronized (instanceDataGuard) { - // update currentInstanceData with the most recent results - currentInstanceData = refreshFuture; - // schedule a replacement before the SSLContext expires; - nextInstanceData = executor - .schedule(this::performRefresh, secondsUntilRefresh(), - TimeUnit.SECONDS); + Futures.addCallback(refreshFuture, + new FutureCallback() { + public void onSuccess(InstanceData instanceData) { + synchronized (instanceDataGuard) { + // update currentInstanceData with the most recent results + currentInstanceData = refreshFuture; + // schedule a replacement before the SSLContext expires; + nextInstanceData = executor + .schedule(() -> performRefresh(), + secondsUntilRefresh(), + TimeUnit.SECONDS); + } } - }, - executor); + + public void onFailure(Throwable t) { + logger.log(Level.WARNING, + "An error occurred while performing refresh. Retrying immediately.", t); + synchronized (instanceDataGuard) { + InstanceData instanceData = null; + try { + instanceData = getInstanceData(); + } catch (Exception e) { + // this means the result was invalid + } + if (instanceData == null || instanceData.getExpiration().toInstant() + .isBefore(Instant.now())) { + // replace current if it is expired or invalid + currentInstanceData = refreshFuture; + } + nextInstanceData = Futures.immediateFuture(performRefresh()); + } + } + }, executor); + return refreshFuture; }