SnLib v1.24.1
Fixes
SnYml.flush() no longer blocks on a write it can land itself.
flush() joined the scheduled async write with a 10-second Future.get on the calling thread. On
a live 1.21.8 Paper server, a consumer that paired save(); flush(); on the primary thread saw
that join expire its full budget on every single call - ten seconds of frozen server per admin
command, with a watchdog thread dump each time. The data still landed, because flush() fell
through to an inline write once the timeout expired; only the clock was lost.
flush() now takes the staged snapshot over and writes it itself. The scheduled drain then finds
nothing staged and exits.
Two waits are involved and only one is bounded, which is the design:
- Waiting on somebody else's in-flight write is a courtesy, capped at 10s.
ioLockbecame a
ReentrantLockfor this - an intrinsic monitor cannot be acquired with a timeout, and a
version that usedsynchronizedwould hang teardown forever on a wedged disk. - Writing the snapshot
save()staged is not a courtesy. It always happens, blocking
uninterruptibly if the disk is wedged, exactly as the previous code did after its timeout.
drainPendingWrites() now holds ioLock across the take as well as the write, so a concurrent
flush() cannot return while the drain is holding the only copy of a snapshot.
Compatibility
No public surface changes. SnApi.LEVEL is unchanged and japicmp reports no incompatibility, so
consumers pinned at any earlier 1.x need no recompile.
Consumers need no change. save() alone was always the complete persist path at runtime;
flush() is a teardown primitive the context already calls for every mounted file. Pairing
save(); flush(); in a command or listener was never necessary and is what triggered the freeze.