Skip to content

Commit 4c1b2cd

Browse files
committed
fix(db): replace FirstOrCreate+Assign with explicit Find→Create/Save in UpsertAlert
Root cause: GORM v2's FirstOrCreate+Assign does not reliably update existing records — Assign attaches attributes before the query but the actual UPDATE is inconsistently applied. Dismissed/fixed alerts fetched from GitHub were silently dropped, leaving DB records permanently stuck as state='open'. Symptom: security_alerts rows had state='open' with dismissed_at or fixed_at populated — an impossible combination from GitHub's API that indicated the DB state was never updated after initial creation. Fix: explicit lookup + create-or-save: 1. Find by (repository_id, alert_type, alert_number) 2. If not found: Create (new record) 3. If found: Save (full UPDATE with current GitHub state, preserving ID/CreatedAt) Verified: bsv-blockchain/arcade and bsv-blockchain/go-wallet-toolbox now show 0 alerts after sync (were showing 3 and 8 respectively — all dismissed/fixed).
1 parent d0340ad commit 4c1b2cd

1 file changed

Lines changed: 18 additions & 4 deletions

File tree

internal/db/repository_analytics.go

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package db
33
import (
44
"context"
55
"errors"
6+
"fmt"
67
"time"
78

89
"gorm.io/gorm"
@@ -258,12 +259,25 @@ func (r *analyticsRepo) GetSnapshotHistory(ctx context.Context, repoID uint, sin
258259
// UpsertAlert creates or updates a security alert
259260
// Matches by repository_id, alert_type, and alert_number
260261
func (r *analyticsRepo) UpsertAlert(ctx context.Context, alert *SecurityAlert) error {
261-
result := r.db.WithContext(ctx).
262+
// NOTE: FirstOrCreate+Assign does not reliably update existing records in GORM v2
263+
// (Assign only assigns on create in some versions). Use explicit Find→Create/Save instead.
264+
var existing SecurityAlert
265+
err := r.db.WithContext(ctx).
262266
Where("repository_id = ? AND alert_type = ? AND alert_number = ?",
263267
alert.RepositoryID, alert.AlertType, alert.AlertNumber).
264-
Assign(alert).
265-
FirstOrCreate(alert)
266-
return result.Error
268+
First(&existing).Error
269+
270+
if errors.Is(err, gorm.ErrRecordNotFound) {
271+
return r.db.WithContext(ctx).Create(alert).Error
272+
}
273+
if err != nil {
274+
return fmt.Errorf("lookup alert: %w", err)
275+
}
276+
277+
// Preserve DB-managed fields; overwrite all others with current GitHub state
278+
alert.ID = existing.ID
279+
alert.CreatedAt = existing.CreatedAt
280+
return r.db.WithContext(ctx).Save(alert).Error
267281
}
268282

269283
// GetOpenAlerts retrieves open alerts for a repository, optionally filtered by severity

0 commit comments

Comments
 (0)