Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ dependencies {
spring6TestImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-data-jpa', version: '3.0.0'

latestDepTestImplementation group: 'com.h2database', name: 'h2', version: '+'
latestDepTestImplementation group: 'org.springframework', name: 'spring-context', version: '6.+'
latestDepTestImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-data-jpa', version: '3.+'
latestDepTestImplementation group: 'org.springframework', name: 'spring-context', version: '7.+'
latestDepTestImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-data-jpa', version: '4.+'
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Bump the latestDepTest actuator dependency too

With this change latestDepTest now resolves Boot 4 for spring-boot-starter-data-jpa, but the same suite still inherits spring-boot-starter-actuator:2.4.0 from testImplementation (visible in the regenerated lockfile for latestDepTestCompileClasspath). The latest-dep scheduling tests import and instantiate ScheduledTasksEndpoint, so they are still exercising the Boot 2.4 actuator artifact mixed with Boot 4 core/autoconfigure rather than validating the Boot 4 scheduling endpoint; add a latestDepTestImplementation override for the actuator starter/artifact alongside the Boot 4 JPA starter.

Useful? React with 👍 / 👎.

}

tasks.named("latestDepForkedTest", Test) {
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ private Object invokeWithSpan(CharSequence spanName) throws Throwable {
AgentSpan span = startSpan("spring-scheduling", spanName);
try (AgentScope scope = activateSpan(span)) {
return delegate.proceed();
} catch (Throwable throwable) {
DECORATE.onError(span, throwable);
throw throwable;
} finally {
span.finish();
}
Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import java.util.concurrent.CompletableFuture;
import org.springframework.scheduling.annotation.Async;

public class AsyncErrorTask {

public static final String ERROR_MESSAGE = "async boom";

@Async
public CompletableFuture<Integer> asyncThrow() {
throw new IllegalStateException(ERROR_MESSAGE);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;

@Configuration
@EnableAsync
public class AsyncErrorTaskConfig {

@Bean
AsyncErrorTask asyncErrorTask() {
return new AsyncErrorTask();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.springframework.scheduling.annotation.Scheduled;

public class ScheduledErrorTask implements Runnable {

public static final String ERROR_MESSAGE = "scheduled boom";

private final CountDownLatch latch = new CountDownLatch(1);

@Scheduled(fixedRate = 5000)
@Override
public void run() {
try {
throw new IllegalStateException(ERROR_MESSAGE);
} finally {
latch.countDown();
}
}

public boolean blockUntilExecute() throws InterruptedException {
return latch.await(10, TimeUnit.SECONDS);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;

@Configuration
@EnableScheduling
public class ScheduledErrorTaskConfig {

@Bean
public ScheduledErrorTask scheduledErrorTask() {
return new ScheduledErrorTask();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import static org.junit.jupiter.api.Assertions.assertEquals;

import datadog.trace.agent.test.AbstractInstrumentationTest;
import datadog.trace.test.util.Flaky;
import java.util.concurrent.TimeUnit;
import javax.sql.DataSource;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;

class ShedlockTest extends AbstractInstrumentationTest {

@Flaky("task.invocationCount() == 0")
@Test
void shouldNotDisableShedlock() throws InterruptedException {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(ShedlockConfig.class);
try {
JdbcTemplate jdbcTemplate = new JdbcTemplate(context.getBean(DataSource.class));
jdbcTemplate.execute(
"CREATE TABLE shedlock(name VARCHAR(64) NOT NULL PRIMARY KEY, lock_until TIMESTAMP NOT NULL,\n"
+ " locked_at TIMESTAMP NOT NULL, locked_by VARCHAR(255) NOT NULL);");
ShedLockedTask task = context.getBean(ShedLockedTask.class);

// lock is held for more than one second: wait, then verify the task ran exactly once
task.awaitInvocation(1000, TimeUnit.MILLISECONDS);
assertEquals(1, task.invocationCount());
} finally {
context.close();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import static datadog.trace.agent.test.assertions.SpanMatcher.span;
import static datadog.trace.agent.test.assertions.TagsMatcher.defaultTags;
import static datadog.trace.agent.test.assertions.TagsMatcher.error;
import static datadog.trace.agent.test.assertions.TraceMatcher.trace;
import static org.junit.jupiter.api.Assertions.assertThrows;

import datadog.trace.agent.test.AbstractInstrumentationTest;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
* Verifies that when an {@code @Async} method throws an exception, the span produced by the
* spring-scheduling instrumentation captures the error and the associated error tags.
*/
class SpringAsyncErrorTest extends AbstractInstrumentationTest {

@Test
void asyncMethodErrorIsCaptured() {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(AsyncErrorTaskConfig.class);
try {
AsyncErrorTask asyncErrorTask = context.getBean(AsyncErrorTask.class);

// The exception thrown inside the @Async method propagates back through the returned future.
assertThrows(Throwable.class, () -> asyncErrorTask.asyncThrow().join());

assertTraces(
trace(
span()
.resourceName(name -> "AsyncErrorTask.asyncThrow".contentEquals(name))
.error(true)
.tags(
defaultTags(),
error(IllegalStateException.class, AsyncErrorTask.ERROR_MESSAGE))));
} finally {
context.close();
}
}
}
Loading
Loading