1- /**
1+ /*
22 * Licensed to the Apache Software Foundation (ASF) under one
33 * or more contributor license agreements. See the NOTICE file
44 * distributed with this work for additional information
2727import static org .apache .hadoop .hbase .client .ConnectionUtils .isRemote ;
2828import static org .apache .hadoop .hbase .client .ConnectionUtils .timelineConsistentRead ;
2929import static org .apache .hadoop .hbase .util .FutureUtils .addListener ;
30-
30+ import io .opentelemetry .api .trace .Span ;
31+ import io .opentelemetry .api .trace .StatusCode ;
32+ import io .opentelemetry .context .Scope ;
3133import java .io .IOException ;
3234import java .util .concurrent .CompletableFuture ;
3335import java .util .concurrent .TimeUnit ;
3436import java .util .concurrent .atomic .AtomicInteger ;
3537import org .apache .hadoop .hbase .HRegionLocation ;
3638import org .apache .hadoop .hbase .TableName ;
3739import org .apache .hadoop .hbase .client .metrics .ScanMetrics ;
40+ import org .apache .hadoop .hbase .client .trace .TableOperationSpanBuilder ;
3841import org .apache .hadoop .hbase .ipc .HBaseRpcController ;
42+ import org .apache .hadoop .hbase .trace .TraceUtil ;
3943import org .apache .yetus .audience .InterfaceAudience ;
40-
4144import org .apache .hbase .thirdparty .io .netty .util .Timer ;
42-
4345import org .apache .hadoop .hbase .shaded .protobuf .RequestConverter ;
4446import org .apache .hadoop .hbase .shaded .protobuf .generated .ClientProtos .ClientService ;
4547import org .apache .hadoop .hbase .shaded .protobuf .generated .ClientProtos .ClientService .Interface ;
@@ -85,6 +87,17 @@ class AsyncClientScanner {
8587
8688 private final ScanResultCache resultCache ;
8789
90+ /*
91+ * The `span` instance is accessed concurrently by several threads, but we use only basic
92+ * synchronization. The class claims to be `@ThreadSafe` so we rely on the implementation to
93+ * correctly handle concurrent use. The instance itself is initialized in the `start` method,
94+ * so we cannot make it `final`. Because the instance is created before any consuming runnable
95+ * is submitted to a worker pool, it should be enough to mark it as `volatile`. To avoid over-
96+ * paying the price of the memory barrier, where a consumer makes multiple uses of the `span`
97+ * instance, we use a local final non-volatile reference.
98+ */
99+ private volatile Span span = null ;
100+
88101 public AsyncClientScanner (Scan scan , AdvancedScanResultConsumer consumer , TableName tableName ,
89102 AsyncConnectionImpl conn , Timer retryTimer , long pauseNs , long pauseForCQTBENs ,
90103 int maxAttempts , long scanTimeoutNs , long rpcTimeoutNs , int startLogErrorsCnt ) {
@@ -140,26 +153,37 @@ public OpenScannerResponse(HRegionLocation loc, boolean isRegionServerRemote, In
140153
141154 private CompletableFuture <OpenScannerResponse > callOpenScanner (HBaseRpcController controller ,
142155 HRegionLocation loc , ClientService .Interface stub ) {
143- boolean isRegionServerRemote = isRemote (loc .getHostname ());
144- incRPCCallsMetrics (scanMetrics , isRegionServerRemote );
145- if (openScannerTries .getAndIncrement () > 1 ) {
146- incRPCRetriesMetrics (scanMetrics , isRegionServerRemote );
147- }
148- CompletableFuture <OpenScannerResponse > future = new CompletableFuture <>();
149- try {
150- ScanRequest request = RequestConverter .buildScanRequest (loc .getRegion ().getRegionName (), scan ,
151- scan .getCaching (), false );
152- stub .scan (controller , request , resp -> {
153- if (controller .failed ()) {
154- future .completeExceptionally (controller .getFailed ());
155- return ;
156- }
157- future .complete (new OpenScannerResponse (loc , isRegionServerRemote , stub , controller , resp ));
158- });
159- } catch (IOException e ) {
160- future .completeExceptionally (e );
156+ final Span localSpan = span ;
157+ try (Scope ignored = localSpan .makeCurrent ()) {
158+ boolean isRegionServerRemote = isRemote (loc .getHostname ());
159+ incRPCCallsMetrics (scanMetrics , isRegionServerRemote );
160+ if (openScannerTries .getAndIncrement () > 1 ) {
161+ incRPCRetriesMetrics (scanMetrics , isRegionServerRemote );
162+ }
163+ CompletableFuture <OpenScannerResponse > future = new CompletableFuture <>();
164+ try {
165+ ScanRequest request = RequestConverter .buildScanRequest (
166+ loc .getRegion ().getRegionName (), scan , scan .getCaching (), false );
167+ stub .scan (controller , request , resp -> {
168+ try (Scope ignored1 = localSpan .makeCurrent ()) {
169+ if (controller .failed ()) {
170+ final IOException e = controller .getFailed ();
171+ future .completeExceptionally (e );
172+ TraceUtil .setError (localSpan , e );
173+ localSpan .end ();
174+ return ;
175+ }
176+ future .complete (new OpenScannerResponse (
177+ loc , isRegionServerRemote , stub , controller , resp ));
178+ }
179+ });
180+ } catch (IOException e ) {
181+ TraceUtil .setError (localSpan , e );
182+ localSpan .end ();
183+ future .completeExceptionally (e );
184+ }
185+ return future ;
161186 }
162- return future ;
163187 }
164188
165189 private void startScan (OpenScannerResponse resp ) {
@@ -173,26 +197,41 @@ private void startScan(OpenScannerResponse resp) {
173197 .pauseForCQTBE (pauseForCQTBENs , TimeUnit .NANOSECONDS ).maxAttempts (maxAttempts )
174198 .startLogErrorsCnt (startLogErrorsCnt ).start (resp .controller , resp .resp ),
175199 (hasMore , error ) -> {
176- if (error != null ) {
177- consumer .onError (error );
178- return ;
179- }
180- if (hasMore ) {
181- openScanner ();
182- } else {
183- consumer .onComplete ();
200+ final Span localSpan = span ;
201+ try (Scope ignored = localSpan .makeCurrent ()) {
202+ if (error != null ) {
203+ try {
204+ consumer .onError (error );
205+ return ;
206+ } finally {
207+ TraceUtil .setError (localSpan , error );
208+ localSpan .end ();
209+ }
210+ }
211+ if (hasMore ) {
212+ openScanner ();
213+ } else {
214+ try {
215+ consumer .onComplete ();
216+ } finally {
217+ localSpan .setStatus (StatusCode .OK );
218+ localSpan .end ();
219+ }
220+ }
184221 }
185222 });
186223 }
187224
188225 private CompletableFuture <OpenScannerResponse > openScanner (int replicaId ) {
189- return conn .callerFactory .<OpenScannerResponse > single ().table (tableName )
190- .row (scan .getStartRow ()).replicaId (replicaId ).locateType (getLocateType (scan ))
191- .priority (scan .getPriority ())
192- .rpcTimeout (rpcTimeoutNs , TimeUnit .NANOSECONDS )
193- .operationTimeout (scanTimeoutNs , TimeUnit .NANOSECONDS ).pause (pauseNs , TimeUnit .NANOSECONDS )
194- .pauseForCQTBE (pauseForCQTBENs , TimeUnit .NANOSECONDS ).maxAttempts (maxAttempts )
195- .startLogErrorsCnt (startLogErrorsCnt ).action (this ::callOpenScanner ).call ();
226+ try (Scope ignored = span .makeCurrent ()) {
227+ return conn .callerFactory .<OpenScannerResponse > single ().table (tableName )
228+ .row (scan .getStartRow ()).replicaId (replicaId ).locateType (getLocateType (scan ))
229+ .priority (scan .getPriority ())
230+ .rpcTimeout (rpcTimeoutNs , TimeUnit .NANOSECONDS )
231+ .operationTimeout (scanTimeoutNs , TimeUnit .NANOSECONDS ).pause (pauseNs , TimeUnit .NANOSECONDS )
232+ .pauseForCQTBE (pauseForCQTBENs , TimeUnit .NANOSECONDS ).maxAttempts (maxAttempts )
233+ .startLogErrorsCnt (startLogErrorsCnt ).action (this ::callOpenScanner ).call ();
234+ }
196235 }
197236
198237 private long getPrimaryTimeoutNs () {
@@ -206,15 +245,34 @@ private void openScanner() {
206245 addListener (timelineConsistentRead (conn .getLocator (), tableName , scan , scan .getStartRow (),
207246 getLocateType (scan ), this ::openScanner , rpcTimeoutNs , getPrimaryTimeoutNs (), retryTimer ,
208247 conn .getConnectionMetrics ()), (resp , error ) -> {
209- if (error != null ) {
210- consumer .onError (error );
211- return ;
248+ final Span localSpan = span ;
249+ try (Scope ignored = localSpan .makeCurrent ()) {
250+ if (error != null ) {
251+ try {
252+ consumer .onError (error );
253+ return ;
254+ } finally {
255+ TraceUtil .setError (localSpan , error );
256+ localSpan .end ();
257+ }
258+ }
259+ startScan (resp );
212260 }
213- startScan (resp );
214261 });
215262 }
216263
217264 public void start () {
218- openScanner ();
265+ final Span localSpan = new TableOperationSpanBuilder (conn )
266+ .setTableName (tableName )
267+ .setOperation (scan )
268+ .build ();
269+ if (consumer instanceof AsyncTableResultScanner ) {
270+ AsyncTableResultScanner scanner = (AsyncTableResultScanner ) consumer ;
271+ scanner .setSpan (localSpan );
272+ }
273+ span = localSpan ;
274+ try (Scope ignored = localSpan .makeCurrent ()) {
275+ openScanner ();
276+ }
219277 }
220278}
0 commit comments