-
Notifications
You must be signed in to change notification settings - Fork 192
Fix call to block() in CouchbaseCallbackTransactionManager. #1528
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -44,6 +44,7 @@ | |
import com.couchbase.client.java.transactions.config.TransactionOptions; | ||
import com.couchbase.client.java.transactions.error.TransactionCommitAmbiguousException; | ||
import com.couchbase.client.java.transactions.error.TransactionFailedException; | ||
import reactor.util.context.ContextView; | ||
|
||
/** | ||
* The Couchbase transaction manager, providing support for @Transactional methods. | ||
|
@@ -73,7 +74,7 @@ public CouchbaseCallbackTransactionManager(CouchbaseClientFactory couchbaseClien | |
|
||
@Override | ||
public <T> T execute(TransactionDefinition definition, TransactionCallback<T> callback) throws TransactionException { | ||
boolean createNewTransaction = handlePropagation(definition); | ||
boolean createNewTransaction = handlePropagation(definition, null); | ||
|
||
setOptionsFromDefinition(definition); | ||
|
||
|
@@ -87,8 +88,8 @@ public <T> T execute(TransactionDefinition definition, TransactionCallback<T> ca | |
@Stability.Internal | ||
<T> Flux<T> executeReactive(TransactionDefinition definition, | ||
org.springframework.transaction.reactive.TransactionCallback<T> callback) { | ||
return Flux.defer(() -> { | ||
boolean createNewTransaction = handlePropagation(definition); | ||
return Flux.deferContextual((ctx) -> { | ||
boolean createNewTransaction = handlePropagation(definition, ctx); | ||
|
||
setOptionsFromDefinition(definition); | ||
|
||
|
@@ -187,8 +188,9 @@ public boolean isCompleted() { | |
} | ||
|
||
// Propagation defines what happens when a @Transactional method is called from another @Transactional method. | ||
private boolean handlePropagation(TransactionDefinition definition) { | ||
boolean isExistingTransaction = TransactionalSupport.checkForTransactionInThreadLocalStorage().block().isPresent(); | ||
private boolean handlePropagation(TransactionDefinition definition, ContextView ctx) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What I meant on JCBC-1988 by "Refactor handlePropagation so we call TransactionalSupport.checkForTransactionInThreadLocalStorage() outside it and pass in the result."
This preserves the full safety of the existing checkForTransactionInThreadLocalStorage() check (checking whether we are in either a reactive and blocking transaction), and just seems cleaner and simpler than the solution here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In the execute() path, block() cannot be called as this may be in a NonBlocking thread. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The execute() path is inherently completely blocking (it is the blocking API) and hence can't be called from a NonBlocking thread. If they're trying to do a blocking transaction inside a reactive operator, then that is an application bug. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Did we get to the bottom of how they ended up on the execute() path from a reactive @transactional? I read through 1527 and I took from it that our initial guess was that they didn't do @EnableTransactionManagement, but in fact they had. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The springframework TransactionInterceptor was being used instead of CouchbaseTranasctionInterceptor because the customer was configuring using yaml for configuration and not a class that extends that AbstractCouchbaseConfiguration like we had been doing. AbstractCouchbaseConfiguration has substitution of CouchbaseTransactionInterceptor. |
||
boolean isExistingTransaction = ctx != null ? TransactionalSupport.checkForTransactionInThreadLocalStorage(ctx).isPresent() : | ||
TransactionalSupport.checkForTransactionInThreadLocalStorage().block().isPresent(); | ||
|
||
LOGGER.trace("Deciding propagation behaviour from {} and {}", definition.getPropagationBehavior(), | ||
isExistingTransaction); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't too great as a) it's not DRY or encapsulated - this functionality would really belong in TransactionMarkerOwner, and b) most importantly, it's removing a critical check. We won't know if we're inside a blocking transaction here, as we don't have the ThreadLocal check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right. So (a) requires the modification to TransactionMarkerOwner, and (b) needs what I've described in the other comment.