Skip to content

Commit c600f58

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 67cbdb6 commit c600f58

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
@@ -37,6 +37,7 @@
3737
* @author Michael Reiche
3838
* @author Jorge Rodriguez Martin
3939
* @author Carlos Espinaco
40+
* @author Andy Toone
4041
*/
4142
public class ReactiveCouchbaseTemplate implements ReactiveCouchbaseOperations, ApplicationContextAware {
4243

@@ -186,7 +187,14 @@ public PseudoArgs<?> getPseudoArgs() {
186187
* set the ThreadLocal field
187188
*/
188189
public void setPseudoArgs(PseudoArgs<?> threadLocalArgs) {
189-
this.threadLocalArgs = new ThreadLocal<>();
190+
if (this.threadLocalArgs == null) {
191+
synchronized (this) {
192+
if (this.threadLocalArgs == null) {
193+
this.threadLocalArgs = new ThreadLocal<>();
194+
}
195+
}
196+
}
197+
190198
this.threadLocalArgs.set(threadLocalArgs);
191199
}
192200

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)