yagogarea opened a new issue, #6178:
URL: https://github.com/apache/couchdb/issues/6178
### 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:
```erlang
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
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
1. Start a dev node: `./dev/run -n 1 --admin=adm:pass`
2. Attach a shell: `./dev/remsh`
3. Paste:
```erlang
%% 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.
```
4. The result is:
```erlang
{result,{'EXIT',{noproc,{gen_server,call,
[<0.12353.0>,sync,infinity]}}}}
```
### Your Environment
_No response_
### Additional Context
_No response_
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]