Files
minstrel/internal/db/dbq/library_changes.sql.go
T
bvandeusen 7cfaafd360 feat(#357): library_changes retention compactor
Closes the last deferred follow-up from #357. The library_changes
table is the append-only change log that drives /api/library/sync's
delta semantics — every mutation (scanner upsert, like, playlist
edit, track delete) writes one row. Without a retention policy the
table grows unbounded; the original migration (0025) called out the
follow-up explicitly.

New goroutine: sync.Compactor runs daily, deletes rows where
changed_at < now - 30 days. Logs a row count when non-zero so
operators can see compaction activity in the journal. First tick
fires on startup so a process that hasn't been compacted in a
while catches up immediately.

30-day retention matches the offline-mode spec
(docs/superpowers/specs/2026-05-09-flutter-offline-mode-design.md).
Clients with a cursor older than that hit the existing 410 fallback
path and resync from scratch.

Imported as syncpkg in main.go to follow the existing convention
(see internal/library/scanner.go).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 15:49:07 -04:00

105 lines
2.6 KiB
Go

// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.31.1
// source: library_changes.sql
package dbq
import (
"context"
"github.com/jackc/pgx/v5/pgtype"
)
const deleteOldLibraryChanges = `-- name: DeleteOldLibraryChanges :execrows
DELETE FROM library_changes WHERE changed_at < $1
`
// Removes library_changes rows older than the given cutoff. Run from
// the sync compactor goroutine on a daily tick. Returns the number of
// rows deleted so the worker can log meaningful progress.
func (q *Queries) DeleteOldLibraryChanges(ctx context.Context, changedAt pgtype.Timestamptz) (int64, error) {
result, err := q.db.Exec(ctx, deleteOldLibraryChanges, changedAt)
if err != nil {
return 0, err
}
return result.RowsAffected(), nil
}
const getLibraryChangesSince = `-- name: GetLibraryChangesSince :many
SELECT id, entity_type, entity_id, op, changed_at
FROM library_changes
WHERE id > $1
ORDER BY id ASC
LIMIT $2
`
type GetLibraryChangesSinceParams struct {
ID int64
Limit int32
}
func (q *Queries) GetLibraryChangesSince(ctx context.Context, arg GetLibraryChangesSinceParams) ([]LibraryChange, error) {
rows, err := q.db.Query(ctx, getLibraryChangesSince, arg.ID, arg.Limit)
if err != nil {
return nil, err
}
defer rows.Close()
var items []LibraryChange
for rows.Next() {
var i LibraryChange
if err := rows.Scan(
&i.ID,
&i.EntityType,
&i.EntityID,
&i.Op,
&i.ChangedAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getMaxLibraryChangeCursor = `-- name: GetMaxLibraryChangeCursor :one
SELECT COALESCE(MAX(id), 0)::BIGINT FROM library_changes
`
func (q *Queries) GetMaxLibraryChangeCursor(ctx context.Context) (int64, error) {
row := q.db.QueryRow(ctx, getMaxLibraryChangeCursor)
var column_1 int64
err := row.Scan(&column_1)
return column_1, err
}
const getMinLibraryChangeCursor = `-- name: GetMinLibraryChangeCursor :one
SELECT COALESCE(MIN(id), 0)::BIGINT FROM library_changes
`
func (q *Queries) GetMinLibraryChangeCursor(ctx context.Context) (int64, error) {
row := q.db.QueryRow(ctx, getMinLibraryChangeCursor)
var column_1 int64
err := row.Scan(&column_1)
return column_1, err
}
const insertLibraryChange = `-- name: InsertLibraryChange :exec
INSERT INTO library_changes (entity_type, entity_id, op)
VALUES ($1, $2, $3)
`
type InsertLibraryChangeParams struct {
EntityType string
EntityID string
Op string
}
func (q *Queries) InsertLibraryChange(ctx context.Context, arg InsertLibraryChangeParams) error {
_, err := q.db.Exec(ctx, insertLibraryChange, arg.EntityType, arg.EntityID, arg.Op)
return err
}