From aa57bcf6e37f3dc6fad332dc005281d7902110fc Mon Sep 17 00:00:00 2001 From: bm1549 Date: Tue, 10 Mar 2026 15:43:51 -0400 Subject: [PATCH 1/5] Fix flaky cron trigger test by closing context before assertions The "schedule trigger test according to cron expression" test was flaky because the cron expression (every 5 seconds) could fire a second time between blockUntilExecute() returning and the assertTraces() call completing, producing 3 or 4 traces instead of the expected 2. Fix by closing the Spring application context immediately after the first execution completes, before entering the expect block. The ScheduledTasksEndpoint bean reference is captured before closing so the endpoint assertion in the and: block still works. Co-Authored-By: Claude Opus 4.6 --- .../src/latestDepTest/groovy/SpringSchedulingTest.groovy | 7 ++++--- .../src/test/groovy/SpringSchedulingTest.groovy | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/dd-java-agent/instrumentation/spring/spring-scheduling-3.1/src/latestDepTest/groovy/SpringSchedulingTest.groovy b/dd-java-agent/instrumentation/spring/spring-scheduling-3.1/src/latestDepTest/groovy/SpringSchedulingTest.groovy index 6e3b1760c11..523dd209a2c 100644 --- a/dd-java-agent/instrumentation/spring/spring-scheduling-3.1/src/latestDepTest/groovy/SpringSchedulingTest.groovy +++ b/dd-java-agent/instrumentation/spring/spring-scheduling-3.1/src/latestDepTest/groovy/SpringSchedulingTest.groovy @@ -25,8 +25,12 @@ class SpringSchedulingTest extends InstrumentationSpecification { setup: def context = new AnnotationConfigApplicationContext(TriggerTaskConfig, SchedulingConfig) def task = context.getBean(TriggerTask) + def scheduledTaskEndpoint = context.getBean(ScheduledTasksEndpoint) task.blockUntilExecute() + // Close the context immediately after the first execution to prevent a second cron + // firing before assertions complete, which would produce extra traces and cause flakiness. + context.close() expect: assert task != null @@ -54,13 +58,10 @@ class SpringSchedulingTest extends InstrumentationSpecification { } } and: - def scheduledTaskEndpoint = context.getBean(ScheduledTasksEndpoint) assert scheduledTaskEndpoint != null scheduledTaskEndpoint.scheduledTasks().getCron().each { it.getRunnable().getTarget() == TriggerTask.getName() } - cleanup: - context.close() } def "schedule interval test"() { diff --git a/dd-java-agent/instrumentation/spring/spring-scheduling-3.1/src/test/groovy/SpringSchedulingTest.groovy b/dd-java-agent/instrumentation/spring/spring-scheduling-3.1/src/test/groovy/SpringSchedulingTest.groovy index 6e3b1760c11..523dd209a2c 100644 --- a/dd-java-agent/instrumentation/spring/spring-scheduling-3.1/src/test/groovy/SpringSchedulingTest.groovy +++ b/dd-java-agent/instrumentation/spring/spring-scheduling-3.1/src/test/groovy/SpringSchedulingTest.groovy @@ -25,8 +25,12 @@ class SpringSchedulingTest extends InstrumentationSpecification { setup: def context = new AnnotationConfigApplicationContext(TriggerTaskConfig, SchedulingConfig) def task = context.getBean(TriggerTask) + def scheduledTaskEndpoint = context.getBean(ScheduledTasksEndpoint) task.blockUntilExecute() + // Close the context immediately after the first execution to prevent a second cron + // firing before assertions complete, which would produce extra traces and cause flakiness. + context.close() expect: assert task != null @@ -54,13 +58,10 @@ class SpringSchedulingTest extends InstrumentationSpecification { } } and: - def scheduledTaskEndpoint = context.getBean(ScheduledTasksEndpoint) assert scheduledTaskEndpoint != null scheduledTaskEndpoint.scheduledTasks().getCron().each { it.getRunnable().getTarget() == TriggerTask.getName() } - cleanup: - context.close() } def "schedule interval test"() { From 072361c6b52ffd1c534f95e6251fb13c4a2105d3 Mon Sep 17 00:00:00 2001 From: bm1549 Date: Tue, 10 Mar 2026 16:01:41 -0400 Subject: [PATCH 2/5] Fix ConditionNotSatisfiedError by capturing cron tasks before context close The scheduledTaskEndpoint.scheduledTasks() call fails after context.close() because the endpoint is no longer available. Capture the cron tasks list in setup: before closing the context so the and: assertion block uses the pre-captured value. Co-Authored-By: Claude Sonnet 4.6 --- .../src/latestDepTest/groovy/SpringSchedulingTest.groovy | 4 +++- .../src/test/groovy/SpringSchedulingTest.groovy | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/dd-java-agent/instrumentation/spring/spring-scheduling-3.1/src/latestDepTest/groovy/SpringSchedulingTest.groovy b/dd-java-agent/instrumentation/spring/spring-scheduling-3.1/src/latestDepTest/groovy/SpringSchedulingTest.groovy index 523dd209a2c..f604744aa39 100644 --- a/dd-java-agent/instrumentation/spring/spring-scheduling-3.1/src/latestDepTest/groovy/SpringSchedulingTest.groovy +++ b/dd-java-agent/instrumentation/spring/spring-scheduling-3.1/src/latestDepTest/groovy/SpringSchedulingTest.groovy @@ -28,6 +28,8 @@ class SpringSchedulingTest extends InstrumentationSpecification { def scheduledTaskEndpoint = context.getBean(ScheduledTasksEndpoint) task.blockUntilExecute() + // Capture cron tasks before closing the context (endpoint is unavailable after close). + def cronTasks = scheduledTaskEndpoint.scheduledTasks().getCron() // Close the context immediately after the first execution to prevent a second cron // firing before assertions complete, which would produce extra traces and cause flakiness. context.close() @@ -59,7 +61,7 @@ class SpringSchedulingTest extends InstrumentationSpecification { } and: assert scheduledTaskEndpoint != null - scheduledTaskEndpoint.scheduledTasks().getCron().each { + cronTasks.each { it.getRunnable().getTarget() == TriggerTask.getName() } } diff --git a/dd-java-agent/instrumentation/spring/spring-scheduling-3.1/src/test/groovy/SpringSchedulingTest.groovy b/dd-java-agent/instrumentation/spring/spring-scheduling-3.1/src/test/groovy/SpringSchedulingTest.groovy index 523dd209a2c..f604744aa39 100644 --- a/dd-java-agent/instrumentation/spring/spring-scheduling-3.1/src/test/groovy/SpringSchedulingTest.groovy +++ b/dd-java-agent/instrumentation/spring/spring-scheduling-3.1/src/test/groovy/SpringSchedulingTest.groovy @@ -28,6 +28,8 @@ class SpringSchedulingTest extends InstrumentationSpecification { def scheduledTaskEndpoint = context.getBean(ScheduledTasksEndpoint) task.blockUntilExecute() + // Capture cron tasks before closing the context (endpoint is unavailable after close). + def cronTasks = scheduledTaskEndpoint.scheduledTasks().getCron() // Close the context immediately after the first execution to prevent a second cron // firing before assertions complete, which would produce extra traces and cause flakiness. context.close() @@ -59,7 +61,7 @@ class SpringSchedulingTest extends InstrumentationSpecification { } and: assert scheduledTaskEndpoint != null - scheduledTaskEndpoint.scheduledTasks().getCron().each { + cronTasks.each { it.getRunnable().getTarget() == TriggerTask.getName() } } From 6776e65142fe9bb46054179906e2a2192d687384 Mon Sep 17 00:00:00 2001 From: bm1549 Date: Mon, 16 Mar 2026 22:35:37 -0400 Subject: [PATCH 3/5] Add deterministic reproduction delay to Spring scheduling cron test After closing the Spring context (which stops the scheduler), add a sleep longer than the cron interval to prove that no extra traces appear even under worst-case timing. This makes the previously-flaky race condition fully deterministic: without context.close(), the sleep guarantees extra cron executions; with it, the scheduler is stopped and assertions pass. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../src/latestDepTest/groovy/SpringSchedulingTest.groovy | 3 +++ .../src/test/groovy/SpringSchedulingTest.groovy | 3 +++ 2 files changed, 6 insertions(+) diff --git a/dd-java-agent/instrumentation/spring/spring-scheduling-3.1/src/latestDepTest/groovy/SpringSchedulingTest.groovy b/dd-java-agent/instrumentation/spring/spring-scheduling-3.1/src/latestDepTest/groovy/SpringSchedulingTest.groovy index f604744aa39..6680b7d121a 100644 --- a/dd-java-agent/instrumentation/spring/spring-scheduling-3.1/src/latestDepTest/groovy/SpringSchedulingTest.groovy +++ b/dd-java-agent/instrumentation/spring/spring-scheduling-3.1/src/latestDepTest/groovy/SpringSchedulingTest.groovy @@ -33,6 +33,9 @@ class SpringSchedulingTest extends InstrumentationSpecification { // Close the context immediately after the first execution to prevent a second cron // firing before assertions complete, which would produce extra traces and cause flakiness. context.close() + // Deterministic reproduction: sleep longer than the cron interval (5 s) to prove + // that closing the context stopped the scheduler and no extra traces appear. + Thread.sleep(6000) expect: assert task != null diff --git a/dd-java-agent/instrumentation/spring/spring-scheduling-3.1/src/test/groovy/SpringSchedulingTest.groovy b/dd-java-agent/instrumentation/spring/spring-scheduling-3.1/src/test/groovy/SpringSchedulingTest.groovy index f604744aa39..6680b7d121a 100644 --- a/dd-java-agent/instrumentation/spring/spring-scheduling-3.1/src/test/groovy/SpringSchedulingTest.groovy +++ b/dd-java-agent/instrumentation/spring/spring-scheduling-3.1/src/test/groovy/SpringSchedulingTest.groovy @@ -33,6 +33,9 @@ class SpringSchedulingTest extends InstrumentationSpecification { // Close the context immediately after the first execution to prevent a second cron // firing before assertions complete, which would produce extra traces and cause flakiness. context.close() + // Deterministic reproduction: sleep longer than the cron interval (5 s) to prove + // that closing the context stopped the scheduler and no extra traces appear. + Thread.sleep(6000) expect: assert task != null From 61aca03e2d68d3ad501c173244aa0147b6ba15e3 Mon Sep 17 00:00:00 2001 From: Brian Marks Date: Tue, 17 Mar 2026 12:53:55 -0400 Subject: [PATCH 4/5] Update dd-java-agent/instrumentation/spring/spring-scheduling-3.1/src/latestDepTest/groovy/SpringSchedulingTest.groovy --- .../src/latestDepTest/groovy/SpringSchedulingTest.groovy | 3 --- 1 file changed, 3 deletions(-) diff --git a/dd-java-agent/instrumentation/spring/spring-scheduling-3.1/src/latestDepTest/groovy/SpringSchedulingTest.groovy b/dd-java-agent/instrumentation/spring/spring-scheduling-3.1/src/latestDepTest/groovy/SpringSchedulingTest.groovy index 6680b7d121a..f604744aa39 100644 --- a/dd-java-agent/instrumentation/spring/spring-scheduling-3.1/src/latestDepTest/groovy/SpringSchedulingTest.groovy +++ b/dd-java-agent/instrumentation/spring/spring-scheduling-3.1/src/latestDepTest/groovy/SpringSchedulingTest.groovy @@ -33,9 +33,6 @@ class SpringSchedulingTest extends InstrumentationSpecification { // Close the context immediately after the first execution to prevent a second cron // firing before assertions complete, which would produce extra traces and cause flakiness. context.close() - // Deterministic reproduction: sleep longer than the cron interval (5 s) to prove - // that closing the context stopped the scheduler and no extra traces appear. - Thread.sleep(6000) expect: assert task != null From 24bbcdebd9246bc8347b36c04e15a9c77bbd19fd Mon Sep 17 00:00:00 2001 From: Brian Marks Date: Tue, 17 Mar 2026 12:54:04 -0400 Subject: [PATCH 5/5] Update dd-java-agent/instrumentation/spring/spring-scheduling-3.1/src/test/groovy/SpringSchedulingTest.groovy --- .../src/test/groovy/SpringSchedulingTest.groovy | 3 --- 1 file changed, 3 deletions(-) diff --git a/dd-java-agent/instrumentation/spring/spring-scheduling-3.1/src/test/groovy/SpringSchedulingTest.groovy b/dd-java-agent/instrumentation/spring/spring-scheduling-3.1/src/test/groovy/SpringSchedulingTest.groovy index 6680b7d121a..f604744aa39 100644 --- a/dd-java-agent/instrumentation/spring/spring-scheduling-3.1/src/test/groovy/SpringSchedulingTest.groovy +++ b/dd-java-agent/instrumentation/spring/spring-scheduling-3.1/src/test/groovy/SpringSchedulingTest.groovy @@ -33,9 +33,6 @@ class SpringSchedulingTest extends InstrumentationSpecification { // Close the context immediately after the first execution to prevent a second cron // firing before assertions complete, which would produce extra traces and cause flakiness. context.close() - // Deterministic reproduction: sleep longer than the cron interval (5 s) to prove - // that closing the context stopped the scheduler and no extra traces appear. - Thread.sleep(6000) expect: assert task != null