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 ;
@@ -100,9 +96,13 @@ class HeaderInterceptor implements ClientInterceptor {
10096 public <ReqT , RespT > ClientCall <ReqT , RespT > interceptCall (
10197 MethodDescriptor <ReqT , RespT > method , CallOptions callOptions , Channel next ) {
10298 ApiTracer tracer = callOptions .getOption (TRACER_KEY );
99+ SpannerGrpcStreamTracer streamTracer = new SpannerGrpcStreamTracer ();
100+ CallOptions newOptions =
101+ callOptions .withStreamTracerFactory (new SpannerGrpcStreamTracer .Factory (streamTracer ));
103102 CompositeTracer compositeTracer =
104103 tracer instanceof CompositeTracer ? (CompositeTracer ) tracer : null ;
105- return new SimpleForwardingClientCall <ReqT , RespT >(next .newCall (method , callOptions )) {
104+ final AtomicBoolean headersReceived = new AtomicBoolean (false );
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,29 @@ 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+ if (streamTracer .isOutBoundMessageSent () && !headersReceived .get ()) {
139+ // RPC was sent, but no response headers were received. This can happen in
140+ // case of a timeout, for example.
141+ if (compositeTracer != null ) {
142+ compositeTracer .recordGfeHeaderMissingCount (1L );
143+ if (GapicSpannerRpc .isEnableAFEServerTiming ()) {
144+ // compositeTracer.recordAfeHeaderMissingCount(1L);
145+ }
146+ }
147+ }
148+ super .onClose (status , trailers );
149+ }
134150 },
135151 headers );
136152 } catch (ExecutionException executionException ) {
0 commit comments