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>
141 lines
5.1 KiB
Go
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
|
|
}
|