View Javadoc

1   package com.atlassian.vcache.internal.core.metrics;
2   
3   import com.atlassian.vcache.LocalCacheOperations;
4   import com.atlassian.vcache.internal.MetricLabel;
5   
6   import java.util.Map;
7   import java.util.Optional;
8   import java.util.Set;
9   import java.util.function.Function;
10  import java.util.function.Supplier;
11  
12  import static com.atlassian.vcache.internal.MetricLabel.NUMBER_OF_FACTORY_KEYS;
13  import static com.atlassian.vcache.internal.MetricLabel.NUMBER_OF_HITS;
14  import static com.atlassian.vcache.internal.MetricLabel.NUMBER_OF_MISSES;
15  import static com.atlassian.vcache.internal.MetricLabel.TIMED_FACTORY_CALL;
16  import static com.atlassian.vcache.internal.MetricLabel.TIMED_GET_CALL;
17  import static com.atlassian.vcache.internal.MetricLabel.TIMED_PUT_CALL;
18  import static com.atlassian.vcache.internal.MetricLabel.TIMED_REMOVE_ALL_CALL;
19  import static com.atlassian.vcache.internal.MetricLabel.TIMED_REMOVE_CALL;
20  import static com.atlassian.vcache.internal.MetricLabel.TIMED_SUPPLIER_CALL;
21  import static java.util.Objects.requireNonNull;
22  
23  /**
24   * Wrapper for a {@link LocalCacheOperations} that records metrics.
25   *
26   * @param <K> the key type
27   * @param <V> the value type
28   * @since 1.0.0
29   */
30  abstract class TimedLocalCacheOperations<K, V>
31          implements LocalCacheOperations<K, V> {
32      protected final String cacheName;
33      protected final CacheType cacheType;
34      protected final MetricsRecorder metricsRecorder;
35  
36      TimedLocalCacheOperations(String cacheName, CacheType cacheType, MetricsRecorder metricsRecorder) {
37          this.cacheName = requireNonNull(cacheName);
38          this.cacheType = requireNonNull(cacheType);
39          this.metricsRecorder = requireNonNull(metricsRecorder);
40      }
41  
42      protected abstract LocalCacheOperations<K, V> getDelegate();
43  
44      @Override
45      public Optional<V> get(K key) {
46          try (ElapsedTimer ignored = new ElapsedTimer(
47                  t -> metricsRecorder.record(cacheName, cacheType, TIMED_GET_CALL, t))) {
48              final Optional<V> result = getDelegate().get(key);
49              metricsRecorder.record(
50                      cacheName,
51                      cacheType,
52                      result.isPresent() ? MetricLabel.NUMBER_OF_HITS : MetricLabel.NUMBER_OF_MISSES,
53                      1);
54  
55              return result;
56          }
57      }
58  
59      @Override
60      public V get(K key, Supplier<? extends V> supplier) {
61          try (ElapsedTimer ignored = new ElapsedTimer(
62                  t -> metricsRecorder.record(cacheName, cacheType, TIMED_GET_CALL, t));
63               TimedSupplier<? extends V> timedSupplier = new TimedSupplier<>(supplier, this::handleTimedSupplier)) {
64              return getDelegate().get(key, timedSupplier);
65          }
66      }
67  
68      @Override
69      public Map<K, V> getBulk(Function<Set<K>, Map<K, V>> factory, Iterable<K> keys) {
70          try (ElapsedTimer ignored = new ElapsedTimer(
71                  t -> metricsRecorder.record(cacheName, cacheType, TIMED_GET_CALL, t));
72               TimedFactory<K, V> timedFactory = new TimedFactory<>(factory, this::handleTimedFactory)) {
73              return getDelegate().getBulk(timedFactory, keys);
74          }
75      }
76  
77      @Override
78      public void put(K key, V value) {
79          try (ElapsedTimer ignored = new ElapsedTimer(
80                  t -> metricsRecorder.record(cacheName, cacheType, TIMED_PUT_CALL, t))) {
81              getDelegate().put(key, value);
82          }
83      }
84  
85      @Override
86      public Optional<V> putIfAbsent(K key, V value) {
87          try (ElapsedTimer ignored = new ElapsedTimer(
88                  t -> metricsRecorder.record(cacheName, cacheType, TIMED_PUT_CALL, t))) {
89              return getDelegate().putIfAbsent(key, value);
90          }
91      }
92  
93      @Override
94      public boolean replaceIf(K key, V currentValue, V newValue) {
95          try (ElapsedTimer ignored = new ElapsedTimer(
96                  t -> metricsRecorder.record(cacheName, cacheType, TIMED_PUT_CALL, t))) {
97              return getDelegate().replaceIf(key, currentValue, newValue);
98          }
99      }
100 
101     @Override
102     public boolean removeIf(K key, V value) {
103         try (ElapsedTimer ignored = new ElapsedTimer(
104                 t -> metricsRecorder.record(cacheName, cacheType, TIMED_REMOVE_CALL, t))) {
105             return getDelegate().removeIf(key, value);
106         }
107     }
108 
109     @Override
110     public void remove(K key) {
111         try (ElapsedTimer ignored = new ElapsedTimer(
112                 t -> metricsRecorder.record(cacheName, cacheType, TIMED_REMOVE_CALL, t))) {
113             getDelegate().remove(key);
114         }
115     }
116 
117     @Override
118     public void removeAll() {
119         try (ElapsedTimer ignored = new ElapsedTimer(
120                 t -> metricsRecorder.record(cacheName, cacheType, TIMED_REMOVE_ALL_CALL, t))) {
121             getDelegate().removeAll();
122         }
123     }
124 
125     private void handleTimedSupplier(Optional<Long> time) {
126         if (time.isPresent()) {
127             metricsRecorder.record(cacheName, cacheType, TIMED_SUPPLIER_CALL, time.get());
128         }
129         metricsRecorder.record(
130                 cacheName,
131                 cacheType,
132                 time.isPresent() ? NUMBER_OF_MISSES : NUMBER_OF_HITS,
133                 1);
134     }
135 
136     private void handleTimedFactory(Optional<Long> time, Long numberOfKeys) {
137         time.ifPresent(t -> {
138             metricsRecorder.record(cacheName, cacheType, TIMED_FACTORY_CALL, t);
139             metricsRecorder.record(cacheName, cacheType, NUMBER_OF_FACTORY_KEYS, numberOfKeys);
140         });
141     }
142 }