From c2d68122300a2759e898e10132ae3e2c9959a494 Mon Sep 17 00:00:00 2001 From: Grail Finder Date: Fri, 4 Jul 2025 12:25:20 +0300 Subject: [PATCH] Enha: tx for cron --- crons/main.go | 27 ++++++++++++++++++++++++++- handlers/actions.go | 32 -------------------------------- handlers/game.go | 2 +- main.go | 4 ---- repos/players.go | 14 +++++++++++--- 5 files changed, 38 insertions(+), 41 deletions(-) diff --git a/crons/main.go b/crons/main.go index 9e152de..306dd5d 100644 --- a/crons/main.go +++ b/crons/main.go @@ -29,18 +29,36 @@ func (cm *CronManager) Start() { } func (cm *CronManager) CleanupRooms() { - ctx := context.Background() + ctx, tx, err := cm.repo.InitTx(context.Background()) + if err != nil { + cm.log.Error("failed to init transaction", "err", err) + return + } + defer func() { + if r := recover(); r != nil { + if err := tx.Rollback(); err != nil { + cm.log.Error("failed to rollback transaction", "err", err) + } + panic(r) + } + }() + rooms, err := cm.repo.RoomList(ctx) if err != nil { cm.log.Error("failed to get rooms list", "err", err) + if err := tx.Rollback(); err != nil { + cm.log.Error("failed to rollback transaction", "err", err) + } return } + for _, room := range rooms { players, err := cm.repo.PlayerListByRoom(ctx, room.ID) if err != nil { cm.log.Error("failed to get players for room", "room_id", room.ID, "err", err) continue } + if len(players) == 0 { cm.log.Info("deleting empty room", "room_id", room.ID) if err := cm.repo.RoomDeleteByID(ctx, room.ID); err != nil { @@ -48,6 +66,7 @@ func (cm *CronManager) CleanupRooms() { } continue } + creatorInRoom := false for _, player := range players { if player.Username == room.CreatorName { @@ -55,6 +74,7 @@ func (cm *CronManager) CleanupRooms() { break } } + if !creatorInRoom { cm.log.Info("deleting room because creator left", "room_id", room.ID) for _, player := range players { @@ -73,4 +93,9 @@ func (cm *CronManager) CleanupRooms() { } } } + + if err := tx.Commit(); err != nil { + cm.log.Error("failed to commit transaction", "err", err) + } } + diff --git a/handlers/actions.go b/handlers/actions.go index 983f201..d4c037a 100644 --- a/handlers/actions.go +++ b/handlers/actions.go @@ -47,41 +47,9 @@ func notifyBotIfNeeded(room *models.Room) { } } -// cache - -// func saveState(username string, state *models.UserState) error { -// key := models.CacheStatePrefix + username -// data, err := json.Marshal(state) -// if err != nil { -// return err -// } -// memcache.Set(key, data) -// return nil -// } - -// func getAllNames() []string { -// names := []string{} -// // will not scale -// session := &models.Session{} -// // filter by key size only sessions -// for _, name := range wholeMemStore { -// // xid is 20 in len -// if len(k) != 20 { -// continue -// } -// if err := json.Unmarshal(v, &session); err != nil { -// log.Error("failed to unmarshal", "error", err) -// continue -// } -// names = append(names, session.Username) -// } -// return names -// } - // can room exists without state? I think no func getFullInfoByCtx(ctx context.Context) (*models.FullInfo, error) { resp := &models.FullInfo{} - // state, err := getStateByCtx(ctx) state, err := getPlayerByCtx(ctx) if err != nil { return nil, err diff --git a/handlers/game.go b/handlers/game.go index a4e2ed5..0bccfb1 100644 --- a/handlers/game.go +++ b/handlers/game.go @@ -251,7 +251,7 @@ func HandleStartGame(w http.ResponseWriter, r *http.Request) { func HandleJoinRoom(w http.ResponseWriter, r *http.Request) { roomID := r.URL.Query().Get("id") - room, err := repo.RoomGetByID(r.Context(), roomID) + room, err := repo.RoomGetExtended(r.Context(), roomID) if err != nil { abortWithError(w, err.Error()) return diff --git a/main.go b/main.go index fe79796..8d0782a 100644 --- a/main.go +++ b/main.go @@ -62,20 +62,16 @@ func main() { // Setup graceful shutdown stop := make(chan os.Signal, 1) signal.Notify(stop, os.Interrupt, syscall.SIGTERM) - repo := repos.NewRepoProvider(cfg.DBPath) defer repo.Close() - cm := crons.NewCronManager(repo, slog.Default()) cm.Start() - server := ListenToRequests(cfg.ServerConfig.Port) go func() { if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { panic(err) } }() - <-stop slog.Info("Shutting down server...") ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) diff --git a/repos/players.go b/repos/players.go index 188225e..9a8dd7e 100644 --- a/repos/players.go +++ b/repos/players.go @@ -2,6 +2,7 @@ package repos import ( "context" + "fmt" "gralias/models" "github.com/jmoiron/sqlx" @@ -60,10 +61,17 @@ func (p *RepoProvider) PlayerDelete(ctx context.Context, roomID, username string return err } -func (p *RepoProvider) PlayerSetRoomID(ctx context.Context, username, roomID string) error { +func (p *RepoProvider) PlayerSetRoomID(ctx context.Context, roomID, username string) error { db := getDB(ctx, p.DB) - _, err := db.ExecContext(ctx, "UPDATE players SET room_id = ? WHERE username = ?", roomID, username) - return err + res, err := db.ExecContext(ctx, "UPDATE players SET room_id = ? WHERE username = ?", roomID, username) + if err != nil { + return err + } + affected, _ := res.RowsAffected() + if affected == 0 { + return fmt.Errorf("failed to set room_id (%s) for player (%s)", roomID, username) + } + return nil } func (p *RepoProvider) PlayerExitRoom(ctx context.Context, username string) error {