From a8f49f07291b21d733ac146e73ee7f6616f0cc95 Mon Sep 17 00:00:00 2001 From: Graham Crockford Date: Sat, 23 Dec 2023 21:44:50 +0000 Subject: [PATCH 1/5] Add testing that things seem to generally work OK with virtual threads. Not exhaustive. --- pom.xml | 9 ++ .../testing/AbstractAcceptanceTest.java | 99 +--------------- .../transactionoutbox/testing/BaseTest.java | 106 ++++++++++++++++++ transactionoutbox-virtthreads/pom.xml | 82 ++++++++++++++ .../AbstractVirtualThreadsTest.java | 80 +++++++++++++ .../virtthreads/TestVirtualThreadsH2.java | 3 + .../virtthreads/TestVirtualThreadsH2Jooq.java | 34 ++++++ .../virtthreads/TestVirtualThreadsMySql5.java | 29 +++++ .../virtthreads/TestVirtualThreadsMySql8.java | 29 +++++ .../TestVirtualThreadsOracle21.java | 29 +++++ .../TestVirtualThreadsPostgres16.java | 30 +++++ 11 files changed, 435 insertions(+), 95 deletions(-) create mode 100644 transactionoutbox-testing/src/main/java/com/gruelbox/transactionoutbox/testing/BaseTest.java create mode 100644 transactionoutbox-virtthreads/pom.xml create mode 100644 transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/AbstractVirtualThreadsTest.java create mode 100644 transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/TestVirtualThreadsH2.java create mode 100644 transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/TestVirtualThreadsH2Jooq.java create mode 100644 transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/TestVirtualThreadsMySql5.java create mode 100644 transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/TestVirtualThreadsMySql8.java create mode 100644 transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/TestVirtualThreadsOracle21.java create mode 100644 transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/TestVirtualThreadsPostgres16.java diff --git a/pom.xml b/pom.xml index f038296e..52dcfd81 100644 --- a/pom.xml +++ b/pom.xml @@ -286,6 +286,15 @@ transactionoutbox-spring + + java-21-modules + + [21,) + + + transactionoutbox-virtthreads + + release diff --git a/transactionoutbox-testing/src/main/java/com/gruelbox/transactionoutbox/testing/AbstractAcceptanceTest.java b/transactionoutbox-testing/src/main/java/com/gruelbox/transactionoutbox/testing/AbstractAcceptanceTest.java index d4f16c5f..f297286c 100644 --- a/transactionoutbox-testing/src/main/java/com/gruelbox/transactionoutbox/testing/AbstractAcceptanceTest.java +++ b/transactionoutbox-testing/src/main/java/com/gruelbox/transactionoutbox/testing/AbstractAcceptanceTest.java @@ -5,7 +5,6 @@ import static org.junit.jupiter.api.Assertions.*; import com.gruelbox.transactionoutbox.*; -import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; import java.sql.Connection; import java.sql.DriverManager; @@ -22,40 +21,23 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.IntStream; -import lombok.Builder; import lombok.SneakyThrows; -import lombok.Value; -import lombok.experimental.Accessors; import lombok.extern.slf4j.Slf4j; import org.hamcrest.MatcherAssert; -import org.junit.jupiter.api.*; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @Slf4j -public abstract class AbstractAcceptanceTest { +public abstract class AbstractAcceptanceTest extends BaseTest { private static final Logger LOGGER = LoggerFactory.getLogger(AbstractAcceptanceTest.class); private final ExecutorService unreliablePool = new ThreadPoolExecutor(2, 2, 0L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(16)); private static final Random random = new Random(); - protected HikariDataSource dataSource; - - @BeforeEach - final void baseBeforeEach() { - HikariConfig config = new HikariConfig(); - config.setJdbcUrl(connectionDetails().url()); - config.setUsername(connectionDetails().user()); - config.setPassword(connectionDetails().password()); - config.addDataSourceProperty("cachePrepStmts", "true"); - dataSource = new HikariDataSource(config); - } - - @AfterEach - final void baseAfterEach() { - dataSource.close(); - } /** * Uses a simple direct transaction manager and connection manager and attempts to fire an @@ -585,68 +567,6 @@ public void success(TransactionOutboxEntry entry) { containsInAnyOrder(IntStream.range(0, count * 10).boxed().toArray())); } - protected ConnectionDetails connectionDetails() { - return ConnectionDetails.builder() - .dialect(Dialect.H2) - .driverClassName("org.h2.Driver") - .url( - "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DEFAULT_LOCK_TIMEOUT=60000;LOB_TIMEOUT=2000;MV_STORE=TRUE;DATABASE_TO_UPPER=FALSE") - .user("test") - .password("test") - .build(); - } - - protected TransactionManager txManager() { - return TransactionManager.fromDataSource(dataSource); - } - - protected Persistor persistor() { - return Persistor.forDialect(connectionDetails().dialect()); - } - - protected void clearOutbox() { - DefaultPersistor persistor = Persistor.forDialect(connectionDetails().dialect()); - TransactionManager transactionManager = txManager(); - transactionManager.inTransaction( - tx -> { - try { - persistor.clear(tx); - } catch (SQLException e) { - throw new RuntimeException(e); - } - }); - } - - protected void withRunningFlusher(TransactionOutbox outbox, ThrowingRunnable runnable) - throws Exception { - Thread backgroundThread = - new Thread( - () -> { - while (!Thread.interrupted()) { - try { - // Keep flushing work until there's nothing left to flush - //noinspection StatementWithEmptyBody - while (outbox.flush()) {} - } catch (Exception e) { - log.error("Error flushing transaction outbox. Pausing", e); - } - try { - //noinspection BusyWait - Thread.sleep(250); - } catch (InterruptedException e) { - break; - } - } - }); - backgroundThread.start(); - try { - runnable.run(); - } finally { - backgroundThread.interrupt(); - backgroundThread.join(); - } - } - private static class FailingInstantiator implements Instantiator { private final AtomicInteger attempts; @@ -699,15 +619,4 @@ public Object getInstance(String name) { } } } - - @Value - @Accessors(fluent = true) - @Builder - public static class ConnectionDetails { - String driverClassName; - String url; - String user; - String password; - Dialect dialect; - } } diff --git a/transactionoutbox-testing/src/main/java/com/gruelbox/transactionoutbox/testing/BaseTest.java b/transactionoutbox-testing/src/main/java/com/gruelbox/transactionoutbox/testing/BaseTest.java new file mode 100644 index 00000000..7cf9ed2f --- /dev/null +++ b/transactionoutbox-testing/src/main/java/com/gruelbox/transactionoutbox/testing/BaseTest.java @@ -0,0 +1,106 @@ +package com.gruelbox.transactionoutbox.testing; + +import com.gruelbox.transactionoutbox.*; +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; +import java.sql.SQLException; +import lombok.Builder; +import lombok.Value; +import lombok.experimental.Accessors; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; + +@Slf4j +public abstract class BaseTest { + + protected HikariDataSource dataSource; + + @BeforeEach + final void baseBeforeEach() { + HikariConfig config = new HikariConfig(); + config.setJdbcUrl(connectionDetails().url()); + config.setUsername(connectionDetails().user()); + config.setPassword(connectionDetails().password()); + config.addDataSourceProperty("cachePrepStmts", "true"); + dataSource = new HikariDataSource(config); + } + + @AfterEach + final void baseAfterEach() { + dataSource.close(); + } + + protected ConnectionDetails connectionDetails() { + return ConnectionDetails.builder() + .dialect(Dialect.H2) + .driverClassName("org.h2.Driver") + .url( + "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DEFAULT_LOCK_TIMEOUT=60000;LOB_TIMEOUT=2000;MV_STORE=TRUE;DATABASE_TO_UPPER=FALSE") + .user("test") + .password("test") + .build(); + } + + protected TransactionManager txManager() { + return TransactionManager.fromDataSource(dataSource); + } + + protected Persistor persistor() { + return Persistor.forDialect(connectionDetails().dialect()); + } + + protected void clearOutbox() { + DefaultPersistor persistor = Persistor.forDialect(connectionDetails().dialect()); + TransactionManager transactionManager = txManager(); + transactionManager.inTransaction( + tx -> { + try { + persistor.clear(tx); + } catch (SQLException e) { + throw new RuntimeException(e); + } + }); + } + + protected void withRunningFlusher(TransactionOutbox outbox, ThrowingRunnable runnable) + throws Exception { + Thread backgroundThread = + new Thread( + () -> { + while (!Thread.interrupted()) { + try { + // Keep flushing work until there's nothing left to flush + //noinspection StatementWithEmptyBody + while (outbox.flush()) {} + } catch (Exception e) { + log.error("Error flushing transaction outbox. Pausing", e); + } + try { + //noinspection BusyWait + Thread.sleep(250); + } catch (InterruptedException e) { + break; + } + } + }); + backgroundThread.start(); + try { + runnable.run(); + } finally { + backgroundThread.interrupt(); + backgroundThread.join(); + } + } + + @Value + @Accessors(fluent = true) + @Builder + public static class ConnectionDetails { + String driverClassName; + String url; + String user; + String password; + Dialect dialect; + } +} diff --git a/transactionoutbox-virtthreads/pom.xml b/transactionoutbox-virtthreads/pom.xml new file mode 100644 index 00000000..507b0f69 --- /dev/null +++ b/transactionoutbox-virtthreads/pom.xml @@ -0,0 +1,82 @@ + + + + transactionoutbox-parent + com.gruelbox + ${revision} + + 4.0.0 + Transaction Outbox Virtual Threads support + jar + transactionoutbox-virtthreads + A safe implementation of the transactional outbox pattern for Java (core library) + + 21 + 21 + + + + com.gruelbox + transactionoutbox-core + ${project.version} + test + + + + + org.projectlombok + lombok + + + + + com.gruelbox + transactionoutbox-testing + ${project.version} + test + + + com.gruelbox + transactionoutbox-jooq + ${project.version} + test + + + org.testcontainers + testcontainers + + + org.testcontainers + junit-jupiter + + + org.testcontainers + postgresql + + + org.testcontainers + oracle-xe + + + org.testcontainers + mysql + + + org.postgresql + postgresql + + + com.oracle.database.jdbc + ojdbc11 + + + mysql + mysql-connector-java + + + com.h2database + h2 + + + diff --git a/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/AbstractVirtualThreadsTest.java b/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/AbstractVirtualThreadsTest.java new file mode 100644 index 00000000..4312a1d8 --- /dev/null +++ b/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/AbstractVirtualThreadsTest.java @@ -0,0 +1,80 @@ +package com.gruelbox.transactionoutbox.virtthreads; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.empty; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.gruelbox.transactionoutbox.*; +import com.gruelbox.transactionoutbox.testing.BaseTest; +import com.gruelbox.transactionoutbox.testing.InterfaceProcessor; +import java.time.Duration; +import java.util.concurrent.*; +import java.util.stream.IntStream; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Test; + +@Slf4j +abstract class AbstractVirtualThreadsTest extends BaseTest { + + @Test + final void highVolumeVirtualThreads() throws Exception { + var count = 10; + var latch = new CountDownLatch(count * 10); + var transactionManager = txManager(); + var results = new ConcurrentHashMap(); + var duplicates = new ConcurrentHashMap(); + try (var executor = Executors.newVirtualThreadPerTaskExecutor()) { + var outbox = + TransactionOutbox.builder() + .transactionManager(transactionManager) + .persistor(Persistor.forDialect(connectionDetails().dialect())) + .instantiator(Instantiator.using(clazz -> (InterfaceProcessor) (foo, bar) -> {})) + .submitter(Submitter.withExecutor(executor)) + .attemptFrequency(Duration.ofMillis(500)) + .flushBatchSize(1000) + .listener( + new TransactionOutboxListener() { + @Override + public void success(TransactionOutboxEntry entry) { + Integer i = (Integer) entry.getInvocation().getArgs()[0]; + if (results.putIfAbsent(i, i) != null) { + duplicates.put(i, i); + } + latch.countDown(); + } + }) + .build(); + + withRunningFlusher( + outbox, + () -> { + var futures = + IntStream.range(0, count) + .mapToObj( + i -> + CompletableFuture.runAsync( + () -> + transactionManager.inTransaction( + () -> { + for (int j = 0; j < 10; j++) { + outbox + .schedule(InterfaceProcessor.class) + .process(i * 10 + j, "Whee"); + } + }), + executor)) + .toArray(CompletableFuture[]::new); + CompletableFuture.allOf(futures).join(); + assertTrue(latch.await(30, TimeUnit.SECONDS), "Latch not opened in time"); + }); + + assertThat( + "Should never get duplicates running to full completion", duplicates.keySet(), empty()); + assertThat( + "Only got: " + results.keySet(), + results.keySet(), + containsInAnyOrder(IntStream.range(0, count * 10).boxed().toArray())); + } + } +} diff --git a/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/TestVirtualThreadsH2.java b/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/TestVirtualThreadsH2.java new file mode 100644 index 00000000..9495febf --- /dev/null +++ b/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/TestVirtualThreadsH2.java @@ -0,0 +1,3 @@ +package com.gruelbox.transactionoutbox.virtthreads; + +public class TestVirtualThreadsH2 extends AbstractVirtualThreadsTest {} diff --git a/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/TestVirtualThreadsH2Jooq.java b/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/TestVirtualThreadsH2Jooq.java new file mode 100644 index 00000000..d03a93d3 --- /dev/null +++ b/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/TestVirtualThreadsH2Jooq.java @@ -0,0 +1,34 @@ +package com.gruelbox.transactionoutbox.virtthreads; + +import com.gruelbox.transactionoutbox.ThreadLocalContextTransactionManager; +import com.gruelbox.transactionoutbox.jooq.JooqTransactionListener; +import com.gruelbox.transactionoutbox.jooq.JooqTransactionManager; +import org.jooq.SQLDialect; +import org.jooq.impl.DSL; +import org.jooq.impl.DataSourceConnectionProvider; +import org.jooq.impl.DefaultConfiguration; +import org.jooq.impl.ThreadLocalTransactionProvider; +import org.junit.jupiter.api.BeforeEach; + +public class TestVirtualThreadsH2Jooq extends AbstractVirtualThreadsTest { + + private ThreadLocalContextTransactionManager txm; + + @Override + protected final ThreadLocalContextTransactionManager txManager() { + return txm; + } + + @BeforeEach + final void beforeEach() { + DataSourceConnectionProvider connectionProvider = new DataSourceConnectionProvider(dataSource); + DefaultConfiguration configuration = new DefaultConfiguration(); + configuration.setConnectionProvider(connectionProvider); + configuration.setSQLDialect(SQLDialect.H2); + configuration.setTransactionProvider( + new ThreadLocalTransactionProvider(connectionProvider, true)); + JooqTransactionListener listener = JooqTransactionManager.createListener(); + configuration.set(listener); + txm = JooqTransactionManager.create(DSL.using(configuration), listener); + } +} diff --git a/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/TestVirtualThreadsMySql5.java b/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/TestVirtualThreadsMySql5.java new file mode 100644 index 00000000..aaa732a8 --- /dev/null +++ b/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/TestVirtualThreadsMySql5.java @@ -0,0 +1,29 @@ +package com.gruelbox.transactionoutbox.virtthreads; + +import com.gruelbox.transactionoutbox.Dialect; +import java.time.Duration; +import org.testcontainers.containers.JdbcDatabaseContainer; +import org.testcontainers.containers.MySQLContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +@SuppressWarnings("WeakerAccess") +@Testcontainers +class TestVirtualThreadsMySql5 extends AbstractVirtualThreadsTest { + + @Container + @SuppressWarnings({"rawtypes", "resource"}) + private static final JdbcDatabaseContainer container = + new MySQLContainer<>("mysql:5").withStartupTimeout(Duration.ofHours(1)); + + @Override + protected ConnectionDetails connectionDetails() { + return ConnectionDetails.builder() + .dialect(Dialect.MY_SQL_5) + .driverClassName("com.mysql.cj.jdbc.Driver") + .url(container.getJdbcUrl()) + .user(container.getUsername()) + .password(container.getPassword()) + .build(); + } +} diff --git a/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/TestVirtualThreadsMySql8.java b/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/TestVirtualThreadsMySql8.java new file mode 100644 index 00000000..cf22a597 --- /dev/null +++ b/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/TestVirtualThreadsMySql8.java @@ -0,0 +1,29 @@ +package com.gruelbox.transactionoutbox.virtthreads; + +import com.gruelbox.transactionoutbox.Dialect; +import java.time.Duration; +import org.testcontainers.containers.JdbcDatabaseContainer; +import org.testcontainers.containers.MySQLContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +@SuppressWarnings("WeakerAccess") +@Testcontainers +class TestVirtualThreadsMySql8 extends AbstractVirtualThreadsTest { + + @Container + @SuppressWarnings({"rawtypes", "resource"}) + private static final JdbcDatabaseContainer container = + new MySQLContainer<>("mysql:8").withStartupTimeout(Duration.ofMinutes(5)); + + @Override + protected ConnectionDetails connectionDetails() { + return ConnectionDetails.builder() + .dialect(Dialect.MY_SQL_8) + .driverClassName("com.mysql.cj.jdbc.Driver") + .url(container.getJdbcUrl()) + .user(container.getUsername()) + .password(container.getPassword()) + .build(); + } +} diff --git a/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/TestVirtualThreadsOracle21.java b/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/TestVirtualThreadsOracle21.java new file mode 100644 index 00000000..0210627a --- /dev/null +++ b/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/TestVirtualThreadsOracle21.java @@ -0,0 +1,29 @@ +package com.gruelbox.transactionoutbox.virtthreads; + +import com.gruelbox.transactionoutbox.Dialect; +import java.time.Duration; +import org.testcontainers.containers.JdbcDatabaseContainer; +import org.testcontainers.containers.OracleContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +@SuppressWarnings("WeakerAccess") +@Testcontainers +class TestVirtualThreadsOracle21 extends AbstractVirtualThreadsTest { + + @Container + @SuppressWarnings("rawtypes") + private static final JdbcDatabaseContainer container = + new OracleContainer("gvenzl/oracle-xe:21-slim").withStartupTimeout(Duration.ofHours(1)); + + @Override + protected ConnectionDetails connectionDetails() { + return ConnectionDetails.builder() + .dialect(Dialect.ORACLE) + .driverClassName("oracle.jdbc.OracleDriver") + .url(container.getJdbcUrl()) + .user(container.getUsername()) + .password(container.getPassword()) + .build(); + } +} diff --git a/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/TestVirtualThreadsPostgres16.java b/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/TestVirtualThreadsPostgres16.java new file mode 100644 index 00000000..1fb0bda4 --- /dev/null +++ b/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/TestVirtualThreadsPostgres16.java @@ -0,0 +1,30 @@ +package com.gruelbox.transactionoutbox.virtthreads; + +import com.gruelbox.transactionoutbox.Dialect; +import java.time.Duration; +import org.testcontainers.containers.JdbcDatabaseContainer; +import org.testcontainers.containers.PostgreSQLContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +@SuppressWarnings("WeakerAccess") +@Testcontainers +class TestVirtualThreadsPostgres16 extends AbstractVirtualThreadsTest { + + @Container + @SuppressWarnings({"rawtypes", "resource"}) + private static final JdbcDatabaseContainer container = + (JdbcDatabaseContainer) + new PostgreSQLContainer("postgres:16").withStartupTimeout(Duration.ofHours(1)); + + @Override + protected ConnectionDetails connectionDetails() { + return ConnectionDetails.builder() + .dialect(Dialect.POSTGRESQL_9) + .driverClassName("org.postgresql.Driver") + .url(container.getJdbcUrl()) + .user(container.getUsername()) + .password(container.getPassword()) + .build(); + } +} From 6d4152b45df5933d044e8ee018a3527782760744 Mon Sep 17 00:00:00 2001 From: Graham Crockford Date: Tue, 26 Dec 2023 18:11:58 +0000 Subject: [PATCH 2/5] Force and detect thread pinning --- transactionoutbox-virtthreads/pom.xml | 6 ++ .../AbstractVirtualThreadsTest.java | 88 ++++++++++++------- 2 files changed, 61 insertions(+), 33 deletions(-) diff --git a/transactionoutbox-virtthreads/pom.xml b/transactionoutbox-virtthreads/pom.xml index 507b0f69..1160527d 100644 --- a/transactionoutbox-virtthreads/pom.xml +++ b/transactionoutbox-virtthreads/pom.xml @@ -42,6 +42,12 @@ ${project.version} test + + me.escoffier.loom + loom-unit + 0.3.0 + test + org.testcontainers testcontainers diff --git a/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/AbstractVirtualThreadsTest.java b/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/AbstractVirtualThreadsTest.java index 4312a1d8..2208563f 100644 --- a/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/AbstractVirtualThreadsTest.java +++ b/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/AbstractVirtualThreadsTest.java @@ -9,43 +9,56 @@ import com.gruelbox.transactionoutbox.testing.BaseTest; import com.gruelbox.transactionoutbox.testing.InterfaceProcessor; import java.time.Duration; -import java.util.concurrent.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.FutureTask; +import java.util.concurrent.TimeUnit; import java.util.stream.IntStream; import lombok.extern.slf4j.Slf4j; +import me.escoffier.loom.loomunit.LoomUnitExtension; +import me.escoffier.loom.loomunit.ShouldNotPin; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; @Slf4j +@ExtendWith(LoomUnitExtension.class) +@ShouldNotPin abstract class AbstractVirtualThreadsTest extends BaseTest { + private static final String VIRTUAL_THREAD_SCHEDULER_PARALLELISM = + "jdk.virtualThreadScheduler.parallelism"; + @Test final void highVolumeVirtualThreads() throws Exception { - var count = 10; + var count = 100; var latch = new CountDownLatch(count * 10); var transactionManager = txManager(); var results = new ConcurrentHashMap(); var duplicates = new ConcurrentHashMap(); - try (var executor = Executors.newVirtualThreadPerTaskExecutor()) { - var outbox = - TransactionOutbox.builder() - .transactionManager(transactionManager) - .persistor(Persistor.forDialect(connectionDetails().dialect())) - .instantiator(Instantiator.using(clazz -> (InterfaceProcessor) (foo, bar) -> {})) - .submitter(Submitter.withExecutor(executor)) - .attemptFrequency(Duration.ofMillis(500)) - .flushBatchSize(1000) - .listener( - new TransactionOutboxListener() { - @Override - public void success(TransactionOutboxEntry entry) { - Integer i = (Integer) entry.getInvocation().getArgs()[0]; - if (results.putIfAbsent(i, i) != null) { - duplicates.put(i, i); - } - latch.countDown(); + var outbox = + TransactionOutbox.builder() + .transactionManager(transactionManager) + .persistor(Persistor.forDialect(connectionDetails().dialect())) + .instantiator(Instantiator.using(clazz -> (InterfaceProcessor) (foo, bar) -> {})) + .submitter(Submitter.withExecutor(Thread::startVirtualThread)) + .attemptFrequency(Duration.ofMillis(500)) + .flushBatchSize(1000) + .listener( + new TransactionOutboxListener() { + @Override + public void success(TransactionOutboxEntry entry) { + Integer i = (Integer) entry.getInvocation().getArgs()[0]; + if (results.putIfAbsent(i, i) != null) { + duplicates.put(i, i); } - }) - .build(); + latch.countDown(); + } + }) + .build(); + var parallelism = System.getProperty(VIRTUAL_THREAD_SCHEDULER_PARALLELISM); + System.setProperty(VIRTUAL_THREAD_SCHEDULER_PARALLELISM, "1"); + try { withRunningFlusher( outbox, () -> { @@ -53,7 +66,7 @@ public void success(TransactionOutboxEntry entry) { IntStream.range(0, count) .mapToObj( i -> - CompletableFuture.runAsync( + new FutureTask( () -> transactionManager.inTransaction( () -> { @@ -63,18 +76,27 @@ public void success(TransactionOutboxEntry entry) { .process(i * 10 + j, "Whee"); } }), - executor)) - .toArray(CompletableFuture[]::new); - CompletableFuture.allOf(futures).join(); + null)) + .toList(); + futures.forEach(Thread::startVirtualThread); + for (var future : futures) { + future.get(); + } assertTrue(latch.await(30, TimeUnit.SECONDS), "Latch not opened in time"); }); - - assertThat( - "Should never get duplicates running to full completion", duplicates.keySet(), empty()); - assertThat( - "Only got: " + results.keySet(), - results.keySet(), - containsInAnyOrder(IntStream.range(0, count * 10).boxed().toArray())); + } finally { + if (parallelism == null) { + System.clearProperty(VIRTUAL_THREAD_SCHEDULER_PARALLELISM); + } else { + System.setProperty(VIRTUAL_THREAD_SCHEDULER_PARALLELISM, parallelism); + } } + + assertThat( + "Should never get duplicates running to full completion", duplicates.keySet(), empty()); + assertThat( + "Only got: " + results.keySet(), + results.keySet(), + containsInAnyOrder(IntStream.range(0, count * 10).boxed().toArray())); } } From b2946843d8fc420f79ca0317aedb90b7bc165a25 Mon Sep 17 00:00:00 2001 From: Graham Crockford Date: Tue, 26 Dec 2023 18:33:49 +0000 Subject: [PATCH 3/5] Reduce thread count --- .../virtthreads/AbstractVirtualThreadsTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/AbstractVirtualThreadsTest.java b/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/AbstractVirtualThreadsTest.java index 2208563f..6aa83186 100644 --- a/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/AbstractVirtualThreadsTest.java +++ b/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/AbstractVirtualThreadsTest.java @@ -30,7 +30,7 @@ abstract class AbstractVirtualThreadsTest extends BaseTest { @Test final void highVolumeVirtualThreads() throws Exception { - var count = 100; + var count = 10; var latch = new CountDownLatch(count * 10); var transactionManager = txManager(); var results = new ConcurrentHashMap(); @@ -80,7 +80,7 @@ public void success(TransactionOutboxEntry entry) { .toList(); futures.forEach(Thread::startVirtualThread); for (var future : futures) { - future.get(); + future.get(20, TimeUnit.SECONDS); } assertTrue(latch.await(30, TimeUnit.SECONDS), "Latch not opened in time"); }); From 11071b5634b842b4c6100fb5ece6467e4b5e97fa Mon Sep 17 00:00:00 2001 From: Graham Crockford Date: Tue, 26 Dec 2023 22:57:13 +0000 Subject: [PATCH 4/5] Fix virtual threads tests and disable on MySQL --- .../src/main/resources/logback-test.xml | 11 +++++++++++ .../virtthreads/AbstractVirtualThreadsTest.java | 7 +++++-- .../virtthreads/TestVirtualThreadsMySql5.java | 2 ++ .../virtthreads/TestVirtualThreadsMySql8.java | 2 ++ .../src/test/resources/logback-test.xml | 11 +++++++++++ 5 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 transactionoutbox-testing/src/main/resources/logback-test.xml create mode 100644 transactionoutbox-virtthreads/src/test/resources/logback-test.xml diff --git a/transactionoutbox-testing/src/main/resources/logback-test.xml b/transactionoutbox-testing/src/main/resources/logback-test.xml new file mode 100644 index 00000000..96692642 --- /dev/null +++ b/transactionoutbox-testing/src/main/resources/logback-test.xml @@ -0,0 +1,11 @@ + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{5} - %msg%n + + + + + + diff --git a/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/AbstractVirtualThreadsTest.java b/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/AbstractVirtualThreadsTest.java index 6aa83186..59e4baa6 100644 --- a/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/AbstractVirtualThreadsTest.java +++ b/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/AbstractVirtualThreadsTest.java @@ -9,6 +9,7 @@ import com.gruelbox.transactionoutbox.testing.BaseTest; import com.gruelbox.transactionoutbox.testing.InterfaceProcessor; import java.time.Duration; +import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.FutureTask; @@ -22,13 +23,13 @@ @Slf4j @ExtendWith(LoomUnitExtension.class) -@ShouldNotPin abstract class AbstractVirtualThreadsTest extends BaseTest { private static final String VIRTUAL_THREAD_SCHEDULER_PARALLELISM = "jdk.virtualThreadScheduler.parallelism"; @Test + @ShouldNotPin final void highVolumeVirtualThreads() throws Exception { var count = 10; var latch = new CountDownLatch(count * 10); @@ -40,7 +41,9 @@ final void highVolumeVirtualThreads() throws Exception { .transactionManager(transactionManager) .persistor(Persistor.forDialect(connectionDetails().dialect())) .instantiator(Instantiator.using(clazz -> (InterfaceProcessor) (foo, bar) -> {})) - .submitter(Submitter.withExecutor(Thread::startVirtualThread)) + .submitter( + Submitter.withExecutor( + r -> Thread.ofVirtual().name(UUID.randomUUID().toString()).start(r))) .attemptFrequency(Duration.ofMillis(500)) .flushBatchSize(1000) .listener( diff --git a/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/TestVirtualThreadsMySql5.java b/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/TestVirtualThreadsMySql5.java index aaa732a8..91365ce9 100644 --- a/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/TestVirtualThreadsMySql5.java +++ b/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/TestVirtualThreadsMySql5.java @@ -2,6 +2,7 @@ import com.gruelbox.transactionoutbox.Dialect; import java.time.Duration; +import org.junit.jupiter.api.Disabled; import org.testcontainers.containers.JdbcDatabaseContainer; import org.testcontainers.containers.MySQLContainer; import org.testcontainers.junit.jupiter.Container; @@ -9,6 +10,7 @@ @SuppressWarnings("WeakerAccess") @Testcontainers +@Disabled class TestVirtualThreadsMySql5 extends AbstractVirtualThreadsTest { @Container diff --git a/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/TestVirtualThreadsMySql8.java b/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/TestVirtualThreadsMySql8.java index cf22a597..661d1a21 100644 --- a/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/TestVirtualThreadsMySql8.java +++ b/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/TestVirtualThreadsMySql8.java @@ -2,6 +2,7 @@ import com.gruelbox.transactionoutbox.Dialect; import java.time.Duration; +import org.junit.jupiter.api.Disabled; import org.testcontainers.containers.JdbcDatabaseContainer; import org.testcontainers.containers.MySQLContainer; import org.testcontainers.junit.jupiter.Container; @@ -9,6 +10,7 @@ @SuppressWarnings("WeakerAccess") @Testcontainers +@Disabled class TestVirtualThreadsMySql8 extends AbstractVirtualThreadsTest { @Container diff --git a/transactionoutbox-virtthreads/src/test/resources/logback-test.xml b/transactionoutbox-virtthreads/src/test/resources/logback-test.xml new file mode 100644 index 00000000..96692642 --- /dev/null +++ b/transactionoutbox-virtthreads/src/test/resources/logback-test.xml @@ -0,0 +1,11 @@ + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{5} - %msg%n + + + + + + From 9d96f8695fda709e9e2767710c92d14e6c7a289c Mon Sep 17 00:00:00 2001 From: Graham Crockford Date: Tue, 26 Dec 2023 23:46:14 +0000 Subject: [PATCH 5/5] Latest MySQL driver --- pom.xml | 6 ++-- transactionoutbox-acceptance/pom.xml | 4 +-- transactionoutbox-jooq/pom.xml | 4 +-- .../testing/AbstractAcceptanceTest.java | 31 ++++++++++--------- transactionoutbox-virtthreads/pom.xml | 4 +-- .../virtthreads/TestVirtualThreadsMySql5.java | 5 +-- .../virtthreads/TestVirtualThreadsMySql8.java | 5 +-- .../TestVirtualThreadsOracle21.java | 5 +-- .../TestVirtualThreadsPostgres16.java | 5 +-- 9 files changed, 37 insertions(+), 32 deletions(-) diff --git a/pom.xml b/pom.xml index 52dcfd81..86004eba 100644 --- a/pom.xml +++ b/pom.xml @@ -158,9 +158,9 @@ test - mysql - mysql-connector-java - 8.0.33 + com.mysql + mysql-connector-j + 8.2.0 test diff --git a/transactionoutbox-acceptance/pom.xml b/transactionoutbox-acceptance/pom.xml index 64570f7c..99704440 100644 --- a/transactionoutbox-acceptance/pom.xml +++ b/transactionoutbox-acceptance/pom.xml @@ -61,8 +61,8 @@ ojdbc11 - mysql - mysql-connector-java + com.mysql + mysql-connector-j com.h2database diff --git a/transactionoutbox-jooq/pom.xml b/transactionoutbox-jooq/pom.xml index e449c602..c01e5916 100644 --- a/transactionoutbox-jooq/pom.xml +++ b/transactionoutbox-jooq/pom.xml @@ -71,8 +71,8 @@ ojdbc11 - mysql - mysql-connector-java + com.mysql + mysql-connector-j diff --git a/transactionoutbox-testing/src/main/java/com/gruelbox/transactionoutbox/testing/AbstractAcceptanceTest.java b/transactionoutbox-testing/src/main/java/com/gruelbox/transactionoutbox/testing/AbstractAcceptanceTest.java index f297286c..2e48f70f 100644 --- a/transactionoutbox-testing/src/main/java/com/gruelbox/transactionoutbox/testing/AbstractAcceptanceTest.java +++ b/transactionoutbox-testing/src/main/java/com/gruelbox/transactionoutbox/testing/AbstractAcceptanceTest.java @@ -1,5 +1,7 @@ package com.gruelbox.transactionoutbox.testing; +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.empty; import static org.junit.jupiter.api.Assertions.*; @@ -23,7 +25,6 @@ import java.util.stream.IntStream; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; -import org.hamcrest.MatcherAssert; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.Test; @@ -89,15 +90,15 @@ public void success(TransactionOutboxEntry entry) { outbox.schedule(InterfaceProcessor.class).process(3, "Whee"); try { // Should not be fired until after commit - assertFalse(latch.await(2, TimeUnit.SECONDS)); + assertFalse(latch.await(2, SECONDS)); } catch (InterruptedException e) { fail("Interrupted"); } }); // Should be fired after commit - assertTrue(chainedLatch.await(2, TimeUnit.SECONDS)); - assertTrue(latch.await(1, TimeUnit.SECONDS)); + assertTrue(chainedLatch.await(2, SECONDS)); + assertTrue(latch.await(1, SECONDS)); assertTrue(gotScheduled.get()); } @@ -226,7 +227,7 @@ public void success(TransactionOutboxEntry entry) { .schedule(ClassProcessor.class) .process("6")); - MatcherAssert.assertThat(ids, containsInAnyOrder("1", "2", "4", "6")); + assertThat(ids, containsInAnyOrder("1", "2", "4", "6")); } /** @@ -254,7 +255,7 @@ final void dataSourceConnectionProviderReflectionInstantiatorConcreteClass() transactionManager.inTransaction(() -> outbox.schedule(ClassProcessor.class).process(myId)); - assertTrue(latch.await(2, TimeUnit.SECONDS)); + assertTrue(latch.await(2, SECONDS)); assertEquals(List.of(myId), ClassProcessor.PROCESSED); } } @@ -346,7 +347,7 @@ public T requireTransactionReturns( } postCommitHooks.forEach(Runnable::run); - assertTrue(latch.await(2, TimeUnit.SECONDS)); + assertTrue(latch.await(2, SECONDS)); assertEquals(List.of(myId), ClassProcessor.PROCESSED); } } @@ -377,7 +378,7 @@ final void retryBehaviour() throws Exception { () -> { transactionManager.inTransaction( () -> outbox.schedule(InterfaceProcessor.class).process(3, "Whee")); - assertTrue(latch.await(15, TimeUnit.SECONDS)); + assertTrue(latch.await(15, SECONDS)); }); } @@ -449,12 +450,12 @@ final void lastAttemptTime_updatesEveryTime() throws Exception { () -> { transactionManager.inTransaction( () -> outbox.schedule(InterfaceProcessor.class).process(3, "Whee")); - assertTrue(blockLatch.await(10, TimeUnit.SECONDS)); + assertTrue(blockLatch.await(10, SECONDS)); assertTrue( (Boolean) transactionManager.inTransactionReturns( tx -> outbox.unblock(orderedEntryListener.getBlocked().getId()))); - assertTrue(successLatch.await(10, TimeUnit.SECONDS)); + assertTrue(successLatch.await(10, SECONDS)); var orderedEntryEvents = orderedEntryListener.getOrderedEntries(); log.info("The entry life cycle is: {}", orderedEntryEvents); @@ -503,12 +504,12 @@ final void blockAndThenUnblockForRetry() throws Exception { () -> { transactionManager.inTransaction( () -> outbox.schedule(InterfaceProcessor.class).process(3, "Whee")); - assertTrue(blockLatch.await(3, TimeUnit.SECONDS)); + assertTrue(blockLatch.await(3, SECONDS)); assertTrue( (Boolean) transactionManager.inTransactionReturns( tx -> outbox.unblock(latchListener.getBlocked().getId()))); - assertTrue(successLatch.await(3, TimeUnit.SECONDS)); + assertTrue(successLatch.await(3, SECONDS)); }); } @@ -556,12 +557,12 @@ public void success(TransactionOutboxEntry entry) { outbox.schedule(InterfaceProcessor.class).process(i * 10 + j, "Whee"); } })); - assertTrue(latch.await(30, TimeUnit.SECONDS), "Latch not opened in time"); + assertTrue(latch.await(30, SECONDS), "Latch not opened in time"); }); - MatcherAssert.assertThat( + assertThat( "Should never get duplicates running to full completion", duplicates.keySet(), empty()); - MatcherAssert.assertThat( + assertThat( "Only got: " + results.keySet(), results.keySet(), containsInAnyOrder(IntStream.range(0, count * 10).boxed().toArray())); diff --git a/transactionoutbox-virtthreads/pom.xml b/transactionoutbox-virtthreads/pom.xml index 1160527d..4db8afdb 100644 --- a/transactionoutbox-virtthreads/pom.xml +++ b/transactionoutbox-virtthreads/pom.xml @@ -77,8 +77,8 @@ ojdbc11 - mysql - mysql-connector-java + com.mysql + mysql-connector-j com.h2database diff --git a/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/TestVirtualThreadsMySql5.java b/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/TestVirtualThreadsMySql5.java index 91365ce9..308f579e 100644 --- a/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/TestVirtualThreadsMySql5.java +++ b/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/TestVirtualThreadsMySql5.java @@ -1,6 +1,7 @@ package com.gruelbox.transactionoutbox.virtthreads; -import com.gruelbox.transactionoutbox.Dialect; +import static com.gruelbox.transactionoutbox.Dialect.MY_SQL_5; + import java.time.Duration; import org.junit.jupiter.api.Disabled; import org.testcontainers.containers.JdbcDatabaseContainer; @@ -21,7 +22,7 @@ class TestVirtualThreadsMySql5 extends AbstractVirtualThreadsTest { @Override protected ConnectionDetails connectionDetails() { return ConnectionDetails.builder() - .dialect(Dialect.MY_SQL_5) + .dialect(MY_SQL_5) .driverClassName("com.mysql.cj.jdbc.Driver") .url(container.getJdbcUrl()) .user(container.getUsername()) diff --git a/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/TestVirtualThreadsMySql8.java b/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/TestVirtualThreadsMySql8.java index 661d1a21..b2233c7c 100644 --- a/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/TestVirtualThreadsMySql8.java +++ b/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/TestVirtualThreadsMySql8.java @@ -1,6 +1,7 @@ package com.gruelbox.transactionoutbox.virtthreads; -import com.gruelbox.transactionoutbox.Dialect; +import static com.gruelbox.transactionoutbox.Dialect.MY_SQL_8; + import java.time.Duration; import org.junit.jupiter.api.Disabled; import org.testcontainers.containers.JdbcDatabaseContainer; @@ -21,7 +22,7 @@ class TestVirtualThreadsMySql8 extends AbstractVirtualThreadsTest { @Override protected ConnectionDetails connectionDetails() { return ConnectionDetails.builder() - .dialect(Dialect.MY_SQL_8) + .dialect(MY_SQL_8) .driverClassName("com.mysql.cj.jdbc.Driver") .url(container.getJdbcUrl()) .user(container.getUsername()) diff --git a/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/TestVirtualThreadsOracle21.java b/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/TestVirtualThreadsOracle21.java index 0210627a..6ccd450e 100644 --- a/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/TestVirtualThreadsOracle21.java +++ b/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/TestVirtualThreadsOracle21.java @@ -1,6 +1,7 @@ package com.gruelbox.transactionoutbox.virtthreads; -import com.gruelbox.transactionoutbox.Dialect; +import static com.gruelbox.transactionoutbox.Dialect.ORACLE; + import java.time.Duration; import org.testcontainers.containers.JdbcDatabaseContainer; import org.testcontainers.containers.OracleContainer; @@ -19,7 +20,7 @@ class TestVirtualThreadsOracle21 extends AbstractVirtualThreadsTest { @Override protected ConnectionDetails connectionDetails() { return ConnectionDetails.builder() - .dialect(Dialect.ORACLE) + .dialect(ORACLE) .driverClassName("oracle.jdbc.OracleDriver") .url(container.getJdbcUrl()) .user(container.getUsername()) diff --git a/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/TestVirtualThreadsPostgres16.java b/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/TestVirtualThreadsPostgres16.java index 1fb0bda4..4d8b8e34 100644 --- a/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/TestVirtualThreadsPostgres16.java +++ b/transactionoutbox-virtthreads/src/test/java/com/gruelbox/transactionoutbox/virtthreads/TestVirtualThreadsPostgres16.java @@ -1,6 +1,7 @@ package com.gruelbox.transactionoutbox.virtthreads; -import com.gruelbox.transactionoutbox.Dialect; +import static com.gruelbox.transactionoutbox.Dialect.POSTGRESQL_9; + import java.time.Duration; import org.testcontainers.containers.JdbcDatabaseContainer; import org.testcontainers.containers.PostgreSQLContainer; @@ -20,7 +21,7 @@ class TestVirtualThreadsPostgres16 extends AbstractVirtualThreadsTest { @Override protected ConnectionDetails connectionDetails() { return ConnectionDetails.builder() - .dialect(Dialect.POSTGRESQL_9) + .dialect(POSTGRESQL_9) .driverClassName("org.postgresql.Driver") .url(container.getJdbcUrl()) .user(container.getUsername())