feat(server/coverart): batch summary log with category breakdown
The existing processed/succeeded/failed tally written to scan_runs
collapses too much to debug "0 successes" symptoms — failed includes
"settled to none (correct)" alongside "left NULL (transient/IO error,
will retry)" alongside "skipped (no MBID)". An operator looking at
{"failed": 144, "succeeded": 0} can't tell whether the enricher is
broken, the upstream is broken, the data is missing MBIDs, or the
artists genuinely aren't in TheAudioDB.
Adds a single end-of-batch Info log per stage with the breakdown:
- eligible: rows returned by the SQL filter
- processed: rows the loop actually touched
- succeeded: art attached (provider success)
- settled_none: tried, no provider had art (legit miss)
- no_mbid: skipped inside the enricher because MBID was NULL
(artist stage only)
- left_null: tried, transient/IO failure, will retry next pass
- errored: internal error during the entry (logged separately)
The JSON written to scan_runs keeps its existing shape so the admin
UI's stage badges aren't disturbed.
This commit is contained in:
@@ -135,6 +135,12 @@ func (e *Enricher) recordArtistArt(ctx context.Context, artistID pgtype.UUID, th
|
||||
// EnrichArtistBatch drains up to limit artists whose eligibility loop
|
||||
// determines they need processing. Iterates serially. Returns
|
||||
// per-batch tallies for the orchestrator's scan-run record.
|
||||
//
|
||||
// Emits a single Info log line at end-of-batch with a category
|
||||
// breakdown (succeeded / settled-none / no-MBID / left-NULL /
|
||||
// errored). The JSON tally written to scan_runs keeps the existing
|
||||
// processed/succeeded/failed shape for backwards compat with the
|
||||
// admin overview UI; the log line is the operator's diagnostic.
|
||||
func (e *Enricher) EnrichArtistBatch(ctx context.Context, limit int, dataDir string,
|
||||
progressCb func(processed, succeeded, failed int)) (processed, succeeded, failed int, err error) {
|
||||
if limit <= 0 {
|
||||
@@ -148,8 +154,10 @@ func (e *Enricher) EnrichArtistBatch(ctx context.Context, limit int, dataDir str
|
||||
if qerr != nil {
|
||||
return 0, 0, 0, fmt.Errorf("list missing artist art: %w", qerr)
|
||||
}
|
||||
var settledNone, noMBID, leftNull, errored int
|
||||
for _, id := range rows {
|
||||
if ctx.Err() != nil {
|
||||
e.logArtistBatchSummary(len(rows), processed, succeeded, settledNone, noMBID, leftNull, errored)
|
||||
return processed, succeeded, failed, ctx.Err()
|
||||
}
|
||||
processed++
|
||||
@@ -157,6 +165,7 @@ func (e *Enricher) EnrichArtistBatch(ctx context.Context, limit int, dataDir str
|
||||
e.logger.Warn("coverart: artist batch entry failed",
|
||||
"artist_id", uuidString(id), "err", eerr)
|
||||
failed++
|
||||
errored++
|
||||
if progressCb != nil {
|
||||
progressCb(processed, succeeded, failed)
|
||||
}
|
||||
@@ -166,25 +175,49 @@ func (e *Enricher) EnrichArtistBatch(ctx context.Context, limit int, dataDir str
|
||||
row, rerr := q.GetArtistByID(ctx, id)
|
||||
if rerr != nil {
|
||||
failed++
|
||||
errored++
|
||||
if progressCb != nil {
|
||||
progressCb(processed, succeeded, failed)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if row.ArtistArtSource != nil &&
|
||||
switch {
|
||||
case row.ArtistArtSource != nil &&
|
||||
*row.ArtistArtSource != "" &&
|
||||
*row.ArtistArtSource != "none" {
|
||||
*row.ArtistArtSource != "none":
|
||||
succeeded++
|
||||
} else {
|
||||
case row.ArtistArtSource != nil && *row.ArtistArtSource == "none":
|
||||
settledNone++
|
||||
failed++
|
||||
case row.Mbid == nil || *row.Mbid == "":
|
||||
noMBID++
|
||||
failed++
|
||||
default:
|
||||
leftNull++
|
||||
failed++
|
||||
}
|
||||
if progressCb != nil {
|
||||
progressCb(processed, succeeded, failed)
|
||||
}
|
||||
}
|
||||
e.logArtistBatchSummary(len(rows), processed, succeeded, settledNone, noMBID, leftNull, errored)
|
||||
return processed, succeeded, failed, nil
|
||||
}
|
||||
|
||||
// logArtistBatchSummary emits a single Info line at end-of-batch with
|
||||
// the full category breakdown including MBID-skipped (artists eligible
|
||||
// by the SQL filter but skipped inside EnrichArtist due to NULL MBID).
|
||||
func (e *Enricher) logArtistBatchSummary(eligible, processed, succeeded, settledNone, noMBID, leftNull, errored int) {
|
||||
e.logger.Info("coverart: artist enrichment batch complete",
|
||||
"eligible", eligible,
|
||||
"processed", processed,
|
||||
"succeeded", succeeded,
|
||||
"settled_none", settledNone,
|
||||
"no_mbid", noMBID,
|
||||
"left_null", leftNull,
|
||||
"errored", errored)
|
||||
}
|
||||
|
||||
// CleanupArtistArt removes the artist-art directory for a deleted
|
||||
// artist. Idempotent — no-op if the directory doesn't exist. Called
|
||||
// from the artist-delete cascade in tracks/service.go after the
|
||||
|
||||
@@ -168,6 +168,12 @@ func (e *Enricher) recordAlbumCover(ctx context.Context, albumID pgtype.UUID, pa
|
||||
// EnrichBatch drains up to limit albums whose eligibility loop
|
||||
// determines they need processing. Iterates serially. Returns
|
||||
// per-batch tallies for the orchestrator's scan-run record.
|
||||
//
|
||||
// Emits a single Info log line at end-of-batch with a category
|
||||
// breakdown (succeeded / settled-none / left-NULL / errored). The
|
||||
// JSON tally written to scan_runs keeps the existing
|
||||
// processed/succeeded/failed shape for backwards compat with the
|
||||
// admin overview UI; the log line is the operator's diagnostic.
|
||||
func (e *Enricher) EnrichBatch(ctx context.Context, limit int,
|
||||
progressCb func(processed, succeeded, failed int)) (processed, succeeded, failed int, err error) {
|
||||
if limit <= 0 {
|
||||
@@ -181,8 +187,10 @@ func (e *Enricher) EnrichBatch(ctx context.Context, limit int,
|
||||
if qerr != nil {
|
||||
return 0, 0, 0, fmt.Errorf("list missing: %w", qerr)
|
||||
}
|
||||
var settledNone, leftNull, errored int
|
||||
for _, id := range rows {
|
||||
if ctx.Err() != nil {
|
||||
e.logBatchSummary("album", len(rows), processed, succeeded, settledNone, leftNull, errored)
|
||||
return processed, succeeded, failed, ctx.Err()
|
||||
}
|
||||
processed++
|
||||
@@ -190,6 +198,7 @@ func (e *Enricher) EnrichBatch(ctx context.Context, limit int,
|
||||
e.logger.Warn("coverart: batch entry failed",
|
||||
"album_id", uuidString(id), "err", eerr)
|
||||
failed++
|
||||
errored++
|
||||
if progressCb != nil {
|
||||
progressCb(processed, succeeded, failed)
|
||||
}
|
||||
@@ -199,27 +208,49 @@ func (e *Enricher) EnrichBatch(ctx context.Context, limit int,
|
||||
row, rerr := q.GetAlbumWithFirstTrackPath(ctx, id)
|
||||
if rerr != nil {
|
||||
failed++
|
||||
errored++
|
||||
if progressCb != nil {
|
||||
progressCb(processed, succeeded, failed)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if row.CoverArtSource != nil &&
|
||||
switch {
|
||||
case row.CoverArtSource != nil &&
|
||||
(*row.CoverArtSource == "sidecar" ||
|
||||
*row.CoverArtSource == "mbcaa" ||
|
||||
*row.CoverArtSource == "embedded" ||
|
||||
*row.CoverArtSource == "theaudiodb") {
|
||||
*row.CoverArtSource == "theaudiodb"):
|
||||
succeeded++
|
||||
} else {
|
||||
case row.CoverArtSource != nil && *row.CoverArtSource == "none":
|
||||
settledNone++
|
||||
failed++
|
||||
default:
|
||||
leftNull++
|
||||
failed++
|
||||
}
|
||||
if progressCb != nil {
|
||||
progressCb(processed, succeeded, failed)
|
||||
}
|
||||
}
|
||||
e.logBatchSummary("album", len(rows), processed, succeeded, settledNone, leftNull, errored)
|
||||
return processed, succeeded, failed, nil
|
||||
}
|
||||
|
||||
// logBatchSummary emits a single Info line at end-of-batch with the
|
||||
// full category breakdown. Operator-side diagnostic; the
|
||||
// processed/succeeded/failed tally collapses too much to debug
|
||||
// "0 successes" symptoms by itself.
|
||||
func (e *Enricher) logBatchSummary(stage string, eligible, processed, succeeded, settledNone, leftNull, errored int) {
|
||||
e.logger.Info("coverart: enrichment batch complete",
|
||||
"stage", stage,
|
||||
"eligible", eligible,
|
||||
"processed", processed,
|
||||
"succeeded", succeeded,
|
||||
"settled_none", settledNone,
|
||||
"left_null", leftNull,
|
||||
"errored", errored)
|
||||
}
|
||||
|
||||
// EnrichRetryMissing drains both NULL and 'none' rows. Used by the
|
||||
// admin bulk-retry endpoint. Clears 'none' to NULL first so
|
||||
// EnrichAlbum picks them up.
|
||||
|
||||
Reference in New Issue
Block a user