2020import com .google .api .core .ApiFutureCallback ;
2121import com .google .api .core .ApiFutures ;
2222import com .google .api .core .SettableApiFuture ;
23+ import com .google .api .gax .rpc .ApiException ;
2324import com .google .api .gax .rpc .ApiStreamObserver ;
2425import com .google .api .gax .rpc .BidiStreamingCallable ;
2526import com .google .api .gax .rpc .ServerStreamingCallable ;
2627import com .google .api .gax .rpc .UnaryCallable ;
2728import com .google .cloud .firestore .spi .v1beta1 .FirestoreRpc ;
2829import com .google .common .base .Preconditions ;
30+ import com .google .common .collect .ImmutableMap ;
2931import com .google .firestore .v1beta1 .BatchGetDocumentsRequest ;
3032import com .google .firestore .v1beta1 .BatchGetDocumentsResponse ;
3133import com .google .firestore .v1beta1 .DatabaseRootName ;
3234import com .google .protobuf .ByteString ;
35+ import io .grpc .Context ;
3336import io .grpc .Status ;
37+ import io .opencensus .common .Scope ;
38+ import io .opencensus .trace .AttributeValue ;
39+ import io .opencensus .trace .Span ;
40+ import io .opencensus .trace .Tracer ;
41+ import io .opencensus .trace .Tracing ;
3442import java .util .ArrayList ;
3543import java .util .HashMap ;
3644import java .util .List ;
@@ -52,6 +60,10 @@ class FirestoreImpl implements Firestore {
5260 private static final String AUTO_ID_ALPHABET =
5361 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" ;
5462
63+ private static final Tracer tracer = Tracing .getTracer ();
64+ private static final io .opencensus .trace .Status TOO_MANY_RETRIES_STATUS =
65+ io .opencensus .trace .Status .ABORTED .withDescription ("too many retries" );
66+
5567 private final FirestoreRpc firestoreClient ;
5668 private final FirestoreOptions firestoreOptions ;
5769 private final ResourcePath databasePath ;
@@ -126,11 +138,24 @@ ApiFuture<List<DocumentSnapshot>> getAll(
126138
127139 ApiStreamObserver <BatchGetDocumentsResponse > responseObserver =
128140 new ApiStreamObserver <BatchGetDocumentsResponse >() {
141+ int numResponses ;
142+
129143 @ Override
130144 public void onNext (BatchGetDocumentsResponse response ) {
131145 DocumentReference documentReference ;
132146 DocumentSnapshot documentSnapshot ;
133147
148+ numResponses ++;
149+ if (numResponses == 1 ) {
150+ tracer
151+ .getCurrentSpan ()
152+ .addAnnotation ("Firestore.BatchGet: First response" );
153+ } else if (numResponses % 100 == 0 ) {
154+ tracer
155+ .getCurrentSpan ()
156+ .addAnnotation ("Firestore.BatchGet: Received 100 responses" );
157+ }
158+
134159 switch (response .getResultCase ()) {
135160 case FOUND :
136161 documentReference =
@@ -161,11 +186,13 @@ public void onNext(BatchGetDocumentsResponse response) {
161186
162187 @ Override
163188 public void onError (Throwable throwable ) {
189+ tracer .getCurrentSpan ().addAnnotation ("Firestore.BatchGet: Error" );
164190 futureList .setException (throwable );
165191 }
166192
167193 @ Override
168194 public void onCompleted () {
195+ tracer .getCurrentSpan ().addAnnotation ("Firestore.BatchGet: Complete" );
169196 List <DocumentSnapshot > documentSnapshots = new ArrayList <>();
170197
171198 for (DocumentReference documentReference : documentReferences ) {
@@ -187,6 +214,13 @@ public void onCompleted() {
187214 request .addDocuments (docRef .getName ());
188215 }
189216
217+ tracer
218+ .getCurrentSpan ()
219+ .addAnnotation (
220+ "Firestore.BatchGet: Start" ,
221+ ImmutableMap .of (
222+ "numDocuments" , AttributeValue .longAttributeValue (documentReferences .length )));
223+
190224 streamRequest (request .build (), responseObserver , firestoreClient .batchGetDocumentsCallable ());
191225
192226 return futureList ;
@@ -213,11 +247,26 @@ private <T> void runTransaction(
213247 final Transaction .Function <T > transactionCallback ,
214248 final SettableApiFuture <T > resultFuture ,
215249 final TransactionOptions options ) {
250+ Span span = tracer .spanBuilder ("CloudFirestore.Transaction" ).startSpan ();
251+ try (Scope s = tracer .withSpan (span )) {
252+ runTransactionAttempt (transactionCallback , resultFuture , options , span );
253+ }
254+ }
255+
256+ private <T > void runTransactionAttempt (
257+ final Transaction .Function <T > transactionCallback ,
258+ final SettableApiFuture <T > resultFuture ,
259+ final TransactionOptions options ,
260+ final Span span ) {
216261 final Transaction transaction = new Transaction (this , options .getPreviousTransactionId ());
217262 final Executor userCallbackExecutor =
218- options .getExecutor () != null ? options .getExecutor () : firestoreClient .getExecutor ();
263+ Context .currentContextExecutor (
264+ options .getExecutor () != null ? options .getExecutor () : firestoreClient .getExecutor ());
219265
220266 final int attemptsRemaining = options .getNumberOfAttempts () - 1 ;
267+ span .addAnnotation (
268+ "Start runTransaction" ,
269+ ImmutableMap .of ("attemptsRemaining" , AttributeValue .longAttributeValue (attemptsRemaining )));
221270
222271 ApiFutures .addCallback (
223272 transaction .begin (),
@@ -253,6 +302,8 @@ public void onFailure(Throwable throwable) {
253302
254303 @ Override
255304 public void onSuccess (List <WriteResult > writeResults ) {
305+ span .setStatus (io .opencensus .trace .Status .OK );
306+ span .end ();
256307 resultFuture .set (userResult );
257308 }
258309 });
@@ -279,19 +330,27 @@ public void run() {
279330
280331 private void maybeRetry () {
281332 if (attemptsRemaining > 0 ) {
282- runTransaction (
333+ span .addAnnotation ("retrying" );
334+ runTransactionAttempt (
283335 transactionCallback ,
284336 resultFuture ,
285337 new TransactionOptions (
286- attemptsRemaining , options .getExecutor (), transaction .getTransactionId ()));
338+ attemptsRemaining , options .getExecutor (), transaction .getTransactionId ()),
339+ span );
287340 } else {
341+ span .setStatus (TOO_MANY_RETRIES_STATUS );
342+ span .end ();
288343 rejectTransaction (
289344 FirestoreException .serverRejected (
290345 Status .ABORTED , "Transaction was cancelled because of too many retries." ));
291346 }
292347 }
293348
294349 private void rejectTransaction (final Throwable throwable ) {
350+ if (throwable instanceof ApiException ) {
351+ span .setStatus (TraceUtil .statusFromApiException ((ApiException ) throwable ));
352+ }
353+ span .end ();
295354 if (transaction .isPending ()) {
296355 ApiFutures .addCallback (
297356 transaction .rollback (),
0 commit comments