Fix deadlock when updating tracker pattern last_matched_at

Replace per-row UPDATE inside the detected-tracker loop with a single
bulk UPDATE ... WHERE id = ANY(...) after the loop. The old approach
locked pattern rows in request-dependent order, causing deadlocks
under concurrent ReportDetectedTrackers calls.

Signed-off-by: Émile Ré <emile@getprobo.com>
This commit is contained in:
Émile Ré
2026-05-13 11:18:05 +04:00
parent 1d367ad51f
commit 6c6137f9f2
2 changed files with 50 additions and 5 deletions

View File

@@ -2023,6 +2023,7 @@ func (s *Service) ReportDetectedTrackers(
inserted := 0
now := time.Now()
var matchedPatternIDs []gid.GID
for _, dc := range req.Cookies {
if err := s.reportDetectedTracker(
@@ -2040,6 +2041,7 @@ func (s *Service) ReportDetectedTrackers(
InitiatorURL: dc.InitiatorURL,
},
&inserted,
&matchedPatternIDs,
); err != nil {
return err
}
@@ -2060,6 +2062,7 @@ func (s *Service) ReportDetectedTrackers(
InitiatorURL: ds.InitiatorURL,
},
&inserted,
&matchedPatternIDs,
); err != nil {
return err
}
@@ -2083,6 +2086,13 @@ func (s *Service) ReportDetectedTrackers(
}
}
if len(matchedPatternIDs) > 0 {
var patterns coredata.TrackerPatterns
if err := patterns.UpdateLastMatchedAt(ctx, tx, scope, matchedPatternIDs, now); err != nil {
return fmt.Errorf("cannot update tracker pattern last_matched_at: %w", err)
}
}
if inserted > 0 {
if err := banner.SetPatternAnalysisRequested(ctx, tx); err != nil {
return fmt.Errorf("cannot request pattern analysis: %w", err)
@@ -2112,6 +2122,7 @@ func (s *Service) reportDetectedTracker(
now time.Time,
info detectedTrackerInfo,
inserted *int,
matchedPatternIDs *[]gid.GID,
) error {
var matchedPattern coredata.TrackerPattern
err := matchedPattern.FindMatchingPattern(ctx, tx, scope, banner.ID, info.TrackerType, info.Identifier)
@@ -2126,11 +2137,7 @@ func (s *Service) reportDetectedTracker(
var patternID *gid.GID
if err == nil {
patternID = &matchedPattern.ID
matchedPattern.LastMatchedAt = &now
matchedPattern.UpdatedAt = now
if updateErr := matchedPattern.Update(ctx, tx, scope); updateErr != nil {
return fmt.Errorf("cannot update tracker pattern last_matched_at: %w", updateErr)
}
*matchedPatternIDs = append(*matchedPatternIDs, matchedPattern.ID)
} else {
newPattern := &coredata.TrackerPattern{
ID: gid.New(scope.GetTenantID(), coredata.TrackerPatternEntityType),

View File

@@ -801,6 +801,44 @@ WHERE
return count, nil
}
func (tps *TrackerPatterns) UpdateLastMatchedAt(
ctx context.Context,
tx pg.Tx,
scope Scoper,
patternIDs []gid.GID,
matchedAt time.Time,
) error {
if len(patternIDs) == 0 {
return nil
}
q := `
UPDATE tracker_patterns
SET
last_matched_at = @matched_at,
updated_at = @updated_at
WHERE
%s
AND id = ANY(@pattern_ids)
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{
"pattern_ids": patternIDs,
"matched_at": matchedAt,
"updated_at": matchedAt,
}
maps.Copy(args, scope.SQLArguments())
_, err := tx.Exec(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot update last_matched_at for tracker patterns: %w", err)
}
return nil
}
func (tps *TrackerPatterns) MoveToCategoryByCookieCategoryID(
ctx context.Context,
tx pg.Tx,