Inherit mapping when merging exacts into glob

The pattern-analysis worker created the merged glob blank and re-armed
mapping, discarding the org ThirdParty and description already resolved
on the exacts it absorbed. That forced a full re-map (LLM/web-search)
and opened a window where an in-flight exact could vanish mid-mapping.

Seed the glob from the merged exacts when they unanimously agree on a
single third party, carrying its description too, while still re-arming
mapping so the glob derives its own catalog row. With the third party
pre-set, the mapping worker skips the expensive org/disambiguation
resolution. Conflicting or unresolved groups stay blank as before.

The catalog link is deliberately not inherited: it is keyed on the
exact pattern string, not the glob template, so the mapping worker
resolves the right row itself.

Signed-off-by: Émile Ré <emile@probo.com>
This commit is contained in:
Émile Ré
2026-05-29 17:40:37 +02:00
parent dbd868679d
commit 8e0dc0b7eb
2 changed files with 203 additions and 1 deletions

View File

@@ -161,6 +161,16 @@ func (h *patternAnalysisHandler) Process(ctx context.Context, banner coredata.Co
source := bestSource(group)
// Carry over the resolved org ThirdParty (and its
// description) from the merged exacts when they agree,
// so the glob is seeded rather than re-mapped from
// scratch. Mapping is still re-armed below so the glob
// derives its own catalog row; the pre-set third party
// lets the mapping worker skip the expensive org
// resolution. Only the insert path consumes these: an
// existing glob is reloaded and keeps its own mapping.
inheritedThirdPartyID, inheritedDescription := inheritedMapping(group)
now := time.Now()
globPattern := &coredata.TrackerPattern{
ID: gid.New(banner.ID.TenantID(), coredata.TrackerPatternEntityType),
@@ -172,7 +182,8 @@ func (h *patternAnalysisHandler) Process(ctx context.Context, banner coredata.Co
MatchType: coredata.TrackerPatternMatchTypeGlob,
DisplayName: key.template,
MaxAgeSeconds: maxAge,
Description: "",
ThirdPartyID: inheritedThirdPartyID,
Description: inheritedDescription,
Source: source,
MappingRequestedAt: &now,
CreatedAt: now,
@@ -712,6 +723,47 @@ func bestSource(patterns []*coredata.TrackerPattern) *coredata.CookieSource {
return &src
}
// inheritedMapping rolls up the resolved org ThirdParty of a group of
// exact patterns being merged into a glob, so the glob can be seeded
// instead of re-mapped from scratch. It returns a third party only when
// every resolved member agrees on a single id: a conflicting group (or
// one with no resolved member) returns nil, leaving the glob blank for a
// fresh mapping pass. When a third party is chosen, the description of
// the first member carrying that same id with non-empty text is returned
// too; the catalog link (common_tracker_pattern_id) is deliberately not
// inherited, since it is keyed on the exact pattern string rather than
// the glob template and the mapping worker derives the right row itself.
func inheritedMapping(patterns []*coredata.TrackerPattern) (*gid.GID, string) {
var thirdPartyID *gid.GID
for _, p := range patterns {
if p.ThirdPartyID == nil {
continue
}
if thirdPartyID == nil {
thirdPartyID = p.ThirdPartyID
continue
}
if *thirdPartyID != *p.ThirdPartyID {
return nil, ""
}
}
if thirdPartyID == nil {
return nil, ""
}
for _, p := range patterns {
if p.ThirdPartyID != nil && *p.ThirdPartyID == *thirdPartyID && p.Description != "" {
return thirdPartyID, p.Description
}
}
return thirdPartyID, ""
}
func (h *patternAnalysisHandler) adoptUncategorisedPatterns(
ctx context.Context,
tx pg.Tx,