Skip to content

rows: a state its family refuses while it scatters is forgotten after one recall - #1543

Merged
ashhart merged 2 commits into
ashhart:mainfrom
wojo:up-scatter
Oct 29, 2026
Merged

ashhart merged 2 commits into
ashhart:mainfrom
wojo:up-scatter

Conversation

@wojo

@wojo wojo commented Oct 28, 2026

Copy link
Copy Markdown
Contributor

Fixes #1539.

A disk state that its family refuses while it scatters is now forgotten after one recall, the way a file that fails
its own checks already is.

What changes

  • One copy path. rows_read.one and the read-ahead branch of read called the family's scatter with a bare
    try. Both now go through one helper. A refusal goes to bad with the file being copied (a block's .blk, or the
    state's own .st), so that file is counted, logged and unlinked once, with every state that names it.
  • What counts as a refusal. Words the family will not take (DiskWords, KeptWordsDiffer), or a size or range
    that does not fit its blank (SnapshotSize, BadKeptRange). They are one switch (refused), so a family's new
    refusal is one line. On main, Nemotron raises SnapshotSize, and Qwen3.5 and Flash Next raise KeptWordsDiffer and
    BadKeptRange, all from inside scatter. GLM's DiskWords arrives with GLM-5.3-Flash on CUDA keeps conversation states on disk, every rank's part (--learn-conversations) #1411.
  • Other errors pass through as before. A failing link or memory keeps the state and its read.
  • Reads ahead. bad now drops the read ahead of every state it forgets (Prefetch.forget), whoever called it.
    Before, a taken read stayed listed until a sweep, and a state of the same name written later could have joined it
    again, refused words and all.

Tests

rows_refusal_test.zig recalls a state with the fault on a shared head block (both states go), on the state's own
file (only it goes) and with a transient error (kept). It does this synchronously and with both states read ahead,
and checks the counts, the states, the files and the reads of both. The Fake family gains a scatters fault (after
n copies, every one fails with a given error).

  • With the fix reverted, both tests fail at rows_refusal_test.zig:126 (expected 1, found 0: nothing counted, the
    state kept).
  • With KeptWordsDiffer or BadKeptRange removed from refused, both fail at the same line.
  • With only Prefetch.forget removed from bad, the read-ahead test fails at :137 (expected 2, found 0: the
    forgotten states' reads never freed).

Host (Zig 0.17.0, an M4 Mac), on the branch's head: test-rows-host, test-rows-prefetch-host,
test-learn-recovery, test-cuda-host, test-snapshot-index-host, test-lane-api-host and test-golden pass
(286/286 steps). zig fmt --check and git diff --check are clean, and lean_check.py reports 426 on main and on the
branch. No GPU run: on main a scatter refusal follows only a writer bug, and these tests drive it through the Fake
family.

By Robert Wojciechowski (wojo on GitHub, @rwojo on X)

wojo and others added 2 commits October 29, 2026 11:35
… one recall

rows_read.one and the read-ahead branch of read called the family's scatter with a bare try, so a refusal
inside it skipped bad: nothing was counted or logged and the state stayed in the store, where every later
recall that picked it read its files again. Nemotron's SnapshotSize, Qwen3.5's and FlashNext's KeptWordsDiffer
and BadKeptRange, and DiskWords where a family raises it all took that path.

Both paths now copy through one helper that sends a refusal to bad with the file being copied (a block's .blk,
or the state's own .st), so that file is counted, logged and unlinked with every state naming it, once. A
refusal is words the family will not take (DiskWords, KeptWordsDiffer) or a size or range that does not fit
its blank (SnapshotSize, BadKeptRange); they are listed in one switch (refused), so a family's new one is a
single line. Other errors, a failing link or memory, pass through as before and keep the state and its read.

bad now drops the read ahead of every state it forgets (Prefetch.forget), whichever caller got there: a taken
read stayed listed until a sweep, and would have been joined again, with the refused words, by a state of the
same name that is written later.

The Fake family gains a scatters fault (after n copies, every one fails with a given error).
rows_refusal_test.zig recalls a state with the fault on a shared head block (both states going), on the
state's own file (only it going) and with a transient error (kept), synchronously and with both states read
ahead, checking the counts, the states, the files and the reads of both.

Fixes ashhart#1539.
@ashhart
ashhart merged commit 1017c5c into ashhart:main Oct 29, 2026
9 checks passed
@ashhart

ashhart commented Oct 31, 2026

Copy link
Copy Markdown
Owner

Thank you @wojo. This is in TensorFold 1.2.0, released today: https://github.com/ashhart/TensorFold/releases/tag/v1.2.0

It landed with your commits under your own name, so you're listed as a TensorFold contributor, and it's credited to you in the release notes and the changelog.

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.

Rows store: a disk state refused while it scatters is not forgotten, so every recall reads it again

2 participants