refactor: deduplicate persistence, add upsert queries, throttle snake saves
- Replace Create+Get+Update with UpsertGame/UpsertSnakeGame queries - Extract free functions (saveGame, loadGame, etc.) from duplicated receiver methods on Store and Instance types - Remove duplicate generateID from snake package, reuse game.GenerateID - Throttle snake game DB writes to every 2s instead of every tick - Fix double-lock in c4game chat handler - Update all code for sqlc pointer types (*string instead of sql.NullString)
This commit is contained in:
@@ -7,63 +7,21 @@ package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"time"
|
||||
)
|
||||
|
||||
const createGame = `-- name: CreateGame :one
|
||||
INSERT INTO games (id, board, current_turn, status)
|
||||
VALUES (?, ?, ?, ?)
|
||||
RETURNING id, board, current_turn, status, winner_user_id, winning_cells, created_at, updated_at, rematch_game_id, game_type, grid_width, grid_height, max_players, game_mode, score, snake_speed
|
||||
`
|
||||
|
||||
type CreateGameParams struct {
|
||||
ID string
|
||||
Board string
|
||||
CurrentTurn int64
|
||||
Status int64
|
||||
}
|
||||
|
||||
func (q *Queries) CreateGame(ctx context.Context, arg CreateGameParams) (Game, error) {
|
||||
row := q.db.QueryRowContext(ctx, createGame,
|
||||
arg.ID,
|
||||
arg.Board,
|
||||
arg.CurrentTurn,
|
||||
arg.Status,
|
||||
)
|
||||
var i Game
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Board,
|
||||
&i.CurrentTurn,
|
||||
&i.Status,
|
||||
&i.WinnerUserID,
|
||||
&i.WinningCells,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.RematchGameID,
|
||||
&i.GameType,
|
||||
&i.GridWidth,
|
||||
&i.GridHeight,
|
||||
&i.MaxPlayers,
|
||||
&i.GameMode,
|
||||
&i.Score,
|
||||
&i.SnakeSpeed,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const createGamePlayer = `-- name: CreateGamePlayer :exec
|
||||
INSERT INTO game_players (game_id, user_id, guest_player_id, nickname, color, slot)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
`
|
||||
|
||||
type CreateGamePlayerParams struct {
|
||||
GameID string
|
||||
UserID sql.NullString
|
||||
GuestPlayerID sql.NullString
|
||||
Nickname string
|
||||
Color int64
|
||||
Slot int64
|
||||
GameID string `db:"game_id" json:"game_id"`
|
||||
UserID *string `db:"user_id" json:"user_id"`
|
||||
GuestPlayerID *string `db:"guest_player_id" json:"guest_player_id"`
|
||||
Nickname string `db:"nickname" json:"nickname"`
|
||||
Color int64 `db:"color" json:"color"`
|
||||
Slot int64 `db:"slot" json:"slot"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateGamePlayer(ctx context.Context, arg CreateGamePlayerParams) error {
|
||||
@@ -91,13 +49,13 @@ const getActiveGames = `-- name: GetActiveGames :many
|
||||
SELECT id, board, current_turn, status, winner_user_id, winning_cells, created_at, updated_at, rematch_game_id, game_type, grid_width, grid_height, max_players, game_mode, score, snake_speed FROM games WHERE game_type = 'connect4' AND status < 2
|
||||
`
|
||||
|
||||
func (q *Queries) GetActiveGames(ctx context.Context) ([]Game, error) {
|
||||
func (q *Queries) GetActiveGames(ctx context.Context) ([]*Game, error) {
|
||||
rows, err := q.db.QueryContext(ctx, getActiveGames)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []Game
|
||||
var items []*Game
|
||||
for rows.Next() {
|
||||
var i Game
|
||||
if err := rows.Scan(
|
||||
@@ -120,7 +78,7 @@ func (q *Queries) GetActiveGames(ctx context.Context) ([]Game, error) {
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
items = append(items, &i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
@@ -135,7 +93,7 @@ const getGame = `-- name: GetGame :one
|
||||
SELECT id, board, current_turn, status, winner_user_id, winning_cells, created_at, updated_at, rematch_game_id, game_type, grid_width, grid_height, max_players, game_mode, score, snake_speed FROM games WHERE id = ?
|
||||
`
|
||||
|
||||
func (q *Queries) GetGame(ctx context.Context, id string) (Game, error) {
|
||||
func (q *Queries) GetGame(ctx context.Context, id string) (*Game, error) {
|
||||
row := q.db.QueryRowContext(ctx, getGame, id)
|
||||
var i Game
|
||||
err := row.Scan(
|
||||
@@ -156,20 +114,20 @@ func (q *Queries) GetGame(ctx context.Context, id string) (Game, error) {
|
||||
&i.Score,
|
||||
&i.SnakeSpeed,
|
||||
)
|
||||
return i, err
|
||||
return &i, err
|
||||
}
|
||||
|
||||
const getGamePlayers = `-- name: GetGamePlayers :many
|
||||
SELECT game_id, user_id, guest_player_id, nickname, color, slot, created_at FROM game_players WHERE game_id = ?
|
||||
`
|
||||
|
||||
func (q *Queries) GetGamePlayers(ctx context.Context, gameID string) ([]GamePlayer, error) {
|
||||
func (q *Queries) GetGamePlayers(ctx context.Context, gameID string) ([]*GamePlayer, error) {
|
||||
rows, err := q.db.QueryContext(ctx, getGamePlayers, gameID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []GamePlayer
|
||||
var items []*GamePlayer
|
||||
for rows.Next() {
|
||||
var i GamePlayer
|
||||
if err := rows.Scan(
|
||||
@@ -183,7 +141,7 @@ func (q *Queries) GetGamePlayers(ctx context.Context, gameID string) ([]GamePlay
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
items = append(items, &i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
@@ -201,13 +159,13 @@ WHERE gp.user_id = ?
|
||||
ORDER BY g.updated_at DESC
|
||||
`
|
||||
|
||||
func (q *Queries) GetGamesByUserID(ctx context.Context, userID sql.NullString) ([]Game, error) {
|
||||
func (q *Queries) GetGamesByUserID(ctx context.Context, userID *string) ([]*Game, error) {
|
||||
rows, err := q.db.QueryContext(ctx, getGamesByUserID, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []Game
|
||||
var items []*Game
|
||||
for rows.Next() {
|
||||
var i Game
|
||||
if err := rows.Scan(
|
||||
@@ -230,7 +188,7 @@ func (q *Queries) GetGamesByUserID(ctx context.Context, userID sql.NullString) (
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
items = append(items, &i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
@@ -257,21 +215,21 @@ ORDER BY g.updated_at DESC
|
||||
`
|
||||
|
||||
type GetUserActiveGamesRow struct {
|
||||
ID string
|
||||
Status int64
|
||||
CurrentTurn int64
|
||||
UpdatedAt sql.NullTime
|
||||
MyColor int64
|
||||
OpponentNickname sql.NullString
|
||||
ID string `db:"id" json:"id"`
|
||||
Status int64 `db:"status" json:"status"`
|
||||
CurrentTurn int64 `db:"current_turn" json:"current_turn"`
|
||||
UpdatedAt *time.Time `db:"updated_at" json:"updated_at"`
|
||||
MyColor int64 `db:"my_color" json:"my_color"`
|
||||
OpponentNickname *string `db:"opponent_nickname" json:"opponent_nickname"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetUserActiveGames(ctx context.Context, userID sql.NullString) ([]GetUserActiveGamesRow, error) {
|
||||
func (q *Queries) GetUserActiveGames(ctx context.Context, userID *string) ([]*GetUserActiveGamesRow, error) {
|
||||
rows, err := q.db.QueryContext(ctx, getUserActiveGames, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []GetUserActiveGamesRow
|
||||
var items []*GetUserActiveGamesRow
|
||||
for rows.Next() {
|
||||
var i GetUserActiveGamesRow
|
||||
if err := rows.Scan(
|
||||
@@ -284,7 +242,7 @@ func (q *Queries) GetUserActiveGames(ctx context.Context, userID sql.NullString)
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
items = append(items, &i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
@@ -295,31 +253,38 @@ func (q *Queries) GetUserActiveGames(ctx context.Context, userID sql.NullString)
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const updateGame = `-- name: UpdateGame :exec
|
||||
UPDATE games
|
||||
SET board = ?, current_turn = ?, status = ?, winner_user_id = ?, winning_cells = ?, rematch_game_id = ?, updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = ?
|
||||
const upsertGame = `-- name: UpsertGame :exec
|
||||
INSERT INTO games (id, board, current_turn, status, game_type, winner_user_id, winning_cells, rematch_game_id)
|
||||
VALUES (?, ?, ?, ?, 'connect4', ?, ?, ?)
|
||||
ON CONFLICT(id) DO UPDATE SET
|
||||
board = excluded.board,
|
||||
current_turn = excluded.current_turn,
|
||||
status = excluded.status,
|
||||
winner_user_id = excluded.winner_user_id,
|
||||
winning_cells = excluded.winning_cells,
|
||||
rematch_game_id = excluded.rematch_game_id,
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
`
|
||||
|
||||
type UpdateGameParams struct {
|
||||
Board string
|
||||
CurrentTurn int64
|
||||
Status int64
|
||||
WinnerUserID sql.NullString
|
||||
WinningCells sql.NullString
|
||||
RematchGameID sql.NullString
|
||||
ID string
|
||||
type UpsertGameParams struct {
|
||||
ID string `db:"id" json:"id"`
|
||||
Board string `db:"board" json:"board"`
|
||||
CurrentTurn int64 `db:"current_turn" json:"current_turn"`
|
||||
Status int64 `db:"status" json:"status"`
|
||||
WinnerUserID *string `db:"winner_user_id" json:"winner_user_id"`
|
||||
WinningCells *string `db:"winning_cells" json:"winning_cells"`
|
||||
RematchGameID *string `db:"rematch_game_id" json:"rematch_game_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateGame(ctx context.Context, arg UpdateGameParams) error {
|
||||
_, err := q.db.ExecContext(ctx, updateGame,
|
||||
func (q *Queries) UpsertGame(ctx context.Context, arg UpsertGameParams) error {
|
||||
_, err := q.db.ExecContext(ctx, upsertGame,
|
||||
arg.ID,
|
||||
arg.Board,
|
||||
arg.CurrentTurn,
|
||||
arg.Status,
|
||||
arg.WinnerUserID,
|
||||
arg.WinningCells,
|
||||
arg.RematchGameID,
|
||||
arg.ID,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user