update session stores to decide TTLs

This commit is contained in:
2026-06-09 12:31:27 -04:00
parent e41a81c487
commit 22c4666fa7
4 changed files with 61 additions and 38 deletions
+19 -20
View File
@@ -7,7 +7,6 @@ import (
"astraltech.xyz/accountmanager/src/logging" "astraltech.xyz/accountmanager/src/logging"
"astraltech.xyz/accountmanager/src/store" "astraltech.xyz/accountmanager/src/store"
"astraltech.xyz/accountmanager/src/worker"
) )
const SessionCookieName = "session_token" const SessionCookieName = "session_token"
@@ -39,10 +38,10 @@ func (manager *SessionManager) SetStoreType(storeType StoreType, params ...any)
case InMemory: case InMemory:
{ {
manager.store = store.NewMemoryStore[*SessionData]() manager.store = store.NewMemoryStore[*SessionData]()
worker.CreateWorker(time.Minute*5, func() { // worker.CreateWorker(time.Minute*5, func() {
inMemStore, _ := manager.store.(*store.MemoryStore[*SessionData]) // inMemStore, _ := manager.store.(*store.MemoryStore[*SessionData])
cleanupInMemoryStore(inMemStore) // cleanupInMemoryStore(inMemStore)
}) // })
break break
} }
case Redis: case Redis:
@@ -70,7 +69,7 @@ func (manager *SessionManager) CreateSession(userID string) (cookie *http.Cookie
CSRFToken: CSRFToken, CSRFToken: CSRFToken,
ExpiresAt: time.Now().Add(time.Hour), ExpiresAt: time.Now().Add(time.Hour),
} }
err = manager.store.Create(token, &newSessionData) err = manager.store.CreateExpiring(token, &newSessionData, time.Hour)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -110,22 +109,22 @@ func (manager *SessionManager) GetSession(r *http.Request) (*SessionData, error)
return data, nil return data, nil
} }
func cleanupInMemoryStore(m *store.MemoryStore[*SessionData]) { // func cleanupInMemoryStore(m *store.MemoryStore[*SessionData]) {
logging.Debug("Cleaning up memory store sessions") // logging.Debug("Cleaning up memory store sessions")
now := time.Now() // now := time.Now()
m.Lock.Lock() // m.Lock.Lock()
defer m.Lock.Unlock() // defer m.Lock.Unlock()
deleted := 0 // deleted := 0
for id, session := range m.Sessions { // for id, session := range m.Sessions {
if now.After(session.ExpiresAt) { // if now.After(session.ExpiresAt) {
delete(m.Sessions, id) // delete(m.Sessions, id)
deleted = deleted + 1 // deleted = deleted + 1
} // }
} // }
logging.Infof("Cleaned up %d stale sessions", deleted) // logging.Infof("Cleaned up %d stale sessions", deleted)
} // }
func (manager *SessionManager) DeleteSession(sessionId string) error { func (manager *SessionManager) DeleteSession(sessionId string) error {
return manager.store.Delete(sessionId) return manager.store.Delete(sessionId)
+5
View File
@@ -1,9 +1,14 @@
package store package store
import (
"time"
)
// A simple key value store that can either just be single instance in memory or a redis server (for now) // A simple key value store that can either just be single instance in memory or a redis server (for now)
type KeyValueStore[Value any] interface { type KeyValueStore[Value any] interface {
Create(key string, value Value) error Create(key string, value Value) error
CreateExpiring(key string, value Value, ttl time.Duration) error
Get(key string) (Value, error) Get(key string) (Value, error)
Update(key string, session Value) error Update(key string, session Value) error
Delete(key string) error Delete(key string) error
+32 -15
View File
@@ -2,60 +2,77 @@ package store
import ( import (
"sync" "sync"
"time"
"astraltech.xyz/accountmanager/src/logging" "astraltech.xyz/accountmanager/src/logging"
) )
type ValueEntry[Value any] struct {
value Value
expiry time.Time
}
type MemoryStore[Value any] struct { type MemoryStore[Value any] struct {
Sessions map[string]Value Keys map[string]ValueEntry[Value]
Lock sync.RWMutex
Lock sync.RWMutex
} }
func NewMemoryStore[Value any]() *MemoryStore[Value] { func NewMemoryStore[Value any]() *MemoryStore[Value] {
logging.Debug("Creating new in memory session store") logging.Debug("Creating new in memory session store")
store := &MemoryStore[Value]{ store := &MemoryStore[Value]{
Sessions: make(map[string]Value), Keys: make(map[string]ValueEntry[Value]),
} }
return store return store
} }
func (m *MemoryStore[Value]) Create(key string, session Value) (err error) { func (m *MemoryStore[Value]) CreateExpiring(key string, value Value, duration time.Duration) error {
hashedkey := HashKey(key) hashedkey := HashKey(key)
m.Lock.Lock() m.Lock.Lock()
defer m.Lock.Unlock() defer m.Lock.Unlock()
_, exist := m.Sessions[hashedkey] _, exist := m.Keys[hashedkey]
if exist { if exist {
return ErrKeyAlreadyExists return ErrKeyAlreadyExists
} }
m.Sessions[hashedkey] = session m.Keys[hashedkey] = ValueEntry[Value]{
value: value,
expiry: time.Now().Add(duration),
}
return nil return nil
} }
func (m *MemoryStore[Value]) Create(key string, session Value) error {
return m.CreateExpiring(key, session, 0)
}
func (m *MemoryStore[Value]) Get(key string) (Value, error) { func (m *MemoryStore[Value]) Get(key string) (Value, error) {
var data Value var data ValueEntry[Value]
m.Lock.RLock() m.Lock.RLock()
hashedkey := HashKey(key) hashedkey := HashKey(key)
data, exists := m.Sessions[hashedkey] data, exists := m.Keys[hashedkey]
m.Lock.RUnlock() m.Lock.RUnlock()
if exists == false { if exists == false {
return data, ErrKeyNotFound return data.value, ErrKeyNotFound
} }
return data, nil return data.value, nil
} }
func (m *MemoryStore[Value]) Update(sessionID string, session Value) error { func (m *MemoryStore[Value]) Update(sessionID string, value Value) error {
hashedkey := HashKey(sessionID) hashedkey := HashKey(sessionID)
m.Lock.Lock() m.Lock.Lock()
defer m.Lock.Unlock() defer m.Lock.Unlock()
_, exist := m.Sessions[hashedkey] current_key, exist := m.Keys[hashedkey]
if !exist { if !exist {
return ErrKeyNotFound return ErrKeyNotFound
} }
m.Sessions[hashedkey] = session m.Keys[hashedkey] = ValueEntry[Value]{
value: value,
expiry: current_key.expiry,
}
return nil return nil
} }
@@ -64,10 +81,10 @@ func (m *MemoryStore[Value]) Delete(sessionID string) error {
m.Lock.Lock() m.Lock.Lock()
defer m.Lock.Unlock() defer m.Lock.Unlock()
_, exist := m.Sessions[hashedkey] _, exist := m.Keys[hashedkey]
if !exist { if !exist {
return ErrKeyNotFound return ErrKeyNotFound
} }
delete(m.Sessions, hashedkey) delete(m.Keys, hashedkey)
return nil return nil
} }
+5 -3
View File
@@ -43,8 +43,7 @@ func NewRedisStore[Value any](redis_server string, prefix string) *RedisStore[Va
} }
return store return store
} }
func (m *RedisStore[Value]) CreateExpiring(key string, value Value, ttl time.Duration) error {
func (m *RedisStore[Value]) Create(key string, value Value) (err error) {
hashedSession := m.RedisHash(key) hashedSession := m.RedisHash(key)
data, err := json.Marshal(value) data, err := json.Marshal(value)
@@ -52,7 +51,7 @@ func (m *RedisStore[Value]) Create(key string, value Value) (err error) {
return ErrKeyBackend return ErrKeyBackend
} }
created, err := m.client.SetNX(m.ctx, hashedSession, data, time.Hour).Result() created, err := m.client.SetNX(m.ctx, hashedSession, data, ttl).Result()
if err != nil { if err != nil {
logging.Error(err.Error()) logging.Error(err.Error())
return ErrKeyBackend return ErrKeyBackend
@@ -63,6 +62,9 @@ func (m *RedisStore[Value]) Create(key string, value Value) (err error) {
} }
return nil return nil
} }
func (m *RedisStore[Value]) Create(key string, value Value) error {
return m.CreateExpiring(key, value, 0)
}
func (m *RedisStore[Value]) Get(sessionID string) (Value, error) { func (m *RedisStore[Value]) Get(sessionID string) (Value, error) {
hashed := m.RedisHash(sessionID) hashed := m.RedisHash(sessionID)
var session_data Value var session_data Value