View Javadoc

1   package com.atlassian.vcache.internal.core.metrics;
2   
3   import com.atlassian.vcache.CasIdentifier;
4   import com.atlassian.vcache.DirectExternalCache;
5   import com.atlassian.vcache.ExternalWriteOperationsUnbuffered;
6   import com.atlassian.vcache.IdentifiedValue;
7   
8   import java.util.Map;
9   import java.util.Optional;
10  import java.util.concurrent.CompletionStage;
11  import java.util.function.Supplier;
12  
13  import static com.atlassian.vcache.internal.MetricLabel.NUMBER_OF_FAILED_IDENTIFIED_GET;
14  import static com.atlassian.vcache.internal.MetricLabel.NUMBER_OF_FAILED_IDENTIFIED_REMOVE;
15  import static com.atlassian.vcache.internal.MetricLabel.NUMBER_OF_FAILED_IDENTIFIED_REPLACE;
16  import static com.atlassian.vcache.internal.MetricLabel.NUMBER_OF_HITS;
17  import static com.atlassian.vcache.internal.MetricLabel.NUMBER_OF_MISSES;
18  import static com.atlassian.vcache.internal.MetricLabel.TIMED_IDENTIFIED_GET_CALL;
19  import static com.atlassian.vcache.internal.MetricLabel.TIMED_IDENTIFIED_REMOVE_CALL;
20  import static com.atlassian.vcache.internal.MetricLabel.TIMED_IDENTIFIED_REPLACE_CALL;
21  import static com.atlassian.vcache.internal.core.VCacheCoreUtils.whenPositive;
22  import static com.atlassian.vcache.internal.core.metrics.CacheType.EXTERNAL;
23  import static com.atlassian.vcache.internal.core.metrics.TimedUtils.whenCompletableFuture;
24  import static java.util.Objects.requireNonNull;
25  
26  /**
27   * Wrapper for a {@link DirectExternalCache} that records metrics.
28   *
29   * @param <V> the value type
30   * @since 1.0.0
31   */
32  class TimedDirectExternalCache<V>
33          extends TimedExternalWriteOperationsUnbuffered<V>
34          implements DirectExternalCache<V> {
35      private final DirectExternalCache<V> delegate;
36  
37      TimedDirectExternalCache(MetricsRecorder metricsRecorder, DirectExternalCache<V> delegate) {
38          super(metricsRecorder);
39          this.delegate = requireNonNull(delegate);
40      }
41  
42      @Override
43      protected ExternalWriteOperationsUnbuffered<V> getDelegateOps() {
44          return delegate;
45      }
46  
47      @Override
48      protected DirectExternalCache<V> getDelegate() {
49          return delegate;
50      }
51  
52      @Override
53      public CompletionStage<Optional<IdentifiedValue<V>>> getIdentified(String key) {
54          try (ElapsedTimer ignored = new ElapsedTimer(
55                  t -> metricsRecorder.record(getDelegate().getName(), EXTERNAL, TIMED_IDENTIFIED_GET_CALL, t))) {
56              final CompletionStage<Optional<IdentifiedValue<V>>> result = getDelegate().getIdentified(key);
57  
58              whenCompletableFuture(result, future -> {
59                  if (future.isCompletedExceptionally()) {
60                      metricsRecorder.record(getDelegate().getName(), EXTERNAL, NUMBER_OF_FAILED_IDENTIFIED_GET, 1);
61                  } else {
62                      final Optional<IdentifiedValue<V>> rj = future.join();
63                      metricsRecorder.record(
64                              getDelegate().getName(),
65                              EXTERNAL,
66                              rj.isPresent() ? NUMBER_OF_HITS : NUMBER_OF_MISSES,
67                              1);
68                  }
69              });
70  
71              return result;
72          }
73      }
74  
75      @Override
76      public CompletionStage<IdentifiedValue<V>> getIdentified(String key, Supplier<V> supplier) {
77          try (ElapsedTimer ignored = new ElapsedTimer(
78                  t -> metricsRecorder.record(getDelegate().getName(), EXTERNAL, TIMED_IDENTIFIED_GET_CALL, t));
79               TimedSupplier<V> timedSupplier = new TimedSupplier<>(supplier, this::handleTimedSupplier)) {
80  
81              final CompletionStage<IdentifiedValue<V>> result = getDelegate().getIdentified(key, timedSupplier);
82  
83              whenCompletableFuture(result, future -> {
84                  if (future.isCompletedExceptionally()) {
85                      metricsRecorder.record(getDelegate().getName(), EXTERNAL, NUMBER_OF_FAILED_IDENTIFIED_GET, 1);
86                  }
87              });
88  
89              return result;
90          }
91      }
92  
93      @Override
94      public CompletionStage<Map<String, Optional<IdentifiedValue<V>>>> getBulkIdentified(Iterable<String> keys) {
95          try (ElapsedTimer ignored = new ElapsedTimer(
96                  t -> metricsRecorder.record(getDelegate().getName(), EXTERNAL, TIMED_IDENTIFIED_GET_CALL, t))) {
97              final CompletionStage<Map<String, Optional<IdentifiedValue<V>>>> result = getDelegate().getBulkIdentified(keys);
98  
99              whenCompletableFuture(result, future -> {
100 
101                 if (future.isCompletedExceptionally()) {
102                     metricsRecorder.record(getDelegate().getName(), EXTERNAL, NUMBER_OF_FAILED_IDENTIFIED_GET, 1);
103                 } else {
104                     final Map<String, Optional<IdentifiedValue<V>>> rj = future.join();
105                     final long hits = rj.values().stream().filter(Optional::isPresent).count();
106 
107                     whenPositive(hits,
108                             v -> metricsRecorder.record(getDelegate().getName(), EXTERNAL, NUMBER_OF_HITS, v));
109                     whenPositive(rj.size() - hits,
110                             v -> metricsRecorder.record(getDelegate().getName(), EXTERNAL, NUMBER_OF_MISSES, v));
111                 }
112             });
113 
114             return result;
115         }
116     }
117 
118     @Override
119     public CompletionStage<Boolean> removeIf(String key, CasIdentifier casId) {
120         try (ElapsedTimer ignored = new ElapsedTimer(
121                 t -> metricsRecorder.record(getDelegate().getName(), EXTERNAL, TIMED_IDENTIFIED_REMOVE_CALL, t))) {
122             final CompletionStage<Boolean> result = getDelegate().removeIf(key, casId);
123 
124             whenCompletableFuture(result, future -> {
125                 if (future.isCompletedExceptionally()) {
126                     metricsRecorder.record(getDelegate().getName(), EXTERNAL, NUMBER_OF_FAILED_IDENTIFIED_REMOVE, 1);
127                 }
128             });
129 
130             return result;
131         }
132     }
133 
134     @Override
135     public CompletionStage<Boolean> replaceIf(String key, CasIdentifier casId, V newValue) {
136         try (ElapsedTimer ignored = new ElapsedTimer(
137                 t -> metricsRecorder.record(getDelegate().getName(), EXTERNAL, TIMED_IDENTIFIED_REPLACE_CALL, t))) {
138             final CompletionStage<Boolean> result = getDelegate().replaceIf(key, casId, newValue);
139             whenCompletableFuture(result, future -> {
140                 if (future.isCompletedExceptionally()) {
141                     metricsRecorder.record(getDelegate().getName(), EXTERNAL, NUMBER_OF_FAILED_IDENTIFIED_REPLACE, 1);
142                 }
143             });
144 
145             return result;
146         }
147     }
148 }