-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
[FIXED] Don't InstallSnapshot
during shutdown, would race with monitorStream
/monitorConsumer
#6153
Merged
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
derekcollison
approved these changes
Nov 20, 2024
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
…rStream/monitorConsumer Signed-off-by: Maurice van Veen <github@mauricevanveen.com>
MauriceVanVeen
force-pushed
the
maurice/install-snapshot-race
branch
from
November 20, 2024 15:09
1097171
to
6938f97
Compare
MauriceVanVeen
pushed a commit
that referenced
this pull request
Nov 21, 2024
…itorStream`/`monitorConsumer` (#6153) When stopping a stream or consumer, we would attempt to install a snapshot. However, this would race with what's happening in `monitorStream`/`monitorConsumer` at that time. For example: 1. In `applyStreamEntries` we call into `mset.processJetStreamMsg` to persist one or multiple messages. 2. We call `mset.stop(..)` either before or during the above. 3. In `mset.stop(..)` we'd wait for `mset.processJetStreamMsg` to release the lock so we can enter `mset.stateSnapshotLocked()`. **We create a snapshot with new state here!** 4. Now we call into `InstallSnapshot` to persist above snapshot, but `n.applied` does not contain the right value, the value will be lower. 5. Then `applyStreamEntries` finishes and we end with calling `n.Applied(..)`. This would be a race condition depending on if 4 happened before or after 5. It's essential that the snapshot we make is aligned with the `n.applied` value. If we don't that means we'll replay and need to increase `mset.clfs` which will snowball into stream desync due to this shift. The only place where we can guarantee that the snapshot and applied are aligned is in `doSnapshot` of `monitorStream` and `monitorConsumer` (and `monitorCluster`), so we must not attempt installing snapshots outside of those. Signed-off-by: Maurice van Veen <github@mauricevanveen.com>
derekcollison
added a commit
that referenced
this pull request
Dec 18, 2024
Since the race condition of installing snapshots during shutdown was fixed in #6153, we have not re-introduced this snapshotting but done in the right place. (Meta already has this) Signed-off-by: Maurice van Veen <github@mauricevanveen.com>
derekcollison
added a commit
that referenced
this pull request
Jan 10, 2025
Calling healthz could result in streams and/or consumers to be restarted. There's a race condition that can happen where a user recreates a stream/consumer and the health check kicks in at that moment. This would result in reviving a just-deleted stream/consumer, resulting in either dead streams/consumers remaining or potentially leaderless states if different RAFT groups would remain. A stream/consumer must not be restarted in the health check as it has no awareness of what's happening in another part of the system. Is the stream just deleted, is it restarting due to an error, is it actually stopped due to a bug? It can't know, and it shouldn't assume it's safe to restart. Due to the way the JS lock is used combined with creating copies of the stream/consumer assignment means that various race conditions can happen where restarting would be the wrong choice. More importantly (and put simply), stream/consumer assignments MUST only be changed via meta entries or meta snapshots. Doing it in any other place can result in race conditions/ordering issues. (Just like snapshotting in any other place than in the monitor routine resulted in race conditions before: #6153) Detecting and correcting RAFT node skew is kept, although likely the health check shouldn't be doing that either. However, there was a bug where RAFT node skew would be detected for a consumer, it would be deleted, but not recreated if it was initially created within <10s. That's now fixed as well. Signed-off-by: Maurice van Veen <github@mauricevanveen.com>
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
When stopping a stream or consumer, we would attempt to install a snapshot. However, this would race with what's happening in
monitorStream
/monitorConsumer
at that time.For example:
applyStreamEntries
we call intomset.processJetStreamMsg
to persist one or multiple messages.mset.stop(..)
either before or during the above.mset.stop(..)
we'd wait formset.processJetStreamMsg
to release the lock so we can entermset.stateSnapshotLocked()
. We create a snapshot with new state here!InstallSnapshot
to persist above snapshot, butn.applied
does not contain the right value, the value will be lower.applyStreamEntries
finishes and we end with callingn.Applied(..)
.This would be a race condition depending on if 4 happened before or after 5.
It's essential that the snapshot we make is aligned with the
n.applied
value. If we don't that means we'll replay and need to increasemset.clfs
which will snowball into stream desync due to this shift.The only place where we can guarantee that the snapshot and applied are aligned is in
doSnapshot
ofmonitorStream
andmonitorConsumer
(andmonitorCluster
), so we must not attempt installing snapshots outside of those.Signed-off-by: Maurice van Veen github@mauricevanveen.com