scs icon indicating copy to clipboard operation
scs copied to clipboard

Switching to a different session store leaves memstore goroutine unreleased

Open phongphongg opened this issue 1 year ago • 3 comments

scs.New() automatically invokes memstore.New(), which creates a goroutine to handle expired session cleanup. Because memstore is the default store, switching to a different store (like gormstore) leaves the memstore goroutine active and unreleased

https://github.com/alexedwards/scs/blob/7e11d57e8885fd38c604d4f79f0eef355650b997/session.go#L106

// Initialize a new session manager and configure it to use gormstore as the session store.
sessionManager = scs.New()
if sessionManager.Store, err = gormstore.New(db); err != nil {
    log.Fatal(err)
}

Workaround solution:

if memStore, ok := sessionManager.Store.(*memstore.MemStore); ok {
    // HACK: Not sure why, but it seems a delay is needed before stopping the cleanup goroutine.
    time.Sleep(time.Second)
    memStore.StopCleanup()
}

if sessionManager.Store, err = gormstore.New(db); err != nil {
    log.Fatal(err)
}

phongphongg avatar Oct 27 '24 10:10 phongphongg

You're right, it does. Good spot 👍

I'm not sure what the best course of action here is. Memstore needs to remain the default store to avoid breaking backward compatibility. The cleanup needs to continue to run automatically to avoid breaking backward compatibility. We can't unexport the Store field and require everyone to use a new SetStore() method (which releases the memstore goroutine) without breaking backward compatibility. I don't want to issue a v3 of this package just to resolve this issue.

So I'm a bit stuck. Living with an unreleased goroutine (which won't be using much resources) also isn't ideal but may be the least bad option here. Can you think of any other possibilities?

alexedwards avatar Feb 12 '25 12:02 alexedwards

Would be nice to have ability bypass the New() and instantiate the SessionManager without constructor. It is currently impossible because contextKey is private.

dpotapov avatar Apr 01 '25 03:04 dpotapov

@alexedwards Would you be open to just adding a NewWithOptions (functional options) or NewWithConfig approach from https://github.com/alexedwards/scs/issues/180 ?

Functional options are pretty standard at this point for instantiating things in Go, and avoids creating more of these exact compatibility issues in the future.

francoposa avatar May 05 '25 04:05 francoposa

@dpotapov That's a great suggestion, thank you 👍

I've made ContextKey an exported field in https://github.com/alexedwards/scs/commit/fb8d6f122cfb2228bb787059a8908bb27ecb4cda, which makes it possible to work around this issue by creating your own SessionManager instance, rather than using the New constructor.

cc @phongphongg

alexedwards avatar Sep 27 '25 11:09 alexedwards