Skip to content

Commit

Permalink
Better handling of and in tournament score updates.
Browse files Browse the repository at this point in the history
  • Loading branch information
zyro committed Nov 11, 2023
1 parent 2f18823 commit 49598b5
Show file tree
Hide file tree
Showing 2 changed files with 14 additions and 16 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ The format is based on [keep a changelog](http://keepachangelog.com) and this pr
- Upgrade GRPC dependency.
- Writing tournament scores now updates number of scores even if submitted score is not an improvement.
- Move internal queries with variable number of args to a fixed number of args syntax.
- Better handling of `num_score` and `max_num_score` in tournament score updates.
- Build with Go 1.21.4 and use Debian bookworm-slim for base docker images.

### Fixed
Expand Down
29 changes: 13 additions & 16 deletions server/core_tournament.go
Original file line number Diff line number Diff line change
Expand Up @@ -545,34 +545,31 @@ func TournamentRecordWrite(ctx context.Context, logger *zap.Logger, db *sql.DB,
query := `INSERT INTO leaderboard_record (leaderboard_id, owner_id, username, score, subscore, metadata, expiry_time, max_num_score)
VALUES ($1, $2, $3, $9, $10, COALESCE($7, '{}'::JSONB), $4, $8)
ON CONFLICT (owner_id, leaderboard_id, expiry_time)
DO UPDATE SET ` + opSQL + `, num_score = leaderboard_record.num_score + 1, metadata = COALESCE($7, leaderboard_record.metadata), username = COALESCE($3, leaderboard_record.username), update_time = now() RETURNING (SELECT (score,subscore) FROM leaderboard_record WHERE leaderboard_id=$1 AND owner_id=$2 AND expiry_time=$4)`
DO UPDATE SET ` + opSQL + `, num_score = leaderboard_record.num_score + 1, metadata = COALESCE($7, leaderboard_record.metadata), username = COALESCE($3, leaderboard_record.username), update_time = now() RETURNING (SELECT (score,subscore,num_score,max_num_score) FROM leaderboard_record WHERE leaderboard_id=$1 AND owner_id=$2 AND expiry_time=$4)`
params = append(params, leaderboard.MaxNumScore, scoreAbs, subscoreAbs)

if err := ExecuteInTx(ctx, db, func(tx *sql.Tx) error {
dbOldScores := int64Tuple{}
err := tx.QueryRowContext(ctx, query, params...).Scan(&dbOldScores)
if err != nil && err != sql.ErrNoRows {
if err != nil && !errors.Is(err, sql.ErrNoRows) {
var pgErr *pgconn.PgError
if errors.As(err, &pgErr) && pgErr.Code == dbErrorUniqueViolation && strings.Contains(pgErr.Message, "leaderboard_record_pkey") {
return errTournamentWriteNoop
}
return err
}

if dbOldScores.Valid && len(dbOldScores.Tuple) == 2 {
var dbNumScore int64
var dbMaxNumScore int64
if dbOldScores.Valid && len(dbOldScores.Tuple) == 4 {
oldScore = &dbOldScores.Tuple[0]
oldSubscore = &dbOldScores.Tuple[1]
}

var dbNumScore int
var dbMaxNumScore int

// This query could probably be avoided by adding the num_scores to the result of the insert and adding one to it,
// and if there is an error at this point it will be a sql.ErrNoRows meaning this is an insert so we can set it to one.
err = tx.QueryRowContext(ctx, "SELECT num_score, max_num_score FROM leaderboard_record WHERE leaderboard_id = $1 AND owner_id = $2 AND expiry_time = $3", leaderboard.Id, ownerId, expiryTime).Scan(&dbNumScore, &dbMaxNumScore)
if err != nil {
logger.Error("Error reading leaderboard record.", zap.Error(err))
return err
dbNumScore = dbOldScores.Tuple[2] + 1
dbMaxNumScore = dbOldScores.Tuple[3]
} else {
// There was no previous score.
dbNumScore = 1
dbMaxNumScore = int64(leaderboard.MaxNumScore)
}

// Check if the max number of submissions has been reached.
Expand All @@ -594,8 +591,8 @@ func TournamentRecordWrite(ctx context.Context, logger *zap.Logger, db *sql.DB,
}

return nil
}); err != nil && err != errTournamentWriteNoop {
if err == runtime.ErrTournamentWriteMaxNumScoreReached || err == runtime.ErrTournamentMaxSizeReached {
}); err != nil && !errors.Is(err, errTournamentWriteNoop) {
if errors.Is(err, runtime.ErrTournamentWriteMaxNumScoreReached) || errors.Is(err, runtime.ErrTournamentMaxSizeReached) {
logger.Info("Aborted writing tournament record", zap.String("reason", err.Error()), zap.String("tournament_id", tournamentId), zap.String("owner_id", ownerId.String()))
} else {
logger.Error("Could not write tournament record", zap.Error(err), zap.String("tournament_id", tournamentId), zap.String("owner_id", ownerId.String()))
Expand Down

0 comments on commit 49598b5

Please sign in to comment.