@@ -263,6 +263,8 @@ func (it *messageIterator) receive(maxToPull int32) ([]*Message, error) {
263263
264264 var rmsgs []* pb.ReceivedMessage
265265 var err error
266+ // This is a blocking call, because reading from the stream blocks.
267+ // We want to make sure this is canceled.
266268 rmsgs , err = it .recvMessages ()
267269 // If stopping the iterator results in the grpc stream getting shut down and
268270 // returning an error here, treat the same as above and return EOF.
@@ -359,9 +361,9 @@ func (it *messageIterator) receive(maxToPull int32) ([]*Message, error) {
359361
360362 // If exactly once is enabled, we should wait until modack responses are successes
361363 // before attempting to process messages.
362- it .sendModAck (ackIDs , deadline , false , true )
364+ ctx := context .Background ()
365+ it .sendModAck (ctx , ackIDs , deadline , false , true )
363366 for ackID , ar := range ackIDs {
364- ctx := context .Background ()
365367 _ , err := ar .Get (ctx )
366368 if err != nil {
367369 delete (pendingMessages , ackID )
@@ -508,16 +510,16 @@ func (it *messageIterator) sender() {
508510 }
509511 if sendNacks {
510512 // Nack indicated by modifying the deadline to zero.
511- it .sendModAck (nacks , 0 , false , false )
513+ it .sendModAck (context . Background (), nacks , 0 , false , false )
512514 }
513515 if sendModAcks {
514- it .sendModAck (modAcks , dl , true , false )
516+ it .sendModAck (context . Background (), modAcks , dl , true , false )
515517 }
516518 if sendPing {
517519 it .pingStream ()
518520 }
519521 if sendReceipt {
520- it .sendModAck (receipts , dl , true , true )
522+ it .sendModAck (context . Background (), receipts , dl , true , true )
521523 }
522524 }
523525}
@@ -559,7 +561,7 @@ type ackFunc = func(ctx context.Context, subName string, ackIds []string) error
559561type ackRecordStat = func (ctx context.Context , toSend []string )
560562type retryAckFunc = func (toRetry map [string ]* ipubsub.AckResult )
561563
562- func (it * messageIterator ) sendAckWithFunc (m map [string ]* AckResult , ackFunc ackFunc , retryAckFunc retryAckFunc , ackRecordStat ackRecordStat ) {
564+ func (it * messageIterator ) sendAckWithFunc (ctx context. Context , m map [string ]* AckResult , ackFunc ackFunc , retryAckFunc retryAckFunc , ackRecordStat ackRecordStat ) {
563565 ackIDs := make ([]string , 0 , len (m ))
564566 for ackID := range m {
565567 ackIDs = append (ackIDs , ackID )
@@ -575,9 +577,7 @@ func (it *messageIterator) sendAckWithFunc(m map[string]*AckResult, ackFunc ackF
575577 go func (toSend []string ) {
576578 defer wg .Done ()
577579 ackRecordStat (it .ctx , toSend )
578- // Use context.Background() as the call's context, not it.ctx. We don't
579- // want to cancel this RPC when the iterator is stopped.
580- cctx , cancel2 := context .WithTimeout (context .Background (), 60 * time .Second )
580+ cctx , cancel2 := context .WithTimeout (ctx , 60 * time .Second )
581581 defer cancel2 ()
582582 err := ackFunc (cctx , it .subName , toSend )
583583 if exactlyOnceDelivery {
@@ -602,7 +602,8 @@ func (it *messageIterator) sendAckWithFunc(m map[string]*AckResult, ackFunc ackF
602602// sendAck is used to confirm acknowledgement of a message. If exactly once delivery is
603603// enabled, we'll retry these messages for a short duration in a goroutine.
604604func (it * messageIterator ) sendAck (m map [string ]* AckResult ) {
605- it .sendAckWithFunc (m , func (ctx context.Context , subName string , ackIDs []string ) error {
605+ ctx := context .Background ()
606+ it .sendAckWithFunc (ctx , m , func (ctx context.Context , subName string , ackIDs []string ) error {
606607 // For each ackID (message), setup links to the main subscribe span.
607608 // If this is a nack, also remove it from active spans.
608609 // If the ackID is not found, don't create any more spans.
@@ -667,7 +668,7 @@ func (it *messageIterator) sendAck(m map[string]*AckResult) {
667668// percentile in order to capture the highest amount of time necessary without
668669// considering 1% outliers. If the ModAck RPC fails and exactly once delivery is
669670// enabled, we retry it in a separate goroutine for a short duration.
670- func (it * messageIterator ) sendModAck (m map [string ]* AckResult , deadline time.Duration , logOnInvalid , isReceipt bool ) {
671+ func (it * messageIterator ) sendModAck (ctx context. Context , m map [string ]* AckResult , deadline time.Duration , logOnInvalid , isReceipt bool ) {
671672 deadlineSec := int32 (deadline / time .Second )
672673 isNack := deadline == 0
673674 var spanName , eventStart , eventEnd string
@@ -680,7 +681,7 @@ func (it *messageIterator) sendModAck(m map[string]*AckResult, deadline time.Dur
680681 eventStart = eventModackStart
681682 eventEnd = eventModackEnd
682683 }
683- it .sendAckWithFunc (m , func (ctx context.Context , subName string , ackIDs []string ) error {
684+ it .sendAckWithFunc (ctx , m , func (ctx context.Context , subName string , ackIDs []string ) error {
684685 if it .enableTracing {
685686 // For each ackID (message), link back to the main subscribe span.
686687 // If this is a nack, also remove it from active spans.
@@ -1016,3 +1017,19 @@ func processResults(errorStatus *status.Status, ackResMap map[string]*AckResult,
10161017 }
10171018 return completedResults , retryResults
10181019}
1020+
1021+ // nackInventory nacks all the current messages being held by the iterator.
1022+ // This does not stop the existing callbacks, and does not try to remove
1023+ // messages from the scheduler. This is used specifically for when the
1024+ // user configured ShutdownOptions is set to NackImmediately
1025+ func (it * messageIterator ) nackInventory (ctx context.Context ) {
1026+ it .mu .Lock ()
1027+ defer it .mu .Unlock ()
1028+
1029+ toNack := make (map [string ]* ipubsub.AckResult )
1030+ for ackID := range it .keepAliveDeadlines {
1031+ // Use a dummy AckResult since we don't propagate nacks back to the user.
1032+ toNack [ackID ] = newSuccessAckResult ()
1033+ }
1034+ it .sendModAck (ctx , toNack , 0 , false , false )
1035+ }
0 commit comments