diff --git a/core-io/src/main/java/com/couchbase/client/core/topology/ClusterTopologyBuilder.java b/core-io/src/main/java/com/couchbase/client/core/topology/ClusterTopologyBuilder.java index 1f6f87ae9..72e8a9fae 100644 --- a/core-io/src/main/java/com/couchbase/client/core/topology/ClusterTopologyBuilder.java +++ b/core-io/src/main/java/com/couchbase/client/core/topology/ClusterTopologyBuilder.java @@ -120,7 +120,7 @@ public NodeBuilder serverGroup(@Nullable String serverGroup) { public HostAndServicePorts build() { NodeIdentifier id = this.id != null ? this.id : new NodeIdentifier(canonicalHost, 8091, hostForNetworkConnections); - return new HostAndServicePorts(hostForNetworkConnections, ports, id, ketamaAuthority, serverGroup); + return new HostAndServicePorts(hostForNetworkConnections, ports, id, ketamaAuthority, serverGroup, null); } } diff --git a/core-io/src/main/java/com/couchbase/client/core/topology/HostAndServicePorts.java b/core-io/src/main/java/com/couchbase/client/core/topology/HostAndServicePorts.java index c58484bd2..f14e6737b 100644 --- a/core-io/src/main/java/com/couchbase/client/core/topology/HostAndServicePorts.java +++ b/core-io/src/main/java/com/couchbase/client/core/topology/HostAndServicePorts.java @@ -52,6 +52,7 @@ public class HostAndServicePorts implements KetamaRingNode { emptyMap(), new NodeIdentifier("", 0, ""), null, + null, null ); @@ -60,19 +61,22 @@ public class HostAndServicePorts implements KetamaRingNode { private final NodeIdentifier id; private final @Nullable HostAndPort ketamaAuthority; private final @Nullable String serverGroup; + private final @Nullable String appTelemetryPath; public HostAndServicePorts( String host, Map ports, NodeIdentifier id, @Nullable HostAndPort ketamaAuthority, - @Nullable String serverGroup + @Nullable String serverGroup, + @Nullable String appTelemetryPath ) { this.host = requireNonNull(host); this.ports = unmodifiableMap(newEnumMap(ServiceType.class, ports)); this.id = requireNonNull(id); this.ketamaAuthority = ketamaAuthority; this.serverGroup = serverGroup; + this.appTelemetryPath = appTelemetryPath; } public boolean inaccessible() { @@ -109,10 +113,23 @@ public Map ports() { return ports; } + /** + * Returns the name of the server group this node belongs to, + * or null if Couchbase Server version is less than 7.6.2. + */ public @Nullable String serverGroup() { return serverGroup; } + /** + * Returns the HTTP path that accepts application telemetry WebSocket connections + * (on management service part), or null if this node does not currently accept + * application telemetry connections. + */ + public @Nullable String appTelemetryPath() { + return appTelemetryPath; + } + public boolean has(ServiceType serviceType) { return ports.containsKey(serviceType); } @@ -129,7 +146,7 @@ public HostAndServicePorts without(ServiceType service, ServiceType... moreServi temp.remove(t); } - return new HostAndServicePorts(this.host, temp, this.id, this.ketamaAuthority, this.serverGroup); + return new HostAndServicePorts(this.host, temp, this.id, this.ketamaAuthority, this.serverGroup, this.appTelemetryPath); } @Stability.Internal @@ -137,7 +154,7 @@ public HostAndServicePorts withKetamaAuthority(@Nullable HostAndPort ketamaAutho if (Objects.equals(this.ketamaAuthority, ketamaAuthority)) { return this; } - return new HostAndServicePorts(this.host, this.ports, this.id, ketamaAuthority, this.serverGroup); + return new HostAndServicePorts(this.host, this.ports, this.id, ketamaAuthority, this.serverGroup, this.appTelemetryPath); } boolean matches(SeedNode seedNode) { @@ -153,10 +170,14 @@ private boolean portEquals(ServiceType serviceType, int port) { @Override public boolean equals(Object o) { - if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; HostAndServicePorts that = (HostAndServicePorts) o; - return host.equals(that.host) && ports.equals(that.ports) && Objects.equals(ketamaAuthority, that.ketamaAuthority); + return Objects.equals(host, that.host) && + Objects.equals(ports, that.ports) && + Objects.equals(id, that.id) && + Objects.equals(ketamaAuthority, that.ketamaAuthority) + && Objects.equals(serverGroup, that.serverGroup) + && Objects.equals(appTelemetryPath, that.appTelemetryPath); } @Override @@ -172,6 +193,7 @@ public String toString() { ", id=" + redactSystem(id) + ", ketamaAuthority=" + redactSystem(ketamaAuthority) + ", serverGroup=" + redactMeta(serverGroup) + + ", appTelemetryPath=" + redactSystem(appTelemetryPath) + '}'; } diff --git a/core-io/src/main/java/com/couchbase/client/core/topology/HostAndServicePortsParser.java b/core-io/src/main/java/com/couchbase/client/core/topology/HostAndServicePortsParser.java index b1e2a33cd..40af2cdd6 100644 --- a/core-io/src/main/java/com/couchbase/client/core/topology/HostAndServicePortsParser.java +++ b/core-io/src/main/java/com/couchbase/client/core/topology/HostAndServicePortsParser.java @@ -54,8 +54,8 @@ public static Map parse( ) { Map raw = parseIntermediate(json); HostAndPort ketamaAuthority = getKetamaAuthority(raw); - String serverGroup = json.path("serverGroup").asText(); // Absent prior to Couchbase Server 7.6.2 - final String serverGroupFinal = serverGroup.isEmpty() ? null : serverGroup; + String serverGroup = json.path("serverGroup").textValue(); // Added in Couchbase Server 7.6.2 + String appTelemetryPath = json.path("appTelemetryPath").textValue(); // Added in Couchbase Server 8.0.0 return transformValues(raw, value -> new HostAndServicePorts( @@ -63,7 +63,8 @@ public static Map parse( portSelector.selectPorts(value.rawServicePorts), getId(value.host, raw), ketamaAuthority, - serverGroupFinal + serverGroup, + appTelemetryPath ) ); } diff --git a/core-io/src/test/java/com/couchbase/client/core/topology/ClusterTopologyParserTest.java b/core-io/src/test/java/com/couchbase/client/core/topology/ClusterTopologyParserTest.java index 5860f85a4..7c8b70523 100644 --- a/core-io/src/test/java/com/couchbase/client/core/topology/ClusterTopologyParserTest.java +++ b/core-io/src/test/java/com/couchbase/client/core/topology/ClusterTopologyParserTest.java @@ -291,6 +291,30 @@ void shouldParseServerGroupsIfPresent() { ); } + @Test + void appTelemetryPathIsNullIfAbsent() { + ClusterTopology config = parser.parseResource("config_7.2.2_1kv_2query.json"); + config.nodes().forEach(node -> assertNull(node.appTelemetryPath())); + } + + @Test + void shouldParseAppTelemetryPathIfPresent() { + // TODO: replace with config from actual 8.0.0 build + ClusterTopology config = parser.parseResource("config_8.0.0-prerelease_app_telemetry.json"); + assertEquals( + mapOf( + "192.168.106.128", "/foo", + "192.168.106.129", "/bar", + "192.168.106.130", "/zot" + ), + config.nodes().stream() + .collect(toMap( + HostAndServicePorts::host, + hostAndServicePorts -> requireNonNull(hostAndServicePorts.appTelemetryPath(), "Missing app telemetry path: " + hostAndServicePorts) + )) + ); + } + public CouchbaseBucketTopology requireCouchbaseBucket(ClusterTopology cluster) { try { return (CouchbaseBucketTopology) cluster.requireBucket().bucket(); diff --git a/core-io/src/test/java/com/couchbase/client/core/topology/TopologyTestUtils.java b/core-io/src/test/java/com/couchbase/client/core/topology/TopologyTestUtils.java index c93ff11af..9103c9118 100644 --- a/core-io/src/test/java/com/couchbase/client/core/topology/TopologyTestUtils.java +++ b/core-io/src/test/java/com/couchbase/client/core/topology/TopologyTestUtils.java @@ -40,7 +40,7 @@ public static NodeIdentifier nodeId(String host, int port) { } public static HostAndServicePorts node(String host, Map ports) { - return new HostAndServicePorts(host, ports, nodeId(host, ports.getOrDefault(ServiceType.MANAGER, 8091)), null, null); + return new HostAndServicePorts(host, ports, nodeId(host, ports.getOrDefault(ServiceType.MANAGER, 8091)), null, null, null); } @Deprecated diff --git a/core-io/src/test/resources/com/couchbase/client/core/config/config_8.0.0-prerelease_app_telemetry.json b/core-io/src/test/resources/com/couchbase/client/core/config/config_8.0.0-prerelease_app_telemetry.json new file mode 100644 index 000000000..8641f64ce --- /dev/null +++ b/core-io/src/test/resources/com/couchbase/client/core/config/config_8.0.0-prerelease_app_telemetry.json @@ -0,0 +1 @@ +{"rev":289,"revEpoch":1,"name":"travel-sample","nodeLocator":"vbucket","bucketType":"membase","storageBackend":"couchstore","uuid":"7aee1f3bde34b9c8ef7514cc28e29180","uri":"/pools/default/buckets/travel-sample?bucket_uuid=7aee1f3bde34b9c8ef7514cc28e29180","streamingUri":"/pools/default/bucketsStreaming/travel-sample?bucket_uuid=7aee1f3bde34b9c8ef7514cc28e29180","numVBuckets":1024,"bucketCapabilitiesVer":"","bucketCapabilities":["collections","durableWrite","tombstonedUserXAttrs","couchapi","subdoc.ReplaceBodyWithXattr","subdoc.DocumentMacroSupport","subdoc.ReviveDocument","dcp.IgnorePurgedTombstones","preserveExpiry","querySystemCollection","mobileSystemCollection","subdoc.ReplicaRead","rangeScan","dcp","cbhello","touch","cccp","xdcrCheckpointing","nodesExt","xattr"],"collectionsManifestUid":"2","ddocs":{"uri":"/pools/default/buckets/travel-sample/ddocs"},"vBucketServerMap":{"hashAlgorithm":"CRC","numReplicas":1,"serverList":["192.168.106.128:11210","192.168.106.129:11210","192.168.106.130:11210"],"vBucketMap":[[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[0,2],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,0],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[1,2],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,0],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1],[2,1]]},"nodes":[{"couchApiBase":"http://192.168.106.128:8092/travel-sample%2B7aee1f3bde34b9c8ef7514cc28e29180","hostname":"192.168.106.128:8091","ports":{"direct":11210}},{"couchApiBase":"http://192.168.106.129:8092/travel-sample%2B7aee1f3bde34b9c8ef7514cc28e29180","hostname":"192.168.106.129:8091","ports":{"direct":11210}},{"couchApiBase":"http://192.168.106.130:8092/travel-sample%2B7aee1f3bde34b9c8ef7514cc28e29180","hostname":"192.168.106.130:8091","ports":{"direct":11210}}],"nodesExt":[{"services":{"capi":8092,"capiSSL":18092,"fts":8094,"ftsGRPC":9130,"ftsGRPCSSL":19130,"ftsSSL":18094,"indexAdmin":9100,"indexHttp":9102,"indexHttps":19102,"indexScan":9101,"indexStreamCatchup":9104,"indexStreamInit":9103,"indexStreamMaint":9105,"kv":11210,"kvSSL":11207,"mgmt":8091,"mgmtSSL":18091,"n1ql":8093,"n1qlSSL":18093,"projector":9999},"thisNode":true,"hostname":"192.168.106.128","serverGroup":"Group 1","appTelemetryPath":"/foo"},{"services":{"capi":8092,"capiSSL":18092,"fts":8094,"ftsGRPC":9130,"ftsGRPCSSL":19130,"ftsSSL":18094,"indexAdmin":9100,"indexHttp":9102,"indexHttps":19102,"indexScan":9101,"indexStreamCatchup":9104,"indexStreamInit":9103,"indexStreamMaint":9105,"kv":11210,"kvSSL":11207,"mgmt":8091,"mgmtSSL":18091,"n1ql":8093,"n1qlSSL":18093,"projector":9999},"hostname":"192.168.106.129","serverGroup":"Group 1","appTelemetryPath":"/bar"},{"services":{"capi":8092,"capiSSL":18092,"fts":8094,"ftsGRPC":9130,"ftsGRPCSSL":19130,"ftsSSL":18094,"indexAdmin":9100,"indexHttp":9102,"indexHttps":19102,"indexScan":9101,"indexStreamCatchup":9104,"indexStreamInit":9103,"indexStreamMaint":9105,"kv":11210,"kvSSL":11207,"mgmt":8091,"mgmtSSL":18091,"n1ql":8093,"n1qlSSL":18093,"projector":9999},"hostname":"192.168.106.130","serverGroup":"Group 2","appTelemetryPath":"/zot"}],"clusterCapabilitiesVer":[1,0],"clusterCapabilities":{"n1ql":["costBasedOptimizer","indexAdvisor","javaScriptFunctions","inlineFunctions","enhancedPreparedStatements","readFromReplica"],"search":["vectorSearch","scopedSearchIndex"]},"clusterUUID":"f4aff221d68c30ee2f3e6f7e6b2362ef","clusterName":"test-cluster"}