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
28
29
30
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 }