Skip to content

Commit 4466961

Browse files
authored
Merge pull request #5175 from getsentry/feat/cache-tracing-sample
feat(samples): [Cache Tracing 4] Add cache tracing e2e sample
2 parents 2f4eb6b + d2c83ac commit 4466961

File tree

9 files changed

+146
-0
lines changed

9 files changed

+146
-0
lines changed

gradle/libs.versions.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ androidx-recyclerview = { module = "androidx.recyclerview:recyclerview", version
9999
androidx-browser = { module = "androidx.browser:browser", version = "1.8.0" }
100100
async-profiler = { module = "tools.profiler:async-profiler", version.ref = "asyncProfiler" }
101101
async-profiler-jfr-converter = { module = "tools.profiler:jfr-converter", version.ref = "asyncProfiler" }
102+
caffeine = { module = "com.github.ben-manes.caffeine:caffeine" }
102103
coil-compose = { module = "io.coil-kt:coil-compose", version = "2.6.0" }
103104
commons-compress = {module = "org.apache.commons:commons-compress", version = "1.25.0"}
104105
context-propagation = { module = "io.micrometer:context-propagation", version = "1.1.0" }
@@ -193,6 +194,7 @@ springboot4-starter-restclient = { module = "org.springframework.boot:spring-boo
193194
springboot4-starter-webclient = { module = "org.springframework.boot:spring-boot-starter-webclient", version.ref = "springboot4" }
194195
springboot4-starter-jdbc = { module = "org.springframework.boot:spring-boot-starter-jdbc", version.ref = "springboot4" }
195196
springboot4-starter-actuator = { module = "org.springframework.boot:spring-boot-starter-actuator", version.ref = "springboot4" }
197+
springboot4-starter-cache = { module = "org.springframework.boot:spring-boot-starter-cache", version.ref = "springboot4" }
196198
timber = { module = "com.jakewharton.timber:timber", version = "4.7.1" }
197199

198200
# Animalsniffer signature

sentry-samples/sentry-samples-spring-boot-4/build.gradle.kts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ dependencies {
5757
implementation(projects.sentryQuartz)
5858
implementation(projects.sentryAsyncProfiler)
5959

60+
// cache tracing
61+
implementation(libs.springboot4.starter.cache)
62+
implementation(libs.caffeine)
63+
6064
// database query tracing
6165
implementation(projects.sentryJdbc)
6266
runtimeOnly(libs.hsqldb)
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package io.sentry.samples.spring.boot4;
2+
3+
import org.springframework.web.bind.annotation.DeleteMapping;
4+
import org.springframework.web.bind.annotation.GetMapping;
5+
import org.springframework.web.bind.annotation.PathVariable;
6+
import org.springframework.web.bind.annotation.PostMapping;
7+
import org.springframework.web.bind.annotation.RequestBody;
8+
import org.springframework.web.bind.annotation.RequestMapping;
9+
import org.springframework.web.bind.annotation.RestController;
10+
11+
@RestController
12+
@RequestMapping("/cache/")
13+
public class CacheController {
14+
private final TodoService todoService;
15+
16+
public CacheController(TodoService todoService) {
17+
this.todoService = todoService;
18+
}
19+
20+
@GetMapping("{id}")
21+
Todo get(@PathVariable Long id) {
22+
return todoService.get(id);
23+
}
24+
25+
@PostMapping
26+
Todo save(@RequestBody Todo todo) {
27+
return todoService.save(todo);
28+
}
29+
30+
@DeleteMapping("{id}")
31+
void delete(@PathVariable Long id) {
32+
todoService.delete(id);
33+
}
34+
}

sentry-samples/sentry-samples-spring-boot-4/src/main/java/io/sentry/samples/spring/boot4/SentryDemoApplication.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import org.springframework.boot.SpringApplication;
1010
import org.springframework.boot.autoconfigure.SpringBootApplication;
1111
import org.springframework.boot.restclient.RestTemplateBuilder;
12+
import org.springframework.cache.annotation.EnableCaching;
1213
import org.springframework.context.annotation.Bean;
1314
import org.springframework.scheduling.annotation.EnableScheduling;
1415
import org.springframework.scheduling.quartz.CronTriggerFactoryBean;
@@ -19,6 +20,7 @@
1920
import org.springframework.web.reactive.function.client.WebClient;
2021

2122
@SpringBootApplication
23+
@EnableCaching
2224
@EnableScheduling
2325
public class SentryDemoApplication {
2426
public static void main(String[] args) {
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package io.sentry.samples.spring.boot4;
2+
3+
import java.util.Map;
4+
import java.util.concurrent.ConcurrentHashMap;
5+
import org.springframework.cache.annotation.CacheEvict;
6+
import org.springframework.cache.annotation.CachePut;
7+
import org.springframework.cache.annotation.Cacheable;
8+
import org.springframework.stereotype.Service;
9+
10+
@Service
11+
public class TodoService {
12+
private final Map<Long, Todo> store = new ConcurrentHashMap<>();
13+
14+
@Cacheable(value = "todos", key = "#id")
15+
public Todo get(Long id) {
16+
return store.get(id);
17+
}
18+
19+
@CachePut(value = "todos", key = "#todo.id")
20+
public Todo save(Todo todo) {
21+
store.put(todo.getId(), todo);
22+
return todo;
23+
}
24+
25+
@CacheEvict(value = "todos", key = "#id")
26+
public void delete(Long id) {
27+
store.remove(id);
28+
}
29+
}

sentry-samples/sentry-samples-spring-boot-4/src/main/resources/application.properties

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ sentry.logs.enabled=true
2020
sentry.profile-session-sample-rate=1.0
2121
sentry.profiling-traces-dir-path=tmp/sentry/profiling-traces
2222
sentry.profile-lifecycle=TRACE
23+
sentry.enable-cache-tracing=true
24+
spring.cache.cache-names=todos
25+
spring.cache.caffeine.spec=maximumSize=500,expireAfterAccess=600s
2326

2427
# Uncomment and set to true to enable aot compatibility
2528
# This flag disables all AOP related features (i.e. @SentryTransaction, @SentrySpan)
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package io.sentry.systemtest
2+
3+
import io.sentry.systemtest.util.TestHelper
4+
import kotlin.test.Test
5+
import kotlin.test.assertEquals
6+
import org.junit.Before
7+
8+
class CacheSystemTest {
9+
lateinit var testHelper: TestHelper
10+
11+
@Before
12+
fun setup() {
13+
testHelper = TestHelper("http://localhost:8080")
14+
testHelper.reset()
15+
}
16+
17+
@Test
18+
fun `cache put and get produce spans`() {
19+
val restClient = testHelper.restClient
20+
21+
// Save a todo (triggers @CachePut -> cache.put span)
22+
val todo = Todo(1L, "test-todo", false)
23+
restClient.saveCachedTodo(todo)
24+
assertEquals(200, restClient.lastKnownStatusCode)
25+
26+
testHelper.ensureTransactionReceived { transaction, _ ->
27+
testHelper.doesTransactionContainSpanWithOp(transaction, "cache.put")
28+
}
29+
30+
testHelper.reset()
31+
32+
// Get the todo (triggers @Cacheable -> cache.get span, should be a hit)
33+
restClient.getCachedTodo(1L)
34+
assertEquals(200, restClient.lastKnownStatusCode)
35+
36+
testHelper.ensureTransactionReceived { transaction, _ ->
37+
testHelper.doesTransactionContainSpanWithOp(transaction, "cache.get")
38+
}
39+
}
40+
41+
@Test
42+
fun `cache evict produces span`() {
43+
val restClient = testHelper.restClient
44+
45+
restClient.deleteCachedTodo(1L)
46+
47+
testHelper.ensureTransactionReceived { transaction, _ ->
48+
testHelper.doesTransactionContainSpanWithOp(transaction, "cache.remove")
49+
}
50+
}
51+
}

sentry-system-test-support/api/sentry-system-test-support.api

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,7 +548,9 @@ public final class io/sentry/systemtest/util/RestTestClient : io/sentry/systemte
548548
public static synthetic fun createPerson$default (Lio/sentry/systemtest/util/RestTestClient;Lio/sentry/systemtest/Person;Ljava/util/Map;ILjava/lang/Object;)Lio/sentry/systemtest/Person;
549549
public final fun createPersonDistributedTracing (Lio/sentry/systemtest/Person;Ljava/util/Map;)Lio/sentry/systemtest/Person;
550550
public static synthetic fun createPersonDistributedTracing$default (Lio/sentry/systemtest/util/RestTestClient;Lio/sentry/systemtest/Person;Ljava/util/Map;ILjava/lang/Object;)Lio/sentry/systemtest/Person;
551+
public final fun deleteCachedTodo (J)V
551552
public final fun errorWithFeatureFlag (Ljava/lang/String;)Ljava/lang/String;
553+
public final fun getCachedTodo (J)Lio/sentry/systemtest/Todo;
552554
public final fun getCountMetric ()Ljava/lang/String;
553555
public final fun getDistributionMetric (J)Ljava/lang/String;
554556
public final fun getGaugeMetric (J)Ljava/lang/String;
@@ -558,6 +560,7 @@ public final class io/sentry/systemtest/util/RestTestClient : io/sentry/systemte
558560
public final fun getTodo (J)Lio/sentry/systemtest/Todo;
559561
public final fun getTodoRestClient (J)Lio/sentry/systemtest/Todo;
560562
public final fun getTodoWebclient (J)Lio/sentry/systemtest/Todo;
563+
public final fun saveCachedTodo (Lio/sentry/systemtest/Todo;)Lio/sentry/systemtest/Todo;
561564
}
562565

563566
public final class io/sentry/systemtest/util/SentryMockServerClient : io/sentry/systemtest/util/LoggingInsecureRestClient {

sentry-system-test-support/src/main/kotlin/io/sentry/systemtest/util/RestTestClient.kt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,24 @@ class RestTestClient(private val backendBaseUrl: String) : LoggingInsecureRestCl
5050
return callTyped(request, true)
5151
}
5252

53+
fun getCachedTodo(id: Long): Todo? {
54+
val request = Request.Builder().url("$backendBaseUrl/cache/$id")
55+
56+
return callTyped(request, true)
57+
}
58+
59+
fun saveCachedTodo(todo: Todo): Todo? {
60+
val request = Request.Builder().url("$backendBaseUrl/cache/").post(toRequestBody(todo))
61+
62+
return callTyped(request, true)
63+
}
64+
65+
fun deleteCachedTodo(id: Long) {
66+
val request = Request.Builder().url("$backendBaseUrl/cache/$id").delete()
67+
68+
call(request, true)
69+
}
70+
5371
fun checkFeatureFlag(flagKey: String): FeatureFlagResponse? {
5472
val request = Request.Builder().url("$backendBaseUrl/feature-flag/check/$flagKey")
5573

0 commit comments

Comments
 (0)