2828import com .google .common .cache .Cache ;
2929import com .google .common .cache .CacheBuilder ;
3030import com .google .spanner .admin .database .v1 .DatabaseName ;
31- import io .grpc .CallOptions ;
32- import io .grpc .Channel ;
33- import io .grpc .ClientCall ;
34- import io .grpc .ClientInterceptor ;
31+ import io .grpc .*;
3532import io .grpc .ForwardingClientCall .SimpleForwardingClientCall ;
3633import io .grpc .ForwardingClientCallListener .SimpleForwardingClientCallListener ;
37- import io .grpc .Metadata ;
38- import io .grpc .MethodDescriptor ;
3934import io .grpc .alts .AltsContextUtil ;
4035import io .opencensus .stats .MeasureMap ;
4136import io .opencensus .stats .Stats ;
5045import java .util .HashMap ;
5146import java .util .Map ;
5247import java .util .concurrent .ExecutionException ;
48+ import java .util .concurrent .atomic .AtomicBoolean ;
5349import java .util .logging .Level ;
5450import java .util .logging .Logger ;
5551import java .util .regex .Matcher ;
@@ -102,7 +98,11 @@ public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
10298 ApiTracer tracer = callOptions .getOption (TRACER_KEY );
10399 CompositeTracer compositeTracer =
104100 tracer instanceof CompositeTracer ? (CompositeTracer ) tracer : null ;
105- return new SimpleForwardingClientCall <ReqT , RespT >(next .newCall (method , callOptions )) {
101+ final AtomicBoolean headersReceived = new AtomicBoolean (false );
102+ SpannerGrpcStreamTracer streamTracer = new SpannerGrpcStreamTracer ();
103+ CallOptions newOptions =
104+ callOptions .withStreamTracerFactory (new SpannerGrpcStreamTracer .Factory (streamTracer ));
105+ return new SimpleForwardingClientCall <ReqT , RespT >(next .newCall (method , newOptions )) {
106106 @ Override
107107 public void start (Listener <RespT > responseListener , Metadata headers ) {
108108 try {
@@ -124,13 +124,33 @@ public void start(Listener<RespT> responseListener, Metadata headers) {
124124 new SimpleForwardingClientCallListener <RespT >(responseListener ) {
125125 @ Override
126126 public void onHeaders (Metadata metadata ) {
127+ headersReceived .set (true );
127128 // Check if the call uses DirectPath by inspecting the ALTS context.
128129 boolean isDirectPathUsed = AltsContextUtil .check (getAttributes ());
129130 addDirectPathUsedAttribute (compositeTracer , isDirectPathUsed );
130131 processHeader (
131132 metadata , tagContext , attributes , span , compositeTracer , isDirectPathUsed );
132133 super .onHeaders (metadata );
133134 }
135+
136+ @ Override
137+ public void onClose (Status status , Metadata trailers ) {
138+ // Check if RPC was sent from gRPC client, but no response headers were received.
139+ // This can happen in
140+ // case of a timeout, for example.
141+ if (streamTracer .isOutBoundMessageSent () && !headersReceived .get ()) {
142+ if (compositeTracer != null ) {
143+ compositeTracer .recordGfeHeaderMissingCount (1L );
144+ // Disable afe_connectivity_error_count metric as AFE header is disabled in
145+ // backend
146+ // currently.
147+ // if (GapicSpannerRpc.isEnableAFEServerTiming()) {
148+ // compositeTracer.recordAfeHeaderMissingCount(1L);
149+ // }
150+ }
151+ }
152+ super .onClose (status , trailers );
153+ }
134154 },
135155 headers );
136156 } catch (ExecutionException executionException ) {
0 commit comments