All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.mongodb.connection.ClusterSettings Maven / Gradle / Ivy

Go to download

The Java operations layer for the MongoDB Java Driver. Third parties can wrap this layer to provide custom higher-level APIs

There is a newer version: 5.3.0-beta0
Show newest version
/*
 * Copyright 2008-present MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.mongodb.connection;

import com.mongodb.ConnectionString;
import com.mongodb.ServerAddress;
import com.mongodb.annotations.Immutable;
import com.mongodb.annotations.NotThreadSafe;
import com.mongodb.event.ClusterListener;
import com.mongodb.internal.connection.ServerAddressHelper;
import com.mongodb.lang.Nullable;
import com.mongodb.selector.ServerSelector;

import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.mongodb.assertions.Assertions.isTrueArgument;
import static com.mongodb.assertions.Assertions.notNull;
import static com.mongodb.internal.connection.ServerAddressHelper.createServerAddress;
import static java.lang.String.format;
import static java.util.Collections.singletonList;
import static java.util.Collections.unmodifiableList;
import static java.util.concurrent.TimeUnit.MILLISECONDS;

/**
 * Settings for the cluster.
 *
 * @since 3.0
 */
@Immutable
public final class ClusterSettings {
    private final String srvHost;
    private final Integer srvMaxHosts;
    private final String srvServiceName;
    private final List hosts;
    private final ClusterConnectionMode mode;
    private final ClusterType requiredClusterType;
    private final String requiredReplicaSetName;
    private final ServerSelector serverSelector;
    private final long localThresholdMS;
    private final long serverSelectionTimeoutMS;
    private final List clusterListeners;

    /**
     * Get a builder for this class.
     *
     * @return a new Builder for creating ClusterSettings.
     */
    public static Builder builder() {
        return new Builder();
    }

    /**
     * Creates a builder instance.
     *
     * @param clusterSettings existing ClusterSettings to default the builder settings on.
     * @return a builder
     * @since 3.5
     */
    public static Builder builder(final ClusterSettings clusterSettings) {
        return builder().applySettings(clusterSettings);
    }

    /**
     * A builder for the cluster settings.
     */
    @NotThreadSafe
    public static final class Builder {
        private static final List DEFAULT_HOSTS = singletonList(new ServerAddress());
        private String srvHost;
        private Integer srvMaxHosts;
        private String srvServiceName = "mongodb";
        private List hosts = DEFAULT_HOSTS;
        private ClusterConnectionMode mode;
        private ClusterType requiredClusterType = ClusterType.UNKNOWN;
        private String requiredReplicaSetName;
        private ServerSelector serverSelector;
        private long serverSelectionTimeoutMS = MILLISECONDS.convert(30, TimeUnit.SECONDS);
        private long localThresholdMS = MILLISECONDS.convert(15, MILLISECONDS);
        private List clusterListeners = new ArrayList<>();

        private Builder() {
        }

