Skip to content

Commit 5ad6103

Browse files
authored
feat: Use new certificate refresh logic
This will make the certificate refresh timing in the java jdbc connector consistent with the other connectors.
1 parent 7528d9e commit 5ad6103

File tree

4 files changed

+123
-47
lines changed

4 files changed

+123
-47
lines changed

core/src/main/java/com/google/cloud/sql/core/CloudSqlInstance.java

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,6 @@ class CloudSqlInstance {
5555

5656
private static final Logger logger = Logger.getLogger(CloudSqlInstance.class.getName());
5757
private static final String SQL_LOGIN_SCOPE = "https://www.googleapis.com/auth/sqlservice.login";
58-
// defaultRefreshBuffer is the minimum amount of time for which a
59-
// certificate must be valid to ensure the next refresh attempt has adequate
60-
// time to complete.
61-
private static final Duration DEFAULT_REFRESH_BUFFER = Duration.ofMinutes(4);
6258

6359
private final ListeningScheduledExecutorService executor;
6460
private final SqlAdminApiFetcher apiFetcher;
@@ -71,6 +67,8 @@ class CloudSqlInstance {
7167
private final RateLimiter<Object> forcedRenewRateLimiter =
7268
RateLimiter.burstyBuilder(2, Duration.ofSeconds(30)).build();
7369

70+
private final RefreshCalculator refreshCalculator = new RefreshCalculator();
71+
7472
@GuardedBy("instanceDataGuard")
7573
private ListenableFuture<InstanceData> currentInstanceData;
7674

@@ -112,22 +110,6 @@ class CloudSqlInstance {
112110
}
113111
}
114112

115-
static long secondsUntilRefresh(Date expiration) {
116-
Duration timeUntilExp = Duration.between(Instant.now(), expiration.toInstant());
117-
118-
if (timeUntilExp.compareTo(Duration.ofHours(1)) < 0) {
119-
if (timeUntilExp.compareTo(DEFAULT_REFRESH_BUFFER) < 0) {
120-
// If the time until the certificate expires is less the refresh buffer, schedule the
121-
// refresh immediately
122-
return 0;
123-
}
124-
// Otherwise schedule a refresh in (timeUntilExp - buffer) seconds
125-
return timeUntilExp.minus(DEFAULT_REFRESH_BUFFER).getSeconds();
126-
}
127-
// If the time until the certificate expires is longer than an hour, return timeUntilExp//2
128-
return timeUntilExp.dividedBy(2).getSeconds();
129-
}
130-
131113
static GoogleCredentials getDownscopedCredentials(OAuth2Credentials credentials) {
132114
GoogleCredentials downscoped;
133115
try {
@@ -270,7 +252,8 @@ public void onSuccess(InstanceData instanceData) {
270252
nextInstanceData =
271253
executor.schedule(
272254
() -> performRefresh(),
273-
secondsUntilRefresh(getInstanceData().getExpiration()),
255+
refreshCalculator.calculateSecondsUntilNextRefresh(
256+
Instant.now(), getInstanceData().getExpiration().toInstant()),
274257
TimeUnit.SECONDS);
275258
}
276259
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright 2023 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.cloud.sql.core;
18+
19+
import java.time.Duration;
20+
import java.time.Instant;
21+
22+
/**
23+
* RefreshCalculator determines the number of seconds until the next refresh operation using the
24+
* same algorithm used by the other Connectors.
25+
*/
26+
class RefreshCalculator {
27+
28+
// defaultRefreshBuffer is the minimum amount of time for which a
29+
// certificate must be valid to ensure the next refresh attempt has adequate
30+
// time to complete.
31+
private static final Duration DEFAULT_REFRESH_BUFFER = Duration.ofMinutes(4);
32+
33+
long calculateSecondsUntilNextRefresh(Instant now, Instant expiration) {
34+
Duration timeUntilExp = Duration.between(now, expiration);
35+
36+
if (timeUntilExp.compareTo(Duration.ofHours(1)) < 0) {
37+
if (timeUntilExp.compareTo(DEFAULT_REFRESH_BUFFER) < 0) {
38+
// If the time until the certificate expires is less the refresh buffer, schedule the
39+
// refresh immediately
40+
return 0;
41+
}
42+
// Otherwise schedule a refresh in (timeUntilExp - buffer) seconds
43+
return timeUntilExp.minus(DEFAULT_REFRESH_BUFFER).getSeconds();
44+
}
45+
46+
// If the time until the certificate expires is longer than an hour, return timeUntilExp//2
47+
return timeUntilExp.dividedBy(2).getSeconds();
48+
}
49+
}

core/src/test/java/com/google/cloud/sql/core/CloudSqlInstanceTest.java

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,6 @@
2323

2424
import com.google.auth.oauth2.GoogleCredentials;
2525
import com.google.auth.oauth2.OAuth2Credentials;
26-
import java.time.Duration;
27-
import java.time.Instant;
28-
import java.util.Date;
29-
import org.junit.Assert;
3026
import org.junit.Before;
3127
import org.junit.Test;
3228
import org.junit.runner.RunWith;
@@ -67,26 +63,4 @@ public void throwsErrorForWrongCredentialType() {
6763
.hasMessageThat()
6864
.contains("Failed to downscope credentials for IAM Authentication");
6965
}
70-
71-
@Test
72-
public void timeUntilRefreshImmediate() {
73-
Date expiration = Date.from(Instant.now().plus(Duration.ofMinutes(3)));
74-
assertThat(CloudSqlInstance.secondsUntilRefresh(expiration)).isEqualTo(0L);
75-
}
76-
77-
@Test
78-
public void timeUntilRefresh1Hr() {
79-
Date expiration = Date.from(Instant.now().plus(Duration.ofMinutes(59)));
80-
long expected = Duration.ofMinutes(59).minus(Duration.ofMinutes(4)).getSeconds();
81-
Assert.assertEquals(
82-
(float) CloudSqlInstance.secondsUntilRefresh(expiration), (float) expected, 1);
83-
}
84-
85-
@Test
86-
public void timeUntilRefresh24Hr() {
87-
Date expiration = Date.from(Instant.now().plus(Duration.ofHours(23)));
88-
long expected = Duration.ofHours(23).dividedBy(2).getSeconds();
89-
Assert.assertEquals(
90-
(float) CloudSqlInstance.secondsUntilRefresh(expiration), (float) expected, 1);
91-
}
9266
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Copyright 2023 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.google.cloud.sql.core;
17+
18+
import static com.google.common.truth.Truth.assertThat;
19+
import static java.time.temporal.ChronoUnit.SECONDS;
20+
21+
import java.time.Duration;
22+
import java.time.Instant;
23+
import java.util.Arrays;
24+
import java.util.Collection;
25+
import org.junit.Test;
26+
import org.junit.runner.RunWith;
27+
import org.junit.runners.Parameterized;
28+
import org.junit.runners.Parameterized.Parameters;
29+
30+
@RunWith(Parameterized.class)
31+
public class RefreshCalculatorTest {
32+
33+
private final Duration input;
34+
private final Duration want;
35+
36+
@Parameters(name = "Test {0}: calculateSecondsUntilNextRefresh({1})={2}")
37+
public static Collection<Object[]> data() {
38+
return Arrays.asList(
39+
new Object[][] {
40+
{"when expiration is greater than 1 hour", Duration.ofHours(4), Duration.ofHours(2)},
41+
{"when expiration is equal to 1 hour", Duration.ofHours(1), Duration.ofMinutes(30)},
42+
{
43+
"when expiration is less than 1 hour, but greater than 4 minutes",
44+
Duration.ofMinutes(5),
45+
Duration.ofMinutes(1)
46+
},
47+
{"when expiration is less than 4 minutes", Duration.ofMinutes(3), Duration.ofMinutes(0)},
48+
{"when expiration is now", Duration.ofMinutes(0), Duration.ofMinutes(0)},
49+
{"when expiration is 62 minutes", Duration.ofMinutes(62), Duration.ofMinutes(31)},
50+
{"when expiration is 58 minutes", Duration.ofMinutes(58), Duration.ofMinutes(54)},
51+
});
52+
}
53+
54+
public RefreshCalculatorTest(String name, Duration input, Duration want) {
55+
this.input = input;
56+
this.want = want;
57+
this.refreshCalculator = new RefreshCalculator();
58+
}
59+
60+
private static final Instant NOW = Instant.now().truncatedTo(SECONDS);
61+
private RefreshCalculator refreshCalculator;
62+
63+
@Test
64+
public void testDuration() {
65+
Duration nextRefresh =
66+
Duration.ofSeconds(
67+
refreshCalculator.calculateSecondsUntilNextRefresh(NOW, NOW.plus(input)));
68+
assertThat(nextRefresh).isEqualTo(want);
69+
}
70+
}

0 commit comments

Comments
 (0)