fix: address code review findings

- Start Web UI before the blocking initial sync so the dashboard is
  reachable during the (rate-limited, potentially multi-minute) first
  sync; fold the immediate sync into startPeriodicSync's overlap guard
  so it can never race a concurrent tick over the shared DB / MB client.
- Make MarkNotificationSent idempotent: INSERT OR IGNORE for same-second
  PK collisions, and explicitly swallow FK violations when a release was
  pruned by a concurrent re-sync. Prevents a single vanished/duplicate
  release from aborting the digest mark-sent loop and re-sending.
- Do not abort NotifyOnce's mark-sent loop on a single failure; log and
  continue so every release in the batch is marked.
- NULL-safe reads: COALESCE(type,''), COALESCE(release_date,'') in the
  external_releases and unnotified readers to match the cache reader.
- Update/extend tests for the new idempotency and startup contracts.
This commit is contained in:
2026-07-20 06:27:42 +03:00
parent a8aa445d94
commit aee0241bb7
6 changed files with 110 additions and 42 deletions

View File

@@ -93,9 +93,13 @@ func NotifyOnce(ctx context.Context, db *database.DB, sender Sender, cfg config.
return 0, fmt.Errorf("notifier: send digest: %w", err)
}
// The digest has already been delivered at this point. A failure to mark a
// single release must NOT abort the loop: doing so would leave later
// releases unmarked and cause them to be re-notified (duplicate digest) on
// the next run. Log and continue so every release in this batch is marked.
for _, m := range toNotify {
if err := database.MarkNotificationSent(db, m.RGID); err != nil {
return 0, fmt.Errorf("notifier: mark sent for %s: %w", m.RGID, err)
log.Printf("notifier: mark sent for %s failed: %v", m.RGID, err)
}
}
return len(toNotify), nil