Skip to content

fix(ilm): count a version under old and healed headers as one revision - #332

Merged
Vonng merged 1 commit into
mainfrom
claude/multi-pool-ilm-version-dup-210154
Nov 10, 2026
Merged

Vonng merged 1 commit into
mainfrom
claude/multi-pool-ilm-version-dup-210154

Conversation

@Vonng

@Vonng Vonng commented Nov 10, 2026 •

Copy link
Copy Markdown
Member

Regression in RELEASE.2026-11-09T00-00-00Z, introduced by 9f7b493 (the authoritative version history of the #278 fix). The history accepts a version only when one revision of it is on more than half of the drives and on its read quorum, and it identified a revision by the stored version header plus a digest of the metadata. A drive keeps the header that the release which wrote the version there stored: before RELEASE.2024-04-28 without erasure coding, and until RELEASE.2022-01-25 with a signature that counted empty part ETags. A heal writes a current header and leaves the other drives alone. Reads and heal take both for one version; the history counted two revisions.

With 5 to 11 of 16 drives healed (EC:4), objectVersions fails with a read quorum error and every expiry that reads the history is refused on each scanner cycle, for good: every ILM expiry in a versioned bucket with several pools, and ExpiredObjectAllVersions, DelMarkerExpiration and batch purges of all versions with one pool too. No data is deleted. GET works and heal finds nothing to do; the traces are expiry_incomplete_total{reason="history_unavailable"}, the ILM trace, an audit entry and a log line.

versionRevision now gives the key of an object version the signature and the erasure coding as the current release computes them from the decoded metadata. The key of a version a current release wrote carries its header exactly as stored, so nothing changes for it, including the order of versions of one modification time. The stored flags stay in the key, as fileInfoRevisions relies on. Nothing on disk changes; an upgraded node expires the refused keys when the scanner queues them again.

TestPoolsLifecycleHeaderEras accepts both old headers on every count of healed drives. TestPoolsLifecycleHealedOldHeaders wipes and heals half of the drives of a set, then expires a noncurrent version with two pools and all versions with one. Both fail on main.

Validation: the two new tests, 194 tests of the lifecycle, restore, delete-all, tier, migration and xl.meta format suites, and go vet ./cmd pass locally. The full cmd suite was not run. The tests model old headers; no xl.meta written by the old releases was used. An adversarial review approved the change in its second round; the first round replaced a digest-derived signature, which reordered versions of one modification time in the merge.

Not covered: drives of a restored, transitioned version that differ only in the data-directory flag are still two revisions. That is a separate defect. No CHANGELOG entry yet: the section of the next release does not exist.

Design record, in English and Chinese: pgsty/silo.pgsty.com#58.

🤖 Generated with Claude Code

The version history that an ILM expiry reads accepts a version only
when one revision of it is on more than half of the drives and on its
read quorum, and a revision was the header as the drive stores it plus
a digest of the metadata. A drive holds the header that the release
which wrote the version there stored: before RELEASE.2024-04-28
without erasure coding, and until RELEASE.2022-01-25 with a signature
that counted empty part ETags. A heal writes the header of its own
release and leaves the other drives alone. Reads and heal take both
for one version; the history counted two revisions. With 5 to 11 of 16
drives healed (EC:4) neither had the quorum, objectVersions failed
with a read quorum error, and every expiry that reads the history was
refused on each scanner cycle, with nothing to bring the drives
together: every expiry in a versioned bucket with several pools, and
an expiry of all versions of a key with one pool too.

versionRevision now gives the key of an object version the signature
and the erasure coding as the current release computes them from the
decoded metadata. The key of a version this release wrote carries its
header as stored, so its history decides as before, including the
order of versions of one time, which the signature breaks. The digest
covers what both fields say. The stored flags stay in the key: whether
a transitioned version uses its data directory follows the moment a
drive wrote the entry (fileInfoRevisions).

TestPoolsLifecycleHeaderEras accepts both old headers on every count
of healed drives. TestPoolsLifecycleHealedOldHeaders expires a
noncurrent version with two pools, and all versions with one, after
half of the drives were wiped and healed. Both fail before this
change. Drives that differ only in the data directory flag of a
restored version are still two revisions; that is a separate defect.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Signed-off-by: Feng Ruohang <rh@vonng.com>
@Vonng
Vonng merged commit 032ac49 into main Nov 10, 2026
8 checks passed
Vonng added a commit to pgsty/silo.pgsty.com that referenced this pull request Nov 10, 2026
pgsty/silo#332 merged to main as 36c8d1683. Update the status line of
the design record in both languages, and note that its CI ran the full
cmd suite, the race selection and the cross compile. Not released yet.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Signed-off-by: Feng Ruohang <rh@vonng.com>
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