Files
galaxy-game/lobby/internal/adapters/redisstate/keyspace.go
T
2026-04-26 20:34:39 +02:00

69 lines
2.8 KiB
Go

package redisstate
import (
"encoding/base64"
"galaxy/lobby/internal/domain/common"
)
// defaultPrefix is the mandatory `lobby:` namespace prefix shared by every
// Game Lobby Redis key.
const defaultPrefix = "lobby:"
// Keyspace builds the Game Lobby Redis keys that survive the PG_PLAN.md
// §6A and §6B migrations: per-game ephemeral runtime aggregates,
// capability-evaluation guards, gap activation timestamps, and stream
// consumer offsets. The four core enrollment entities (game, application,
// invite, membership) and the Race Name Directory live in PostgreSQL —
// their previous keyspace methods are gone.
//
// All dynamic key segments are encoded with base64url so raw key structure
// does not depend on user-provided or caller-provided characters.
type Keyspace struct{}
// GapActivatedAt returns the Redis key that stores the gap-window
// activation timestamp for one game.
func (Keyspace) GapActivatedAt(gameID common.GameID) string {
return defaultPrefix + "gap_activated_at:" + encodeKeyComponent(gameID.String())
}
// StreamOffset returns the Redis key that stores the last successfully
// processed entry id for one Redis Stream consumer. The streamLabel is
// the short logical identifier of the consumer (e.g. `runtime_results`,
// `gm_events`, `user_lifecycle`), not the full stream name; it stays
// stable when the underlying stream key is renamed.
func (Keyspace) StreamOffset(streamLabel string) string {
return defaultPrefix + "stream_offsets:" + encodeKeyComponent(streamLabel)
}
// GameTurnStat returns the per-user Redis key that stores the
// initial/max stats aggregate for one game. keeps one key per
// user so the Lua-backed SaveInitial and UpdateMax scripts can operate
// on a single primary key without a secondary index.
func (Keyspace) GameTurnStat(gameID common.GameID, userID string) string {
return defaultPrefix + "game_turn_stats:" +
encodeKeyComponent(gameID.String()) + ":" +
encodeKeyComponent(userID)
}
// GameTurnStatsByGame returns the set key that stores every userID for
// which a GameTurnStat key exists for gameID. The set is the lookup
// index used by Load and Delete so they avoid a Redis SCAN over the
// whole keyspace.
func (Keyspace) GameTurnStatsByGame(gameID common.GameID) string {
return defaultPrefix + "game_turn_stats_by_game:" +
encodeKeyComponent(gameID.String())
}
// CapabilityEvaluationGuard returns the Redis key whose presence marks
// gameID as already evaluated by the The capability evaluator
// uses SETNX on this key to make replayed `game_finished` events safe.
func (Keyspace) CapabilityEvaluationGuard(gameID common.GameID) string {
return defaultPrefix + "capability_evaluation:done:" +
encodeKeyComponent(gameID.String())
}
func encodeKeyComponent(value string) string {
return base64.RawURLEncoding.EncodeToString([]byte(value))
}