Skip to content

Commit 89e9b90

Browse files
SkyAndyToonemikereiche
authored andcommitted
ReactiveCouchbaseTemplate can overwrite PseudoArgs. (#1685)
* ReactiveCouchbaseTemplate can overwrite PseudoArgs under heavy concurrency. Raised as issue #1684 * Added author
1 parent efd8688 commit 89e9b90

File tree

2 files changed

+72
-1
lines changed

2 files changed

+72
-1
lines changed

src/main/java/org/springframework/data/couchbase/core/ReactiveCouchbaseTemplate.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
* @author Jorge Rodriguez Martin
4646
* @author Carlos Espinaco
4747
* @author Tigran Babloyan
48+
* @author Andy Toone
4849
*/
4950
public class ReactiveCouchbaseTemplate implements ReactiveCouchbaseOperations, ApplicationContextAware {
5051

@@ -251,7 +252,14 @@ public PseudoArgs<?> getPseudoArgs() {
251252
* set the ThreadLocal field
252253
*/
253254
public void setPseudoArgs(PseudoArgs<?> threadLocalArgs) {
254-
this.threadLocalArgs = new ThreadLocal<>();
255+
if (this.threadLocalArgs == null) {
256+
synchronized (this) {
257+
if (this.threadLocalArgs == null) {
258+
this.threadLocalArgs = new ThreadLocal<>();
259+
}
260+
}
261+
}
262+
255263
this.threadLocalArgs.set(threadLocalArgs);
256264
}
257265

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package org.springframework.data.couchbase.core;
2+
3+
import java.util.concurrent.Semaphore;
4+
import org.springframework.data.couchbase.core.support.PseudoArgs;
5+
import org.springframework.data.couchbase.domain.Config;
6+
import org.springframework.data.couchbase.util.ClusterType;
7+
import org.springframework.data.couchbase.util.IgnoreWhen;
8+
9+
import org.junit.jupiter.api.Test;
10+
import static org.junit.jupiter.api.Assertions.assertEquals;
11+
12+
import org.springframework.beans.factory.annotation.Autowired;
13+
14+
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
15+
16+
@IgnoreWhen(clusterTypes = ClusterType.MOCKED)
17+
@SpringJUnitConfig(Config.class)
18+
public class ReactiveCouchbaseTemplateConcurrencyTests {
19+
20+
@Autowired public CouchbaseTemplate couchbaseTemplate;
21+
22+
@Autowired public ReactiveCouchbaseTemplate reactiveCouchbaseTemplate;
23+
24+
@Test
25+
public void shouldStoreArgsForLocalThread() throws InterruptedException {
26+
// These will consume any args set on the current thread
27+
PseudoArgs<?> args1 = new PseudoArgs<>(reactiveCouchbaseTemplate, "aScope", "aCollection", null, Object.class);
28+
PseudoArgs<?> args2 = new PseudoArgs<>(reactiveCouchbaseTemplate, "aScope", "aCollection", null, Object.class);
29+
30+
// Store args1 on this thread
31+
reactiveCouchbaseTemplate.setPseudoArgs(args1);
32+
33+
final PseudoArgs<?>[] threadArgs = {null};
34+
35+
Semaphore awaitingArgs1 = new Semaphore(0);
36+
Semaphore checkingArgs2 = new Semaphore(0);
37+
38+
Thread t = new Thread(() -> {
39+
// Store args2 on separate thread
40+
reactiveCouchbaseTemplate.setPseudoArgs(args2);
41+
awaitingArgs1.release();
42+
try {
43+
// Wait to check args2
44+
checkingArgs2.acquire();
45+
} catch (InterruptedException e) {
46+
throw new RuntimeException(e);
47+
}
48+
threadArgs[0] = reactiveCouchbaseTemplate.getPseudoArgs();
49+
});
50+
t.start();
51+
52+
// Wait for separate thread to have set args2
53+
awaitingArgs1.acquire();
54+
55+
assertEquals(args1, reactiveCouchbaseTemplate.getPseudoArgs());
56+
checkingArgs2.release();
57+
t.join();
58+
59+
assertEquals(args2, threadArgs[0]);
60+
61+
}
62+
63+
}

0 commit comments

Comments
 (0)