Slow ledger fsync or chunk creation blocks `GET /node/ledger_chunk` on `Ledger::state_lock`, which can use up the worker threads and stop the node answering any request
Maintainer thường phản hồi trong vòng 1 ngày
Chưa có ai nhận issue này.
Đánh giá
- Độ khó
- 5/5
- Thời gian dự kiến
- Hơn một tuần
- Mức phù hợp với người mới
- 8/100
- Loại issue
- Lỗi
- Độ rõ ràng
- Khá rõ ràng
- Mức độ hoạt động
- Sôi nổi
- Công nghệ
- cpp
- Lĩnh vực
- distributed-systems
Hướng nghiên cứu
Start in src/host/ledger.h, where Ledger::commit(), LedgerFile::commit(), Ledger::write_entry() and committed_ledger_path_with_idx() all take state_lock around fsync and chunk creation. Then read the ledger_chunk handler in src/node/rpc/file_serving_handlers.h, which calls the lookup synchronously on a worker thread. Done means a delayed fsync no longer makes /node/version or a ledger_chunk request block all workers. The issue lists three candidate fixes, so the design choice needs maintainer input first.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
Describe the bug
Since 7.0.19, ledger writes no longer go through the host-enclave ringbuffer (#8405), and ledger file I/O runs on the task worker threads. This works as intended for a slow disk on its own. When one ledger fsync or chunk open is blocked for 30 s, a 7.0.23 node keeps answering /node/version and /node/state, while a 7.0.17 node stops answering completely.
One case still freezes the node:
Ledger::commit()takesstate_lock(ledger.h#L2023-L2051), andLedgerFile::commit()then fsyncs the completed chunk while the lock is held (ledger.h#L1086-L1105). The comment there says this is deliberate, so thatcommitted_ledger_path_with_idx()only returns complete files.Ledger::write_entry()also holds the lock (ledger.h#L1845-L1850) while it creates the next chunk file.- The handler for
GET /node/ledger_chunk?since=N(file_serving_handlers.h#L1178-L1179) callscommitted_ledger_path_with_idx()synchronously on a worker thread (file_serving_handlers.h#L803-L804). That function takesstate_lock(ledger.h#L2078-L2081).
So while an fsync is slow, every ledger_chunk request holds a worker thread until the fsync finishes. With the default worker_threads: 1 (two task workers), one request is enough: the other worker is the one running the fsync. No worker is left, and the node stops answering every endpoint, including /node/version, until the fsync returns. With worker_threads: 3 (four workers), the node froze when the third concurrent ledger_chunk request arrived.
In Kubernetes, a liveness probe on a /node endpoint then restarts a node that is only waiting for slow storage. Any client that polls ledger_chunk, for example to archive committed chunks, makes this likely during storage latency spikes.
To Reproduce
- Start a 3-node network with the release
loggingsample app and the devel RPM (virtual build),--ledger-chunk-bytes 5KBand the defaultworker_threads: 1. Send about 20POST /app/log/publicper second. - On one node, delay the next ledger fsync by 30 s:
strace -f -p <cchost pid> -e trace=fsync,openat -e inject=fsync:delay_enter=30s:when=1. The node log confirms it:Operation took too long ( 30.02s): Committing ledger file - fsync(ledger_609). - Poll
GET /node/versionandGET /node/stateon that node every 0.5 s, with a 2 s timeout. All of them succeed during the delay. - Repeat, and during the delay send one
GET /node/ledger_chunk?since=100to the same node. From that request until the fsync returns, every poll times out. Theledger_chunkrequest itself takes about 27 s.
| Version | Delayed for 30 s | Polls during the delay that failed |
|---|---|---|
| 7.0.17 | fsync (primary or backup), chunk create (primary) | 112/112 in each test |
| 7.0.23 | fsync (primary or backup), chunk create (primary) | 0/112 (max latency 0.33 s) |
| 7.0.23 | fsync + 1 ledger_chunk request (two runs, two nodes) |
100/112, all after the request |
7.0.23, worker_threads: 3 |
fsync + 3 ledger_chunk requests 4 s apart |
68/112, all after the 3rd request |
Expected behavior
A slow ledger fsync shouldn't stop the node serving endpoints that don't need the ledger, and /node endpoints should stay responsive.
Environment information
- CCF 7.0.23.
src/host/ledger.his identical onmainat9cdc86cc78a42ee27ea167a7a347c9d924bf4841. - Reproduced with the virtual build in Azure Linux 3.0 containers. Production runs C++ applications on the SNP platform (confidential Azure Container Instances), with the ledger on an SMB file share, where single fsync or file-create calls sometimes take 40-55 s.
Frequency
Every time a ledger_chunk request reaches a node while its ledger fsync or chunk creation is blocked. In our deployment that depends on storage latency spikes, which happen several times a week on some file shares.
Additional context / possible fixes
- Don't hold
state_lockfor the fsync inLedger::commit(). For example, mark the file as being committed under the lock, fsync and rename it outside the lock, then publish it as committed under the lock.committed_ledger_path_with_idx()would only return published files, which keeps today's guarantee. - Or don't make
ledger_chunklookups wait for an in-progress commit. For example, try the lock and answer503withRetry-Afterwhen it's held, or keep the committed-chunk index under a separate lock. - Or serve
ledger_chunk, and other readers ofstate_locksuch as historical reads of committed ranges, without blocking a worker thread.
Related (not necessarily a bug): with 7.0.23, a primary whose ledger fsync or chunk create stalls loses leadership within about 3 s, although its HTTP endpoints stay up. Outbound node-to-node messages, including heartbeats, wait for all ledger mutations submitted before them (node_connections.h#L413-L423, ledger_subsystem.h#L210-L221). So a storage latency spike on the primary still causes an election.
- Ngôn ngữ chính
- C++
- Star
- 876
- Fork
- 264
- Merge trung bình
- 1 ngày 16 giờ
- Pull request đã merge (30 ngày)
- 127
Chuẩn bị môi trường
Khởi chạy dev container của dự án ngay trên trình duyệt, bằng tài khoản GitHub của bạn.
- Không có Dockerfile hay tệp Docker Compose
- Có mẫu pull request
- Đọc hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của microsoft/CCF
-
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 48/100
microsoft/CCF#8688 · 1 reaction ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 35/100
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 35/100
Maintainer thường phản hồi trong vòng 1 ngày
-
Make stderr output exclusive to fatal paths and consistent across all fatal pathsCó thể đã có người làm @achamayou đã nhận 2 ngày trước. Đang mở
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 35/100
Maintainer thường phản hồi trong vòng 1 ngày
-
raft.h hardeningĐang mở
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 25/100
microsoft/CCF#8606 · 3 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
Tất cả issue của microsoft/CCF
Issue tương tự
-
bug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 85/100
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
Niko1221/Strata#5560 · 2 bình luận ·
Maintainer thường phản hồi trong vòng 4 ngày
-
NVIDIA windows
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 76/100
Maintainer thường phản hồi trong vòng 1 ngày
-
[辞書] C/C++のプリプロセッサ指令名を英字のままにするĐang mở要トリアージ 辞書
Độ khó 1/5 1-3 giờ Mức phù hợp với người mới 86/100
yksr-melt/Meltype#1625 · 1 reaction ·
Maintainer thường phản hồi trong vòng 1 ngày
-
[yugabyted] xcluster set_up and add_to_replication fail for database names containing - . or (Đang mởkind/bug priority/medium qa_automation
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
yugabyte/yugabyte-db#37324 ·
Maintainer thường phản hồi trong vòng 1 ngày