2828import dev .failsafe .RateLimiter ;
2929import java .io .IOException ;
3030import java .security .KeyPair ;
31- import java .time .Duration ;
3231import java .time .Instant ;
3332import java .time .temporal .ChronoUnit ;
3433import java .util .List ;
@@ -57,8 +56,7 @@ class CloudSqlInstance {
5756 private final ListenableFuture <KeyPair > keyPair ;
5857 private final Object instanceDataGuard = new Object ();
5958 // Limit forced refreshes to 1 every minute.
60- private final RateLimiter <Object > forcedRenewRateLimiter =
61- RateLimiter .burstyBuilder (2 , Duration .ofSeconds (30 )).build ();
59+ private final RateLimiter <Object > forcedRenewRateLimiter ;
6260
6361 private final RefreshCalculator refreshCalculator = new RefreshCalculator ();
6462
@@ -68,6 +66,9 @@ class CloudSqlInstance {
6866 @ GuardedBy ("instanceDataGuard" )
6967 private ListenableFuture <InstanceData > nextInstanceData ;
7068
69+ @ GuardedBy ("instanceDataGuard" )
70+ private boolean forceRefreshRunning ;
71+
7172 /**
7273 * Initializes a new Cloud SQL instance based on the given connection name.
7374 *
@@ -82,12 +83,14 @@ class CloudSqlInstance {
8283 AuthType authType ,
8384 CredentialFactory tokenSourceFactory ,
8485 ListeningScheduledExecutorService executor ,
85- ListenableFuture <KeyPair > keyPair ) {
86+ ListenableFuture <KeyPair > keyPair ,
87+ RateLimiter <Object > forcedRenewRateLimiter ) {
8688 this .instanceName = new CloudSqlInstanceName (connectionName );
8789 this .instanceDataSupplier = instanceDataSupplier ;
8890 this .authType = authType ;
8991 this .executor = executor ;
9092 this .keyPair = keyPair ;
93+ this .forcedRenewRateLimiter = forcedRenewRateLimiter ;
9194
9295 if (authType == AuthType .IAM ) {
9396 HttpRequestInitializer source = tokenSourceFactory .create ();
@@ -159,20 +162,21 @@ String getPreferredIp(List<String> preferredTypes) {
159162 */
160163 void forceRefresh () {
161164 synchronized (instanceDataGuard ) {
162- nextInstanceData .cancel (false );
163- if (nextInstanceData .isCancelled ()) {
164- logger .fine (
165- "Force Refresh: the next refresh operation was cancelled."
166- + " Scheduling new refresh operation immediately." );
167- currentInstanceData = executor .submit (this ::performRefresh );
168- nextInstanceData = currentInstanceData ;
169- } else {
170- logger .fine (
171- "Force Refresh: the next refresh operation is already running."
172- + " Marking it as the current operation." );
173- // Otherwise it's already running, so just move next to current.
174- currentInstanceData = nextInstanceData ;
165+ // Don't force a refresh until the current forceRefresh operation
166+ // has produced a successful refresh.
167+ if (forceRefreshRunning ) {
168+ return ;
175169 }
170+
171+ forceRefreshRunning = true ;
172+ nextInstanceData .cancel (false );
173+ logger .fine (
174+ String .format (
175+ "[%s] Force Refresh: the next refresh operation was cancelled."
176+ + " Scheduling new refresh operation immediately." ,
177+ instanceName ));
178+ currentInstanceData = executor .submit (this ::performRefresh );
179+ nextInstanceData = currentInstanceData ;
176180 }
177181 }
178182
@@ -182,10 +186,14 @@ void forceRefresh() {
182186 * would expire.
183187 */
184188 private InstanceData performRefresh () throws InterruptedException , ExecutionException {
185- logger .fine ("Refresh Operation: Acquiring rate limiter permit." );
189+ logger .fine (
190+ String .format ("[%s] Refresh Operation: Acquiring rate limiter permit." , instanceName ));
186191 // To avoid unreasonable SQL Admin API usage, use a rate limit to throttle our usage.
187192 forcedRenewRateLimiter .acquirePermit ();
188- logger .fine ("Refresh Operation: Acquired rate limiter permit. Starting refresh..." );
193+ logger .fine (
194+ String .format (
195+ "[%s] Refresh Operation: Acquired rate limiter permit. Starting refresh..." ,
196+ instanceName ));
189197
190198 try {
191199 InstanceData data =
@@ -194,15 +202,16 @@ private InstanceData performRefresh() throws InterruptedException, ExecutionExce
194202
195203 logger .fine (
196204 String .format (
197- "Refresh Operation: Completed refresh with new certificate expiration at %s." ,
198- data .getExpiration ().toInstant ().toString ()));
205+ "[%s] Refresh Operation: Completed refresh with new certificate expiration at %s." ,
206+ instanceName , data .getExpiration ().toInstant ().toString ()));
199207 long secondsToRefresh =
200208 refreshCalculator .calculateSecondsUntilNextRefresh (
201209 Instant .now (), data .getExpiration ().toInstant ());
202210
203211 logger .fine (
204212 String .format (
205- "Refresh Operation: Next operation scheduled at %s." ,
213+ "[%s] Refresh Operation: Next operation scheduled at %s." ,
214+ instanceName ,
206215 Instant .now ()
207216 .plus (secondsToRefresh , ChronoUnit .SECONDS )
208217 .truncatedTo (ChronoUnit .SECONDS )
@@ -212,12 +221,17 @@ private InstanceData performRefresh() throws InterruptedException, ExecutionExce
212221 currentInstanceData = Futures .immediateFuture (data );
213222 nextInstanceData =
214223 executor .schedule (this ::performRefresh , secondsToRefresh , TimeUnit .SECONDS );
224+ // Refresh completed successfully, reset forceRefreshRunning.
225+ forceRefreshRunning = false ;
215226 }
216-
217227 return data ;
218228 } catch (ExecutionException | InterruptedException e ) {
219229 logger .log (
220- Level .FINE , "Refresh Operation: Failed! Starting next refresh operation immediately." , e );
230+ Level .FINE ,
231+ String .format (
232+ "[%s] Refresh Operation: Failed! Starting next refresh operation immediately." ,
233+ instanceName ),
234+ e );
221235 synchronized (instanceDataGuard ) {
222236 nextInstanceData = executor .submit (this ::performRefresh );
223237 }
@@ -228,4 +242,20 @@ private InstanceData performRefresh() throws InterruptedException, ExecutionExce
228242 SslData getSslData () {
229243 return getInstanceData ().getSslData ();
230244 }
245+
246+ ListenableFuture <InstanceData > getNext () {
247+ synchronized (instanceDataGuard ) {
248+ return this .nextInstanceData ;
249+ }
250+ }
251+
252+ ListenableFuture <InstanceData > getCurrent () {
253+ synchronized (instanceDataGuard ) {
254+ return this .currentInstanceData ;
255+ }
256+ }
257+
258+ public CloudSqlInstanceName getInstanceName () {
259+ return instanceName ;
260+ }
231261}
0 commit comments