Skip to content

Commit 8d78ec9

Browse files
committed
Fix race condition accessing ServerDescription
Further reduce the possibility of introducing a race by removing the Server#getDescription method JAVA-3911
1 parent 1152442 commit 8d78ec9

File tree

8 files changed

+49
-46
lines changed

8 files changed

+49
-46
lines changed

driver-core/src/main/com/mongodb/internal/connection/DefaultServer.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -124,12 +124,6 @@ public void onResult(final InternalConnection result, final Throwable t) {
124124
});
125125
}
126126

127-
@Override
128-
public ServerDescription getDescription() {
129-
isTrue("open", !isClosed());
130-
return description;
131-
}
132-
133127
@Override
134128
public void resetToConnecting() {
135129
serverStateListener.stateChanged(new ChangeEvent<>(description, ServerDescription.builder()

driver-core/src/main/com/mongodb/internal/connection/Server.java

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818

1919
import com.mongodb.annotations.ThreadSafe;
2020
import com.mongodb.internal.async.SingleResultCallback;
21-
import com.mongodb.connection.ServerDescription;
2221

2322
/**
2423
* A logical connection to a MongoDB server.
@@ -27,13 +26,6 @@
2726
*/
2827
@ThreadSafe
2928
public interface Server {
30-
/**
31-
* Gets the description of this server. Implementations of this method should not block if the server has not yet been successfully
32-
* contacted, but rather return immediately a @code{ServerDescription} in a @code{ServerConnectionState.CONNECTING} state.
33-
*
34-
* @return the description of this server
35-
*/
36-
ServerDescription getDescription();
3729

3830
/**
3931
* <p>Gets a connection to this server. The connection should be released after the caller is done with it.</p>

driver-core/src/main/com/mongodb/internal/connection/SingleServerCluster.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import com.mongodb.event.ServerDescriptionChangedEvent;
3131

3232
import static com.mongodb.assertions.Assertions.isTrue;
33+
import static com.mongodb.connection.ServerConnectionState.CONNECTING;
3334
import static java.lang.String.format;
3435
import static java.util.Collections.emptyList;
3536
import static java.util.Collections.singletonList;
@@ -55,7 +56,8 @@ public SingleServerCluster(final ClusterId clusterId, final ClusterSettings sett
5556
// In other words, we are leaking a reference to "this" from the constructor.
5657
synchronized (this) {
5758
this.server = createServer(settings.getHosts().get(0), new DefaultServerDescriptionChangedListener());
58-
publishDescription(server.getDescription());
59+
publishDescription(ServerDescription.builder().state(CONNECTING).address(settings.getHosts().get(0))
60+
.build());
5961
}
6062
}
6163

driver-core/src/test/functional/com/mongodb/internal/binding/AsyncSingleConnectionBinding.java

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ public class AsyncSingleConnectionBinding extends AbstractReferenceCounted imple
4848
private AsyncConnection writeConnection;
4949
private volatile Server readServer;
5050
private volatile Server writeServer;
51+
private volatile ServerDescription readServerDescription;
52+
private volatile ServerDescription writeServerDescription;
5153

5254
/**
5355
* Create a new binding with the given cluster.
@@ -79,6 +81,7 @@ public AsyncSingleConnectionBinding(final Cluster cluster, final ReadPreference
7981
public void onResult(final ServerTuple result, final Throwable t) {
8082
if (t == null) {
8183
writeServer = result.getServer();
84+
writeServerDescription = result.getServerDescription();
8285
latch.countDown();
8386
}
8487
}
@@ -88,6 +91,7 @@ public void onResult(final ServerTuple result, final Throwable t) {
8891
public void onResult(final ServerTuple result, final Throwable t) {
8992
if (t == null) {
9093
readServer = result.getServer();
94+
readServerDescription = result.getServerDescription();
9195
latch.countDown();
9296
}
9397
}
@@ -162,14 +166,14 @@ public void getReadConnectionSource(final SingleResultCallback<AsyncConnectionSo
162166
if (readPreference == primary()) {
163167
getWriteConnectionSource(callback);
164168
} else {
165-
callback.onResult(new SingleAsyncConnectionSource(readServer, readConnection), null);
169+
callback.onResult(new SingleAsyncConnectionSource(readServerDescription, readConnection), null);
166170
}
167171
}
168172

169173
@Override
170174
public void getWriteConnectionSource(final SingleResultCallback<AsyncConnectionSource> callback) {
171175
isTrue("open", getCount() > 0);
172-
callback.onResult(new SingleAsyncConnectionSource(writeServer, writeConnection), null);
176+
callback.onResult(new SingleAsyncConnectionSource(writeServerDescription, writeConnection), null);
173177
}
174178

175179
@Override
@@ -182,18 +186,19 @@ public void release() {
182186
}
183187

184188
private final class SingleAsyncConnectionSource extends AbstractReferenceCounted implements AsyncConnectionSource {
185-
private final Server server;
189+
private final ServerDescription serverDescription;
186190
private final AsyncConnection connection;
187191

188-
private SingleAsyncConnectionSource(final Server server, final AsyncConnection connection) {
189-
this.server = server;
192+
private SingleAsyncConnectionSource(final ServerDescription serverDescription,
193+
final AsyncConnection connection) {
194+
this.serverDescription = serverDescription;
190195
this.connection = connection;
191196
AsyncSingleConnectionBinding.this.retain();
192197
}
193198

194199
@Override
195200
public ServerDescription getServerDescription() {
196-
return server.getDescription();
201+
return serverDescription;
197202
}
198203

199204
@Override

driver-core/src/test/functional/com/mongodb/internal/binding/SingleConnectionBinding.java

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
import com.mongodb.internal.connection.Cluster;
2222
import com.mongodb.internal.connection.Connection;
2323
import com.mongodb.internal.connection.NoOpSessionContext;
24-
import com.mongodb.internal.connection.Server;
24+
import com.mongodb.internal.connection.ServerTuple;
2525
import com.mongodb.internal.selector.ReadPreferenceServerSelector;
2626
import com.mongodb.internal.selector.WritableServerSelector;
2727
import com.mongodb.internal.session.SessionContext;
@@ -39,9 +39,8 @@ public class SingleConnectionBinding implements ReadWriteBinding {
3939
private final ReadPreference readPreference;
4040
private final Connection readConnection;
4141
private final Connection writeConnection;
42-
private final Server readServer;
43-
private final Server writeServer;
44-
42+
private final ServerDescription readServerDescription;
43+
private final ServerDescription writeServerDescription;
4544
private int count = 1;
4645

4746
/**
@@ -62,10 +61,12 @@ public SingleConnectionBinding(final Cluster cluster) {
6261
public SingleConnectionBinding(final Cluster cluster, final ReadPreference readPreference) {
6362
notNull("cluster", cluster);
6463
this.readPreference = notNull("readPreference", readPreference);
65-
writeServer = cluster.selectServer(new WritableServerSelector()).getServer();
66-
writeConnection = writeServer.getConnection();
67-
readServer = cluster.selectServer(new ReadPreferenceServerSelector(readPreference)).getServer();
68-
readConnection = readServer.getConnection();
64+
ServerTuple writeServerTuple = cluster.selectServer(new WritableServerSelector());
65+
writeServerDescription = writeServerTuple.getServerDescription();
66+
writeConnection = writeServerTuple.getServer().getConnection();
67+
ServerTuple readServerTuple = cluster.selectServer(new ReadPreferenceServerSelector(readPreference));
68+
readServerDescription = readServerTuple.getServerDescription();
69+
readConnection = readServerTuple.getServer().getConnection();
6970
}
7071

7172
@Override
@@ -100,7 +101,7 @@ public ConnectionSource getReadConnectionSource() {
100101
if (readPreference == primary()) {
101102
return getWriteConnectionSource();
102103
} else {
103-
return new SingleConnectionSource(readServer, readConnection);
104+
return new SingleConnectionSource(readServerDescription, readConnection);
104105
}
105106
}
106107

@@ -112,23 +113,23 @@ public SessionContext getSessionContext() {
112113
@Override
113114
public ConnectionSource getWriteConnectionSource() {
114115
isTrue("open", getCount() > 0);
115-
return new SingleConnectionSource(writeServer, writeConnection);
116+
return new SingleConnectionSource(writeServerDescription, writeConnection);
116117
}
117118

118119
private final class SingleConnectionSource implements ConnectionSource {
120+
private final ServerDescription serverDescription;
119121
private final Connection connection;
120-
private final Server server;
121122
private int count = 1;
122123

123-
SingleConnectionSource(final Server server, final Connection connection) {
124-
this.server = server;
124+
SingleConnectionSource(final ServerDescription serverDescription, final Connection connection) {
125+
this.serverDescription = serverDescription;
125126
this.connection = connection;
126127
SingleConnectionBinding.this.retain();
127128
}
128129

129130
@Override
130131
public ServerDescription getServerDescription() {
131-
return server.getDescription();
132+
return serverDescription;
132133
}
133134

134135
@Override

driver-core/src/test/functional/com/mongodb/internal/connection/SingleServerClusterTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,15 +103,15 @@ public void shouldGetServerWithOkDescription() {
103103
setUpCluster(getPrimary());
104104

105105
// when
106-
Server server = cluster.selectServer(new ServerSelector() {
106+
ServerTuple serverTuple = cluster.selectServer(new ServerSelector() {
107107
@Override
108108
public List<ServerDescription> select(final ClusterDescription clusterDescription) {
109109
return getPrimaries(clusterDescription);
110110
}
111-
}).getServer();
111+
});
112112

113113
// then
114-
assertTrue(server.getDescription().isOk());
114+
assertTrue(serverTuple.getServerDescription().isOk());
115115
}
116116

117117
@Test

driver-core/src/test/unit/com/mongodb/internal/connection/BaseClusterSpecification.groovy

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ class BaseClusterSpecification extends Specification {
116116

117117
expect:
118118
cluster.selectServer(new ReadPreferenceServerSelector(ReadPreference.secondary()))
119-
.server.description.address == firstServer
119+
.serverDescription.address == firstServer
120120
}
121121

122122
def 'should use server selector passed to selectServer if server selector in cluster settings is null'() {
@@ -132,7 +132,7 @@ class BaseClusterSpecification extends Specification {
132132
factory.sendNotification(thirdServer, REPLICA_SET_PRIMARY, allServers)
133133

134134
expect:
135-
cluster.selectServer(new ServerAddressSelector(firstServer)).server.description.address == firstServer
135+
cluster.selectServer(new ServerAddressSelector(firstServer)).serverDescription.address == firstServer
136136
}
137137

138138
def 'should timeout with useful message'() {
@@ -190,7 +190,7 @@ class BaseClusterSpecification extends Specification {
190190

191191
expect:
192192
cluster.selectServer(new ReadPreferenceServerSelector(ReadPreference.primary()))
193-
.server.description.address == thirdServer
193+
.serverDescription.address == thirdServer
194194

195195
cleanup:
196196
cluster?.close()
@@ -272,10 +272,10 @@ class BaseClusterSpecification extends Specification {
272272
factory.sendNotification(firstServer, REPLICA_SET_SECONDARY, allServers)
273273

274274
when:
275-
def server = selectServerAsyncAndGet(cluster, firstServer)
275+
def serverDescription = selectServerAsync(cluster, firstServer).getDescription()
276276

277277
then:
278-
server.description.address == firstServer
278+
serverDescription.address == firstServer
279279

280280
cleanup:
281281
cluster?.close()
@@ -300,8 +300,8 @@ class BaseClusterSpecification extends Specification {
300300
factory.sendNotification(thirdServer, REPLICA_SET_SECONDARY, allServers)
301301

302302
then:
303-
secondServerLatch.get().description.address == secondServer
304-
thirdServerLatch.get().description.address == thirdServer
303+
secondServerLatch.getDescription().address == secondServer
304+
thirdServerLatch.getDescription().address == thirdServer
305305

306306
cleanup:
307307
cluster?.close()
@@ -361,6 +361,7 @@ class BaseClusterSpecification extends Specification {
361361
def serverLatch = new ServerLatch()
362362
cluster.selectServerAsync(new ServerAddressSelector(serverAddress)) { ServerTuple result, MongoException e ->
363363
serverLatch.server = result != null ? result.getServer() : null
364+
serverLatch.serverDescription = result != null ? result.serverDescription : null
364365
serverLatch.throwable = e
365366
serverLatch.latch.countDown()
366367
}
@@ -370,6 +371,7 @@ class BaseClusterSpecification extends Specification {
370371
class ServerLatch {
371372
CountDownLatch latch = new CountDownLatch(1)
372373
Server server
374+
ServerDescription serverDescription
373375
Throwable throwable
374376

375377
def get() {
@@ -379,5 +381,13 @@ class BaseClusterSpecification extends Specification {
379381
}
380382
server
381383
}
384+
385+
def getDescription() {
386+
latch.await()
387+
if (throwable != null) {
388+
throw throwable
389+
}
390+
serverDescription
391+
}
382392
}
383393
}

driver-core/src/test/unit/com/mongodb/internal/connection/TestServer.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,6 @@ public int getConnectCount() {
9090
return connectCount;
9191
}
9292

93-
@Override
9493
public ServerDescription getDescription() {
9594
return description;
9695
}

0 commit comments

Comments
 (0)