Skip to content

Commit 10b0bf7

Browse files
authored
feat: improve refresh duration calculation (#364)
The change here updates the calculation of how long to wait before starting a refresh cycle. This is a port of GoogleCloudPlatform/alloydb-go-connector#103.
1 parent 86e27ac commit 10b0bf7

File tree

2 files changed

+62
-8
lines changed

2 files changed

+62
-8
lines changed

internal/cloudsql/instance.go

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
14-
1514
package cloudsql
1615

1716
import (
@@ -28,11 +27,6 @@ import (
2827
sqladmin "google.golang.org/api/sqladmin/v1beta4"
2928
)
3029

31-
const (
32-
// refreshBuffer is the amount of time before a result expires to start a new refresh attempt.
33-
refreshBuffer = 5 * time.Minute
34-
)
35-
3630
var (
3731
// Instance connection name is the format <PROJECT>:<REGION>:<INSTANCE>
3832
// Additionally, we have to support legacy "domain-scoped" projects (e.g. "google.com:PROJECT")
@@ -274,6 +268,22 @@ func (i *Instance) result(ctx context.Context) (*refreshOperation, error) {
274268
return res, nil
275269
}
276270

271+
// refreshDuration returns the duration to wait before starting the next
272+
// refresh. Usually that duration will be half of the time until certificate
273+
// expiration.
274+
func refreshDuration(now, certExpiry time.Time) time.Duration {
275+
d := certExpiry.Sub(now)
276+
if d < time.Hour {
277+
// Something is wrong with the certificate, refresh now.
278+
if d < 5*time.Minute {
279+
return 0
280+
}
281+
// Otherwise, wait five minutes before starting the refresh cycle.
282+
return 5 * time.Minute
283+
}
284+
return d / 2
285+
}
286+
277287
// scheduleRefresh schedules a refresh operation to be triggered after a given duration. The returned refreshOperation
278288
// can be used to either Cancel or Wait for the operations result.
279289
func (i *Instance) scheduleRefresh(d time.Duration) *refreshOperation {
@@ -306,8 +316,8 @@ func (i *Instance) scheduleRefresh(d time.Duration) *refreshOperation {
306316
return
307317
default:
308318
}
309-
nextRefresh := i.cur.expiry.Add(-refreshBuffer)
310-
i.next = i.scheduleRefresh(time.Until(nextRefresh))
319+
t := refreshDuration(time.Now(), i.cur.expiry)
320+
i.next = i.scheduleRefresh(t)
311321
})
312322
return res
313323
}

internal/cloudsql/instance_test.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,3 +259,47 @@ func TestClose(t *testing.T) {
259259
t.Fatalf("failed to retrieve connect info: %v", err)
260260
}
261261
}
262+
263+
func TestRefreshDuration(t *testing.T) {
264+
now := time.Now()
265+
tcs := []struct {
266+
desc string
267+
expiry time.Time
268+
want time.Duration
269+
}{
270+
{
271+
desc: "when expiration is greater than 1 hour",
272+
expiry: now.Add(4 * time.Hour),
273+
want: 2 * time.Hour,
274+
},
275+
{
276+
desc: "when expiration is equal to 1 hour",
277+
expiry: now.Add(time.Hour),
278+
want: 30 * time.Minute,
279+
},
280+
{
281+
desc: "when expiration is less than 1 hour, but greater than 5 minutes",
282+
expiry: now.Add(6 * time.Minute),
283+
want: 5 * time.Minute,
284+
},
285+
{
286+
desc: "when expiration is less than 5 minutes",
287+
expiry: now.Add(4 * time.Minute),
288+
want: 0,
289+
},
290+
{
291+
desc: "when expiration is now",
292+
expiry: now,
293+
want: 0,
294+
},
295+
}
296+
for _, tc := range tcs {
297+
t.Run(tc.desc, func(t *testing.T) {
298+
got := refreshDuration(now, tc.expiry)
299+
// round to the second to remove millisecond differences
300+
if got.Round(time.Second) != tc.want {
301+
t.Fatalf("time until refresh: want = %v, got = %v", tc.want, got)
302+
}
303+
})
304+
}
305+
}

0 commit comments

Comments
 (0)