Documentation
¶
Index ¶
- type Entry
- type LayeredStore
- func (s *LayeredStore) CountForWorker(workerID string) int
- func (s *LayeredStore) CountForWorkers(workerIDs []string) int
- func (s *LayeredStore) Delete(sessionID string)
- func (s *LayeredStore) DeleteByWorker(workerID string) int
- func (s *LayeredStore) EntriesForWorker(workerID string) map[string]Entry
- func (s *LayeredStore) Get(sessionID string) (Entry, bool)
- func (s *LayeredStore) RerouteWorker(oldWorkerID, newWorkerID string) int
- func (s *LayeredStore) Set(sessionID string, entry Entry)
- func (s *LayeredStore) SweepIdle(maxAge time.Duration) int
- func (s *LayeredStore) Touch(sessionID string) bool
- type MemoryStore
- func (s *MemoryStore) CountForWorker(workerID string) int
- func (s *MemoryStore) CountForWorkers(workerIDs []string) int
- func (s *MemoryStore) Delete(sessionID string)
- func (s *MemoryStore) DeleteByWorker(workerID string) int
- func (s *MemoryStore) EntriesForWorker(workerID string) map[string]Entry
- func (s *MemoryStore) Get(sessionID string) (Entry, bool)
- func (s *MemoryStore) RerouteWorker(oldWorkerID, newWorkerID string) int
- func (s *MemoryStore) Set(sessionID string, entry Entry)
- func (s *MemoryStore) SweepIdle(maxAge time.Duration) int
- func (s *MemoryStore) Touch(sessionID string) bool
- type PostgresStore
- func (s *PostgresStore) CountForWorker(workerID string) int
- func (s *PostgresStore) CountForWorkers(workerIDs []string) int
- func (s *PostgresStore) Delete(sessionID string)
- func (s *PostgresStore) DeleteByWorker(workerID string) int
- func (s *PostgresStore) EntriesForWorker(workerID string) map[string]Entry
- func (s *PostgresStore) Get(sessionID string) (Entry, bool)
- func (s *PostgresStore) RerouteWorker(oldWorkerID, newWorkerID string) int
- func (s *PostgresStore) RunExpiry(ctx context.Context, interval time.Duration)
- func (s *PostgresStore) Set(sessionID string, entry Entry)
- func (s *PostgresStore) SweepIdle(maxAge time.Duration) int
- func (s *PostgresStore) Touch(sessionID string) bool
- type RedisStore
- func (s *RedisStore) CountForWorker(workerID string) int
- func (s *RedisStore) CountForWorkers(workerIDs []string) int
- func (s *RedisStore) Delete(sessionID string)
- func (s *RedisStore) DeleteByWorker(workerID string) int
- func (s *RedisStore) EntriesForWorker(workerID string) map[string]Entry
- func (s *RedisStore) Get(sessionID string) (Entry, bool)
- func (s *RedisStore) RerouteWorker(oldWorkerID, newWorkerID string) int
- func (s *RedisStore) Set(sessionID string, entry Entry)
- func (s *RedisStore) SweepIdle(_ time.Duration) int
- func (s *RedisStore) Touch(sessionID string) bool
- type Store
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Entry ¶ added in v0.0.2
type Entry struct {
WorkerID string
UserSub string // bound user identity; empty when OIDC is not configured
LastAccess time.Time // updated on every proxy request; used for idle sweep
}
Entry holds the state for a single session.
type LayeredStore ¶ added in v0.0.4
type LayeredStore struct {
// contains filtered or unexported fields
}
LayeredStore layers a cache Store over a primary Store (see #286, parent #262). The primary is the source of truth (Postgres in production); the cache is an optional optimization (Redis).
Reads: cache first; on miss, fall back to primary and populate the cache on the way out.
Writes: primary first; cache mirrored best-effort. Cache errors are already swallowed inside the concrete stores (they log and return false/zero), so LayeredStore just calls both sequentially — the primary operation's outcome is the one surfaced to callers.
Aggregate queries (CountForWorker, EntriesForWorker) always go to the primary: the cache may hold a subset and can't answer accurately.
func NewLayeredStore ¶ added in v0.0.4
func NewLayeredStore(primary, cache Store) *LayeredStore
func (*LayeredStore) CountForWorker ¶ added in v0.0.4
func (s *LayeredStore) CountForWorker(workerID string) int
func (*LayeredStore) CountForWorkers ¶ added in v0.0.4
func (s *LayeredStore) CountForWorkers(workerIDs []string) int
func (*LayeredStore) Delete ¶ added in v0.0.4
func (s *LayeredStore) Delete(sessionID string)
func (*LayeredStore) DeleteByWorker ¶ added in v0.0.4
func (s *LayeredStore) DeleteByWorker(workerID string) int
func (*LayeredStore) EntriesForWorker ¶ added in v0.0.4
func (s *LayeredStore) EntriesForWorker(workerID string) map[string]Entry
func (*LayeredStore) Get ¶ added in v0.0.4
func (s *LayeredStore) Get(sessionID string) (Entry, bool)
func (*LayeredStore) RerouteWorker ¶ added in v0.0.4
func (s *LayeredStore) RerouteWorker(oldWorkerID, newWorkerID string) int
func (*LayeredStore) Set ¶ added in v0.0.4
func (s *LayeredStore) Set(sessionID string, entry Entry)
func (*LayeredStore) SweepIdle ¶ added in v0.0.4
func (s *LayeredStore) SweepIdle(maxAge time.Duration) int
SweepIdle runs against the primary. RedisStore.SweepIdle is a no-op because native TTL already expires idle cache entries around the same cutoff — idle sweep and TTL share the idle_ttl setting.
func (*LayeredStore) Touch ¶ added in v0.0.4
func (s *LayeredStore) Touch(sessionID string) bool
type MemoryStore ¶ added in v0.0.3
type MemoryStore struct {
// contains filtered or unexported fields
}
MemoryStore is a concurrent in-memory implementation of Store.
func NewMemoryStore ¶ added in v0.0.3
func NewMemoryStore() *MemoryStore
func (*MemoryStore) CountForWorker ¶ added in v0.0.3
func (s *MemoryStore) CountForWorker(workerID string) int
func (*MemoryStore) CountForWorkers ¶ added in v0.0.3
func (s *MemoryStore) CountForWorkers(workerIDs []string) int
CountForWorkers returns the total session count across the given worker IDs.
func (*MemoryStore) Delete ¶ added in v0.0.3
func (s *MemoryStore) Delete(sessionID string)
func (*MemoryStore) DeleteByWorker ¶ added in v0.0.3
func (s *MemoryStore) DeleteByWorker(workerID string) int
DeleteByWorker removes all sessions mapped to the given worker. Linear scan — acceptable at max_workers = 100.
func (*MemoryStore) EntriesForWorker ¶ added in v0.0.3
func (s *MemoryStore) EntriesForWorker(workerID string) map[string]Entry
EntriesForWorker returns a snapshot of all sessions for a worker.
func (*MemoryStore) Get ¶ added in v0.0.3
func (s *MemoryStore) Get(sessionID string) (Entry, bool)
Get returns the entry for the given session ID.
func (*MemoryStore) RerouteWorker ¶ added in v0.0.3
func (s *MemoryStore) RerouteWorker(oldWorkerID, newWorkerID string) int
RerouteWorker reassigns all sessions from oldWorkerID to newWorkerID. Used by container transfer to migrate sessions to the new worker.
func (*MemoryStore) Set ¶ added in v0.0.3
func (s *MemoryStore) Set(sessionID string, entry Entry)
Set creates or replaces a session entry.
func (*MemoryStore) SweepIdle ¶ added in v0.0.3
func (s *MemoryStore) SweepIdle(maxAge time.Duration) int
SweepIdle removes sessions whose LastAccess is older than maxAge. Returns the number of sessions removed.
func (*MemoryStore) Touch ¶ added in v0.0.3
func (s *MemoryStore) Touch(sessionID string) bool
Touch updates the LastAccess timestamp for an existing session. Returns false if the session does not exist.
type PostgresStore ¶ added in v0.0.4
type PostgresStore struct {
// contains filtered or unexported fields
}
PostgresStore implements session.Store against the blockyard_sessions table, making Postgres the source of truth for sticky sessions (see #286, parent #262).
expires_at is populated from idleTTL on every Set/Touch. A background sweep (RunExpiry) deletes rows whose expires_at has passed. This replaces Redis-native TTL expiry.
func NewPostgresStore ¶ added in v0.0.4
func NewPostgresStore(db *sqlx.DB, idleTTL time.Duration) *PostgresStore
NewPostgresStore returns a store backed by the blockyard_sessions table. idleTTL drives the expires_at column; 0 means "never expire" and is written as a far-future timestamp so the sweep column keeps a sensible default without needing NULL handling.
func (*PostgresStore) CountForWorker ¶ added in v0.0.4
func (s *PostgresStore) CountForWorker(workerID string) int
func (*PostgresStore) CountForWorkers ¶ added in v0.0.4
func (s *PostgresStore) CountForWorkers(workerIDs []string) int
func (*PostgresStore) Delete ¶ added in v0.0.4
func (s *PostgresStore) Delete(sessionID string)
func (*PostgresStore) DeleteByWorker ¶ added in v0.0.4
func (s *PostgresStore) DeleteByWorker(workerID string) int
func (*PostgresStore) EntriesForWorker ¶ added in v0.0.4
func (s *PostgresStore) EntriesForWorker(workerID string) map[string]Entry
func (*PostgresStore) Get ¶ added in v0.0.4
func (s *PostgresStore) Get(sessionID string) (Entry, bool)
func (*PostgresStore) RerouteWorker ¶ added in v0.0.4
func (s *PostgresStore) RerouteWorker(oldWorkerID, newWorkerID string) int
func (*PostgresStore) RunExpiry ¶ added in v0.0.4
func (s *PostgresStore) RunExpiry(ctx context.Context, interval time.Duration)
RunExpiry deletes rows whose expires_at has passed, every interval. Blocks until ctx is cancelled. Caller runs it in a goroutine.
This replaces Redis-native TTL expiry: without it, sessions that are created and then never touched again (e.g. a user closes their tab mid-handshake) would live forever. SweepIdle is not a substitute — that runs only when the operator configures session_idle_ttl.
func (*PostgresStore) Set ¶ added in v0.0.4
func (s *PostgresStore) Set(sessionID string, entry Entry)
func (*PostgresStore) SweepIdle ¶ added in v0.0.4
func (s *PostgresStore) SweepIdle(maxAge time.Duration) int
SweepIdle deletes sessions whose last_access is older than maxAge. Matches MemoryStore semantics — callers drive idle sweeps via the autoscaler. Natural expiry (expires_at) is handled separately by RunExpiry so the two loops can run at different cadences.
func (*PostgresStore) Touch ¶ added in v0.0.4
func (s *PostgresStore) Touch(sessionID string) bool
type RedisStore ¶ added in v0.0.3
type RedisStore struct {
// contains filtered or unexported fields
}
RedisStore implements session.Store using Redis hashes.
Key schema:
{prefix}session:{sessionID} -> hash {worker_id, user_sub, last_access}
Each session key has a TTL equal to idleTTL. Touch refreshes it. SweepIdle is a no-op — Redis TTL expiry handles idle cleanup.
func NewRedisStore ¶ added in v0.0.3
func NewRedisStore(client *redisstate.Client, idleTTL time.Duration) *RedisStore
func (*RedisStore) CountForWorker ¶ added in v0.0.3
func (s *RedisStore) CountForWorker(workerID string) int
func (*RedisStore) CountForWorkers ¶ added in v0.0.3
func (s *RedisStore) CountForWorkers(workerIDs []string) int
func (*RedisStore) Delete ¶ added in v0.0.3
func (s *RedisStore) Delete(sessionID string)
func (*RedisStore) DeleteByWorker ¶ added in v0.0.3
func (s *RedisStore) DeleteByWorker(workerID string) int
func (*RedisStore) EntriesForWorker ¶ added in v0.0.3
func (s *RedisStore) EntriesForWorker(workerID string) map[string]Entry
func (*RedisStore) RerouteWorker ¶ added in v0.0.3
func (s *RedisStore) RerouteWorker(oldWorkerID, newWorkerID string) int
func (*RedisStore) Set ¶ added in v0.0.3
func (s *RedisStore) Set(sessionID string, entry Entry)
func (*RedisStore) SweepIdle ¶ added in v0.0.3
func (s *RedisStore) SweepIdle(_ time.Duration) int
SweepIdle is a no-op for Redis — TTL-based expiry handles idle cleanup.
func (*RedisStore) Touch ¶ added in v0.0.3
func (s *RedisStore) Touch(sessionID string) bool
type Store ¶
type Store interface {
Get(sessionID string) (Entry, bool)
Set(sessionID string, entry Entry)
Touch(sessionID string) bool
Delete(sessionID string)
DeleteByWorker(workerID string) int
CountForWorker(workerID string) int
CountForWorkers(workerIDs []string) int
RerouteWorker(oldWorkerID, newWorkerID string) int
EntriesForWorker(workerID string) map[string]Entry
SweepIdle(maxAge time.Duration) int
}
Store defines the contract for session state storage. MemoryStore is the in-process implementation; Redis implements the same interface for shared state during rolling updates.