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>
76 lines
3.5 KiB
SQL
76 lines
3.5 KiB
SQL
-- 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).
|
|
|
|
-- name: GcCloseStalePlayEvents :execrows
|
|
-- 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).
|
|
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';
|
|
|
|
-- name: GcClosePlaySessionsWithNoRecentEvents :execrows
|
|
-- 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.
|
|
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')
|
|
);
|
|
|
|
-- name: GcExpireScrobbleQueueFailedRows :execrows
|
|
-- 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.).
|
|
DELETE FROM scrobble_queue
|
|
WHERE status = 'failed'
|
|
AND enqueued_at < now() - INTERVAL '14 days';
|
|
|
|
-- name: GcResetStuckSystemPlaylistRuns :execrows
|
|
-- 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).
|
|
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';
|
|
|
|
-- name: GcDeleteExpiredPasswordResets :execrows
|
|
-- 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.
|
|
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');
|
|
|
|
-- name: GcDeleteExpiredSessions :execrows
|
|
-- Sessions past their idle (30 days) or absolute (1 year) limit. They already
|
|
-- fail auth through GetSessionByTokenHash's filter; this only clears the rows.
|
|
DELETE FROM sessions
|
|
WHERE last_seen_at <= now() - interval '30 days'
|
|
OR created_at <= now() - interval '365 days';
|