Skip to content

ADR-0038: bounded untracked bulk mutation batches

Status: accepted for the documented bounded contract. All 718 provider units and 255 live provider cases pass, including fourteen bulk cases. The node throughput report (maintainer-local record) retains three process runs, 486 timed observations and 54 separate valid protocol probes.

EF 10.0.12 exposes ExecuteUpdateAsync and ExecuteDeleteAsync in the core non-relational query pipeline. Implement its actual translation hooks and compile a dedicated nonquery expression into an asynchronous executor. Keep EF responsible for model metadata, parameter extraction and compiled query caching. Do not add an independent IQueryable or pretend that these operations use tracked-save semantics.

GraphBridge's finite batch deliberately trades an additional selection/lock phase for bounded client memory, mapped-value validation and an explicit stale-candidate failure. Read at most 10,001 candidates to prove the 10,000-row cap, validate schema, lock and recheck the selected rows, then send one parameterized UNWIND update/delete in the same transaction. Reuse finalized graph bindings and scalar conversion; no entity or second state tracker is created. Physical IDs are temporary server locators only. All assignments use the original mapped values; supported integer arithmetic is checked before mutation. This is bounded scalar assignment evaluation, not arbitrary client predicate execution or compiled user code.

Update only requested mutable properties and generated row versions. Do not change keys/endpoints, refresh EF's tracker, run SaveChanges interception, silently delete relationships or imply original-value concurrency handling. Callers can express expected tokens in predicates. A stale candidate found during lock/recheck aborts the entire current transaction; later inserts are not fenced. Successful borrowed transactions remain caller-owned. Cancellation and uncertain-commit handling follow the existing async transaction contract.

The initial plain-node checkpoint now extends to association/identified edges and required/optional native-reference roots. Graph roots validate required endpoint and type-wide edge constraints, lock candidates/endpoints through their declared key properties, then repeat the complete identity/membership read before mutation. Native-reference deletion removes its modeled incoming link and retains restrictive deletion for all other relationships. An external-edge failure rolls back link removal too. The node throughput gate is now accepted with timing, allocation and complete metadata/transaction exchange evidence against tracked saves and a model-specific native batch. Measured multirow gains do not imply every size or graph shape is faster; one-row update is slightly slower than tracked EF. Graph throughput, byte-aware budgets and sustained contention remain future investigations.

Evidence: seven focused protocol tests pass within 718 units; fourteen live bulk cases pass within the complete 255-case provider regression, zero failures/skips and zero build warnings/errors. Live evidence verifies the node boundaries plus graph identity/membership, endpoint preservation, real competing writes, post-selection duplicates and the 10,001-row cap. The optional-reference seed contains three children; an initial test expectation of two was corrected after inspecting that fixture, with sibling preservation asserted. The fixture/network were removed; no hosted workflow was dispatched. The contract separates verified graph semantics from the measured ordinary-node throughput scope. The benchmark fixture was also removed after successful completion; sampled free memory stayed above 8.329 GiB.