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
25
26
27
28
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 }