-
Notifications
You must be signed in to change notification settings - Fork 44
fix(AdvancedFileOutput): prevent StreamSink closed race during concurrent flush and file rotation (v3)fix Bad state: StreamSink is closed #107
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 3.0.0
Are you sure you want to change the base?
Conversation
|
Sorry, but now I am confused. Additionally, I still can't reproduce your issue and without any way to test for it and I can't really accept a (working?) fix as it will be nearly guaranteed to regress if not taken into account in future changes. Besides all that, your explanation of the "root cause" also doesn't make sense to me.
And to be honest, this description looks a lot like it was generated by AI... |
|
Thanks for your explanation!
Please note: The |

Summary
This PR re-introduces the fix for a rare concurrency issue in AdvancedFileOutput that can cause
Bad state: StreamSink is closed exceptions when a timer-triggered flush overlaps with a file rotation or sink closing.
Background
Sorry for closing the previous PR.
I initially submitted this fix against version 2.x, where we first observed the issue in production.
However, since all 2.x branches have now been removed, I re-tested on the latest 3.0.0 branch and confirmed
that the issue still occasionally occurs.
Therefore, I’m re-submitting this PR targeting v3.
Root Cause
When _flushBuffer() (triggered by the flush timer) and _updateTargetFile() (triggered by _targetFileUpdater
for rotation) execute concurrently, both may call _closeSink() or _openSink().
If the timer fires while the sink is being closed or reopened, the old _IOSink becomes invalid,
resulting in the runtime error:
How to Reproduce
The issue is rare in normal configurations but can be consistently reproduced by:
• Setting maxDelay and fileUpdateDuration to very short intervals (tens or hundreds of milliseconds);
• Using a very small maxBufferSize and maxFileSizeKB to force frequent flushes and rotations;
• Continuously writing large log messages in a tight loop.
In this setup, when _targetFileUpdater triggers _updateTargetFile() and calls _closeSink()
while another _flushBuffer() is still running, the flush timer tries to write to a closed sink,
throwing the above exception.
Fix
• Add synchronization guards to ensure that _flushBuffer, _closeSink, and _updateTargetFile
do not run concurrently.
• Skip or delay flush attempts if _sink is already closing or reopening.
This ensures file rotation and flush operations are serialized and prevents writing to a closed sink.
Notes
• Originally observed in production (monitored via Sentry).
• Still reproducible in v3.0.0 under stress test conditions.
• This fix is fully backward-compatible and safe for existing users.