876f994904
revive flagged the if/else where the else branch was a return — invert the condition and return the non-stale skip path early so the reap flow drops one indent level.
66 lines
2.3 KiB
Go
66 lines
2.3 KiB
Go
package library
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"log/slog"
|
|
"time"
|
|
|
|
"github.com/jackc/pgx/v5"
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
|
|
"git.fabledsword.com/bvandeusen/minstrel/internal/coverart"
|
|
"git.fabledsword.com/bvandeusen/minstrel/internal/db/dbq"
|
|
)
|
|
|
|
// StuckScanThreshold is the age past which an in-flight scan_runs row is
|
|
// reaped on the next TryStartScan attempt. Real scans on realistic libraries
|
|
// finish well within this; the threshold is set far above legitimate scan
|
|
// duration so a normally-running scan is never reaped accidentally.
|
|
const StuckScanThreshold = 1 * time.Hour
|
|
|
|
// TryStartScan attempts to start a new scan, with built-in handling for
|
|
// stuck/stale in-flight rows.
|
|
//
|
|
// Behavior:
|
|
// - No in-flight row → starts RunScan in a detached goroutine; returns (true, nil, nil).
|
|
// - In-flight row, started_at > StuckScanThreshold ago → reaps via FinishScanRun
|
|
// with error_message="reaped (stale)", then starts new scan; returns (true, nil, nil).
|
|
// - In-flight row, started_at within threshold → returns (false, &row, nil); caller
|
|
// decides what to do (HTTP handler returns 409; scheduler logs skip).
|
|
// - DB error during the in-flight check or reap → returns (false, nil, err).
|
|
func TryStartScan(ctx context.Context, pool *pgxpool.Pool,
|
|
scanner *Scanner, enricher *coverart.Enricher,
|
|
logger *slog.Logger, cfg RunScanConfig,
|
|
) (started bool, existing *dbq.GetInFlightScanRunRow, err error) {
|
|
q := dbq.New(pool)
|
|
row, qerr := q.GetInFlightScanRun(ctx)
|
|
if qerr == nil {
|
|
// Found an in-flight row. Reap if stale, else skip.
|
|
age := time.Since(row.StartedAt.Time)
|
|
if age <= StuckScanThreshold {
|
|
return false, &row, nil
|
|
}
|
|
logger.Warn("reaping stale in-flight scan",
|
|
"id", row.ID, "started_at", row.StartedAt.Time, "age", age)
|
|
if rerr := q.FinishScanRun(ctx, dbq.FinishScanRunParams{
|
|
ID: row.ID,
|
|
Column2: "reaped (stale)",
|
|
}); rerr != nil {
|
|
return false, nil, fmt.Errorf("reap stale scan: %w", rerr)
|
|
}
|
|
// Fall through to start a new scan.
|
|
} else if !errors.Is(qerr, pgx.ErrNoRows) {
|
|
return false, nil, fmt.Errorf("in-flight check: %w", qerr)
|
|
}
|
|
|
|
// No in-flight (or just reaped). Start RunScan detached.
|
|
go func() {
|
|
if _, runErr := RunScan(ctx, pool, scanner, enricher, logger, cfg); runErr != nil {
|
|
logger.Warn("scan run failed", "err", runErr)
|
|
}
|
|
}()
|
|
return true, nil, nil
|
|
}
|