From abca4e6d62e65a279ef001bbe8572513511b3d35 Mon Sep 17 00:00:00 2001 From: Michael Reiche <48999328+mikereiche@users.noreply.github.com> Date: Thu, 15 Jun 2023 18:25:06 -0700 Subject: [PATCH] Allow template.save() to find if it is in a transaction. (#1758) Closes #1757. --- .../core/ReactiveCouchbaseTemplate.java | 2 +- .../SDKTransactionsSaveIntegrationTests.java | 19 +++++++++++++------ .../util/TransactionTestUtil.java | 13 +++++++++++++ 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/springframework/data/couchbase/core/ReactiveCouchbaseTemplate.java b/src/main/java/org/springframework/data/couchbase/core/ReactiveCouchbaseTemplate.java index d11245af..8190c93c 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ReactiveCouchbaseTemplate.java +++ b/src/main/java/org/springframework/data/couchbase/core/ReactiveCouchbaseTemplate.java @@ -80,7 +80,7 @@ public class ReactiveCouchbaseTemplate implements ReactiveCouchbaseOperations, A String scope = scopeAndCollection.length > 0 ? scopeAndCollection[0] : null; String collection = scopeAndCollection.length > 1 ? scopeAndCollection[1] : null; - return Mono.defer(() -> { + return Mono.deferContextual( ctx1 -> { final CouchbasePersistentEntity mapperEntity = getConverter().getMappingContext() .getPersistentEntity(entity.getClass()); final CouchbasePersistentProperty versionProperty = mapperEntity.getVersionProperty(); diff --git a/src/test/java/org/springframework/data/couchbase/transactions/sdk/SDKTransactionsSaveIntegrationTests.java b/src/test/java/org/springframework/data/couchbase/transactions/sdk/SDKTransactionsSaveIntegrationTests.java index 47b05d10..fc3c7c8a 100644 --- a/src/test/java/org/springframework/data/couchbase/transactions/sdk/SDKTransactionsSaveIntegrationTests.java +++ b/src/test/java/org/springframework/data/couchbase/transactions/sdk/SDKTransactionsSaveIntegrationTests.java @@ -18,11 +18,11 @@ package org.springframework.data.couchbase.transactions.sdk; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; -import static org.springframework.data.couchbase.transactions.util.TransactionTestUtil.assertNotInTransaction; +import static org.springframework.data.couchbase.transactions.util.TransactionTestUtil.assertInTransaction; +import static org.springframework.data.couchbase.transactions.util.TransactionTestUtil.assertInReactiveTransaction; +import static org.springframework.data.couchbase.transactions.util.TransactionTestUtil.assertNotInReactiveTransaction; import org.springframework.data.couchbase.domain.PersonWithoutVersion; -import reactor.core.publisher.Mono; -import reactor.core.scheduler.Schedulers; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -32,7 +32,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.couchbase.CouchbaseClientFactory; import org.springframework.data.couchbase.core.CouchbaseTemplate; import org.springframework.data.couchbase.core.ReactiveCouchbaseTemplate; -import org.springframework.data.couchbase.domain.Person; import org.springframework.data.couchbase.transactions.TransactionsConfig; import org.springframework.data.couchbase.util.Capabilities; import org.springframework.data.couchbase.util.ClusterType; @@ -55,19 +54,22 @@ public class SDKTransactionsSaveIntegrationTests extends JavaIntegrationTests { @BeforeEach public void beforeEachTest() { assertNotInTransaction(); + assertNotInReactiveTransaction(); } @AfterEach public void afterEachTest() { assertNotInTransaction(); + assertNotInReactiveTransaction(); } + @DisplayName("ReactiveCouchbaseTemplate.save() called inside a reactive SDK transaction should work") @Test public void reactiveSaveInReactiveTransaction() { couchbaseClientFactory.getCluster().reactive().transactions().run(ctx -> { PersonWithoutVersion p = new PersonWithoutVersion("Walter", "White"); - return reactiveOps.save(p); + return reactiveOps.save(p).then(assertInReactiveTransaction()); }).block(); } @@ -75,18 +77,22 @@ public class SDKTransactionsSaveIntegrationTests extends JavaIntegrationTests { @Test public void reactiveSaveInBlockingTransaction() { couchbaseClientFactory.getCluster().transactions().run(ctx -> { + assertInTransaction(); PersonWithoutVersion p = new PersonWithoutVersion("Walter", "White"); reactiveOps.save(p).block(); }); } + // This should not work because ops.save(p) calls block() so everything in that call + // does not have the reactive context (which has the transaction context) + // what happens is the ops.save(p) is not in a transaction. (it will call upsert instead of insert) @DisplayName("ReactiveCouchbaseTemplate.save() called inside a reactive SDK transaction should work") @Test public void blockingSaveInReactiveTransaction() { couchbaseClientFactory.getCluster().reactive().transactions().run(ctx -> { PersonWithoutVersion p = new PersonWithoutVersion("Walter", "White"); ops.save(p); - return Mono.empty(); + return assertInReactiveTransaction(); }).block(); } @@ -94,6 +100,7 @@ public class SDKTransactionsSaveIntegrationTests extends JavaIntegrationTests { @Test public void blockingSaveInBlockingTransaction() { couchbaseClientFactory.getCluster().transactions().run(ctx -> { + assertInTransaction(); PersonWithoutVersion p = new PersonWithoutVersion("Walter", "White"); ops.save(p); }); diff --git a/src/test/java/org/springframework/data/couchbase/transactions/util/TransactionTestUtil.java b/src/test/java/org/springframework/data/couchbase/transactions/util/TransactionTestUtil.java index 17634086..46c254af 100644 --- a/src/test/java/org/springframework/data/couchbase/transactions/util/TransactionTestUtil.java +++ b/src/test/java/org/springframework/data/couchbase/transactions/util/TransactionTestUtil.java @@ -19,6 +19,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import org.springframework.data.couchbase.core.TransactionalSupport; +import reactor.core.publisher.Mono; /** * Utility methods for transaction tests. @@ -35,4 +36,16 @@ public class TransactionTestUtil { public static void assertNotInTransaction() { assertFalse(TransactionalSupport.checkForTransactionInThreadLocalStorage().block().isPresent()); } + + public static Mono assertInReactiveTransaction(T... obj) { + return Mono.deferContextual((ctx1) -> + TransactionalSupport.checkForTransactionInThreadLocalStorage() + .flatMap(ctx2 -> ctx2.isPresent() ? (obj.length>0 ? Mono.just(obj[0]) : Mono.empty()) : Mono.error(new RuntimeException("in transaction")))); + } + + public static Mono assertNotInReactiveTransaction(T... obj) { + return Mono.deferContextual((ctx1) -> + TransactionalSupport.checkForTransactionInThreadLocalStorage() + .flatMap(ctx2 -> !ctx2.isPresent() ? (obj.length>0 ? Mono.just(obj[0]) : Mono.empty()) : Mono.error(new RuntimeException("in transaction")))); + } }