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                  } else {
87                      metricsRecorder.record(
88                              getDelegate().getName(),
89                              EXTERNAL,
90                              timedSupplier.wasInvoked() ? NUMBER_OF_MISSES : NUMBER_OF_HITS,
91                              1);
92                  }
93              });
94  
95              return result;
96          }
97      }
98  
99      @Override
100     public CompletionStage<Map<String, Optional<IdentifiedValue<V>>>> getBulkIdentified(Iterable<String> keys) {
101         try (ElapsedTimer ignored = new ElapsedTimer(
102                 t -> metricsRecorder.record(getDelegate().getName(), EXTERNAL, TIMED_IDENTIFIED_GET_CALL, t))) {
103             final CompletionStage<Map<String, Optional<IdentifiedValue<V>>>> result = getDelegate().getBulkIdentified(keys);
104 
105             whenCompletableFuture(result, future -> {
106 
107                 if (future.isCompletedExceptionally()) {
108                     metricsRecorder.record(getDelegate().getName(), EXTERNAL, NUMBER_OF_FAILED_IDENTIFIED_GET, 1);
109                 } else {
110                     final Map<String, Optional<IdentifiedValue<V>>> rj = future.join();
111                     final long hits = rj.values().stream().filter(Optional::isPresent).count();
112 
113                     whenPositive(hits,
114                             v -> metricsRecorder.record(getDelegate().getName(), EXTERNAL, NUMBER_OF_HITS, v));
115                     whenPositive(rj.size() - hits,
116                             v -> metricsRecorder.record(getDelegate().getName(), EXTERNAL, NUMBER_OF_MISSES, v));
117                 }
118             });
119 
120             return result;
121         }
122     }
123 
124     @Override
125     public CompletionStage<Boolean> removeIf(String key, CasIdentifier casId) {
126         try (ElapsedTimer ignored = new ElapsedTimer(
127                 t -> metricsRecorder.record(getDelegate().getName(), EXTERNAL, TIMED_IDENTIFIED_REMOVE_CALL, t))) {
128             final CompletionStage<Boolean> result = getDelegate().removeIf(key, casId);
129 
130             whenCompletableFuture(result, future -> {
131                 if (future.isCompletedExceptionally()) {
132                     metricsRecorder.record(getDelegate().getName(), EXTERNAL, NUMBER_OF_FAILED_IDENTIFIED_REMOVE, 1);
133                 }
134             });
135 
136             return result;
137         }
138     }
139 
140     @Override
141     public CompletionStage<Boolean> replaceIf(String key, CasIdentifier casId, V newValue) {
142         try (ElapsedTimer ignored = new ElapsedTimer(
143                 t -> metricsRecorder.record(getDelegate().getName(), EXTERNAL, TIMED_IDENTIFIED_REPLACE_CALL, t))) {
144             final CompletionStage<Boolean> result = getDelegate().replaceIf(key, casId, newValue);
145             whenCompletableFuture(result, future -> {
146                 if (future.isCompletedExceptionally()) {
147                     metricsRecorder.record(getDelegate().getName(), EXTERNAL, NUMBER_OF_FAILED_IDENTIFIED_REPLACE, 1);
148                 }
149             });
150 
151             return result;
152         }
153     }
154 }