replicated: whole-cluster SIGKILL discards acked messages already in every node's segment files (recovery truncates to a frontier only checkpoints publish)
メンテナーはふだん 1 日以内に返信
評価
- 難易度
- 4/5
- 見積もり時間
- 3〜5日
- 初心者へのやさしさ
- 45/100
調査の方向性
Start with core/integration/tests/cluster/crash_durability.rs and run the failing replicated recovery test using the command in the issue. Then trace the issue’s named recovery paths, PartitionPrepareJournal::open, recover_unpublished_tail, recover_segment_files, and PartitionPersistence::append_batch. Done means the replicated test recovers the acknowledged payloads after whole-cluster SIGKILL, with recovery still stopping at an invalid or torn record.
索引モデルが issue の本文から書いたものです。
説明
Bug description
With durability = replicated, a whole-cluster SIGKILL loses acknowledged messages that were already written to every node's segment files. On restart, each replica truncates its segment back to the last published WAL frontier and logs:
WARN journal::partition_journal::segments: discarding unpublished segment bytes beyond the durable WAL frontier group=0 incarnation=1 start_offset=0 durable_bytes=0 discarded_bytes=9510
Buffered appends do not publish the frontier. Only PartitionPrepareJournal::sync(), checkpoint rewrites and a few control operations (segment enable, view certification) do. PartitionPersistence::append_batch calls sync() only for durable appends, so a replicated partition's frontier stays where its last checkpoint or control operation left it. Recovery trusts only the frontier: when both slots verify, PartitionPrepareJournal::open discards everything after it, as the comment on recover_unpublished_tail says ("the tail is discarded as it always was"). recover_segment_files then truncates the segment to segments.tail.
SIGKILL does not lose a completed write(), because the page cache outlives the process. That is the premise of crash_durability.rs, and the README states the same contract:
A crash (SIGKILL, OOM kill) loses the WAL writes not yet handed to the OS, and power loss also loses what the OS had not written to disk. Under
replicated,qor more such crashes at once can lose the most recently acknowledged writes.
The second sentence alone does not bound the loss, but the first one does: SIGKILL loses the writes not yet handed to the OS. Expected: a whole-cluster SIGKILL loses at most the writes still in process memory. Actual: it loses everything after the last frontier publication, even though those bytes are on disk on all three nodes. Checkpoints, durable appends, persisted consumer-offset stores, the shutdown drain and a few control operations publish the frontier. replicated appends do not, so on a busy replicated partition the published frontier can sit far behind the data.
Failing test
PR #4632 adds given_replicated_eager_flush_topic_when_the_whole_cluster_is_killed_should_recover_flushed_data to core/integration/tests/cluster/crash_durability.rs. It is the replicated twin of the existing given_eager_flush_topic_when_the_whole_cluster_is_killed_should_recover_flushed_data:
- Create a 1-partition topic with
messages_required_to_save = 1anddurability = Replicated. - Send 30 single-message batches and record each confirmed offset.
- Wait until every node's segment files contain all 30 payloads (
wait_until_payloads_installed). kill_cluster()(SIGKILL on every node), thenrestart_cluster().- Poll from offset 0.
On master b15c8e1e9 it fails with:
replicated acked data already in every node's segments must survive a whole-cluster SIGKILL: 0 of 30 acked offsets readable (0 polled, in order: true), first missing or payload-mismatched offset: Some(0)
The persisted twin passes in the same run. All three nodes log durable_bytes=0 discarded_bytes=9510, which is 30 messages of 317 bytes.
Real-process numbers
A 3-node cluster in Docker (apache/iggy:edge, revision 44c7dc834), 2 CPUs and 4 GiB per node, 2 shards, data on volumes. Six producers send about 2,000 msg/s in batches of 20 to a topic with 6 partitions, replicated, and default flush thresholds. Producers retry every send until it is acknowledged. All three nodes get SIGKILL 20 s into the run and restart 15 s later. A verifier then reads every partition from offset 0.
- Lost: 33,860 of 179,760 acknowledged messages. About 40,000 were sent before the kill, so recovery kept roughly the first 15%.
- Order and gaps: no reordering, no offset gaps, no duplicates. The lost messages are a suffix of each partition.
- Discarded bytes: each node logs the discard line for every partition, with the same numbers on all three nodes. For example, partition 5 kept
durable_bytes=525039and droppeddiscarded_bytes=2677984. The discarded bytes summed over the 6 partitions (about 10.6 MB at 319 bytes per message) equal the lost count. persisted: 0 lost in the same scenario.
The same scenario on f6dfbdbb3, before #4387 added the frontier-owned segment recovery, lost 2,829 of 179,400 (1.6%). We reported that on #4266.
Possible directions (for discussion, not in the PR)
- On open, let a
replicatedpartition adopt the WAL records after the published frontier that verify as an unbroken chain (op sequence, parent checksum, segment reservation), and stop at the first record that does not verify. A torn tail after power loss stops the walk. The log stays untrusted for the election as #4500 already does, so a quorum of trusted replicas still decides the view. - If this loss is intended, change the README to say that a whole-cluster crash under
replicatedloses everything since the last frontier publication, which on a busy partition can be most of the data written since the cluster started.
Versions
- master
b15c8e1e9(integration test, built from source) apache/iggy:edgerevision44c7dc834(real-process run)
Reproduction
cargo build --bin iggy-server
cargo test -p integration --test mod -- --ignored given_replicated_eager_flush_topic_when_the_whole_cluster_is_killed_should_recover_flushed_data
Related: #4266 (recovery contract for Replicated, closed by #4500), #4092 (durability policies), #4387 (frontier-owned segment recovery).
Found and reproduced with an AI agent (Claude Code), following CONTRIBUTING's "Bugs Found by an Agent".
- 主要言語
- Rust
- スター
- 5.2k
- フォーク
- 516
- 平均マージ
- 2日 17時間
- マージ済み PR(30日)
- 155
環境構築
このプロジェクトの開発コンテナを、あなたの GitHub アカウントでブラウザ上に起動します。
- Dockerfile または Docker Compose ファイルあり
- プルリクエストのテンプレートあり
- コントリビューションガイドを読む
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
apache/iggy のほかの issue
-
Go examples: add a message envelope producer and consumer対応中かも @ThunderGod77 が 6 日前に担当しました。 オープンgo good first issue
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
apache/iggy#4694 · コメント 3 件 · 担当者 1 名 ·
メンテナーはふだん 1 日以内に返信
-
HTTP client treats a consumer group as a plain consumer対応中かも @mohitduhan19 が 24 日前に担当しました。 オープンbug rust server
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
メンテナーはふだん 1 日以内に返信
-
Add the fail-fast connection option to the Rust and Python quick-starts対応中かも @QIW9 が 11 日前に担当しました。 オープンdocs good first issue
難易度 1/5 1〜3時間 初心者へのやさしさ 92/100
メンテナーはふだん 1 日以内に返信
-
ResourceNotFound from the binary protocol prints an empty key対応中かも @xalestar が 2 日前に担当しました。 オープンapi python rust
apache/iggy#4767 · コメント 2 件 · 担当者 1 名 ·
メンテナーはふだん 1 日以内に返信
-
Go SDK: validate personal access token name length before serialization対応中かも @p4rtridge が 3 日前に担当しました。 オープンbug go good first issue
メンテナーはふだん 1 日以内に返信
似ている issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
RustCrypto/utils#1597 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 68/100
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 68/100
max-sixty/worktrunk#4727 · コメント 7 件 · リアクション 1 件 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 86/100
メンテナーはふだん 1 日以内に返信
-
follow-up
難易度 2/5 1〜3時間 初心者へのやさしさ 73/100
carrick-sh/carrick#1859 ·
メンテナーはふだん 1 日以内に返信