Skip to content

Commit a85db53

Browse files
committed
Fixing bug with multiple threads using configurators
Ran into this with the Spark connector. We don't need the fix right away in the Spark connector, as we can synchronize the method that creates a client for now.
1 parent f61a6f6 commit a85db53

File tree

2 files changed

+55
-8
lines changed

2 files changed

+55
-8
lines changed

marklogic-client-api/src/main/java/com/marklogic/client/DatabaseClientFactory.java

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,7 @@
2828
import java.security.cert.CertificateParsingException;
2929
import java.security.cert.X509Certificate;
3030
import java.time.Instant;
31-
import java.util.ArrayList;
32-
import java.util.Collection;
33-
import java.util.Collections;
34-
import java.util.HashMap;
35-
import java.util.List;
36-
import java.util.Map;
37-
import java.util.Set;
31+
import java.util.*;
3832
import java.util.function.Function;
3933

4034
import javax.naming.InvalidNameException;
@@ -63,7 +57,8 @@
6357
*/
6458
public class DatabaseClientFactory {
6559

66-
static private List<ClientConfigurator<?>> clientConfigurators = new ArrayList<>();
60+
static private List<ClientConfigurator<?>> clientConfigurators = Collections.synchronizedList(new ArrayList<>());
61+
6762
static private HandleFactoryRegistry handleRegistry =
6863
HandleFactoryRegistryImpl.newDefault();
6964

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package com.marklogic.client.extra.okhttpclient;
2+
3+
import com.marklogic.client.DatabaseClientFactory;
4+
import com.marklogic.client.test.Common;
5+
import org.junit.jupiter.api.Test;
6+
7+
import java.util.ArrayList;
8+
import java.util.List;
9+
import java.util.concurrent.ExecutorService;
10+
import java.util.concurrent.Executors;
11+
import java.util.concurrent.Future;
12+
import java.util.concurrent.TimeUnit;
13+
14+
import static org.junit.jupiter.api.Assertions.assertEquals;
15+
16+
public class OkHttpClientConfiguratorTest {
17+
18+
/**
19+
* Not a test exactly of OkHttp, but rather that multiple threads can add configurators and create clients without
20+
* encountering a ConcurrentModificationException, which used to occur due to the list of configurators in
21+
* DatabaseClientFactory not being synchronized.
22+
*
23+
* @throws Exception
24+
*/
25+
@Test
26+
void multipleThreads() throws Exception {
27+
final int clientsToCreate = 100;
28+
29+
ExecutorService service = Executors.newFixedThreadPool(16);
30+
List<Future> futures = new ArrayList<>();
31+
for (int i = 0; i < clientsToCreate; i++) {
32+
futures.add(service.submit(() -> {
33+
DatabaseClientFactory.addConfigurator((OkHttpClientConfigurator) client -> {
34+
client.callTimeout(10, TimeUnit.SECONDS);
35+
});
36+
Common.newClient().release();
37+
}));
38+
}
39+
40+
int clientsCreated = 0;
41+
for (Future f : futures) {
42+
f.get();
43+
clientsCreated++;
44+
}
45+
service.shutdown();
46+
47+
assertEquals(clientsToCreate, clientsCreated, "The expectation is that many threads can " +
48+
"add a configurator and create a client at the same time because DatabaseClientFactory " +
49+
"uses a synchronized list for the configurators. So all of the expected clients should be " +
50+
"created successfully with no concurrent modification errors occurring.");
51+
}
52+
}

0 commit comments

Comments
 (0)