        /**
         * Applies the clusterSettings to the builder
         *
         * 

Note: Overwrites all existing settings

* * @param clusterSettings the clusterSettings * @return this * @since 3.7 */ public Builder applySettings(final ClusterSettings clusterSettings) { notNull("clusterSettings", clusterSettings); srvHost = clusterSettings.srvHost; srvServiceName = clusterSettings.srvServiceName; srvMaxHosts = clusterSettings.srvMaxHosts; hosts = clusterSettings.hosts; mode = clusterSettings.mode; requiredReplicaSetName = clusterSettings.requiredReplicaSetName; requiredClusterType = clusterSettings.requiredClusterType; localThresholdMS = clusterSettings.localThresholdMS; serverSelectionTimeoutMS = clusterSettings.serverSelectionTimeoutMS; clusterListeners = new ArrayList<>(clusterSettings.clusterListeners); serverSelector = clusterSettings.serverSelector; return this; } /** * Sets the host name to use in order to look up an SRV DNS record to find the MongoDB hosts. * *

* Note that when setting srvHost via {@code ClusterSettings.Builder}, the driver will NOT process any associated TXT records * associated with the host. In order to enable the processing of TXT records while still using {@code MongoClientSettings}, * specify the SRV host via connection string and apply the connection string to the settings, e.g. * {@code MongoClientSettings.builder().applyConnectionString(new ConnectionString("mongodb+srv://host1.acme.com")) }. *

* * @param srvHost the SRV host name * @return this * @see com.mongodb.MongoClientSettings.Builder#applyConnectionString(ConnectionString) * @see ClusterSettings.Builder#applyConnectionString(ConnectionString) */ public Builder srvHost(final String srvHost) { if (this.hosts != DEFAULT_HOSTS) { throw new IllegalArgumentException("Can not set both hosts and srvHost"); } this.srvHost = srvHost; return this; } /** * Sets the maximum number of hosts to connect to when using SRV protocol. * * @param srvMaxHosts the maximum number of hosts to connect to when using SRV protocol * @return this * @since 4.4 */ public Builder srvMaxHosts(final Integer srvMaxHosts) { this.srvMaxHosts = srvMaxHosts; return this; } /** * Sets the SRV service name. * *

* The SRV resource record (RFC 2782) * service name, which is limited to 15 characters * (RFC 6335 section 5.1). * If specified, it is combined with the single host name specified by * {@link #getHosts()} as follows: {@code _srvServiceName._tcp.hostName}. The combined string is an SRV resource record * name (RFC 1035 section 2.3.1), which is limited to 255 * characters (RFC 1035 section 2.3.4). *

* * @param srvServiceName the SRV service name * @return this * @since 4.5 */ public Builder srvServiceName(final String srvServiceName) { this.srvServiceName = notNull("srvServiceName", srvServiceName); return this; } /** * Sets the hosts for the cluster. Any duplicate server addresses are removed from the list. * * @param hosts the seed list of hosts * @return this */ public Builder hosts(final List hosts) { notNull("hosts", hosts); if (hosts.isEmpty()) { throw new IllegalArgumentException("hosts list may not be empty"); } if (srvHost != null) { throw new IllegalArgumentException("srvHost must be null"); } Set hostsSet = new LinkedHashSet<>(hosts.size()); for (ServerAddress serverAddress : hosts) { notNull("serverAddress", serverAddress); hostsSet.add(createServerAddress(serverAddress.getHost(), serverAddress.getPort())); } this.hosts = unmodifiableList(new ArrayList<>(hostsSet)); return this; } /** * Sets the mode for this cluster. * * @param mode the cluster connection mode * @return this; */ public Builder mode(final ClusterConnectionMode mode) { this.mode = notNull("mode", mode); return this; } /** * Sets the required replica set name for the cluster. * * @param requiredReplicaSetName the required replica set name. * @return this * @see #getRequiredReplicaSetName() */ public Builder requiredReplicaSetName(@Nullable final String requiredReplicaSetName) { this.requiredReplicaSetName = requiredReplicaSetName; return this; } /** * Sets the required cluster type for the cluster. * * @param requiredClusterType the required cluster type * @return this */ public Builder requiredClusterType(final ClusterType requiredClusterType) { this.requiredClusterType = notNull("requiredClusterType", requiredClusterType); return this; } /** * Sets the local threshold. * * @param localThreshold the acceptable latency difference, in milliseconds, which must be >= 0 * @param timeUnit the time unit * @throws IllegalArgumentException if {@code localThreshold < 0} * @return this * @since 3.7 */ public Builder localThreshold(final long localThreshold, final TimeUnit timeUnit) { isTrueArgument("localThreshold must be >= 0", localThreshold >= 0); this.localThresholdMS = MILLISECONDS.convert(localThreshold, timeUnit); return this; } /** * Adds a server selector for the cluster to apply before selecting a server. * * @param serverSelector the server selector to apply as selector. * @return this * @see #getServerSelector() */ public Builder serverSelector(final ServerSelector serverSelector) { this.serverSelector = serverSelector; return this; } /** * Sets the timeout to apply when selecting a server. If the timeout expires before a server is found to handle a request, a * {@link com.mongodb.MongoTimeoutException} will be thrown. The default value is 30 seconds. * *

A value of 0 means that it will timeout immediately if no server is available. A negative value means to wait * indefinitely.

* * @param serverSelectionTimeout the timeout * @param timeUnit the time unit * @return this */ public Builder serverSelectionTimeout(final long serverSelectionTimeout, final TimeUnit timeUnit) { this.serverSelectionTimeoutMS = MILLISECONDS.convert(serverSelectionTimeout, timeUnit); return this; } /** * Adds a cluster listener. * * @param clusterListener the non-null cluster listener * @return this * @since 3.3 */ public Builder addClusterListener(final ClusterListener clusterListener) { notNull("clusterListener", clusterListener); clusterListeners.add(clusterListener); return this; } /** * Sets the cluster listeners. * * @param clusterListeners list of cluster listeners * @return this * @since 4.5 */ public Builder clusterListenerList(final List clusterListeners) { notNull("clusterListeners", clusterListeners); this.clusterListeners = new ArrayList<>(clusterListeners); return this; } /** * Takes the settings from the given {@code ConnectionString} and applies them to the builder * * @param connectionString the connection string containing details of how to connect to MongoDB * @return this */ public Builder applyConnectionString(final ConnectionString connectionString) { Boolean directConnection = connectionString.isDirectConnection(); Boolean loadBalanced = connectionString.isLoadBalanced(); if (loadBalanced != null && loadBalanced) { mode(ClusterConnectionMode.LOAD_BALANCED); if (connectionString.isSrvProtocol()) { srvHost(connectionString.getHosts().get(0)); } else { hosts(singletonList(createServerAddress(connectionString.getHosts().get(0)))); } } else if (connectionString.isSrvProtocol()) { mode(ClusterConnectionMode.MULTIPLE); srvHost(connectionString.getHosts().get(0)); Integer srvMaxHosts = connectionString.getSrvMaxHosts(); if (srvMaxHosts != null) { srvMaxHosts(srvMaxHosts); } String srvServiceName = connectionString.getSrvServiceName(); if (srvServiceName != null) { srvServiceName(srvServiceName); } } else if ((directConnection != null && directConnection) || (directConnection == null && connectionString.getHosts().size() == 1 && connectionString.getRequiredReplicaSetName() == null)) { mode(ClusterConnectionMode.SINGLE) .hosts(singletonList(createServerAddress(connectionString.getHosts().get(0)))); } else { List seedList = connectionString.getHosts().stream() .map(ServerAddressHelper::createServerAddress) .collect(Collectors.toList()); mode(ClusterConnectionMode.MULTIPLE).hosts(seedList); } requiredReplicaSetName(connectionString.getRequiredReplicaSetName()); Integer serverSelectionTimeout = connectionString.getServerSelectionTimeout(); if (serverSelectionTimeout != null) { serverSelectionTimeout(serverSelectionTimeout, MILLISECONDS); } Integer localThreshold = connectionString.getLocalThreshold(); if (localThreshold != null) { localThreshold(localThreshold, MILLISECONDS); } return this; } /** * Build the settings from the builder. * * @return the cluster settings */ public ClusterSettings build() { return new ClusterSettings(this); } } /** * Gets the host name from which to lookup SRV record for the seed list * @return the SRV host, or null if none specified * @since 3.10 */ @Nullable public String getSrvHost() { return srvHost; } /** * Gets the maximum number of hosts to connect to when using SRV protocol. * * @return the maximum number of hosts to connect to when using SRV protocol. Defaults to null. * @since 4.4 */ @Nullable public Integer getSrvMaxHosts() { return srvMaxHosts; } /** * Gets the SRV service name. * *

* The SRV resource record (RFC 2782) * service name, which is limited to 15 characters * (RFC 6335 section 5.1). * If specified, it is combined with the single host name specified by * {@link #getHosts()} as follows: {@code _srvServiceName._tcp.hostName}. The combined string is an SRV resource record * name (RFC 1035 section 2.3.1), which is limited to 255 * characters (RFC 1035 section 2.3.4). *

* * @return the SRV service name, which defaults to {@code "mongodb"} * @since 4.5 */ public String getSrvServiceName() { return srvServiceName; } /** * Gets the seed list of hosts for the cluster. * * @return the seed list of hosts */ public List getHosts() { return hosts; } /** * Gets the mode. * * @return the mode */ public ClusterConnectionMode getMode() { return mode; } /** * Gets the required cluster type * * @return the required cluster type */ public ClusterType getRequiredClusterType() { return requiredClusterType; } /** * Gets the required replica set name. * * @return the required replica set name * @see Builder#requiredReplicaSetName(String) */ @Nullable public String getRequiredReplicaSetName() { return requiredReplicaSetName; } /** * Gets the server selector. * *

The server selector augments the normal server selection rules applied by the driver when determining * which server to send an operation to. At the point that it's called by the driver, the * {@link com.mongodb.connection.ClusterDescription} which is passed to it contains a list of * {@link com.mongodb.connection.ServerDescription} instances which satisfy either the configured {@link com.mongodb.ReadPreference} * for any read operation or ones that can take writes (e.g. a standalone, mongos, or replica set primary). *

*

The server selector can then filter the {@code ServerDescription} list using whatever criteria that is required by the * application.

*

After this selector executes, two additional selectors are applied by the driver:

*
    *
  • select from within the latency window
  • *
  • select a random server from those remaining
  • *
*

To skip the latency window selector, an application can:

*
    *
  • configure the local threshold to a sufficiently high value so that it doesn't exclude any servers
  • *
  • return a list containing a single server from this selector (which will also make the random member selector a no-op)
  • *
* * @return the server selector, which may be null */ @Nullable public ServerSelector getServerSelector() { return serverSelector; } /** * Gets the timeout to apply when selecting a server. If the timeout expires before a server is found to * handle a request, a {@link com.mongodb.MongoTimeoutException} will be thrown. The default value is 30 seconds. * *

A value of 0 means that it will timeout immediately if no server is available. A negative value means to wait * indefinitely.

* * @param timeUnit the time unit * @return the timeout in the given time unit */ public long getServerSelectionTimeout(final TimeUnit timeUnit) { return timeUnit.convert(serverSelectionTimeoutMS, MILLISECONDS); } /** * Gets the local threshold. When choosing among multiple MongoDB servers to send a request, the MongoClient will only * send that request to a server whose ping time is less than or equal to the server with the fastest ping time plus the local * threshold. * *

For example, let's say that the client is choosing a server to send a query when the read preference is {@code * ReadPreference.secondary()}, and that there are three secondaries, server1, server2, and server3, whose ping times are 10, 15, and 16 * milliseconds, respectively. With a local threshold of 5 milliseconds, the client will send the query to either * server1 or server2 (randomly selecting between the two). *

* *

Default is 15 milliseconds.

* * @param timeUnit the time unit * @return the local threshold in the given timeunit. * @since 3.7 * @mongodb.driver.manual reference/program/mongos/#cmdoption--localThreshold Local Threshold */ public long getLocalThreshold(final TimeUnit timeUnit) { return timeUnit.convert(localThresholdMS, MILLISECONDS); } /** * Gets the cluster listeners. The default value is an empty list. * * @return the cluster listeners * @since 3.3 */ public List getClusterListeners() { return clusterListeners; } @Override public boolean equals(final Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } ClusterSettings that = (ClusterSettings) o; return localThresholdMS == that.localThresholdMS && serverSelectionTimeoutMS == that.serverSelectionTimeoutMS && Objects.equals(srvHost, that.srvHost) && Objects.equals(srvMaxHosts, that.srvMaxHosts) && srvServiceName.equals(that.srvServiceName) && hosts.equals(that.hosts) && mode == that.mode && requiredClusterType == that.requiredClusterType && Objects.equals(requiredReplicaSetName, that.requiredReplicaSetName) && Objects.equals(serverSelector, that.serverSelector) && clusterListeners.equals(that.clusterListeners); } @Override public int hashCode() { return Objects.hash(srvHost, srvMaxHosts, srvServiceName, hosts, mode, requiredClusterType, requiredReplicaSetName, serverSelector, localThresholdMS, serverSelectionTimeoutMS, clusterListeners); } @Override public String toString() { return "{" + (hosts.isEmpty() ? "" : "hosts=" + hosts) + (srvHost == null ? "" : ", srvHost=" + srvHost) + (srvServiceName == null ? "" : ", srvServiceName=" + srvServiceName) + (srvMaxHosts == null ? "" : ", srvMaxHosts=" + srvMaxHosts) + ", mode=" + mode + ", requiredClusterType=" + requiredClusterType + ", requiredReplicaSetName='" + requiredReplicaSetName + '\'' + ", serverSelector='" + serverSelector + '\'' + ", clusterListeners='" + clusterListeners + '\'' + ", serverSelectionTimeout='" + serverSelectionTimeoutMS + " ms" + '\'' + ", localThreshold='" + localThresholdMS + " ms" + '\'' + '}'; } /** * Returns a short, pretty description for these ClusterSettings. * * @return a String description of the relevant settings. */ public String getShortDescription() { return "{" + (hosts.isEmpty() ? "" : "hosts=" + hosts) + (srvHost == null ? "" : ", srvHost=" + srvHost) + ", mode=" + mode + ", requiredClusterType=" + requiredClusterType + ", serverSelectionTimeout='" + serverSelectionTimeoutMS + " ms" + '\'' + (requiredReplicaSetName == null ? "" : ", requiredReplicaSetName='" + requiredReplicaSetName + '\'') + '}'; } private ClusterSettings(final Builder builder) { if (builder.srvHost != null) { if (builder.srvHost.contains(":")) { throw new IllegalArgumentException("The srvHost can not contain a host name that specifies a port"); } if (builder.srvHost.split("\\.").length < 3) { throw new IllegalArgumentException(format("An SRV host name '%s' was provided that does not contain at least three parts. " + "It must contain a hostname, domain name and a top level domain.", builder.srvHost)); } } if (builder.hosts.size() > 1 && builder.requiredClusterType == ClusterType.STANDALONE) { throw new IllegalArgumentException("Multiple hosts cannot be specified when using ClusterType.STANDALONE."); } if (builder.requiredReplicaSetName != null) { if (builder.requiredClusterType == ClusterType.UNKNOWN) { builder.requiredClusterType = ClusterType.REPLICA_SET; } else if (builder.requiredClusterType != ClusterType.REPLICA_SET) { throw new IllegalArgumentException("When specifying a replica set name, only ClusterType.UNKNOWN and " + "ClusterType.REPLICA_SET are valid."); } } if (builder.mode == ClusterConnectionMode.LOAD_BALANCED && builder.srvHost == null && builder.hosts.size() != 1) { throw new IllegalArgumentException("Multiple hosts cannot be specified when in load balancing mode"); } srvHost = builder.srvHost; srvMaxHosts = builder.srvMaxHosts; srvServiceName = builder.srvServiceName; hosts = builder.hosts; if (srvHost != null) { if (builder.mode == ClusterConnectionMode.SINGLE) { throw new IllegalArgumentException("An SRV host name was provided but the connection mode is not MULTIPLE"); } mode = builder.mode != null ? builder.mode : ClusterConnectionMode.MULTIPLE; } else { if (builder.mode == ClusterConnectionMode.SINGLE && builder.hosts.size() > 1) { throw new IllegalArgumentException("Can not directly connect to more than one server"); } mode = builder.mode != null ? builder.mode : hosts.size() == 1 ? ClusterConnectionMode.SINGLE : ClusterConnectionMode.MULTIPLE; } requiredReplicaSetName = builder.requiredReplicaSetName; requiredClusterType = builder.requiredClusterType; localThresholdMS = builder.localThresholdMS; serverSelector = builder.serverSelector; serverSelectionTimeoutMS = builder.serverSelectionTimeoutMS; clusterListeners = unmodifiableList(builder.clusterListeners); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy