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 }
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 }