From 528495c75af58e1f5e3e9226d4590d9c1ab1fdeb Mon Sep 17 00:00:00 2001 From: Eric Rakestraw Date: Fri, 9 Oct 2026 03:36:29 +0000 Subject: [PATCH] Index resolution metadata updates --- docs/roadmap/implementation.md | 2 +- internal/engine/load_metadata.go | 126 +++++++++++++---------- internal/engine/resolution.go | 20 +++- internal/engine/resolution_index_test.go | 45 ++++++++ 4 files changed, 135 insertions(+), 58 deletions(-) diff --git a/docs/roadmap/implementation.md b/docs/roadmap/implementation.md index ed032ac..a009636 100644 --- a/docs/roadmap/implementation.md +++ b/docs/roadmap/implementation.md @@ -218,7 +218,7 @@ Roadmap candidate 3, first integration. Owner: `internal/engine/resolution.go`. Validate: `go test ./internal/engine -run 'Resolution|Resolve|Callback|Path'` and the everyday check. Snapshot ownership/contents remain unchanged. -## Stage 8: Replace per-delta whole-metadata scans +## Stage 8: Replace per-delta whole-metadata scans — Complete Roadmap candidate 3, second integration. Owner: engine `load_metadata.go`. diff --git a/internal/engine/load_metadata.go b/internal/engine/load_metadata.go index 62c5f74..ee760af 100644 --- a/internal/engine/load_metadata.go +++ b/internal/engine/load_metadata.go @@ -280,8 +280,23 @@ func cloneMetadata(metadata Metadata) Metadata { } func applyResolutionMetadata(metadata *Metadata, resolved resolutionResult) { - for _, snapshot := range resolved.snapshots { - pointer := snapshot.change.Path.String() + finalPaths := make(map[string]*document.Node) + indexDocument(resolved.final, Path{}, finalPaths) + snapshotCoverage, snapshotPointers := newResolutionCoverageIndexFromSnapshots(resolved.snapshots) + var deltaCoverage pathPrefixIndex + snapshotIDs := make([]int, len(resolved.deltas)) + for id, delta := range resolved.deltas { + if delta.path.valid() { + deltaCoverage.insert(delta.path.String(), id) + } + snapshotIDs[id] = -1 + if snapshotID, ok := snapshotCoverage.deepestCoveringID(delta.path); ok { + snapshotIDs[id] = snapshotID + } + } + + for snapshotID, snapshot := range resolved.snapshots { + pointer := snapshotPointers[snapshotID] change := cloneChange(snapshot.change) if info, ok := metadata.fields[pointer]; ok { change.Previous = []Origin{cloneOrigin(info.Origin)} @@ -290,52 +305,40 @@ func applyResolutionMetadata(metadata *Metadata, resolved resolutionResult) { } metadata.history[pointer] = append(metadata.history[pointer], change) } - appendResolutionDescendantHistory(metadata, resolved) - for _, delta := range resolved.deltas { - record := coveringResolutionSnapshot(resolved.snapshots, delta.path) - if record == nil { - continue - } - for pointer, info := range metadata.fields { - path, err := ParsePath(pointer) - if err != nil || !pathContains(delta.path, path) { - continue - } - info.Origin = cloneOrigin(record.change.Origin) - metadata.fields[pointer] = info - } - } - refreshResolutionFields(metadata.fields, resolved.final, resolved.snapshots) + + appendResolutionDescendantHistory(metadata, resolved.snapshots, snapshotPointers, finalPaths, &deltaCoverage, snapshotIDs) + applyResolutionFieldOrigins(metadata.fields, resolved.snapshots, &deltaCoverage, snapshotIDs) + refreshResolutionFields(metadata.fields, finalPaths, resolved.snapshots, snapshotCoverage) } -func appendResolutionDescendantHistory(metadata *Metadata, resolved resolutionResult) { - finalPaths := make(map[string]*document.Node) - indexDocument(resolved.final, Path{}, finalPaths) - for _, delta := range resolved.deltas { - record := coveringResolutionSnapshot(resolved.snapshots, delta.path) - if record == nil { +func appendResolutionDescendantHistory(metadata *Metadata, snapshots []resolutionSnapshot, snapshotPointers []string, finalPaths map[string]*document.Node, deltaIndex *pathPrefixIndex, snapshotIDs []int) { + allPointers := make(map[string]struct{}, len(metadata.history)+len(finalPaths)) + for pointer := range metadata.history { + if _, err := ParsePath(pointer); err == nil { + allPointers[pointer] = struct{}{} + } + } + for pointer := range finalPaths { + allPointers[pointer] = struct{}{} + } + buckets := make([][]string, len(snapshotIDs)) + for pointer := range allPointers { + deltaIndex.visitAncestors(pointer, func(deltaID int) { + snapshotID := snapshotIDs[deltaID] + if snapshotID < 0 || pointer == snapshotPointers[snapshotID] { + return + } + buckets[deltaID] = append(buckets[deltaID], pointer) + }) + } + for deltaID, pointers := range buckets { + snapshotID := snapshotIDs[deltaID] + if snapshotID < 0 { continue } - affected := make(map[string]bool) - for pointer := range metadata.history { - if path, err := ParsePath(pointer); err == nil && pathContains(delta.path, path) { - affected[pointer] = true - } - } - for pointer := range finalPaths { - if path, err := ParsePath(pointer); err == nil && pathContains(delta.path, path) { - affected[pointer] = true - } - } - pointers := make([]string, 0, len(affected)) - for pointer := range affected { - if pointer != record.change.Path.String() { - pointers = append(pointers, pointer) - } - } slices.Sort(pointers) for _, pointer := range pointers { - change := cloneChange(record.change) + change := cloneChange(snapshots[snapshotID].change) if info, ok := metadata.fields[pointer]; ok { change.Previous = []Origin{cloneOrigin(info.Origin)} } @@ -344,20 +347,33 @@ func appendResolutionDescendantHistory(metadata *Metadata, resolved resolutionRe } } -func coveringResolutionSnapshot(snapshots []resolutionSnapshot, path Path) *resolutionSnapshot { - var covering *resolutionSnapshot - for index := range snapshots { - snapshot := &snapshots[index] - if pathContains(snapshot.change.Path, path) && (covering == nil || len(snapshot.change.Path.segments) > len(covering.change.Path.segments)) { - covering = snapshot +func applyResolutionFieldOrigins(fields map[string]FieldInfo, snapshots []resolutionSnapshot, deltaIndex *pathPrefixIndex, snapshotIDs []int) { + buckets := make([][]string, len(snapshotIDs)) + for pointer := range fields { + if _, err := ParsePath(pointer); err != nil { + continue + } + deltaIndex.visitAncestors(pointer, func(deltaID int) { + if snapshotIDs[deltaID] >= 0 { + buckets[deltaID] = append(buckets[deltaID], pointer) + } + }) + } + for deltaID, pointers := range buckets { + snapshotID := snapshotIDs[deltaID] + if snapshotID < 0 { + continue + } + origin := snapshots[snapshotID].change.Origin + for _, pointer := range pointers { + info := fields[pointer] + info.Origin = cloneOrigin(origin) + fields[pointer] = info } } - return covering } -func refreshResolutionFields(fields map[string]FieldInfo, final *document.Node, snapshots []resolutionSnapshot) { - finalPaths := make(map[string]*document.Node) - indexDocument(final, Path{}, finalPaths) +func refreshResolutionFields(fields map[string]FieldInfo, finalPaths map[string]*document.Node, snapshots []resolutionSnapshot, snapshotCoverage *resolutionCoverageIndex) { for pointer := range fields { if _, ok := finalPaths[pointer]; !ok { delete(fields, pointer) @@ -370,10 +386,8 @@ func refreshResolutionFields(fields map[string]FieldInfo, final *document.Node, continue } origin := Origin{Layer: LayerResolution} - if path, err := ParsePath(pointer); err == nil { - if covering := coveringResolutionSnapshot(snapshots, path); covering != nil { - origin = cloneOrigin(covering.change.Origin) - } + if snapshotID, ok := snapshotCoverage.deepestCoveringPointerID(pointer); ok { + origin = cloneOrigin(snapshots[snapshotID].change.Origin) } fields[pointer] = FieldInfo{Null: node.Kind == document.Null, Origin: origin} } diff --git a/internal/engine/resolution.go b/internal/engine/resolution.go index 78f9a65..9075916 100644 --- a/internal/engine/resolution.go +++ b/internal/engine/resolution.go @@ -361,12 +361,30 @@ func newResolutionCoverageIndexFromRecords(records []*resolutionRecord) *resolut return index } +func newResolutionCoverageIndexFromSnapshots(snapshots []resolutionSnapshot) (*resolutionCoverageIndex, []string) { + index := &resolutionCoverageIndex{depths: make([]int, len(snapshots))} + pointers := make([]string, len(snapshots)) + for id, snapshot := range snapshots { + path := snapshot.change.Path + index.depths[id] = len(path.segments) + pointers[id] = path.String() + if path.valid() { + index.prefix.insert(pointers[id], id) + } + } + return index, pointers +} + func (index *resolutionCoverageIndex) deepestCoveringID(path Path) (int, bool) { if !path.valid() { return 0, false } + return index.deepestCoveringPointerID(path.String()) +} + +func (index *resolutionCoverageIndex) deepestCoveringPointerID(pointer string) (int, bool) { bestID, bestDepth, found := 0, -1, false - index.prefix.visitAncestors(path.String(), func(id int) { + index.prefix.visitAncestors(pointer, func(id int) { depth := index.depths[id] if !found || depth > bestDepth || (depth == bestDepth && id < bestID) { bestID, bestDepth, found = id, depth, true diff --git a/internal/engine/resolution_index_test.go b/internal/engine/resolution_index_test.go index bca5d6e..fb6f4fe 100644 --- a/internal/engine/resolution_index_test.go +++ b/internal/engine/resolution_index_test.go @@ -71,3 +71,48 @@ func TestDocumentLookupMatchesPathScan(t *testing.T) { } } } + +func TestApplyResolutionMetadataPreservesOverlappingDeltaOrder(t *testing.T) { + oldOrigin := Origin{Layer: LayerBase} + parentOrigin := Origin{Layer: LayerResolution, Operation: ChangeNormalization} + childOrigin := Origin{Layer: LayerResolution, Operation: ChangeDerivedDefault} + leaf := &document.Node{Kind: document.String, Text: "value"} + final := &document.Node{Kind: document.Mapping, Mapping: []document.Entry{{ + Key: "group", + Value: &document.Node{Kind: document.Mapping, Mapping: []document.Entry{{ + Key: "item", + Value: &document.Node{Kind: document.Mapping, Mapping: []document.Entry{{Key: "leaf", Value: leaf}}}, + }}}, + }}} + metadata := &Metadata{ + fields: map[string]FieldInfo{"/group/item/leaf": {Origin: oldOrigin}}, + history: make(map[string][]Change), + } + resolved := resolutionResult{ + final: final, + deltas: []documentDelta{{path: Keys("group")}, {path: Keys("group", "item")}}, + snapshots: []resolutionSnapshot{ + {change: Change{Path: Keys("group"), Origin: parentOrigin}}, + {change: Change{Path: Keys("group", "item"), Origin: childOrigin}}, + }, + } + + applyResolutionMetadata(metadata, resolved) + + field := metadata.fields["/group/item/leaf"] + if field.Origin.Operation != childOrigin.Operation { + t.Fatalf("final leaf origin operation = %q, want %q", field.Origin.Operation, childOrigin.Operation) + } + changes := metadata.history["/group/item/leaf"] + if len(changes) != 2 { + t.Fatalf("leaf history has %d entries, want 2: %#v", len(changes), changes) + } + if changes[0].Origin.Operation != parentOrigin.Operation || changes[1].Origin.Operation != childOrigin.Operation { + t.Fatalf("leaf history operations = %q, %q; want %q, %q", changes[0].Origin.Operation, changes[1].Origin.Operation, parentOrigin.Operation, childOrigin.Operation) + } + for index, change := range changes { + if len(change.Previous) != 1 || change.Previous[0].Layer != oldOrigin.Layer { + t.Errorf("leaf history[%d] Previous = %#v, want original origin", index, change.Previous) + } + } +}