Repository navigation
Conversation
Propose that a served root's optimize, cleanup, full-text rebuild and repair run in the server holding the cluster lock, as owned operations behind asynchronous graph-scoped routes. Optimize is cut into per-table units that stage without gates and publish in batches under an exact pin compare-and-swap; writers retry over maintenance commits; cleanup protects same-process reads and gains a declared retention floor. Record pending amendments in the decision logs of the three accepted RFCs whose sentences the draft would supersede, and add the registry row.
|
Recommendation: revise. Why, what, how, impactA graph served from S3 cannot be maintained on 0.14. Optimize, cleanup, the full-text rebuild, and repair run only in a direct process. That process accepts local storage only. A direct process beside a live server stops on the cluster lock. This RFC gives that work to the server that holds the cluster lock. It cuts optimize into per-table units, stages them with no writer gates, and publishes them in short batches. Each batch is one graph commit. Writers retry when the new commits are maintenance commits. Cleanup keeps a retention floor and, later, stops taking every writer gate. The impact is that foreground writes can continue during maintenance, and an S3 graph can be compacted without a second publisher. The publication rule in Phase A is fit to accept. Six points in the text are not. They change row survival, write success, or which open design owns indexes. What I checkedI read this RFC at These premises hold:
A6 is the right rule for Phase A. The unit effect is one table pin plus the schema identity. The batch still goes through one The pending decision-log entries are the right way to amend accepted RFCs. They do not pretend a draft has retired those sentences. Blocking
Optional
Tradeoff and liabilityPhase A, after the retry rule and the budget are corrected, removes a real liability. An S3 graph can be maintained by the one process that already publishes. Writers no longer stop for a whole optimize. The state that earns its cost is two policy actions, a pin set on the existing read guard, and later one A hand-built rebase and a second index fold do not earn their cost. Lance already builds the detached manifest. #1156 already owns incremental indexes. The concepts in several places are publication, the pin check, reader protection, and index catch-up. One publisher (the existing exact-head compare-and-swap), one read guard, and one index reconciler make those invariants hold. A later maintenance feature then adds a unit and a route. What Phase A gives up is one commit per optimize. A run publishes several batches, and physical layout can lag between them. Logical rows must stay the same. That matches invariant 7. The exact pin check stays. I would not give that up to make more units publish. Lance 14 row-address BTREE is correctly left as a guard. I did not treat the 14.0.0-rc.2 notes as a stable contract. The Lance 13 path is enough: on stable row ids, a row-id index follows its rows and an address index drops coverage of rewritten fragments ( Validation
RecommendationRevise the six blocking points in the text. I would accept the lock-holder placement and the Phase A publication rule after those corrections. Inline comments
The promise in M2 says a write never fails because of maintenance. This sentence says the same. The mechanism can still fail the write. The cap is 3 retries. Batches are 5 seconds apart, so a load longer than about 15 seconds can see more than 3 maintenance commits. The map drops old entries and entries from before boot. A missing entry uses today's Use that existing loop. Retry while every commit between the captured head and the new head is a maintenance commit, and stop at the existing attempt budget. In Phase A, bound the map by the attempt, not by a count the batch pacing can exceed. The backstop (about line 433) waits one whole unit, not one publish window. Name that wait as the one exception, and bound it by the unit time.
Apply the rewrite groups onto the current manifest. Reassign fragment ids above that manifest's high-water ( The shipped unit is a compaction commit plus a fold (
This budget is also the backstop hold. Phase A still folds a lagging index by a whole rebuild (
Give the whole-index fold its own budget, or leave folds out of Phase A units until the incremental reconciler exists. Suspend a table when one unsplittable task cannot fit the unit budget. Do not reserve that table again.
PR #1156 owns this algorithm, and its rule is different. On Lance 13 the full-text merge does not filter old entries. #1156 cites This paragraph merges every kind into one segment and re-issues the full-text certificate on that merge. That publishes the stale full-text case. Point Phase C at #1156 and keep the whole rebuild until that RFC's guard is green.
PR #1150 adds Add The policy action has to be one action. This RFC gives
The cited range is not only the head check. Keep tag validation, unattached-snapshot roots, and merge-base roots. Delete a file only when no live pin lists it. The chain walk proves that only when every new commit inherits files from its parent or writes new files. A pin that is itself a version in the reclaim set skips that table. State that this includes a branch created at an old commit and a tag that names a reclaimed version. |
Bound the writer promise by the existing 32-attempt loop, with writer priority and the backstop's one-unit wait as the named exception. Replace the TransactionRebase classifier with applying the rewrite groups onto the current manifest through the detached path, dropping every link the engine cannot prove. Split folds out of compaction units into index units with their own budget, suspend tables whose task or fold cannot fit, and point incremental index work at the incremental index reconciliation RFC. Add index build to the served operation set with the one-release grant overlap, keep the collector's tag, branch and incarnation checks in Phase B, and keep automatic compaction behind an explicit flag.
|
Revised in Blocking
Optional
|
|
Recommendation: revise. Why, what, how, impactA graph served from S3 cannot be maintained on 0.14. Optimize, cleanup, the full-text rebuild, and repair run only in a direct process. That process accepts local storage only. This RFC gives that work to the server that holds the cluster lock. Compaction and index catch-up become separate units. Each batch is one graph commit. A write retries inside its existing loop when every new commit is a maintenance commit. Phase A publication is fit to accept. The exact pin check stays. The six earlier blockers are fixed in the text, except the new writer-priority rule below. PR #1177 still specifies a second contract for the same command. Earlier findings1. Retry contract. Partly resolved. The cap of 3 is gone. The loop is the one mutations, loads, and Blob writes already use ( Writer priority is new. It is not safe yet. See blocking finding 1. 2. Phase B rebase. Resolved. Detached commit does not call The re-stage rule matches the code. New ids have to sit above P's high-water, and they must not be 0. 3. Budgets. Resolved. Folds are index units. They are outside the 20 second budget, and the backstop does not reserve them. A compaction task that does not fit the byte budget suspends the table. Lance commits any subset of a plan ( 4. Phase C and #1156. Resolved. The all-kinds single-segment merge and the certificate re-issue are gone. Whole rebuild stays until the #1156 guard is green. On Lance 13, #1156 can filtered-merge a BTREE and still rebuilds full text and vector. This RFC does not publish the stale full-text case. 5. Index build. Resolved.
6. Phase B cleanup. Resolved.
Earlier optional notes. Resolved. F7 matches the full-text Blocking1. Writer priority and the backstop stop each otherM2 promises one extra attempt. The lane then publishes no further batch on that branch until the write finishes (writer priority, about line 565). That ban also covers the backstop batch. The backstop reserves the table before the writer takes a gate (about line 484). A write that has already retried then waits on the reservation, so it does not finish. The reserved unit needs a batch publish to release the reservation. Writer priority forbids that publish. The text does not say that the unit timer runs while the unit waits to publish. If the timer covers that wait, the unit is cancelled and the table is suspended ( Steady writes to that table hit this path. The table is the one the backstop exists to compact. Other tables on the branch also wait, because the ban is per branch. Writers in general do not livelock. After one retry the lane stops, so a fresh attempt does not see a later maintenance commit. Maintenance still publishes between generations of writes. The failure is this cycle. A batch that has already taken the publish gates can still commit after the write decides to retry. That commit can force a second retry. M2 allows one. Required rule:
2. PR #1177 is a second contract for the same command#1177 implements a different served-optimize RFC. It adds the policy action This RFC forbids that journal (A3 and the deny-list). It removes the whole-run gate (F2, M2). #1175 already adds Name #1177 in this RFC. State that this design replaces that journal and that action. Otherwise two recovery rules can both merge. Optional1. Name the operation fields the rebase readsA keyed insert is A delete uses The guard row tests an untouched delete and an overlapping update. Add an overlapping delete. The rebased manifest must not contain the deleted rows. 2. The Phase A map bound is wider than the sentencePriority starts after the retry. Before that retry, one long attempt can see a batch every 5 seconds. Each batch adds a map entry. The entry stays until the attempt ends. The map is bounded by pacing times the attempt duration. It is not bounded by the number of attempts. Entries are kept, so a missing entry does not fail the write. Correct the sentence. 3. Publish the version
|
docs/rfcs/2026-10-26-served-graph-maintenance.md. A graph served from S3 cannot be maintained on 0.14: optimize, cleanup, the full-text rebuild and repair are direct-only, the direct CLI accepts only local storage since #1115, and a direct run beside a running server refuses on the cluster lock (#1082). Moving today's code into the server would not be enough, because optimize holds every main-branch gate for its whole run and refuses on any main publication, and cleanup holds every writer gate on every branch. #646 asks for maintenance through the owning server, and #958 for reads that survive cleanup.The draft makes the server that holds the cluster lock the only maintenance authority for a served root, with asynchronous graph-scoped routes, CLI dispatch to them and MCP tools; standalone graphs keep the direct CLI. Optimize becomes per-table units that stage without gates and publish in batches, one graph commit each. The guard that keeps this correct is unchanged in strength: each batch publishes with the exact-head compare-and-swap, the head now read under the gate, plus the exact pin of every unit's table, so a rewrite staged on an older pin can never land and resurrect rows. Writers retry over maintenance commits instead of failing; cleanup gains a same-process pin registry, a declared retention floor and, in Phase B, per-table monotone validation instead of the global barrier; maintenance gets
maintainandreclaimgrants, a commit marker at the next storage stamp, and a reconciler, off by default, in Phase C. The RFC records the approved decisions O1 to O8. It adds dated pending-amendment entries to graph access and cross-process writes, RFC 0011 Decision 11 and server runtime and online deployment, quoting the sentences it would supersede; their bodies are unchanged, because a draft cannot retire an accepted contract. The draft RFC 0046, the invariants page and the write-path guide are listed under documents to update on implementation and are not edited, since behavior has not changed.Every
file:linein the RFC was re-checked atda1c4636; the spec it comes from was written at7711e4a3, and the drifted references (the operation inventory, the load re-prepare bound,plan_table_compaction,stage_index_fold, the hand-built Rewrite, the deny-list entry) now carry current lines. Lance 13 references were read in the crates.io 13.0.0 sources. The documentation checks pass locally. No code,openapi.jsonorcluster.yamlshape changes in this pull request.For review: the decision to challenge is A6, that a maintenance unit's authority is its table's registration and pin plus the schema identity, without the graph head; it would be wrong if any content-preserving effect read another table's state, and the adjacency rebuild is kept out of units for that reason. Re-checking against main changed six details from the spec, each worth a second look: the marker is
maintenance_run_id, becauseGraphCommit.kindalready records publication state; the chain walk is the collector'sdiscover_published, sincewalk_chainno longer exists; the starvation backstop reserves its table before writers take any gate, because holding a table gate and then waiting for the branch gate would invert the gate order; the pin registry replaces the export exclusion for optimize and cleanup only, because branch create and delete take it too; a reclaimed historical version answers 410version_reclaimedwhere the server answers 500internaltoday, a status change on an existing failure; and a standalone graph takes retention from flags, since it has no declaration file. Thestorage_hygienefield in thecluster.yamlblock is my concrete form of the spec's "where storage hygiene is configured", since serving credentials cannot in general read lifecycle rules. Least certain: the Lance 14 facts (BTREE becoming row-address based, the distributed index optimize) come from the 14.0.0-rc.2 notes and pull requests, not a stable source, andTransactionRebaseover a detached chain is unexecuted, which is why Phase B's rebase is gated on a Lance fence guard.