33import static com .google .common .base .Preconditions .checkArgument ;
44
55import ch .cern .cta .rpc .CtaRpcGrpc ;
6+ import ch .cern .cta .rpc .Response ;
67import com .google .common .annotations .VisibleForTesting ;
78import com .google .common .base .Splitter ;
89import com .google .common .base .Throwables ;
910import com .google .common .net .HostAndPort ;
1011import com .google .common .util .concurrent .ThreadFactoryBuilder ;
1112import diskCacheV111 .util .CacheException ;
1213import dmg .util .command .Command ;
14+ import dmg .util .command .Option ;
1315import io .grpc .ChannelCredentials ;
1416import io .grpc .ConnectivityState ;
1517import io .grpc .Deadline ;
1618import io .grpc .InsecureChannelCredentials ;
1719import io .grpc .ManagedChannel ;
20+ import io .grpc .Status ;
1821import io .grpc .StatusRuntimeException ;
1922import io .grpc .TlsChannelCredentials ;
2023import io .grpc .netty .shaded .io .grpc .netty .NettyChannelBuilder ;
3740import java .util .stream .Collectors ;
3841
3942import ch .cern .cta .rpc .CtaRpcGrpc .CtaRpcBlockingStub ;
43+ import org .dcache .commons .stats .RequestCounters ;
44+ import org .dcache .commons .stats .RequestExecutionTimeGauges ;
4045import org .dcache .namespace .FileAttribute ;
4146import org .dcache .nearline .cta .xrootd .DataMover ;
4247import org .dcache .pool .nearline .spi .FlushRequest ;
@@ -138,6 +143,29 @@ public class CtaNearlineStorage implements NearlineStorage {
138143 */
139144 private boolean dio ;
140145
146+
147+ /**
148+ * Actions that can be performed by this nearline storage. A shorthand version of cta.eos.CtaEos.Workflow.EventType
149+ */
150+ enum Action {
151+ GET_ARCHIVE_ID ,
152+ SUBMIT_FLUSH ,
153+ SUBMIT_STAGE ,
154+ CANCEL_FLUSH ,
155+ CANCEL_STAGE ,
156+ DELETE_REQUEST ,
157+ }
158+
159+ /**
160+ * gRPC request execution statistics
161+ */
162+ private final RequestExecutionTimeGauges <Action > requestStats = new RequestExecutionTimeGauges <>("CTA gRPC Requests statistics" );
163+
164+ /**
165+ * gRPC request counters
166+ */
167+ private final RequestCounters <Action > requestCounters = new RequestCounters <>("CTA gRPC Requests counters" );
168+
141169 public CtaNearlineStorage (String type , String name ) {
142170
143171 Objects .requireNonNull (type , "HSM type is not provided" );
@@ -187,7 +215,7 @@ public void flush(Iterable<FlushRequest> requests) {
187215 }
188216
189217 var createRequest = ctaRequestFactory .getCreateRequest (fr .getFileAttributes ());
190- var createResponse = cta .withDeadline (getRequestDeadline ()).create (createRequest );
218+ var createResponse = withStats ( Action . GET_ARCHIVE_ID , () -> cta .withDeadline (getRequestDeadline ()).create (createRequest ) );
191219 LOGGER .info ("{}: Create new archive id {} for {}" , fr .getId (),
192220 createResponse .getArchiveFileId (),
193221 fr .getFileAttributes ().getPnfsId ()
@@ -240,7 +268,7 @@ public void completed(Set<URI> uris) {
240268 };
241269
242270 var ar = ctaRequestFactory .getStoreRequest (r , ctaArchiveId );
243- var response = cta .withDeadline (getRequestDeadline ()).archive (ar );
271+ var response = withStats ( Action . CANCEL_FLUSH , () -> cta .withDeadline (getRequestDeadline ()).archive (ar ) );
244272
245273 LOGGER .info ("{} : {} : archive id {}, request: {}" ,
246274 r .getId (),
@@ -256,7 +284,7 @@ public void completed(Set<URI> uris) {
256284 @ Override
257285 public void cancel () {
258286 try {
259- cta .withDeadline (getRequestDeadline ()).delete (cancelRequest );
287+ withStats ( Action . CANCEL_FLUSH , () -> cta .withDeadline (getRequestDeadline ()).delete (cancelRequest ) );
260288 super .cancel ();
261289 } catch (StatusRuntimeException e ) {
262290 LOGGER .error ("Failed to cancel flush request: {}" , e .getMessage ());
@@ -313,7 +341,7 @@ public void completed(Set<Checksum> checksums) {
313341 }
314342
315343 var rr = ctaRequestFactory .getStageRequest (r );
316- var response = cta .withDeadline (getRequestDeadline ()).retrieve (rr );
344+ var response = withStats ( Action . SUBMIT_STAGE , () -> cta .withDeadline (getRequestDeadline ()).retrieve (rr ) );
317345 LOGGER .info ("{} : {} : request: {}" ,
318346 r .getId (),
319347 r .getFileAttributes ().getPnfsId (),
@@ -326,7 +354,7 @@ public void completed(Set<Checksum> checksums) {
326354 public void cancel () {
327355 // on cancel send the request to CTA; on success cancel the requests
328356 try {
329- cta .withDeadline (getRequestDeadline ()).cancelRetrieve (cancelRequest );
357+ withStats ( Action . CANCEL_STAGE , () -> cta .withDeadline (getRequestDeadline ()).cancelRetrieve (cancelRequest ) );
330358 super .cancel ();
331359 } catch (StatusRuntimeException e ) {
332360 LOGGER .error ("Failed to cancel retrieve request: {}" , e .getMessage ());
@@ -362,7 +390,7 @@ public void remove(Iterable<RemoveRequest> requests) {
362390
363391 try {
364392 var deleteRequest = ctaRequestFactory .getRemoveRequest (r );
365- var result = cta .withDeadline (getRequestDeadline ()).delete (deleteRequest );
393+ var result = withStats ( Action . DELETE_REQUEST , () -> cta .withDeadline (getRequestDeadline ()).delete (deleteRequest ) );
366394 LOGGER .info ("Delete request {} submitted {}" ,
367395 r .getId (),
368396 r .getUri ()
@@ -576,4 +604,52 @@ public String call() {
576604 return sb .toString ();
577605 }
578606 }
607+
608+
609+ /**
610+ * Wraps a gRPC request with statistics collection.
611+ *
612+ * @param action the action type for which the statistics are collected.
613+ * @param callable the gRPC request to execute.
614+ * @return the response from the gRPC request.
615+ * @throws Exception if the gRPC request fails.
616+ */
617+ private Response withStats (Action action , Callable <Response > callable ) throws StatusRuntimeException {
618+ try {
619+ var t0 = System .nanoTime ();
620+ var response = callable .call ();
621+ requestStats .getGauge (action ).update (TimeUnit .NANOSECONDS .toMillis (System .nanoTime () - t0 ));
622+ requestCounters .incrementRequests (action );
623+
624+ return response ;
625+ } catch (Exception e ) {
626+ requestCounters .incrementFailed (action );
627+ Throwables .throwIfInstanceOf (e , StatusRuntimeException .class );
628+ throw new StatusRuntimeException (Status .UNKNOWN );
629+ }
630+ }
631+
632+
633+ @ Command (name ="show stats" ,
634+ hint = "show gRPC requests statistics" ,
635+ description = "Show gRPC requests statistics for CTA nearline storage" )
636+ public class StatsCommand implements Callable <String > {
637+
638+ @ Option (name = "c" , usage = "clear current statistics values" )
639+ boolean clean ;
640+
641+ @ Override
642+ public String call () {
643+ StringBuilder sb = new StringBuilder ();
644+ sb .append (requestStats );
645+ sb .append ("\n \n " );
646+ sb .append (requestCounters );
647+
648+ if (clean ) {
649+ requestStats .reset ();
650+ requestCounters .reset ();
651+ }
652+ return sb .toString ();
653+ }
654+ }
579655}
0 commit comments