Skip to content

Commit

Permalink
fix: memory leak in jwks (#425)
Browse files Browse the repository at this point in the history
  • Loading branch information
sattvikc authored Sep 3, 2024
1 parent ee538d6 commit f2c10d0
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 7 deletions.
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [unreleased]

## [0.24.1] - 2024-09-07
## [0.24.2] - 2024-09-03

- Fixes memory leak with the JWKS cache.

## [0.24.1] - 2024-08-07

- Improves debug logs for error handlers.

Expand Down
62 changes: 62 additions & 0 deletions recipe/session/jwksMemory_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package session

import (
"runtime"
"testing"
"time"

"github.com/stretchr/testify/assert"
"github.com/supertokens/supertokens-golang/recipe/session/sessmodels"
"github.com/supertokens/supertokens-golang/supertokens"
"github.com/supertokens/supertokens-golang/test/unittesting"
)

func TestThatThereIsNoMemoryLeakWithJWKSCache(t *testing.T) {
configValue := supertokens.TypeInput{
Supertokens: &supertokens.ConnectionInfo{
ConnectionURI: "http://localhost:8080",
},
AppInfo: supertokens.AppInfo{
AppName: "SuperTokens",
WebsiteDomain: "supertokens.io",
APIDomain: "api.supertokens.io",
},
RecipeList: []supertokens.Recipe{
Init(&sessmodels.TypeInput{
JWKSRefreshIntervalSec: &[]uint64{0}[0],
}),
},
}
BeforeEach()
unittesting.StartUpST("localhost", "8080")
defer AfterEach()
err := supertokens.Init(configValue)
if err != nil {
t.Error(err.Error())
}

testServer := GetTestServer(t)
defer func() {
testServer.Close()
}()

sess, err := CreateNewSessionWithoutRequestResponse("public", "testuser", map[string]interface{}{}, map[string]interface{}{}, nil)
assert.NoError(t, err)

accessToken := sess.GetAccessToken()

_, err = GetSessionWithoutRequestResponse(accessToken, nil, nil)
assert.NoError(t, err)

numGoroutinesBeforeJWKSRefresh := runtime.NumGoroutine()

for i := 0; i < 100; i++ {
_, err = GetSessionWithoutRequestResponse(accessToken, nil, nil)
assert.NoError(t, err)

time.Sleep(10 * time.Millisecond)
}

time.Sleep(1 * time.Second)
assert.Equal(t, numGoroutinesBeforeJWKSRefresh, runtime.NumGoroutine())
}
10 changes: 5 additions & 5 deletions recipe/session/recipeImplementation.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,11 @@ func getJWKS() (*keyfunc.JWKS, error) {
LastFetched: time.Now().UnixNano() / int64(time.Millisecond),
}

// Dont add to cache if there is an error to keep the logic of checking cache simple
//
// This also has the added benefit where if initially the request failed because the core
// was down and then it comes back up, the next time it will try to request that core again
// after the cache has expired
// Close any existing JWKS in the cache before replacing it
if jwksCache != nil && jwksCache.JWKS != nil {
jwksCache.JWKS.EndBackground()
}

jwksCache = &jwksResult

if supertokens.IsRunningInTestMode() {
Expand Down
2 changes: 1 addition & 1 deletion supertokens/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const (
)

// VERSION current version of the lib
const VERSION = "0.24.1"
const VERSION = "0.24.2"

var (
cdiSupported = []string{"3.1"}
Expand Down

0 comments on commit f2c10d0

Please sign in to comment.