Skip to content

Commit

Permalink
feat: improve refresh duration calculation (#364)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
enocom committed Nov 2, 2022
1 parent 86e27ac commit 10b0bf7
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 8 deletions.
26 changes: 18 additions & 8 deletions internal/cloudsql/instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cloudsql

import (
Expand All @@ -28,11 +27,6 @@ import (
sqladmin "google.golang.org/api/sqladmin/v1beta4"
)

const (
// refreshBuffer is the amount of time before a result expires to start a new refresh attempt.
refreshBuffer = 5 * time.Minute
)

var (
// Instance connection name is the format <PROJECT>:<REGION>:<INSTANCE>
// Additionally, we have to support legacy "domain-scoped" projects (e.g. "google.com:PROJECT")
Expand Down Expand Up @@ -274,6 +268,22 @@ func (i *Instance) result(ctx context.Context) (*refreshOperation, error) {
return res, nil
}

// refreshDuration returns the duration to wait before starting the next
// refresh. Usually that duration will be half of the time until certificate
// expiration.
func refreshDuration(now, certExpiry time.Time) time.Duration {
d := certExpiry.Sub(now)
if d < time.Hour {
// Something is wrong with the certificate, refresh now.
if d < 5*time.Minute {
return 0
}
// Otherwise, wait five minutes before starting the refresh cycle.
return 5 * time.Minute
}
return d / 2
}

// scheduleRefresh schedules a refresh operation to be triggered after a given duration. The returned refreshOperation
// can be used to either Cancel or Wait for the operations result.
func (i *Instance) scheduleRefresh(d time.Duration) *refreshOperation {
Expand Down Expand Up @@ -306,8 +316,8 @@ func (i *Instance) scheduleRefresh(d time.Duration) *refreshOperation {
return
default:
}
nextRefresh := i.cur.expiry.Add(-refreshBuffer)
i.next = i.scheduleRefresh(time.Until(nextRefresh))
t := refreshDuration(time.Now(), i.cur.expiry)
i.next = i.scheduleRefresh(t)
})
return res
}
Expand Down
44 changes: 44 additions & 0 deletions internal/cloudsql/instance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -259,3 +259,47 @@ func TestClose(t *testing.T) {
t.Fatalf("failed to retrieve connect info: %v", err)
}
}

func TestRefreshDuration(t *testing.T) {
now := time.Now()
tcs := []struct {
desc string
expiry time.Time
want time.Duration
}{
{
desc: "when expiration is greater than 1 hour",
expiry: now.Add(4 * time.Hour),
want: 2 * time.Hour,
},
{
desc: "when expiration is equal to 1 hour",
expiry: now.Add(time.Hour),
want: 30 * time.Minute,
},
{
desc: "when expiration is less than 1 hour, but greater than 5 minutes",
expiry: now.Add(6 * time.Minute),
want: 5 * time.Minute,
},
{
desc: "when expiration is less than 5 minutes",
expiry: now.Add(4 * time.Minute),
want: 0,
},
{
desc: "when expiration is now",
expiry: now,
want: 0,
},
}
for _, tc := range tcs {
t.Run(tc.desc, func(t *testing.T) {
got := refreshDuration(now, tc.expiry)
// round to the second to remove millisecond differences
if got.Round(time.Second) != tc.want {
t.Fatalf("time until refresh: want = %v, got = %v", tc.want, got)
}
})
}
}

0 comments on commit 10b0bf7

Please sign in to comment.