@@ -3303,7 +3303,7 @@ func (js *jetStream) applyStreamEntries(mset *stream, ce *CommittedEntry, isReco
3303
3303
}
3304
3304
3305
3305
if isRecovering || ! mset .IsLeader () {
3306
- if err := mset .processSnapshot (ss ); err != nil {
3306
+ if err := mset .processSnapshot (ss , ce . Index ); err != nil {
3307
3307
return err
3308
3308
}
3309
3309
}
@@ -8343,11 +8343,12 @@ type streamSyncRequest struct {
8343
8343
FirstSeq uint64 `json:"first_seq"`
8344
8344
LastSeq uint64 `json:"last_seq"`
8345
8345
DeleteRangesOk bool `json:"delete_ranges"`
8346
+ MinApplied uint64 `json:"min_applied"`
8346
8347
}
8347
8348
8348
8349
// Given a stream state that represents a snapshot, calculate the sync request based on our current state.
8349
8350
// Stream lock must be held.
8350
- func (mset * stream ) calculateSyncRequest (state * StreamState , snap * StreamReplicatedState ) * streamSyncRequest {
8351
+ func (mset * stream ) calculateSyncRequest (state * StreamState , snap * StreamReplicatedState , index uint64 ) * streamSyncRequest {
8351
8352
// Shouldn't happen, but consequences are pretty bad if we have the lock held and
8352
8353
// our caller tries to take the lock again on panic defer, as in processSnapshot.
8353
8354
if state == nil || snap == nil || mset .node == nil {
@@ -8357,7 +8358,7 @@ func (mset *stream) calculateSyncRequest(state *StreamState, snap *StreamReplica
8357
8358
if state .LastSeq >= snap .LastSeq {
8358
8359
return nil
8359
8360
}
8360
- return & streamSyncRequest {FirstSeq : state .LastSeq + 1 , LastSeq : snap .LastSeq , Peer : mset .node .ID (), DeleteRangesOk : true }
8361
+ return & streamSyncRequest {FirstSeq : state .LastSeq + 1 , LastSeq : snap .LastSeq , Peer : mset .node .ID (), DeleteRangesOk : true , MinApplied : index }
8361
8362
}
8362
8363
8363
8364
// processSnapshotDeletes will update our current store based on the snapshot
@@ -8493,15 +8494,15 @@ var (
8493
8494
)
8494
8495
8495
8496
// Process a stream snapshot.
8496
- func (mset * stream ) processSnapshot (snap * StreamReplicatedState ) (e error ) {
8497
+ func (mset * stream ) processSnapshot (snap * StreamReplicatedState , index uint64 ) (e error ) {
8497
8498
// Update any deletes, etc.
8498
8499
mset .processSnapshotDeletes (snap )
8499
8500
mset .setCLFS (snap .Failed )
8500
8501
8501
8502
mset .mu .Lock ()
8502
8503
var state StreamState
8503
8504
mset .store .FastState (& state )
8504
- sreq := mset .calculateSyncRequest (& state , snap )
8505
+ sreq := mset .calculateSyncRequest (& state , snap , index )
8505
8506
8506
8507
s , js , subject , n , st := mset .srv , mset .js , mset .sa .Sync , mset .node , mset .cfg .Storage
8507
8508
qname := fmt .Sprintf ("[ACC:%s] stream '%s' snapshot" , mset .acc .Name , mset .cfg .Name )
@@ -8639,7 +8640,7 @@ RETRY:
8639
8640
mset .mu .RLock ()
8640
8641
var state StreamState
8641
8642
mset .store .FastState (& state )
8642
- sreq = mset .calculateSyncRequest (& state , snap )
8643
+ sreq = mset .calculateSyncRequest (& state , snap , index )
8643
8644
mset .mu .RUnlock ()
8644
8645
if sreq == nil {
8645
8646
return nil
@@ -9187,6 +9188,14 @@ func (mset *stream) runCatchup(sendSubject string, sreq *streamSyncRequest) {
9187
9188
9188
9189
// Setup sequences to walk through.
9189
9190
seq , last := sreq .FirstSeq , sreq .LastSeq
9191
+
9192
+ // The follower received a snapshot from another leader, and we've become leader since.
9193
+ // We have an up-to-date log but are behind on applies. We must wait until we've reached the minimum required.
9194
+ // The follower will automatically retry after a timeout, so we can safely return here.
9195
+ if node := mset .raftNode (); node != nil && ! node .HasApplied (sreq .MinApplied ) {
9196
+ return
9197
+ }
9198
+
9190
9199
mset .setCatchupPeer (sreq .Peer , last - seq )
9191
9200
9192
9201
// Check if we can compress during this.
0 commit comments