19
19
import com .hedera .block .protos .BlockStreamServiceGrpcProto ;
20
20
import com .hedera .block .server .data .ObjectEvent ;
21
21
import com .hedera .block .server .mediator .StreamMediator ;
22
+ import io .grpc .stub .ServerCallStreamObserver ;
22
23
import io .grpc .stub .StreamObserver ;
23
24
24
25
import java .time .Clock ;
@@ -40,9 +41,6 @@ public class ConsumerBlockItemObserver implements BlockItemEventHandler<ObjectEv
40
41
private final InstantSource producerLivenessClock ;
41
42
private long producerLivenessMillis ;
42
43
43
- private final InstantSource consumerLivenessClock ;
44
- private long consumerLivenessMillis ;
45
-
46
44
private final CountDownLatch shutdownLatch = new CountDownLatch (1 );
47
45
48
46
private final StreamMediator <ObjectEvent <BlockStreamServiceGrpcProto .BlockItem >, BlockStreamServiceGrpcProto .BlockItemResponse > streamMediator ;
@@ -55,17 +53,29 @@ public class ConsumerBlockItemObserver implements BlockItemEventHandler<ObjectEv
55
53
public ConsumerBlockItemObserver (
56
54
final long timeoutThresholdMillis ,
57
55
final InstantSource producerLivenessClock ,
58
- final InstantSource consumerLivenessClock ,
59
56
final StreamMediator <ObjectEvent <BlockStreamServiceGrpcProto .BlockItem >, BlockStreamServiceGrpcProto .BlockItemResponse > streamMediator ,
60
57
final StreamObserver <BlockStreamServiceGrpcProto .BlockItem > responseStreamObserver ) {
61
58
62
59
this .timeoutThresholdMillis = timeoutThresholdMillis ;
63
60
this .producerLivenessClock = producerLivenessClock ;
64
- this .consumerLivenessClock = consumerLivenessClock ;
65
- this .responseStreamObserver = responseStreamObserver ;
66
61
62
+ // The ServerCallStreamObserver can be configured with a Runnable to
63
+ // be executed if a downstream consumer cancels the stream without
64
+ // sending an HTTP/2 End Stream DATA frame. If triggered, unsubscribe
65
+ // this observer to avoid orphaning subscribed resources.
66
+ if (responseStreamObserver instanceof ServerCallStreamObserver ) {
67
+
68
+ // Unfortunately we have to cast the responseStreamObserver to a ServerCallStreamObserver
69
+ // to register the onCancelHandler.
70
+ ((ServerCallStreamObserver <BlockStreamServiceGrpcProto .BlockItem >)responseStreamObserver )
71
+ .setOnCancelHandler (() -> {
72
+ LOGGER .log (System .Logger .Level .DEBUG , "Consumer cancelled stream. Unsubscribing observer." );
73
+ streamMediator .unsubscribe (this );
74
+ });
75
+ }
76
+
77
+ this .responseStreamObserver = responseStreamObserver ;
67
78
this .producerLivenessMillis = producerLivenessClock .millis ();
68
- this .consumerLivenessMillis = consumerLivenessClock .millis ();
69
79
70
80
this .streamMediator = streamMediator ;
71
81
}
@@ -77,15 +87,9 @@ public ConsumerBlockItemObserver(
77
87
@ Override
78
88
public void onEvent (final ObjectEvent <BlockStreamServiceGrpcProto .BlockItem > event , final long l , final boolean b ) throws Exception {
79
89
80
- // Check if the consumer has timed out. If so, unsubscribe the observer from the mediator.
81
- if (isThresholdExceeded (consumerLivenessMillis )) {
82
- LOGGER .log (System .Logger .Level .DEBUG , "Consumer timeout threshold exceeded." );
83
- streamMediator .unsubscribe (this );
84
- } else {
85
- // Refresh the producer liveness and pass the block to the observer.
86
- producerLivenessMillis = producerLivenessClock .millis ();
87
- responseStreamObserver .onNext (event .get ());
88
- }
90
+ // Refresh the producer liveness and pass the block to the observer.
91
+ producerLivenessMillis = producerLivenessClock .millis ();
92
+ responseStreamObserver .onNext (event .get ());
89
93
}
90
94
91
95
/**
@@ -100,10 +104,6 @@ public void onNext(final BlockStreamServiceGrpcProto.BlockItemResponse blockItem
100
104
if (isThresholdExceeded (producerLivenessMillis )) {
101
105
LOGGER .log (System .Logger .Level .DEBUG , "Producer timeout threshold exceeded. Unsubscribing observer." );
102
106
streamMediator .unsubscribe (this );
103
- } else {
104
- // Refresh the consumer liveness
105
- LOGGER .log (System .Logger .Level .DEBUG , "Received response block " + blockItemResponse );
106
- consumerLivenessMillis = consumerLivenessClock .millis ();
107
107
}
108
108
}
109
109
0 commit comments