Skip to content

Commit 7426ac7

Browse files
adinauerclaude
andcommitted
feat(cache): Add cache.write boolean span attribute
Set cache.write on spans across all four cache wrapper implementations to indicate whether an operation actually modified the cache. This complements the existing cache.hit attribute for read operations. Co-Authored-By: Claude <noreply@anthropic.com>
1 parent bc93435 commit 7426ac7

File tree

10 files changed

+85
-0
lines changed

10 files changed

+85
-0
lines changed

sentry-jcache/src/main/java/io/sentry/jcache/SentryJCacheWrapper.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ public void put(final K key, final V value) {
105105
}
106106
try {
107107
delegate.put(key, value);
108+
span.setData(SpanDataConvention.CACHE_WRITE, true);
108109
span.setStatus(SpanStatus.OK);
109110
} catch (Throwable e) {
110111
span.setStatus(SpanStatus.INTERNAL_ERROR);
@@ -123,6 +124,7 @@ public V getAndPut(final K key, final V value) {
123124
}
124125
try {
125126
final V result = delegate.getAndPut(key, value);
127+
span.setData(SpanDataConvention.CACHE_WRITE, true);
126128
span.setStatus(SpanStatus.OK);
127129
return result;
128130
} catch (Throwable e) {
@@ -143,6 +145,7 @@ public void putAll(final Map<? extends K, ? extends V> map) {
143145
}
144146
try {
145147
delegate.putAll(map);
148+
span.setData(SpanDataConvention.CACHE_WRITE, true);
146149
span.setStatus(SpanStatus.OK);
147150
} catch (Throwable e) {
148151
span.setStatus(SpanStatus.INTERNAL_ERROR);
@@ -161,6 +164,7 @@ public boolean putIfAbsent(final K key, final V value) {
161164
}
162165
try {
163166
final boolean result = delegate.putIfAbsent(key, value);
167+
span.setData(SpanDataConvention.CACHE_WRITE, result);
164168
span.setStatus(SpanStatus.OK);
165169
return result;
166170
} catch (Throwable e) {
@@ -180,6 +184,7 @@ public boolean replace(final K key, final V oldValue, final V newValue) {
180184
}
181185
try {
182186
final boolean result = delegate.replace(key, oldValue, newValue);
187+
span.setData(SpanDataConvention.CACHE_WRITE, result);
183188
span.setStatus(SpanStatus.OK);
184189
return result;
185190
} catch (Throwable e) {
@@ -199,6 +204,7 @@ public boolean replace(final K key, final V value) {
199204
}
200205
try {
201206
final boolean result = delegate.replace(key, value);
207+
span.setData(SpanDataConvention.CACHE_WRITE, result);
202208
span.setStatus(SpanStatus.OK);
203209
return result;
204210
} catch (Throwable e) {
@@ -218,6 +224,7 @@ public V getAndReplace(final K key, final V value) {
218224
}
219225
try {
220226
final V result = delegate.getAndReplace(key, value);
227+
span.setData(SpanDataConvention.CACHE_WRITE, result != null);
221228
span.setStatus(SpanStatus.OK);
222229
return result;
223230
} catch (Throwable e) {
@@ -239,6 +246,7 @@ public boolean remove(final K key) {
239246
}
240247
try {
241248
final boolean result = delegate.remove(key);
249+
span.setData(SpanDataConvention.CACHE_WRITE, result);
242250
span.setStatus(SpanStatus.OK);
243251
return result;
244252
} catch (Throwable e) {
@@ -258,6 +266,7 @@ public boolean remove(final K key, final V oldValue) {
258266
}
259267
try {
260268
final boolean result = delegate.remove(key, oldValue);
269+
span.setData(SpanDataConvention.CACHE_WRITE, result);
261270
span.setStatus(SpanStatus.OK);
262271
return result;
263272
} catch (Throwable e) {
@@ -277,6 +286,7 @@ public V getAndRemove(final K key) {
277286
}
278287
try {
279288
final V result = delegate.getAndRemove(key);
289+
span.setData(SpanDataConvention.CACHE_WRITE, result != null);
280290
span.setStatus(SpanStatus.OK);
281291
return result;
282292
} catch (Throwable e) {
@@ -297,6 +307,7 @@ public void removeAll(final Set<? extends K> keys) {
297307
}
298308
try {
299309
delegate.removeAll(keys);
310+
span.setData(SpanDataConvention.CACHE_WRITE, true);
300311
span.setStatus(SpanStatus.OK);
301312
} catch (Throwable e) {
302313
span.setStatus(SpanStatus.INTERNAL_ERROR);
@@ -316,6 +327,7 @@ public void removeAll() {
316327
}
317328
try {
318329
delegate.removeAll();
330+
span.setData(SpanDataConvention.CACHE_WRITE, true);
319331
span.setStatus(SpanStatus.OK);
320332
} catch (Throwable e) {
321333
span.setStatus(SpanStatus.INTERNAL_ERROR);
@@ -337,6 +349,7 @@ public void clear() {
337349
}
338350
try {
339351
delegate.clear();
352+
span.setData(SpanDataConvention.CACHE_WRITE, true);
340353
span.setStatus(SpanStatus.OK);
341354
} catch (Throwable e) {
342355
span.setStatus(SpanStatus.INTERNAL_ERROR);

sentry-jcache/src/test/kotlin/io/sentry/jcache/SentryJCacheWrapperTest.kt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ class SentryJCacheWrapperTest {
6262
assertEquals("myKey", span.description)
6363
assertEquals(SpanStatus.OK, span.status)
6464
assertEquals(true, span.getData(SpanDataConvention.CACHE_HIT_KEY))
65+
assertNull(span.getData(SpanDataConvention.CACHE_WRITE))
6566
assertEquals(listOf("myKey"), span.getData(SpanDataConvention.CACHE_KEY_KEY))
6667
assertEquals("auto.cache.jcache", span.spanContext.origin)
6768
assertEquals("get", span.getData(SpanDataConvention.CACHE_OPERATION_KEY))
@@ -128,6 +129,7 @@ class SentryJCacheWrapperTest {
128129
val span = tx.spans.first()
129130
assertEquals("cache.put", span.operation)
130131
assertEquals(SpanStatus.OK, span.status)
132+
assertEquals(true, span.getData(SpanDataConvention.CACHE_WRITE))
131133
assertEquals(listOf("myKey"), span.getData(SpanDataConvention.CACHE_KEY_KEY))
132134
assertEquals("put", span.getData(SpanDataConvention.CACHE_OPERATION_KEY))
133135
}
@@ -145,6 +147,7 @@ class SentryJCacheWrapperTest {
145147
assertEquals("oldValue", result)
146148
assertEquals(1, tx.spans.size)
147149
assertEquals("cache.getAndPut", tx.spans.first().operation)
150+
assertEquals(true, tx.spans.first().getData(SpanDataConvention.CACHE_WRITE))
148151
assertEquals("getAndPut", tx.spans.first().getData(SpanDataConvention.CACHE_OPERATION_KEY))
149152
}
150153

@@ -163,6 +166,7 @@ class SentryJCacheWrapperTest {
163166
val span = tx.spans.first()
164167
assertEquals("cache.putAll", span.operation)
165168
assertEquals("testCache", span.description)
169+
assertEquals(true, span.getData(SpanDataConvention.CACHE_WRITE))
166170
val cacheKeys = span.getData(SpanDataConvention.CACHE_KEY_KEY) as List<*>
167171
assertTrue(cacheKeys.containsAll(listOf("k1", "k2")))
168172
assertEquals("putAll", span.getData(SpanDataConvention.CACHE_OPERATION_KEY))
@@ -184,6 +188,7 @@ class SentryJCacheWrapperTest {
184188
val span = tx.spans.first()
185189
assertEquals("cache.putIfAbsent", span.operation)
186190
assertEquals(SpanStatus.OK, span.status)
191+
assertEquals(true, span.getData(SpanDataConvention.CACHE_WRITE))
187192
assertEquals(listOf("myKey"), span.getData(SpanDataConvention.CACHE_KEY_KEY))
188193
assertEquals("putIfAbsent", span.getData(SpanDataConvention.CACHE_OPERATION_KEY))
189194
}
@@ -204,6 +209,7 @@ class SentryJCacheWrapperTest {
204209
val span = tx.spans.first()
205210
assertEquals("cache.replace", span.operation)
206211
assertEquals(SpanStatus.OK, span.status)
212+
assertEquals(true, span.getData(SpanDataConvention.CACHE_WRITE))
207213
assertEquals("replace", span.getData(SpanDataConvention.CACHE_OPERATION_KEY))
208214
}
209215

@@ -221,6 +227,7 @@ class SentryJCacheWrapperTest {
221227
val span = tx.spans.first()
222228
assertEquals("cache.replace", span.operation)
223229
assertEquals(SpanStatus.OK, span.status)
230+
assertEquals(true, span.getData(SpanDataConvention.CACHE_WRITE))
224231
assertEquals("replace", span.getData(SpanDataConvention.CACHE_OPERATION_KEY))
225232
}
226233

@@ -240,6 +247,7 @@ class SentryJCacheWrapperTest {
240247
val span = tx.spans.first()
241248
assertEquals("cache.getAndReplace", span.operation)
242249
assertEquals(SpanStatus.OK, span.status)
250+
assertEquals(true, span.getData(SpanDataConvention.CACHE_WRITE))
243251
assertEquals("getAndReplace", span.getData(SpanDataConvention.CACHE_OPERATION_KEY))
244252
}
245253

@@ -258,6 +266,7 @@ class SentryJCacheWrapperTest {
258266
val span = tx.spans.first()
259267
assertEquals("cache.remove", span.operation)
260268
assertEquals(SpanStatus.OK, span.status)
269+
assertEquals(true, span.getData(SpanDataConvention.CACHE_WRITE))
261270
assertEquals("remove", span.getData(SpanDataConvention.CACHE_OPERATION_KEY))
262271
}
263272

@@ -274,6 +283,7 @@ class SentryJCacheWrapperTest {
274283
assertTrue(result)
275284
assertEquals(1, tx.spans.size)
276285
assertEquals("cache.remove", tx.spans.first().operation)
286+
assertEquals(true, tx.spans.first().getData(SpanDataConvention.CACHE_WRITE))
277287
assertEquals("remove", tx.spans.first().getData(SpanDataConvention.CACHE_OPERATION_KEY))
278288
}
279289

@@ -290,6 +300,7 @@ class SentryJCacheWrapperTest {
290300
assertEquals("value", result)
291301
assertEquals(1, tx.spans.size)
292302
assertEquals("cache.getAndRemove", tx.spans.first().operation)
303+
assertEquals(true, tx.spans.first().getData(SpanDataConvention.CACHE_WRITE))
293304
assertEquals("getAndRemove", tx.spans.first().getData(SpanDataConvention.CACHE_OPERATION_KEY))
294305
}
295306

@@ -308,6 +319,7 @@ class SentryJCacheWrapperTest {
308319
val span = tx.spans.first()
309320
assertEquals("cache.removeAll", span.operation)
310321
assertEquals("testCache", span.description)
322+
assertEquals(true, span.getData(SpanDataConvention.CACHE_WRITE))
311323
assertEquals("removeAll", span.getData(SpanDataConvention.CACHE_OPERATION_KEY))
312324
}
313325

@@ -323,6 +335,7 @@ class SentryJCacheWrapperTest {
323335
verify(delegate).removeAll()
324336
assertEquals(1, tx.spans.size)
325337
assertEquals("cache.removeAll", tx.spans.first().operation)
338+
assertEquals(true, tx.spans.first().getData(SpanDataConvention.CACHE_WRITE))
326339
assertEquals("removeAll", tx.spans.first().getData(SpanDataConvention.CACHE_OPERATION_KEY))
327340
}
328341

@@ -340,6 +353,7 @@ class SentryJCacheWrapperTest {
340353
val span = tx.spans.first()
341354
assertEquals("cache.clear", span.operation)
342355
assertEquals(SpanStatus.OK, span.status)
356+
assertEquals(true, span.getData(SpanDataConvention.CACHE_WRITE))
343357
assertNull(span.getData(SpanDataConvention.CACHE_KEY_KEY))
344358
assertEquals("clear", span.getData(SpanDataConvention.CACHE_OPERATION_KEY))
345359
}

sentry-spring-7/src/main/java/io/sentry/spring7/cache/SentryCacheWrapper.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ public SentryCacheWrapper(final @NotNull Cache delegate, final @NotNull IScopes
9696
return valueLoader.call();
9797
});
9898
span.setData(SpanDataConvention.CACHE_HIT_KEY, !loaderInvoked.get());
99+
span.setData(SpanDataConvention.CACHE_WRITE, loaderInvoked.get());
99100
span.setStatus(SpanStatus.OK);
100101
return result;
101102
} catch (Throwable e) {
@@ -171,6 +172,7 @@ public <T> CompletableFuture<T> retrieve(
171172
span.setThrowable(throwable);
172173
} else {
173174
span.setData(SpanDataConvention.CACHE_HIT_KEY, !loaderInvoked.get());
175+
span.setData(SpanDataConvention.CACHE_WRITE, loaderInvoked.get());
174176
span.setStatus(SpanStatus.OK);
175177
}
176178
span.finish();
@@ -186,6 +188,7 @@ public void put(final @NotNull Object key, final @Nullable Object value) {
186188
}
187189
try {
188190
delegate.put(key, value);
191+
span.setData(SpanDataConvention.CACHE_WRITE, true);
189192
span.setStatus(SpanStatus.OK);
190193
} catch (Throwable e) {
191194
span.setStatus(SpanStatus.INTERNAL_ERROR);
@@ -205,6 +208,7 @@ public void put(final @NotNull Object key, final @Nullable Object value) {
205208
}
206209
try {
207210
final ValueWrapper result = delegate.putIfAbsent(key, value);
211+
span.setData(SpanDataConvention.CACHE_WRITE, result == null);
208212
span.setStatus(SpanStatus.OK);
209213
return result;
210214
} catch (Throwable e) {
@@ -225,6 +229,7 @@ public void evict(final @NotNull Object key) {
225229
}
226230
try {
227231
delegate.evict(key);
232+
span.setData(SpanDataConvention.CACHE_WRITE, true);
228233
span.setStatus(SpanStatus.OK);
229234
} catch (Throwable e) {
230235
span.setStatus(SpanStatus.INTERNAL_ERROR);
@@ -243,6 +248,7 @@ public boolean evictIfPresent(final @NotNull Object key) {
243248
}
244249
try {
245250
final boolean result = delegate.evictIfPresent(key);
251+
span.setData(SpanDataConvention.CACHE_WRITE, result);
246252
span.setStatus(SpanStatus.OK);
247253
return result;
248254
} catch (Throwable e) {
@@ -263,6 +269,7 @@ public void clear() {
263269
}
264270
try {
265271
delegate.clear();
272+
span.setData(SpanDataConvention.CACHE_WRITE, true);
266273
span.setStatus(SpanStatus.OK);
267274
} catch (Throwable e) {
268275
span.setStatus(SpanStatus.INTERNAL_ERROR);
@@ -281,6 +288,7 @@ public boolean invalidate() {
281288
}
282289
try {
283290
final boolean result = delegate.invalidate();
291+
span.setData(SpanDataConvention.CACHE_WRITE, true);
284292
span.setStatus(SpanStatus.OK);
285293
return result;
286294
} catch (Throwable e) {

sentry-spring-7/src/test/kotlin/io/sentry/spring7/cache/SentryCacheWrapperTest.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ class SentryCacheWrapperTest {
6161
assertEquals("myKey", span.description)
6262
assertEquals(SpanStatus.OK, span.status)
6363
assertEquals(true, span.getData(SpanDataConvention.CACHE_HIT_KEY))
64+
assertNull(span.getData(SpanDataConvention.CACHE_WRITE))
6465
assertEquals(listOf("myKey"), span.getData(SpanDataConvention.CACHE_KEY_KEY))
6566
assertEquals("auto.cache.spring", span.spanContext.origin)
6667
assertEquals("get", span.getData(SpanDataConvention.CACHE_OPERATION_KEY))
@@ -155,6 +156,7 @@ class SentryCacheWrapperTest {
155156
assertEquals("cached", result)
156157
assertEquals(1, tx.spans.size)
157158
assertEquals(true, tx.spans.first().getData(SpanDataConvention.CACHE_HIT_KEY))
159+
assertEquals(false, tx.spans.first().getData(SpanDataConvention.CACHE_WRITE))
158160
}
159161

160162
@Test
@@ -172,6 +174,7 @@ class SentryCacheWrapperTest {
172174
assertEquals("loaded", result)
173175
assertEquals(1, tx.spans.size)
174176
assertEquals(false, tx.spans.first().getData(SpanDataConvention.CACHE_HIT_KEY))
177+
assertEquals(true, tx.spans.first().getData(SpanDataConvention.CACHE_WRITE))
175178
}
176179

177180
// -- retrieve(Object key) --
@@ -191,6 +194,7 @@ class SentryCacheWrapperTest {
191194
assertEquals("myKey", span.description)
192195
assertEquals(SpanStatus.OK, span.status)
193196
assertEquals(true, span.getData(SpanDataConvention.CACHE_HIT_KEY))
197+
assertNull(span.getData(SpanDataConvention.CACHE_WRITE))
194198
assertEquals("retrieve", span.getData(SpanDataConvention.CACHE_OPERATION_KEY))
195199
assertTrue(span.isFinished)
196200
}
@@ -287,6 +291,7 @@ class SentryCacheWrapperTest {
287291
assertEquals("cached", result.get())
288292
assertEquals(1, tx.spans.size)
289293
assertEquals(true, tx.spans.first().getData(SpanDataConvention.CACHE_HIT_KEY))
294+
assertEquals(false, tx.spans.first().getData(SpanDataConvention.CACHE_WRITE))
290295
assertTrue(tx.spans.first().isFinished)
291296
}
292297

@@ -306,6 +311,7 @@ class SentryCacheWrapperTest {
306311
assertEquals("loaded", result.get())
307312
assertEquals(1, tx.spans.size)
308313
assertEquals(false, tx.spans.first().getData(SpanDataConvention.CACHE_HIT_KEY))
314+
assertEquals(true, tx.spans.first().getData(SpanDataConvention.CACHE_WRITE))
309315
assertTrue(tx.spans.first().isFinished)
310316
}
311317

@@ -355,6 +361,7 @@ class SentryCacheWrapperTest {
355361
val span = tx.spans.first()
356362
assertEquals("cache.put", span.operation)
357363
assertEquals(SpanStatus.OK, span.status)
364+
assertEquals(true, span.getData(SpanDataConvention.CACHE_WRITE))
358365
assertEquals(listOf("myKey"), span.getData(SpanDataConvention.CACHE_KEY_KEY))
359366
assertEquals("put", span.getData(SpanDataConvention.CACHE_OPERATION_KEY))
360367
}
@@ -375,6 +382,7 @@ class SentryCacheWrapperTest {
375382
val span = tx.spans.first()
376383
assertEquals("cache.putIfAbsent", span.operation)
377384
assertEquals(SpanStatus.OK, span.status)
385+
assertEquals(true, span.getData(SpanDataConvention.CACHE_WRITE))
378386
assertEquals(listOf("myKey"), span.getData(SpanDataConvention.CACHE_KEY_KEY))
379387
assertEquals("putIfAbsent", span.getData(SpanDataConvention.CACHE_OPERATION_KEY))
380388
}
@@ -393,6 +401,7 @@ class SentryCacheWrapperTest {
393401
val span = tx.spans.first()
394402
assertEquals("cache.evict", span.operation)
395403
assertEquals(SpanStatus.OK, span.status)
404+
assertEquals(true, span.getData(SpanDataConvention.CACHE_WRITE))
396405
assertEquals("evict", span.getData(SpanDataConvention.CACHE_OPERATION_KEY))
397406
}
398407

@@ -409,6 +418,7 @@ class SentryCacheWrapperTest {
409418
assertTrue(result)
410419
assertEquals(1, tx.spans.size)
411420
assertEquals("cache.evictIfPresent", tx.spans.first().operation)
421+
assertEquals(true, tx.spans.first().getData(SpanDataConvention.CACHE_WRITE))
412422
assertEquals("evictIfPresent", tx.spans.first().getData(SpanDataConvention.CACHE_OPERATION_KEY))
413423
}
414424

@@ -426,6 +436,7 @@ class SentryCacheWrapperTest {
426436
val span = tx.spans.first()
427437
assertEquals("cache.clear", span.operation)
428438
assertEquals(SpanStatus.OK, span.status)
439+
assertEquals(true, span.getData(SpanDataConvention.CACHE_WRITE))
429440
assertNull(span.getData(SpanDataConvention.CACHE_KEY_KEY))
430441
assertEquals("clear", span.getData(SpanDataConvention.CACHE_OPERATION_KEY))
431442
}
@@ -443,6 +454,7 @@ class SentryCacheWrapperTest {
443454
assertTrue(result)
444455
assertEquals(1, tx.spans.size)
445456
assertEquals("cache.invalidate", tx.spans.first().operation)
457+
assertEquals(true, tx.spans.first().getData(SpanDataConvention.CACHE_WRITE))
446458
assertEquals("invalidate", tx.spans.first().getData(SpanDataConvention.CACHE_OPERATION_KEY))
447459
}
448460

0 commit comments

Comments
 (0)