Skip to content

fix(manager): optimize durationToRenewal #6

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
May 29, 2025
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions credentials_provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ func TestCredentialsProviderWithMockIdentityProvider(t *testing.T) {
"test-token",
time.Now().Add(time.Hour),
time.Now(),
int64(time.Hour),
time.Hour.Milliseconds(),
),
}

Expand Down Expand Up @@ -159,7 +159,7 @@ func TestCredentialsProviderWithMockIdentityProvider(t *testing.T) {
"test-token",
time.Now().Add(time.Hour),
time.Now(),
int64(time.Hour),
time.Hour.Milliseconds(),
),
}

Expand Down Expand Up @@ -219,7 +219,7 @@ func TestCredentialsProviderWithMockIdentityProvider(t *testing.T) {
"initial-token",
time.Now().Add(time.Hour),
time.Now(),
int64(time.Hour),
time.Hour.Milliseconds(),
),
}

Expand Down Expand Up @@ -253,7 +253,7 @@ func TestCredentialsProviderWithMockIdentityProvider(t *testing.T) {
"updated-token",
time.Now().Add(time.Hour),
time.Now(),
int64(time.Hour),
time.Hour.Milliseconds(),
)
tm.lock.Unlock()

Expand Down Expand Up @@ -329,7 +329,7 @@ func TestCredentialsProviderSubscribe(t *testing.T) {
rawTokenString,
time.Now().Add(tokenExpiration),
time.Now(),
int64(tokenExpiration),
tokenExpiration.Milliseconds(),
)

listener := &mockCredentialsListener{
Expand Down Expand Up @@ -386,7 +386,7 @@ func TestCredentialsProviderSubscribe(t *testing.T) {
rawTokenString,
time.Now().Add(tokenExpiration),
time.Now(),
int64(tokenExpiration),
tokenExpiration.Milliseconds(),
)
mtm := &mockTokenManager{done: make(chan struct{})}
// Set the token manager factory in the options
Expand Down Expand Up @@ -514,7 +514,7 @@ func TestCredentialsProviderSubscribe(t *testing.T) {
rawTokenString,
time.Now().Add(tokenExpiration),
time.Now(),
int64(tokenExpiration),
tokenExpiration.Milliseconds(),
)
// Set the token manager factory in the options
options := opts
Expand Down
2 changes: 1 addition & 1 deletion entraid_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func (m *fakeTokenManager) GetToken(forceRefresh bool) (*token.Token, error) {
rawTokenString,
time.Now().Add(tokenExpiration),
time.Now(),
int64(tokenExpiration.Seconds()),
tokenExpiration.Milliseconds(),
)
}
return m.token, m.err
Expand Down
20 changes: 10 additions & 10 deletions examples/custom_idp/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,23 @@ module custom_example
go 1.23.4

require (
github.com/redis/go-redis-entraid v1.0.0
github.com/redis/go-redis-entraid v1.0.1
github.com/redis/go-redis/v9 v9.9.0
)

