Skip to content

rfc: a later check reports a held version as a loss - #1119

Merged
ragnorc merged 1 commit into
ModernRelay:mainfrom
ragnorc:rfc/check-reports-held-version-as-loss
Oct 21, 2026
Merged

ragnorc merged 1 commit into
ModernRelay:mainfrom
ragnorc:rfc/check-reports-held-version-as-loss

Conversation

@ragnorc

@ragnorc ragnorc commented Oct 21, 2026

Copy link
Copy Markdown
Contributor

What

disposition.check is a later read. It carries the branch, the publication id and the head before the write, and it cannot see whether the store acknowledged the attempt. The RFC said a check reports a loss only when the attempted version holds another commit and the store never acknowledged this attempt, a condition the check cannot evaluate. It now reports a loss once the attempted version holds another commit, since a create-if-absent cannot land once its version is held. The in-doubt result of an acknowledged attempt stays on the publisher's read-back, which holds the request record.

Why

Optional finding of the final review of #1017 at 33ba2940 ("The later check cannot see the acknowledgement"), which the reviewer said could land after merge. It matches §5 of storage outcomes per operation (#1093): once another commit holds the version, the write is not committed.

Verification

python3 scripts/check-docs.py: Documentation OK. RFC text only; the decision log records the change.

ragnorc commented Oct 21, 2026

Copy link
Copy Markdown
Contributor Author

Why

disposition.check is a later read. It carries the branch, the publication id, and the head before the write. It cannot see whether the store acknowledged the attempt. The old sentence reported a loss only when two facts were both true. The version holds another publication, and the store never acknowledged this attempt. The check cannot test the second fact. A create-if-absent cannot land on a version that already exists. The write is then not committed. The check can say that from the version alone.

What

The check paragraph now reports a loss when the attempted __manifest version holds another publication id. An acknowledged attempt whose version holds another fact stays in doubt on the publisher read-back. The decision log records that split.

How

A linear __manifest publish writes version read_version + 1 with put-if-absent and zero retries. Lance 13 ConditionalPutCommitHandler uses PutMode::Create. An existing object returns CommitConflict. On main, publisher.rs returns that loss when after_ack is false. It returns in doubt only when after_ack is true and the version holds another head. after_ack is set only after commit::overwrite returns success. A later check does not have that flag. #1093 §5 states the same fence for a linear version. Once another commit holds the version, the abandoned request cannot land.

Impact

A later reader can settle an unknown linear write. The reader does not need an acknowledgement bit that no later read stores. The publisher attempt that saw a success return keeps its in-doubt result. This edit adds no type, no row, and no second record. Further control facts still share one attempt id and one version key.

Tradeoff and liability

The edit removes a condition the check cannot observe. That condition would leave a held version unknown for the life of the branch. The in-doubt result stays on the one attempt that saw the success return. Two observers answer from the evidence each one holds. That is one rule with two inputs. It is one source of truth. The observable graph state is the version. When that version holds another publication, the attempt did not commit there, and put-if-absent will not replace the object.

The publisher in-doubt path is the case where a success return and the version disagree. A later reader has only the version, so loss is the client outcome. Five similar control facts need no new record and no new fence.

Blocking

None.

Optional

  1. The new sentence says the publisher read-back holds the request record. The probe ends inside commit::overwrite. A foreign version is a loss while after_ack is false. In doubt on a foreign version happens only for PublishAttemptFailure::AfterAck, after overwrite returns success. Name that success return. See the inline note.
  2. The decision log says "another commit". The publication id is a graph commit id or a control-fact attempt id. A control fact records no graph commit. Two facts on one chain can share a head. Say "another publication". Frontmatter updated is still 2026-10-20. Set it to 2026-10-21. The frontmatter is the date authority.
  3. The unknown-outcomes bullet still says a read of the fact follows the read-back rule, and that rule includes the acknowledgement split. A later read has no after_ack. Point that bullet at the check paragraph for a reader with no request record.

