Record import bookkeeping scalability findings

This commit is contained in:
2026-10-25 14:29:40 +00:00
parent d898cb193d
commit 6440a500f7

View File

@@ -75,3 +75,97 @@ overlay, and passive-resource-context tests. Add a focused behavioral regression
only for a consequential gap exposed by the change; do not assert incidental
canonicalization/stat call counts. Run the development check and applicable
ownership race checks after remediation.
## Algorithmic Audit Scope
Audited worktree: `d898cb1`, with production code at the reviewed implementation
revision above. This follow-up used source inspection and graph caller traces,
not benchmarks or new runtime measurements. It covered acquisition and import
bookkeeping, scope scheduling and projection, filesystem context handoffs,
inheritance preparation/combination/finalization, and reflective projection and
schema construction.
Findings 2–3 are retained inefficiencies in paths carried through the refactor,
not algorithmic regressions introduced by the consolidation. They warrant
focused follow-up without changing the selected configuration model. The audit
did not identify an additional correctness defect. Finding 1 remains open.
The selected stable Kahn scheduler, deterministic sorting, composition rules,
scope boundaries, and required ownership copies are not reconsidered here.
Previously deferred experiments in [future.md](future.md#3-performance-experiments)
and the [clone review](future.md#4-clone-traversal-review) retain their separate
disposition. These findings do not establish new public complexity guarantees,
input restrictions, performance thresholds, or release gates.
## 2. Avoid Searching All Sources For Each Empty Import Selection
Status: open. Priority: low. Category: algorithmic efficiency.
**Evidence.** `walkDeclarations` calls `recordEmptyImportSelection` for each
optional declaration that admits no source. In
[import_acquisition.go](../../internal/engine/import_acquisition.go), lines
289–300, the latter finds the declaring source by scanning `s.sources` from the
beginning, even though traversal already knows which source owns the declaration.
**Complexity.** With S acquired sources and D empty declarations, source lookup
takes O(S × D) comparisons in the worst case, excluding path-copying and source
selection itself. A broad main import of N constant-size fragments, each with
one empty optional declaration, visits source positions 1 through N: the sum of
lookup work is Θ(N²), despite linear source and selection-evidence volume and
constant import depth. Existing input budgets bound the workload but do not
remove this avoidable scaling within those budgets.
**Potential refactoring.** Carry the declaring source's integer index through
the declaration traversal, or maintain a traversal-local source-ID-to-index map.
Either approach makes each evidence-owner lookup O(1), with O(S + D) total
bookkeeping for the map alternative. Keep source order and IDs unchanged. Use
indices rather than retained pointers into the appendable source slice, and
preserve optional-missing rollback and failed-source retention if maintaining
an index across admission.
**Verification.** Reuse the source-local/detached empty-selection and partial
failure coverage in [import_evidence_test.go](../../internal/engine/import_evidence_test.go).
A broad-fragment behavioral case is useful if existing coverage does not protect
the chosen index handoff. Verify the removed scan by source review rather than
adding timing thresholds or assertions about private lookup counts.
## 3. Restrict Import-Scope Edge Preparation To Relevant Nodes
Status: open. Priority: medium. Category: algorithmic efficiency and allocation
scaling.
**Evidence.** `scheduleAcquisitionScopes` in
[imports.go](../../internal/engine/imports.go), lines 57–64, recursively schedules
the outer scope and every pending item-local scope using the same complete
inclusion graph. `scheduleContributionSlots` in
[import_graph.go](../../internal/engine/import_graph.go), lines 185–213, constructs
a local identity-to-slot map but then scans every graph edge and allocates an
edge slice with capacity `len(graph.edges)` for each scope with at least two
eligible contributions. Most edges need not touch that scope.
**Complexity.** For Q such scopes and E inclusion edges, preparing the scheduler
inputs takes O(Q × E) edge inspections and requests O(Q × E) edge-slice capacity
over the operation. This is cumulative allocation demand, not a claim that all
those slices remain live together. For N sibling item imports, each with one
distinct mapping helper, Q and E are both Θ(N), making this preparation Θ(N²)
even at constant nesting depth. The selected scheduler itself is not the
problem: each small item-local graph has constant size, and the outer graph
requires its normal deterministic scheduling work.
**Potential refactoring.** Build a traversal-local adjacency index once after
acquisition, then gather edges for a scope by its participating source identities
and retain only edges whose endpoints both have local slots. Alternatively,
partition the existing edges by their applicable scopes once. Preserve the
current endpoint-membership rule, item-boundary representation, slot placement,
group scheduling, and stable Kahn tie-breaking. For the broad sibling family,
this reduces edge preparation to O(E + P), where P is the total number of local
slots, followed by the unchanged scheduler cost. Keep this an indexing change
around the agreed scheduler, not a different ordering algorithm or a cross-load
cache.
**Verification.** Reuse item/helper interleaving and grouped import scheduling
coverage in [imports_test.go](../../internal/engine/imports_test.go) and
[load_request_test.go](../../internal/engine/load_request_test.go), together with
existing destination/provenance evidence tests. Cover broad independent item
scopes if a consequential gap remains; verify index construction and edge
visitation bounds by source review without new performance gates.