@@ -135,6 +135,12 @@ interface WriteBufferEntry {
135
135
* state.
136
136
*/
137
137
callback ?: WriteCallback ;
138
+ /**
139
+ * Indicates whether the message is allocated in the buffer tracker. Ignored
140
+ * if entryType is not MESSAGE. Should be the return value of
141
+ * bufferTracker.allocate.
142
+ */
143
+ allocated : boolean ;
138
144
}
139
145
140
146
const PREVIONS_RPC_ATTEMPTS_METADATA_KEY = 'grpc-previous-rpc-attempts' ;
@@ -216,6 +222,22 @@ export class RetryingCall implements Call {
216
222
}
217
223
}
218
224
225
+ private maybefreeMessageBufferEntry ( messageIndex : number ) {
226
+ if ( this . state !== 'COMMITTED' ) {
227
+ return ;
228
+ }
229
+ const bufferEntry = this . writeBuffer [ messageIndex ] ;
230
+ if ( bufferEntry . entryType === 'MESSAGE' ) {
231
+ if ( bufferEntry . allocated ) {
232
+ this . bufferTracker . free ( bufferEntry . message ! . message . length , this . callNumber ) ;
233
+ }
234
+ this . writeBuffer [ messageIndex ] = {
235
+ entryType : 'FREED' ,
236
+ allocated : false
237
+ } ;
238
+ }
239
+ }
240
+
219
241
private commitCall ( index : number ) {
220
242
if ( this . state === 'COMMITTED' ) {
221
243
return ;
@@ -237,13 +259,7 @@ export class RetryingCall implements Call {
237
259
this . underlyingCalls [ i ] . call . cancelWithStatus ( Status . CANCELLED , 'Discarded in favor of other hedged attempt' ) ;
238
260
}
239
261
for ( let messageIndex = 0 ; messageIndex < this . underlyingCalls [ index ] . nextMessageToSend - 1 ; messageIndex += 1 ) {
240
- const bufferEntry = this . writeBuffer [ messageIndex ] ;
241
- if ( bufferEntry . entryType === 'MESSAGE' ) {
242
- this . bufferTracker . free ( bufferEntry . message ! . message . length , this . callNumber ) ;
243
- this . writeBuffer [ messageIndex ] = {
244
- entryType : 'FREED'
245
- } ;
246
- }
262
+ this . maybefreeMessageBufferEntry ( messageIndex ) ;
247
263
}
248
264
}
249
265
@@ -513,6 +529,15 @@ export class RetryingCall implements Call {
513
529
this . maybeStartHedgingTimer ( ) ;
514
530
}
515
531
532
+ private handleChildWriteCompleted ( childIndex : number ) {
533
+ const childCall = this . underlyingCalls [ childIndex ] ;
534
+ const messageIndex = childCall . nextMessageToSend ;
535
+ this . writeBuffer [ messageIndex ] . callback ?.( ) ;
536
+ this . maybefreeMessageBufferEntry ( messageIndex ) ;
537
+ childCall . nextMessageToSend += 1 ;
538
+ this . sendNextChildMessage ( childIndex ) ;
539
+ }
540
+
516
541
private sendNextChildMessage ( childIndex : number ) {
517
542
const childCall = this . underlyingCalls [ childIndex ] ;
518
543
if ( childCall . state === 'COMPLETED' ) {
@@ -525,8 +550,7 @@ export class RetryingCall implements Call {
525
550
childCall . call . sendMessageWithContext ( {
526
551
callback : ( error ) => {
527
552
// Ignore error
528
- childCall . nextMessageToSend += 1 ;
529
- this . sendNextChildMessage ( childIndex ) ;
553
+ this . handleChildWriteCompleted ( childIndex ) ;
530
554
}
531
555
} , bufferEntry . message ! . message ) ;
532
556
break ;
@@ -550,25 +574,34 @@ export class RetryingCall implements Call {
550
574
const messageIndex = this . writeBuffer . length ;
551
575
const bufferEntry : WriteBufferEntry = {
552
576
entryType : 'MESSAGE' ,
553
- message : writeObj
577
+ message : writeObj ,
578
+ allocated : this . bufferTracker . allocate ( message . length , this . callNumber )
554
579
} ;
555
580
this . writeBuffer [ messageIndex ] = bufferEntry ;
556
- if ( this . bufferTracker . allocate ( message . length , this . callNumber ) ) {
581
+ if ( bufferEntry . allocated ) {
557
582
context . callback ?.( ) ;
558
583
for ( const [ callIndex , call ] of this . underlyingCalls . entries ( ) ) {
559
584
if ( call . state === 'ACTIVE' && call . nextMessageToSend === messageIndex ) {
560
585
call . call . sendMessageWithContext ( {
561
586
callback : ( error ) => {
562
587
// Ignore error
563
- call . nextMessageToSend += 1 ;
564
- this . sendNextChildMessage ( callIndex ) ;
588
+ this . handleChildWriteCompleted ( callIndex ) ;
565
589
}
566
590
} , message ) ;
567
591
}
568
592
}
569
593
} else {
570
594
this . commitCallWithMostMessages ( ) ;
571
- bufferEntry . callback = context . callback ;
595
+ const call = this . underlyingCalls [ this . committedCallIndex ! ] ;
596
+ bufferEntry . callback = context . callback ;
597
+ if ( call . state === 'ACTIVE' && call . nextMessageToSend === messageIndex ) {
598
+ call . call . sendMessageWithContext ( {
599
+ callback : ( error ) => {
600
+ // Ignore error
601
+ this . handleChildWriteCompleted ( this . committedCallIndex ! ) ;
602
+ }
603
+ } , message ) ;
604
+ }
572
605
}
573
606
}
574
607
startRead ( ) : void {
@@ -584,7 +617,8 @@ export class RetryingCall implements Call {
584
617
this . trace ( 'halfClose called' ) ;
585
618
const halfCloseIndex = this . writeBuffer . length ;
586
619
this . writeBuffer [ halfCloseIndex ] = {
587
- entryType : 'HALF_CLOSE'
620
+ entryType : 'HALF_CLOSE' ,
621
+ allocated : false
588
622
} ;
589
623
for ( const call of this . underlyingCalls ) {
590
624
if ( call ?. state === 'ACTIVE' && call . nextMessageToSend === halfCloseIndex ) {
0 commit comments