[Bug]: `couch_bt_engine:init/2` can crash with `noproc` when `couch_file` idle-closes before `set_db_pid`
维护者通常 1 天内回复
评估
调研方向
Read couch_bt_engine.erl around line 893 and trace the open-to-set_db_pid sequence alongside couch_file’s idle-close checks and the ioq wait described here. The reproduction blocks ioq and waits past the 60-second idle timer; use it to confirm the failure and verify the engine keeps the file alive until it claims it, including when initialization or compaction I/O is delayed.
由索引模型根据 Issue 内容生成。
描述
Version
No response
Describe the problem you're encountering
After couch_file:open/2, the file process is linked to the caller, but nothing holds a lasting monitor on it until init_state/4 calls:
couch_file:set_db_pid(Fd, self())
at couch_bt_engine.erl:893.
couch_file closes itself when it looks idle. init/1 arms:
erlang:send_after(?INITIAL_WAIT, self(), maybe_close)
where ?INITIAL_WAIT is 60 seconds.
When the message arrives, is_idle/1 checks monitored_by. If the only monitor is couch_stats_process_tracker, the process stops with reason normal. If it is busy, it re-checks every 10 seconds using ?MONITOR_CHECK.
So, if the time between open and set_db_pid exceeds 60 seconds and one of those checks lands while nothing monitors the file, couch_file stops.
Callers hold a monitor only for the duration of each direct gen_server:call. A request waiting in the ioq queue monitors the ioq server, not the file, so a backed-up ioq is enough to expose the gap. Compaction I/O is not bypassed by default.
Because the exit reason is normal, the link does not take the caller down. The next direct call fails with noproc.
Expected Behaviour
A file opened by couch_bt_engine:init/2 stays open until the engine has claimed it, however long the init takes. Opening a database or finishing a compaction should not fail because IO was slow.
Steps to Reproduce
- Start a dev node:
./dev/run -n 1 --admin=adm:pass - Attach a shell:
./dev/remsh - Paste:
%% Create a db file with a header.
Path = filename:join(config:get("couchdb", "database_dir"), "idle_close_repro.couch"),
file:delete(Path),
{ok, Fd0} = couch_file:open(Path, [create]),
ok = couch_file:write_header(Fd0, couch_bt_engine_header:new(), [sync]),
ok = couch_file:close(Fd0),
%% Block ioq.
Ioq = whereis(ioq),
true = erlang:suspend_process(Ioq),
%% Open the db. read_header/1 waits on ioq; the 60s timer starts.
{Pid, Ref} = spawn_monitor(fun() ->
ioq:set_io_priority({db_compact, <<"repro">>}),
exit({result, catch couch_bt_engine:init(Path, [])})
end),
timer:sleep(1000),
%% Freeze init and let ioq reply. Now nothing monitors the file.
true = erlang:suspend_process(Pid),
true = erlang:resume_process(Ioq),
%% couch_file idle-closes after 60s.
timer:sleep(61000),
%% Resume init. sync/1 hits the dead file.
true = erlang:resume_process(Pid),
receive {'DOWN', Ref, process, Pid, Reason} -> Reason end.
- The result is:
{result,{'EXIT',{noproc,{gen_server,call,
[<0.12353.0>,sync,infinity]}}}}
Your Environment
No response
Additional Context
No response
- 主要语言
- Erlang
- 星标
- 7k
- 派生
- 1.1k
- 平均合并
- 11 小时 29 分钟
- 30 天内合并 PR
- 37
环境准备
在浏览器里用你自己的 GitHub 账号启动这个项目的开发容器。
- 没有 Dockerfile 或 Docker Compose 文件
- 有 Pull Request 模板
- 阅读贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
apache/couchdb 的其他 Issue
-
bug needs-triage
难度 4/5 1-2 天 新手友好度 45/100
维护者通常 1 天内回复
-
难度 3/5 1-2 天 新手友好度 45/100
维护者通常 1 天内回复
-
Fix: Warning: 'catch ...' is deprecated; please use 'try ... catch ... end' instead可能重新可做 @devx-arjun 于 23 天前认领,目前没有进行中的 PR。 未关闭beginner-friendly build chore patches-welcome
难度 4/5 3-5 天 新手友好度 45/100
维护者通常 1 天内回复
-
bug needs-triage
难度 4/5 3-5 天 新手友好度 48/100
维护者通常 1 天内回复
-
enhancement needs-triage
难度 3/5 1-2 天 新手友好度 55/100
维护者通常 1 天内回复
相似的 Issue
-
难度 2/5 1-3 小时 新手友好度 68/100
cloudevents/sdk-go#1346 ·
-
难度 2/5 1-3 小时 新手友好度 72/100
cyrusimap/cyrus-imapd#6640 ·
维护者通常 2 天内回复
-
GD decoder discards the finfo media type in mediaTypeByBinary() since 4.4.0可能已有人在做 @lazerg 今天认领。 未关闭
难度 2/5 1-3 小时 新手友好度 77/100
Intervention/image#1556 ·
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 78/100
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 60/100
维护者通常 1 天内回复