Skip to content

Commit 4eee9f6

Browse files
authored
Add Client Metadata Update Support. (#1708)
- Introduce ClientMetadata class to consolidate and manage metadata updates. - Add support for updating client metadata after MongoClient initialization. - Add new prose and unified tests to cover metadata update scenarios. JAVA-5870 JAVA-5871
1 parent 8e51059 commit 4eee9f6

File tree

58 files changed

+961
-239
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+961
-239
lines changed

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,11 @@ private ServerTuple(final ClusterableServer server, final ServerDescription desc
7878
}
7979
}
8080

81-
AbstractMultiServerCluster(final ClusterId clusterId, final ClusterSettings settings, final ClusterableServerFactory serverFactory) {
82-
super(clusterId, settings, serverFactory);
81+
AbstractMultiServerCluster(final ClusterId clusterId,
82+
final ClusterSettings settings,
83+
final ClusterableServerFactory serverFactory,
84+
final ClientMetadata clientMetadata) {
85+
super(clusterId, settings, serverFactory, clientMetadata);
8386
isTrue("connection mode is multiple", settings.getMode() == MULTIPLE);
8487
clusterType = settings.getRequiredClusterType();
8588
replicaSetName = settings.getRequiredReplicaSetName();

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

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,11 @@
5353
import java.util.Iterator;
5454
import java.util.List;
5555
import java.util.Objects;
56-
import java.util.stream.Stream;
5756
import java.util.concurrent.ConcurrentLinkedDeque;
5857
import java.util.concurrent.CountDownLatch;
5958
import java.util.concurrent.atomic.AtomicReference;
6059
import java.util.concurrent.locks.ReentrantLock;
60+
import java.util.stream.Stream;
6161

6262
import static com.mongodb.assertions.Assertions.assertNotNull;
6363
import static com.mongodb.assertions.Assertions.isTrue;
@@ -106,28 +106,38 @@ abstract class BaseCluster implements Cluster {
106106
private final ClusterListener clusterListener;
107107
private final Deque<ServerSelectionRequest> waitQueue = new ConcurrentLinkedDeque<>();
108108
private final ClusterClock clusterClock = new ClusterClock();
109+
private final ClientMetadata clientMetadata;
109110
private Thread waitQueueHandler;
110111

111112
private volatile boolean isClosed;
112113
private volatile ClusterDescription description;
113114

114-
BaseCluster(final ClusterId clusterId, final ClusterSettings settings, final ClusterableServerFactory serverFactory) {
115+
BaseCluster(final ClusterId clusterId,
116+
final ClusterSettings settings,
117+
final ClusterableServerFactory serverFactory,
118+
final ClientMetadata clientMetadata) {
115119
this.clusterId = notNull("clusterId", clusterId);
116120
this.settings = notNull("settings", settings);
117121
this.serverFactory = notNull("serverFactory", serverFactory);
118122
this.clusterListener = singleClusterListener(settings);
119123
ClusterOpeningEvent clusterOpeningEvent = new ClusterOpeningEvent(clusterId);
120-
clusterListener.clusterOpening(clusterOpeningEvent);
124+
this.clusterListener.clusterOpening(clusterOpeningEvent);
121125
logTopologyOpening(clusterId, clusterOpeningEvent);
122-
description = new ClusterDescription(settings.getMode(), UNKNOWN, emptyList(),
126+
this.description = new ClusterDescription(settings.getMode(), UNKNOWN, emptyList(),
123127
settings, serverFactory.getSettings());
128+
this.clientMetadata = clientMetadata;
124129
}
125130

126131
@Override
127132
public ClusterClock getClock() {
128133
return clusterClock;
129134
}
130135

136+
@Override
137+
public ClientMetadata getClientMetadata() {
138+
return clientMetadata;
139+
}
140+
131141
@Override
132142
public ServerTuple selectServer(final ServerSelector serverSelector, final OperationContext operationContext) {
133143
isTrue("open", !isClosed());

driver-core/src/main/com/mongodb/internal/connection/ClientMetadataHelper.java renamed to driver-core/src/main/com/mongodb/internal/connection/ClientMetadata.java

Lines changed: 143 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package com.mongodb.internal.connection;
1818

1919
import com.mongodb.MongoDriverInformation;
20+
import com.mongodb.annotations.ThreadSafe;
2021
import com.mongodb.internal.VisibleForTesting;
2122
import com.mongodb.internal.build.MongoDriverVersion;
2223
import com.mongodb.lang.Nullable;
@@ -32,53 +33,64 @@
3233
import java.io.File;
3334
import java.nio.charset.StandardCharsets;
3435
import java.nio.file.Files;
36+
import java.util.ArrayList;
3537
import java.util.List;
38+
import java.util.concurrent.locks.ReentrantReadWriteLock;
3639
import java.util.function.Consumer;
3740

3841
import static com.mongodb.assertions.Assertions.isTrueArgument;
42+
import static com.mongodb.internal.Locks.withLock;
3943
import static com.mongodb.internal.connection.FaasEnvironment.getFaasEnvironment;
4044
import static java.lang.String.format;
4145
import static java.lang.System.getProperty;
4246
import static java.nio.file.Paths.get;
4347

4448
/**
49+
* Represents metadata of the current MongoClient.
50+
*
51+
* Metadata is used to identify the client in the server logs and metrics.
52+
*
4553
* <p>This class is not part of the public API and may be removed or changed at any time</p>
4654
*/
47-
public final class ClientMetadataHelper {
55+
@ThreadSafe
56+
public class ClientMetadata {
4857
private static final String SEPARATOR = "|";
49-
5058
private static final int MAXIMUM_CLIENT_METADATA_ENCODED_SIZE = 512;
51-
52-
@VisibleForTesting(otherwise = VisibleForTesting.AccessModifier.PRIVATE)
53-
static String getOperatingSystemType(final String operatingSystemName) {
54-
if (nameStartsWith(operatingSystemName, "linux")) {
55-
return "Linux";
56-
} else if (nameStartsWith(operatingSystemName, "mac")) {
57-
return "Darwin";
58-
} else if (nameStartsWith(operatingSystemName, "windows")) {
59-
return "Windows";
60-
} else if (nameStartsWith(operatingSystemName, "hp-ux", "aix", "irix", "solaris", "sunos")) {
61-
return "Unix";
62-
} else {
63-
return "unknown";
64-
}
59+
private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
60+
private final String applicationName;
61+
private BsonDocument clientMetadataBsonDocument;
62+
private DriverInformation driverInformation;
63+
64+
public ClientMetadata(@Nullable final String applicationName, final MongoDriverInformation mongoDriverInformation) {
65+
this.applicationName = applicationName;
66+
withLock(readWriteLock.writeLock(), () -> {
67+
this.driverInformation = DriverInformation.from(
68+
mongoDriverInformation.getDriverNames(),
69+
mongoDriverInformation.getDriverVersions(),
70+
mongoDriverInformation.getDriverPlatforms());
71+
this.clientMetadataBsonDocument = createClientMetadataDocument(applicationName, driverInformation);
72+
});
6573
}
6674

67-
private static String getOperatingSystemName() {
68-
return getProperty("os.name", "unknown");
75+
/**
76+
* Returns mutable BsonDocument that represents the client metadata.
77+
*/
78+
public BsonDocument getBsonDocument() {
79+
return withLock(readWriteLock.readLock(), () -> clientMetadataBsonDocument);
6980
}
7081

71-
private static boolean nameStartsWith(final String name, final String... prefixes) {
72-
for (String prefix : prefixes) {
73-
if (name.toLowerCase().startsWith(prefix.toLowerCase())) {
74-
return true;
75-
}
76-
}
77-
return false;
82+
public void append(final MongoDriverInformation mongoDriverInformationToAppend) {
83+
withLock(readWriteLock.writeLock(), () -> {
84+
this.driverInformation.append(
85+
mongoDriverInformationToAppend.getDriverNames(),
86+
mongoDriverInformationToAppend.getDriverVersions(),
87+
mongoDriverInformationToAppend.getDriverPlatforms());
88+
this.clientMetadataBsonDocument = createClientMetadataDocument(applicationName, driverInformation);
89+
});
7890
}
7991

80-
public static BsonDocument createClientMetadataDocument(@Nullable final String applicationName,
81-
@Nullable final MongoDriverInformation mongoDriverInformation) {
92+
private static BsonDocument createClientMetadataDocument(@Nullable final String applicationName,
93+
final DriverInformation driverInformation) {
8294
if (applicationName != null) {
8395
isTrueArgument("applicationName UTF-8 encoding length <= 128",
8496
applicationName.getBytes(StandardCharsets.UTF_8).length <= 128);
@@ -87,27 +99,26 @@ public static BsonDocument createClientMetadataDocument(@Nullable final String a
8799
// client fields are added in "preservation" order:
88100
BsonDocument client = new BsonDocument();
89101
tryWithLimit(client, d -> putAtPath(d, "application.name", applicationName));
90-
MongoDriverInformation baseDriverInfor = getDriverInformation(null);
102+
91103
// required fields:
92104
tryWithLimit(client, d -> {
93-
putAtPath(d, "driver.name", listToString(baseDriverInfor.getDriverNames()));
94-
putAtPath(d, "driver.version", listToString(baseDriverInfor.getDriverVersions()));
105+
putAtPath(d, "driver.name", driverInformation.getInitialDriverName());
106+
putAtPath(d, "driver.version", driverInformation.getInitialDriverVersion());
95107
});
96108
tryWithLimit(client, d -> putAtPath(d, "os.type", getOperatingSystemType(getOperatingSystemName())));
97109
// full driver information:
98-
MongoDriverInformation fullDriverInfo = getDriverInformation(mongoDriverInformation);
99110
tryWithLimit(client, d -> {
100-
putAtPath(d, "driver.name", listToString(fullDriverInfo.getDriverNames()));
101-
putAtPath(d, "driver.version", listToString(fullDriverInfo.getDriverVersions()));
111+
putAtPath(d, "driver.name", listToString(driverInformation.getAllDriverNames()));
112+
putAtPath(d, "driver.version", listToString(driverInformation.getAllDriverVersions()));
102113
});
103114

104115
// optional fields:
105116
FaasEnvironment faasEnvironment = getFaasEnvironment();
106-
ContainerRuntime containerRuntime = ContainerRuntime.determineExecutionContainer();
107-
Orchestrator orchestrator = Orchestrator.determineExecutionOrchestrator();
117+
ClientMetadata.ContainerRuntime containerRuntime = ClientMetadata.ContainerRuntime.determineExecutionContainer();
118+
ClientMetadata.Orchestrator orchestrator = ClientMetadata.Orchestrator.determineExecutionOrchestrator();
108119

109-
tryWithLimit(client, d -> putAtPath(d, "platform", listToString(baseDriverInfor.getDriverPlatforms())));
110-
tryWithLimit(client, d -> putAtPath(d, "platform", listToString(fullDriverInfo.getDriverPlatforms())));
120+
tryWithLimit(client, d -> putAtPath(d, "platform", driverInformation.getInitialDriverPlatform()));
121+
tryWithLimit(client, d -> putAtPath(d, "platform", listToString(driverInformation.getAllDriverPlatforms())));
111122
tryWithLimit(client, d -> putAtPath(d, "os.name", getOperatingSystemName()));
112123
tryWithLimit(client, d -> putAtPath(d, "os.architecture", getProperty("os.arch", "unknown")));
113124
tryWithLimit(client, d -> putAtPath(d, "os.version", getProperty("os.version", "unknown")));
@@ -123,7 +134,6 @@ public static BsonDocument createClientMetadataDocument(@Nullable final String a
123134
return client;
124135
}
125136

126-
127137
private static void putAtPath(final BsonDocument d, final String path, @Nullable final String value) {
128138
if (value == null) {
129139
return;
@@ -180,7 +190,7 @@ static boolean clientMetadataDocumentTooLarge(final BsonDocument document) {
180190
return buffer.getPosition() > MAXIMUM_CLIENT_METADATA_ENCODED_SIZE;
181191
}
182192

183-
public enum ContainerRuntime {
193+
private enum ContainerRuntime {
184194
DOCKER("docker") {
185195
@Override
186196
boolean isCurrentRuntimeContainer() {
@@ -210,8 +220,8 @@ boolean isCurrentRuntimeContainer() {
210220
return false;
211221
}
212222

213-
static ContainerRuntime determineExecutionContainer() {
214-
for (ContainerRuntime allegedContainer : ContainerRuntime.values()) {
223+
static ClientMetadata.ContainerRuntime determineExecutionContainer() {
224+
for (ClientMetadata.ContainerRuntime allegedContainer : ClientMetadata.ContainerRuntime.values()) {
215225
if (allegedContainer.isCurrentRuntimeContainer()) {
216226
return allegedContainer;
217227
}
@@ -245,8 +255,8 @@ boolean isCurrentOrchestrator() {
245255
return false;
246256
}
247257

248-
static Orchestrator determineExecutionOrchestrator() {
249-
for (Orchestrator alledgedOrchestrator : Orchestrator.values()) {
258+
static ClientMetadata.Orchestrator determineExecutionOrchestrator() {
259+
for (ClientMetadata.Orchestrator alledgedOrchestrator : ClientMetadata.Orchestrator.values()) {
250260
if (alledgedOrchestrator.isCurrentOrchestrator()) {
251261
return alledgedOrchestrator;
252262
}
@@ -255,17 +265,6 @@ static Orchestrator determineExecutionOrchestrator() {
255265
}
256266
}
257267

258-
static MongoDriverInformation getDriverInformation(@Nullable final MongoDriverInformation mongoDriverInformation) {
259-
MongoDriverInformation.Builder builder = mongoDriverInformation != null ? MongoDriverInformation.builder(mongoDriverInformation)
260-
: MongoDriverInformation.builder();
261-
return builder
262-
.driverName(MongoDriverVersion.NAME)
263-
.driverVersion(MongoDriverVersion.VERSION)
264-
.driverPlatform(format("Java/%s/%s", getProperty("java.vendor", "unknown-vendor"),
265-
getProperty("java.runtime.version", "unknown-version")))
266-
.build();
267-
}
268-
269268
private static String listToString(final List<String> listOfStrings) {
270269
StringBuilder stringBuilder = new StringBuilder();
271270
int i = 0;
@@ -279,6 +278,95 @@ private static String listToString(final List<String> listOfStrings) {
279278
return stringBuilder.toString();
280279
}
281280

282-
private ClientMetadataHelper() {
281+
@VisibleForTesting(otherwise = VisibleForTesting.AccessModifier.PRIVATE)
282+
public static String getOperatingSystemType(final String operatingSystemName) {
283+
if (nameStartsWith(operatingSystemName, "linux")) {
284+
return "Linux";
285+
} else if (nameStartsWith(operatingSystemName, "mac")) {
286+
return "Darwin";
287+
} else if (nameStartsWith(operatingSystemName, "windows")) {
288+
return "Windows";
289+
} else if (nameStartsWith(operatingSystemName, "hp-ux", "aix", "irix", "solaris", "sunos")) {
290+
return "Unix";
291+
} else {
292+
return "unknown";
293+
}
294+
}
295+
296+
private static String getOperatingSystemName() {
297+
return getProperty("os.name", "unknown");
298+
}
299+
300+
private static boolean nameStartsWith(final String name, final String... prefixes) {
301+
for (String prefix : prefixes) {
302+
if (name.toLowerCase().startsWith(prefix.toLowerCase())) {
303+
return true;
304+
}
305+
}
306+
return false;
307+
}
308+
309+
/**
310+
* Holds driver information of client.driver field
311+
* in {@link ClientMetadata#clientMetadataBsonDocument}.
312+
*/
313+
private static class DriverInformation {
314+
private final List<String> driverNames;
315+
private final List<String> driverVersions;
316+
private final List<String> driverPlatforms;
317+
private final String initialPlatform;
318+
319+
DriverInformation() {
320+
this.driverNames = new ArrayList<>();
321+
driverNames.add(MongoDriverVersion.NAME);
322+
323+
this.driverVersions = new ArrayList<>();
324+
driverVersions.add(MongoDriverVersion.VERSION);
325+
326+
this.initialPlatform = format("Java/%s/%s", getProperty("java.vendor", "unknown-vendor"),
327+
getProperty("java.runtime.version", "unknown-version"));
328+
this.driverPlatforms = new ArrayList<>();
329+
driverPlatforms.add(initialPlatform);
330+
}
331+
332+
static DriverInformation from(final List<String> driverNames,
333+
final List<String> driverVersions,
334+
final List<String> driverPlatforms) {
335+
DriverInformation driverInformation = new DriverInformation();
336+
return driverInformation.append(driverNames, driverVersions, driverPlatforms);
337+
}
338+
339+
DriverInformation append(final List<String> driverNames,
340+
final List<String> driverVersions,
341+
final List<String> driverPlatforms) {
342+
this.driverNames.addAll(driverNames);
343+
this.driverVersions.addAll(driverVersions);
344+
this.driverPlatforms.addAll(driverPlatforms);
345+
return this;
346+
}
347+
348+
public String getInitialDriverPlatform() {
349+
return initialPlatform;
350+
}
351+
352+
public String getInitialDriverName() {
353+
return MongoDriverVersion.NAME;
354+
}
355+
356+
public String getInitialDriverVersion() {
357+
return MongoDriverVersion.VERSION;
358+
}
359+
360+
public List<String> getAllDriverNames() {
361+
return driverNames;
362+
}
363+
364+
public List<String> getAllDriverVersions() {
365+
return driverVersions;
366+
}
367+
368+
public List<String> getAllDriverPlatforms() {
369+
return driverPlatforms;
370+
}
283371
}
284372
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ public interface Cluster extends Closeable {
5757
*/
5858
ClusterClock getClock();
5959

60+
ClientMetadata getClientMetadata();
61+
6062
ServerTuple selectServer(ServerSelector serverSelector, OperationContext operationContext);
6163

6264
void selectServerAsync(ServerSelector serverSelector, OperationContext operationContext,

0 commit comments

Comments
 (0)