ReactiveCouchbaseTemplate can overwrite PseudoArgs. (#1685)

* ReactiveCouchbaseTemplate can overwrite PseudoArgs under heavy concurrency.

Raised as issue #1684

* Added author
This commit is contained in:
Andy Toone
2023-03-07 01:30:26 +00:00
committed by mikereiche
parent efd86888d8
commit 89e9b90dda
2 changed files with 72 additions and 1 deletions

View File

@@ -45,6 +45,7 @@ import com.couchbase.client.java.query.QueryScanConsistency;
* @author Jorge Rodriguez Martin
* @author Carlos Espinaco
* @author Tigran Babloyan
* @author Andy Toone
*/
public class ReactiveCouchbaseTemplate implements ReactiveCouchbaseOperations, ApplicationContextAware {
@@ -251,7 +252,14 @@ public class ReactiveCouchbaseTemplate implements ReactiveCouchbaseOperations, A
* set the ThreadLocal field
*/
public void setPseudoArgs(PseudoArgs<?> threadLocalArgs) {
this.threadLocalArgs = new ThreadLocal<>();
if (this.threadLocalArgs == null) {
synchronized (this) {
if (this.threadLocalArgs == null) {
this.threadLocalArgs = new ThreadLocal<>();
}
}
}
this.threadLocalArgs.set(threadLocalArgs);
}

View File

@@ -0,0 +1,63 @@
package org.springframework.data.couchbase.core;
import java.util.concurrent.Semaphore;
import org.springframework.data.couchbase.core.support.PseudoArgs;
import org.springframework.data.couchbase.domain.Config;
import org.springframework.data.couchbase.util.ClusterType;
import org.springframework.data.couchbase.util.IgnoreWhen;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
@IgnoreWhen(clusterTypes = ClusterType.MOCKED)
@SpringJUnitConfig(Config.class)
public class ReactiveCouchbaseTemplateConcurrencyTests {
@Autowired public CouchbaseTemplate couchbaseTemplate;
@Autowired public ReactiveCouchbaseTemplate reactiveCouchbaseTemplate;
@Test
public void shouldStoreArgsForLocalThread() throws InterruptedException {
// These will consume any args set on the current thread
PseudoArgs<?> args1 = new PseudoArgs<>(reactiveCouchbaseTemplate, "aScope", "aCollection", null, Object.class);
PseudoArgs<?> args2 = new PseudoArgs<>(reactiveCouchbaseTemplate, "aScope", "aCollection", null, Object.class);
// Store args1 on this thread
reactiveCouchbaseTemplate.setPseudoArgs(args1);
final PseudoArgs<?>[] threadArgs = {null};
Semaphore awaitingArgs1 = new Semaphore(0);
Semaphore checkingArgs2 = new Semaphore(0);
Thread t = new Thread(() -> {
// Store args2 on separate thread
reactiveCouchbaseTemplate.setPseudoArgs(args2);
awaitingArgs1.release();
try {
// Wait to check args2
checkingArgs2.acquire();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
threadArgs[0] = reactiveCouchbaseTemplate.getPseudoArgs();
});
t.start();
// Wait for separate thread to have set args2
awaitingArgs1.acquire();
assertEquals(args1, reactiveCouchbaseTemplate.getPseudoArgs());
checkingArgs2.release();
t.join();
assertEquals(args2, threadArgs[0]);
}
}