@@ -342,6 +342,33 @@ func (r *rangeAsserter) apply(
342342 r .seedAppliedAs [seedID ] = cmdID
343343}
344344
345+ // ApplySplitRHS tracks and asserts command applications which split out a new
346+ // range. Accepts the state of the RHS.
347+ func (a * Asserter ) ApplySplitRHS (
348+ rangeID roachpb.RangeID ,
349+ replicaID roachpb.ReplicaID ,
350+ raftAppliedIndex kvpb.RaftIndex ,
351+ raftAppliedIndexTerm kvpb.RaftTerm ,
352+ leaseAppliedIndex kvpb.LeaseAppliedIndex ,
353+ closedTS hlc.Timestamp ,
354+ ) {
355+ // Use a special command ID that signifies a pseudo-proposal that splits this
356+ // range out. This ID does not match any "real" IDs (they are of a different
357+ // size, and sufficiently random even if were of the same size).
358+ const cmdID = kvserverbase .CmdIDKey ("__split__" )
359+ r := a .forRange (rangeID )
360+ // Circumvent the check in r.apply that the command ID must be proposed.
361+ func () {
362+ r .Lock ()
363+ defer r .Unlock ()
364+ r .proposedCmds [cmdID ] = 0
365+ }()
366+ // Pretend that we are applying this pseudo-command.
367+ r .apply (replicaID , cmdID , raftpb.Entry {
368+ Index : uint64 (raftAppliedIndex ), Term : uint64 (raftAppliedIndexTerm ),
369+ }, leaseAppliedIndex , closedTS )
370+ }
371+
345372// ApplySnapshot tracks and asserts snapshot application.
346373func (a * Asserter ) ApplySnapshot (
347374 rangeID roachpb.RangeID ,
@@ -385,13 +412,22 @@ func (r *rangeAsserter) applySnapshot(
385412 }
386413 r .replicaAppliedIndex [replicaID ] = index
387414
388- // We can't have a snapshot without any applied log entries, except when this
389- // is an initial snapshot. It's possible that the initial snapshot follows an
390- // empty entry appended by the raft leader at the start of this term. Since we
391- // don't register this entry as applied, r.log can be empty here.
415+ // Typically, we can't have a snapshot without any applied entries. For most
416+ // ranges, there is at least a pseudo-command (see ApplySplitRHS) that created
417+ // this range as part of a split.
418+ //
419+ // For ranges that were not a result of a split (those created during the
420+ // cluster bootstrap), the only way to see an empty r.log here is that this
421+ // snapshot follows only empty entries appended by the raft leader at the
422+ // start of its term or other no-op entries (see comment in r.apply explaining
423+ // that we do not register such commands).
424+ //
425+ // The latter situation appears impossible, because the bootstrapped ranges
426+ // can't send a snapshot to anyone without applying a config change adding
427+ // another replica - so r.log can't be empty.
392428 //
393- // See the comment in r.apply() method, around the empty cmdID check, and the
394- // comment for r.log saying that there can be gaps in the observed applies .
429+ // TODO(pav-kv): consider dropping this condition, or reporting the initial
430+ // state during the cluster bootstrap for completeness .
395431 if len (r .log ) == 0 {
396432 return
397433 }
0 commit comments