refactor: deduplicate persistence, add upsert queries, throttle snake saves #4

Merged
ryan merged 3 commits from refactor/game-efficiency into main 2026-03-03 05:02:04 +00:00
5 changed files with 48 additions and 12 deletions
Showing only changes of commit 9a20467438 - Show all commits

View File

@@ -4,8 +4,26 @@ import (
"context"
"github.com/ryanhamamura/c4/db/repository"
"github.com/rs/zerolog/log"
)
func (gi *GameInstance) save() error {
err := saveGame(gi.queries, gi.game)
if err != nil {
log.Error().Err(err).Str("game_id", gi.game.ID).Msg("failed to save game")
}
return err
}
func (gi *GameInstance) savePlayer(player *Player, slot int) error {
err := saveGamePlayer(gi.queries, gi.game.ID, player, slot)
if err != nil {
log.Error().Err(err).Str("game_id", gi.game.ID).Int("slot", slot).Msg("failed to save game player")
}
return err
}
// saveGame persists the game state via upsert.
func saveGame(queries *repository.Queries, g *Game) error {
var winnerUserID *string

View File

@@ -49,7 +49,7 @@ func (gs *GameStore) Create() *GameInstance {
gs.gamesMu.Unlock()
if gs.queries != nil {
saveGame(gs.queries, gi.game) //nolint:errcheck
gi.save() //nolint:errcheck
}
return gi
@@ -152,8 +152,8 @@ func (gi *GameInstance) Join(ps *PlayerSession) bool {
}
if gi.queries != nil {
saveGamePlayer(gi.queries, gi.game.ID, ps.Player, slot) //nolint:errcheck
saveGame(gi.queries, gi.game) //nolint:errcheck
gi.savePlayer(ps.Player, slot) //nolint:errcheck
gi.save() //nolint:errcheck
}
gi.notify()
@@ -190,7 +190,7 @@ func (gi *GameInstance) CreateRematch(gs *GameStore) *GameInstance {
gi.game.RematchGameID = &newID
if gi.queries != nil {
if err := saveGame(gi.queries, gi.game); err != nil {
if err := gi.save(); err != nil {
gs.Delete(newID) //nolint:errcheck
gi.game.RematchGameID = nil
return nil
@@ -224,7 +224,7 @@ func (gi *GameInstance) DropPiece(col int, playerColor int) bool {
}
if gi.queries != nil {
saveGame(gi.queries, gi.game) //nolint:errcheck
gi.save() //nolint:errcheck
}
gi.notify()

View File

@@ -62,7 +62,7 @@ func (si *SnakeGameInstance) countdownPhase() {
si.game.Status = StatusInProgress
if si.queries != nil {
saveSnakeGame(si.queries, si.game) //nolint:errcheck
si.save() //nolint:errcheck
}
si.gameMu.Unlock()
si.notify()
@@ -123,7 +123,7 @@ func (si *SnakeGameInstance) gamePhase() {
if time.Since(lastInput) > inactivityLimit {
si.game.Status = StatusFinished
if si.queries != nil {
saveSnakeGame(si.queries, si.game) //nolint:errcheck
si.save() //nolint:errcheck
}
si.gameMu.Unlock()
si.notify()
@@ -193,7 +193,7 @@ func (si *SnakeGameInstance) gamePhase() {
// Throttle DB saves: persist on game over or every 2 seconds
if si.queries != nil && (gameOver || time.Since(lastSave) >= 2*time.Second) {
saveSnakeGame(si.queries, si.game) //nolint:errcheck
si.save() //nolint:errcheck
lastSave = time.Now()
}

View File

@@ -4,8 +4,26 @@ import (
"context"
"github.com/ryanhamamura/c4/db/repository"
"github.com/rs/zerolog/log"
)
func (si *SnakeGameInstance) save() error {
err := saveSnakeGame(si.queries, si.game)
if err != nil {
log.Error().Err(err).Str("game_id", si.game.ID).Msg("failed to save snake game")
}
return err
}
func (si *SnakeGameInstance) savePlayer(player *Player) error {
err := saveSnakePlayer(si.queries, si.game.ID, player)
if err != nil {
log.Error().Err(err).Str("game_id", si.game.ID).Msg("failed to save snake player")
}
return err
}
// saveSnakeGame persists the snake game state via upsert.
func saveSnakeGame(queries *repository.Queries, sg *SnakeGame) error {
boardJSON := "{}"

View File

@@ -62,7 +62,7 @@ func (ss *SnakeStore) Create(width, height int, mode GameMode, speed int) *Snake
ss.gamesMu.Unlock()
if ss.queries != nil {
saveSnakeGame(ss.queries, sg) //nolint:errcheck
si.save() //nolint:errcheck
}
return si
@@ -206,8 +206,8 @@ func (si *SnakeGameInstance) Join(player *Player) bool {
si.game.Players[slot] = player
if si.queries != nil {
saveSnakePlayer(si.queries, si.game.ID, player) //nolint:errcheck
saveSnakeGame(si.queries, si.game) //nolint:errcheck
si.savePlayer(player) //nolint:errcheck
si.save() //nolint:errcheck
}
si.notify()
@@ -293,7 +293,7 @@ func (si *SnakeGameInstance) CreateRematch() *SnakeGameInstance {
si.game.RematchGameID = &newID
if si.queries != nil {
saveSnakeGame(si.queries, si.game) //nolint:errcheck
si.save() //nolint:errcheck
}
si.gameMu.Unlock()