require (
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0-beta.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
golang.org/x/crypto v0.33.0 // indirect
golang.org/x/net v0.35.0 // indirect
golang.org/x/sys v0.30.0 // indirect
golang.org/x/text v0.22.0 // indirect
golang.org/x/crypto v0.38.0 // indirect
golang.org/x/net v0.40.0 // indirect
golang.org/x/sys v0.33.0 // indirect
golang.org/x/text v0.25.0 // indirect
)
52 changes: 25 additions & 27 deletions examples/custom_idp/go.sum
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 h1:g0EZJwz7xkXQiZAI5xi9f3WWFYBlX1CPTrR+NDToRkQ=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0/go.mod h1:XCW7KnZet0Opnr7HccfUw1PLc4CjHqpcaxW8DHklNkQ=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0-beta.1 h1:iw4+KCeCoieuKodp1d5YhAa1TU/GgogCbw8RbGvsfLA=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0-beta.1/go.mod h1:AP8cDnDTGIVvayqKAhwzpcAyTJosXpvLYNmVFJb98x8=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.2.3 h1:BAUsn6/icUFtvUalVwCO0+hSF7qgU9DwwcEfCvtILtw=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.2.3/go.mod h1:QlAsNp4gk9zLD2wiZIvIuv699ynpZ2Tq2ZBp+6MrSEw=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 h1:Gt0j3wceWMwPmiazCa8MzMA0MfhmPIz0Qp0FJ6qcM0U=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.0 h1:j8BorDEigD8UFOSZQiSqAMOOleyQOOQPnUAwV+Ls1gA=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.0/go.mod h1:JdM5psgjfBf5fo2uWOZhflPWyDBZ/O/CNAH9CtsuZE4=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 h1:FPKJS1T+clwv+OLGt13a8UjqeRuh0O4SJ3lUriThc+4=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1/go.mod h1:j2chePtV91HrC22tGoRX3sGY42uF13WzmmV80/OdVAA=
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM=
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE=
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.1 h1:8BKxhZZLX/WosEeoCvWysmKUscfa9v8LIPEEU0JjE2o=
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.1/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs=
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
Expand All @@ -20,36 +20,34 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6 h1:IsMZxCuZqKuao2vNdfD82fjjgPLfyHLpR41Z88viRWs=
github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6/go.mod h1:3VeWNIJaW+O5xpRQbPp0Ybqu1vJd/pm7s2F473HRrkw=
github.com/keybase/go-keychain v0.0.1 h1:way+bWYa6lDppZoZcgMbYsvC7GxljxrskdNInRtuthU=
github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/redis/go-redis-entraid v0.0.0-20250415111332-9d087bc29c12 h1:H5ZfgueBAxs2eAvXtCMEbT2/fLQz/wxW5Ds4c0uzl50=
github.com/redis/go-redis-entraid v0.0.0-20250415111332-9d087bc29c12/go.mod h1:uXKLxCMUAu1VKgWdt8gWc4PWCygiL2pAI5XpnRSVc0w=
github.com/redis/go-redis-entraid v1.0.0/go.mod h1:b+YPtHM3oFJ74Y2eFHPuz1Cp59kUL0fwdiARp27VW8Q=
github.com/redis/go-redis/v9 v9.5.3-0.20250415103233-40a89c56cc52 h1:jRx2gINoJsGKxi/RYXCq1VneAAYes9JxUp13xH2oU2g=
github.com/redis/go-redis/v9 v9.5.3-0.20250415103233-40a89c56cc52/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw=
github.com/redis/go-redis-entraid v1.0.1 h1:Q2gxpSRFLn+KyZuPrF7zDUCQ9iISoUxqzaCjxPqJKQI=
github.com/redis/go-redis-entraid v1.0.1/go.mod h1:OS6s3V1DdSRzOJEIjpK38/w4chZpl/Sy+1pzby+6nEk=
github.com/redis/go-redis/v9 v9.9.0 h1:URbPQ4xVQSQhZ27WMQVmZSo3uT3pL+4IdHVcYq2nVfM=
github.com/redis/go-redis/v9 v9.9.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
3 changes: 1 addition & 2 deletions manager/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package manager
import (
"errors"
"fmt"
"math"
"net"
"os"
"time"
Expand Down Expand Up @@ -177,6 +176,6 @@ func (*defaultIdentityProviderResponseParser) ParseResponse(response shared.Iden
rawToken,
expiresOn,
now,
int64(math.Ceil(time.Until(expiresOn).Seconds())),
time.Until(expiresOn).Milliseconds(),
), nil
}
62 changes: 49 additions & 13 deletions manager/entraid_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,31 +237,67 @@ func (e *entraidTokenManager) stop() (err error) {
// It returns the duration to the next token renewal based on the expiration refresh ratio and the lower bound duration.
// If the token is nil, it returns 0.
// If the time till expiration is less than the lower bound duration, it returns 0 to renew the token now.
//
// This is an optimized version that uses minimal operations
// and integer math for maximum performance, matching the logic of durationToRenewal.
// It calculates the duration until the next token renewal based on:
// 1. The token's TTL (in milliseconds) and expiration refresh ratio
// 2. The lower bound duration for refresh
// 3. The current time and token's expiration time
func (e *entraidTokenManager) durationToRenewal(t *token.Token) time.Duration {
// Fast path: nil token check
if t == nil {
return 0
}
expirationRefreshTime := t.ReceivedAt().Add(time.Duration(float64(t.TTL()) * float64(time.Second) * e.expirationRefreshRatio))
timeTillExpiration := time.Until(t.ExpirationOn())
now := time.Now().UTC()

if expirationRefreshTime.Before(now) {
// Get current time in milliseconds (UTC)
nowMillis := time.Now().UTC().UnixMilli()

// Get expiration time in milliseconds
expMillis := t.ExpirationOn().UnixMilli()

// Fast path: token already expired
if expMillis <= nowMillis {
return 0
}

// if the timeTillExpiration is less than the lower bound (or 0), return 0 to renew the token NOW
if timeTillExpiration <= e.lowerBoundDuration || timeTillExpiration <= 0 {
// Calculate time until expiration in milliseconds
timeTillExpiration := expMillis - nowMillis

// Get lower bound in milliseconds
lowerBoundMillis := e.lowerBoundDuration.Milliseconds()

// Fast path: time until expiration is less than lower bound
if timeTillExpiration <= lowerBoundMillis {
return 0
}

// Calculate refresh time using integer math:
// 1. TTL is already in milliseconds
// 2. Multiply by refresh ratio (as integer percentage)
// 3. Add to received time
ttlMillis := t.TTL() // Already in milliseconds
refreshRatioInt := int64(e.expirationRefreshRatio * 100) // Convert to integer percentage
refreshMillis := (ttlMillis * refreshRatioInt) / 100 // Integer division for ratio
refreshTimeMillis := t.ReceivedAt().UnixMilli() + refreshMillis

// Calculate time until refresh
timeUntilRefresh := refreshTimeMillis - nowMillis

// Fast path: refresh time is in the past
if timeUntilRefresh <= 0 {
return 0
}

// Calculate the time to renew the token based on the expiration refresh ratio
duration := time.Until(expirationRefreshTime)
// Convert to time.Duration for final calculations
timeUntilRefreshDur := time.Duration(timeUntilRefresh) * time.Millisecond
timeTillExpirationDur := time.Duration(timeTillExpiration) * time.Millisecond

// if the duration will take us past the lower bound, return the duration to lower bound
if timeTillExpiration-e.lowerBoundDuration < duration {
return timeTillExpiration - e.lowerBoundDuration
// If refresh would occur after lower bound, use time until lower bound
if timeTillExpirationDur-e.lowerBoundDuration < timeUntilRefreshDur {
return timeTillExpirationDur - e.lowerBoundDuration
}

// return the calculated duration
return duration
// Otherwise use time until refresh
return timeUntilRefreshDur
}
2 changes: 1 addition & 1 deletion manager/manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ var testTokenValid = token.New(
"test",
time.Now().Add(time.Hour),
time.Now(),
int64(time.Hour.Seconds()),
time.Hour.Milliseconds(),
)

func newTestJWTToken(expiresOn time.Time) string {
Expand Down
Loading
Loading