@@ -437,11 +437,13 @@ type consumer struct {
437437 lss * lastSeqSkipList
438438 rlimit * rate.Limiter
439439 reqSub * subscription
440+ resetSub * subscription
440441 ackSub * subscription
441442 ackReplyT string
442443 ackSubj string
443444 nextMsgSubj string
444445 nextMsgReqs * ipQueue [* nextMsgReq ]
446+ resetSubj string
445447 maxp int
446448 pblimit int
447449 maxpb int
@@ -1263,6 +1265,7 @@ func (mset *stream) addConsumerWithAssignment(config *ConsumerConfig, oname stri
12631265 o .ackReplyT = fmt .Sprintf ("%s.%%d.%%d.%%d.%%d.%%d" , pre )
12641266 o .ackSubj = fmt .Sprintf ("%s.*.*.*.*.*" , pre )
12651267 o .nextMsgSubj = fmt .Sprintf (JSApiRequestNextT , mn , o .name )
1268+ o .resetSubj = fmt .Sprintf (JSApiConsumerResetT , mn , o .name )
12661269
12671270 // Check/update the inactive threshold
12681271 o .updateInactiveThreshold (& o .cfg )
@@ -1547,6 +1550,11 @@ func (o *consumer) setLeader(isLeader bool) {
15471550 o .deleteWithoutAdvisory ()
15481551 return
15491552 }
1553+ if o .resetSub , err = o .subscribeInternal (o .resetSubj , o .processResetReq ); err != nil {
1554+ o .mu .Unlock ()
1555+ o .deleteWithoutAdvisory ()
1556+ return
1557+ }
15501558
15511559 // Check on flow control settings.
15521560 if o .cfg .FlowControl {
@@ -1667,8 +1675,9 @@ func (o *consumer) setLeader(isLeader bool) {
16671675 // ok if they are nil, we protect inside unsubscribe()
16681676 o .unsubscribe (o .ackSub )
16691677 o .unsubscribe (o .reqSub )
1678+ o .unsubscribe (o .resetSub )
16701679 o .unsubscribe (o .fcSub )
1671- o .ackSub , o .reqSub , o .fcSub = nil , nil , nil
1680+ o .ackSub , o .reqSub , o .resetSub , o . fcSub = nil , nil , nil , nil
16721681 if o .infoSub != nil {
16731682 o .srv .sysUnsubscribe (o .infoSub )
16741683 o .infoSub = nil
@@ -2596,6 +2605,53 @@ func (o *consumer) updateSkipped(seq uint64) {
25962605 o .propose (b [:])
25972606}
25982607
2608+ func (o * consumer ) resetStartingSeq (seq uint64 , reply string ) {
2609+ o .mu .Lock ()
2610+ defer o .mu .Unlock ()
2611+
2612+ // Must be a minimum of 1.
2613+ if seq <= 0 {
2614+ seq = 1
2615+ }
2616+ o .resetLocalStartingSeq (seq )
2617+ // Clustered mode and R>1.
2618+ if o .node != nil {
2619+ b := make ([]byte , 1 + 8 + len (reply ))
2620+ b [0 ] = byte (resetSeqOp )
2621+ var le = binary .LittleEndian
2622+ le .PutUint64 (b [1 :], seq )
2623+ copy (b [1 + 8 :], reply )
2624+ o .propose (b [:])
2625+ } else if o .store != nil {
2626+ o .store .Reset (seq - 1 )
2627+ if reply != _EMPTY_ {
2628+ o .outq .sendMsg (reply , nil )
2629+ }
2630+ // Cleanup messages that lost interest.
2631+ if o .retention == InterestPolicy {
2632+ if mset := o .mset ; mset != nil {
2633+ o .mu .Unlock ()
2634+ ss := mset .state ()
2635+ o .checkStateForInterestStream (& ss )
2636+ o .mu .Lock ()
2637+ }
2638+ }
2639+
2640+ // Recalculate pending, and re-trigger message delivery.
2641+ o .streamNumPending ()
2642+ o .signalNewMessages ()
2643+ }
2644+ }
2645+
2646+ // Lock should be held.
2647+ func (o * consumer ) resetLocalStartingSeq (seq uint64 ) {
2648+ o .pending , o .rdc = nil , nil
2649+ o .rdq = nil
2650+ o .rdqi .Empty ()
2651+ o .sseq , o .dseq = seq , 1
2652+ o .adflr , o .asflr = o .dseq - 1 , o .sseq - 1
2653+ }
2654+
25992655func (o * consumer ) loopAndForwardProposals (qch chan struct {}) {
26002656 // On exit make sure we nil out pch.
26012657 defer func () {
@@ -4119,6 +4175,35 @@ func (o *consumer) processNextMsgReq(_ *subscription, c *client, _ *Account, _,
41194175 o .nextMsgReqs .push (newNextMsgReq (reply , copyBytes (msg )))
41204176}
41214177
4178+ // processResetReq will reset a consumer to a new starting sequence.
4179+ func (o * consumer ) processResetReq (_ * subscription , c * client , _ * Account , _ , reply string , rmsg []byte ) {
4180+ if reply == _EMPTY_ {
4181+ return
4182+ }
4183+
4184+ sendErr := func (status int , description string ) {
4185+ hdr := fmt .Appendf (nil , "NATS/1.0 %d %s\r \n \r \n " , status , description )
4186+ o .outq .send (newJSPubMsg (reply , _EMPTY_ , _EMPTY_ , hdr , nil , nil , 0 ))
4187+ }
4188+
4189+ hdr , msg := c .msgParts (rmsg )
4190+ if errorOnRequiredApiLevel (hdr ) {
4191+ sendErr (412 , "Required Api Level" )
4192+ return
4193+ }
4194+
4195+ var req JSApiConsumerResetRequest
4196+ if err := json .Unmarshal (msg , & req ); err != nil {
4197+ sendErr (400 , "Bad Request" )
4198+ return
4199+ }
4200+ if req .Seq == 0 {
4201+ sendErr (400 , "Bad Request - Zero Seq" )
4202+ return
4203+ }
4204+ o .resetStartingSeq (req .Seq , reply )
4205+ }
4206+
41224207func (o * consumer ) processNextMsgRequest (reply string , msg []byte ) {
41234208 o .mu .Lock ()
41244209 defer o .mu .Unlock ()
@@ -6059,9 +6144,11 @@ func (o *consumer) stopWithFlags(dflag, sdflag, doSignal, advisory bool) error {
60596144 o .active = false
60606145 o .unsubscribe (o .ackSub )
60616146 o .unsubscribe (o .reqSub )
6147+ o .unsubscribe (o .resetSub )
60626148 o .unsubscribe (o .fcSub )
60636149 o .ackSub = nil
60646150 o .reqSub = nil
6151+ o .resetSub = nil
60656152 o .fcSub = nil
60666153 if o .infoSub != nil {
60676154 o .srv .sysUnsubscribe (o .infoSub )
0 commit comments