Files
minstrel/internal/db/dbq/gc.sql.go
T
bvandeusenandClaude Opus 5.5 1d4ef1a23f docs: query comments say what each query selects and why, not how it came to be (#6277)
Rewrites the internal/db/queries comments to rule 32 and regenerates
dbq, whose doc comments and embedded SQL carry them. The bugs behind the
tiered mixes, the md5 order seed, the score bands and the round-robin
arms are restated as the constraint each one enforces; counts from one
library and stale cross-references are gone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 12:31:13 -04:00

141 lines
5.1 KiB
Go

// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.31.1
// source: gc.sql
package dbq
import (
"context"
)
const gcClosePlaySessionsWithNoRecentEvents = `-- name: GcClosePlaySessionsWithNoRecentEvents :execrows
UPDATE play_sessions
SET ended_at = COALESCE(last_event_at, started_at)
WHERE ended_at IS NULL
AND (
(track_count > 0 AND last_event_at < now() - INTERVAL '6 hours')
OR
(track_count = 0 AND started_at < now() - INTERVAL '1 hour')
)
`
// play_sessions.ended_at has no other writer. Close sessions whose
// last_event_at is older than 6h — treating that as "user moved on" the
// same way audio_service does after grace periods. Sessions with NO events (track_count = 0)
// older than 1h are also closed (stale handshakes from clients that
// never recorded a play). ended_at is set to last_event_at so the
// session's duration reads naturally.
func (q *Queries) GcClosePlaySessionsWithNoRecentEvents(ctx context.Context) (int64, error) {
result, err := q.db.Exec(ctx, gcClosePlaySessionsWithNoRecentEvents)
if err != nil {
return 0, err
}
return result.RowsAffected(), nil
}
const gcCloseStalePlayEvents = `-- name: GcCloseStalePlayEvents :execrows
UPDATE play_events
SET ended_at = COALESCE(
started_at + (duration_played_ms * INTERVAL '1 millisecond'),
now()
)
WHERE ended_at IS NULL
AND started_at < now() - INTERVAL '24 hours'
`
// Background garbage-collector / lifecycle queries: rows nothing else closes
// or removes. Sweeper runs on a long tick (default 1h)
// so per-query cost is amortised; each is idempotent (re-running on
// already-closed/-deleted rows is a no-op).
// Play_events rows opened more than 24h ago that never got a
// play_ended. Client crashed mid-track, network dropped, etc. We
// synthesize ended_at = started_at + duration_played_ms when present,
// otherwise leave duration_played_ms null and stamp ended_at = now()
// so the row stops looking "open" for downstream queries that filter
// ended_at IS NULL. Doesn't touch was_skipped (we genuinely don't
// know if the user skipped).
func (q *Queries) GcCloseStalePlayEvents(ctx context.Context) (int64, error) {
result, err := q.db.Exec(ctx, gcCloseStalePlayEvents)
if err != nil {
return 0, err
}
return result.RowsAffected(), nil
}
const gcDeleteExpiredPasswordResets = `-- name: GcDeleteExpiredPasswordResets :execrows
DELETE FROM password_resets
WHERE (used_at IS NOT NULL AND used_at < now() - INTERVAL '7 days')
OR (used_at IS NULL AND expires_at < now() - INTERVAL '1 hour')
`
// Password_resets accumulates expired + used rows forever. The
// validation path already rejects them; this just keeps the table
// from growing unbounded. Used rows are kept for 7 days for audit;
// unused expired rows go immediately.
func (q *Queries) GcDeleteExpiredPasswordResets(ctx context.Context) (int64, error) {
result, err := q.db.Exec(ctx, gcDeleteExpiredPasswordResets)
if err != nil {
return 0, err
}
return result.RowsAffected(), nil
}
const gcDeleteExpiredSessions = `-- name: GcDeleteExpiredSessions :execrows
DELETE FROM sessions
WHERE last_seen_at <= now() - interval '30 days'
OR created_at <= now() - interval '365 days'
`
// Sessions past their idle (30 days) or absolute (1 year) limit. They already
// fail auth through GetSessionByTokenHash's filter; this only clears the rows.
func (q *Queries) GcDeleteExpiredSessions(ctx context.Context) (int64, error) {
result, err := q.db.Exec(ctx, gcDeleteExpiredSessions)
if err != nil {
return 0, err
}
return result.RowsAffected(), nil
}
const gcExpireScrobbleQueueFailedRows = `-- name: GcExpireScrobbleQueueFailedRows :execrows
DELETE FROM scrobble_queue
WHERE status = 'failed'
AND enqueued_at < now() - INTERVAL '14 days'
`
// Scrobble_queue rows that have been in status='failed' for
// more than 14 days. The worker stops retrying after maxAttempts;
// failed rows accumulate forever otherwise. CASCADE from play_events
// already drops the row when the underlying event is deleted, so this
// only handles persistent failures (token revoked, etc.).
func (q *Queries) GcExpireScrobbleQueueFailedRows(ctx context.Context) (int64, error) {
result, err := q.db.Exec(ctx, gcExpireScrobbleQueueFailedRows)
if err != nil {
return 0, err
}
return result.RowsAffected(), nil
}
const gcResetStuckSystemPlaylistRuns = `-- name: GcResetStuckSystemPlaylistRuns :execrows
UPDATE system_playlist_runs
SET in_flight = false,
last_error = COALESCE(last_error, 'stuck-row auto-reset by gc')
WHERE in_flight = true
AND last_run_at < now() - INTERVAL '10 minutes'
`
// System_playlist_runs.in_flight = true can wedge on a
// goroutine panic between SET in_flight=true and SET in_flight=false.
// The duplicate-prevention check refuses to start a fresh regen while
// in_flight, so a stuck row blocks all future regens for that user.
// Reset rows where last_run_at is older than 10 minutes (regens
// shouldn't take that long).
func (q *Queries) GcResetStuckSystemPlaylistRuns(ctx context.Context) (int64, error) {
result, err := q.db.Exec(ctx, gcResetStuckSystemPlaylistRuns)
if err != nil {
return 0, err
}
return result.RowsAffected(), nil
}