Recommendation

Merge. The check rule matches the linear version key. The optional notes are wording. They do not change the decision.

Validation

Reviewed head f135a0253feb2c7b09cdf068dd7df669bbff9a22. The pull request was open. It had no reviews and no comments.

Main is 13ecdc8b1e1f65f3e4267c2857d5311b58668e65. The RFC file on main matches parent 17ad3d8e6315f3275442acdec8d6ac025fa5a72c. The merge changes this file only.

Inspection of main 13ecdc8b:

  • crates/omnigraph-catalog/src/publisher.rs. after_ack is PublishAttemptFailure::AfterAck. That variant is set on publish.post_merge_pre_ack after commit::overwrite returns. A foreign head with after_ack calls publish_outcome_in_doubt with "the acknowledged version holds another commit". A foreign head without after_ack returns the original error.
  • crates/omnigraph-catalog/src/commit.rs. Linear placement uses CommitBuilder::with_max_retries(0). ManifestRequest::may_have_landed is true for Held, Landed, and Unresolved. Those errors stay opaque and reach read-back. They do not take the in-doubt foreign-version arm.
  • crates/omnigraph-core/src/manifest_request.rs. The probe records whether the manifest request was sent and how it ended. commit::overwrite reads it. The publisher read-back does not.
  • publisher.rs has an empty diff from e466a127 to 13ecdc8b.

Lance 13.0.0 source, crates.io:

  • lance-table-13.0.0 ConditionalPutCommitHandler::commit uses object_store::PutMode::Create. AlreadyExists and Precondition map to CommitConflict.
  • lance-13.0.0 commit_transaction sets target_version = read_version + 1. strict_overwrite is an Overwrite with num_retries == 0. That path does not rebase.
  • commit_detached_transaction uses random_version | DETACHED_VERSION_MASK and retries. The next linear version does not take that id.

Lance transaction page, fetched 2026-10-21. Put-if-not-exists lets one writer create a given manifest file. The page also describes rebase after a conflict. The catalog publish does not take that rebase. Zero retries skip it.

Open heads:

Command, in a detached worktree at f135a025:

PYTHONPATH=/tmp/docs-py python3 scripts/check-docs.py

Result: Documentation OK (367 Markdown files checked).

This pull request changes RFC text only. disposition.check is a later phase and has no implementation. GQT cannot express this contract. The outcome is not a query row, a count, a column type, or a query error. No temporary GQT case was added. No Rust test was run.

Limitations

The loss rule assumes the store honors put-if-absent. Lance 13 refuses default commits to Tencent COS because that store can ignore the precondition. OmniGraph installs ConditionalPutCommitHandler on its datasets. Supported roots are the local store, S3, and Azure.

A detached proposal still settles by its tag, as #1093 §5 says. head_before + 1 is not that version. The new sentence is about the attempted version the check names.

No catalog test was run. This pull request does not change publisher code, and that code is unchanged across the main range above.

No performance number was checked.

Inline

docs/rfcs/2026-10-16-graph-access-serving-and-cross-process-writes.md:396

Optional. This clause says the publisher read-back holds the request record. ManifestRequestProbe ends inside commit::overwrite. A foreign version is a loss while after_ack is false. That is the return Err(err) arm in publisher.rs. In doubt on a foreign version happens only for PublishAttemptFailure::AfterAck, on publish.post_merge_pre_ack, after overwrite returns success. Name that success return.

docs/rfcs/2026-10-16-graph-access-serving-and-cross-process-writes.md:1543

Optional. Say "another publication", not "another commit". The publication id is a graph commit id or a control-fact attempt id. A control fact records no graph commit, and two facts on one chain can share a head. Also set frontmatter updated to 2026-10-21. It is still 2026-10-20. The frontmatter is the date authority.

@ragnorc
ragnorc added this pull request to the merge queue Oct 21, 2026
Merged via the queue into ModernRelay:main with commit 229ea51 Oct 21, 2026
31 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant