diff --git a/source/connect/connection-options/server-selection.txt b/source/connect/connection-options/server-selection.txt index 57772394..2e32a90e 100644 --- a/source/connect/connection-options/server-selection.txt +++ b/source/connect/connection-options/server-selection.txt @@ -1,68 +1,195 @@ -.. TODO: Server Selection page - -Why Does the Driver Throw a Timeout During Server Selection? ------------------------------------------------------------- - -Each driver operation requires that you choose a healthy server -satisfying the :manual:`server selection criteria -`. If you do not select an appropriate -server within the `server selection timeout <{+new-api-root+}/MongoDB.Driver.Legacy/MongoDB.Driver.MongoServerSettings.ServerSelectionTimeout.html>`__, the driver throws a -server selection timeout exception. The exception looks similar to the -following: - -.. code-block:: none - - A timeout occurred after 30000ms selecting a server using CompositeServerSelector{ Selectors = MongoDB.Driver.MongoClient+AreSessionsSupportedServerSelector, LatencyLimitingServerSelector{ AllowedLatencyRange = 00:00:00.0150000 }, OperationsCountServerSelector }. - Client view of cluster state is - { - ClusterId : "1", - Type : "Unknown", - State : "Disconnected", - Servers : - [{ - ServerId: "{ ClusterId : 1, EndPoint : "Unspecified/localhost:27017" }", - EndPoint: "Unspecified/localhost:27017", - ReasonChanged: "Heartbeat", - State: "Disconnected", - ServerVersion: , - TopologyVersion: , - Type: "Unknown", - HeartbeatException: "" - }] - }. - -The error message consists of multiple parts: - -1. The server selection timeout (30000 ms). -#. The server selectors considered (``CompositeServerSelector`` - containing ``AreSessionsSupportedServerSelector``, - ``LatencyLimitingServerSelector``, and - ``OperationsCountServerSelector``). -#. The driver’s current view of the cluster topology. The list of - servers that the driver is aware of is a key part of this view. Each - server description contains an exhaustive description of its current - state including information about an endpoint, a server version, a - server type, and its current health state. If the server encounters issues in - reporting its health, ``HeartbeatException`` contains the exception from the - last failed heartbeat. Analyzing the ``HeartbeatException`` on each - cluster node can assist in diagnosing most server selection issues. - The following heartbeat exceptions are common: - - * ``No connection could be made because the target machine actively - refused it``: The driver cannot see this cluster node. This can be - because the cluster node has crashed, a firewall is preventing - network traffic from reaching the cluster node or port, or some other - network error is preventing traffic from being successfully routed to - the cluster node. - * ``Attempted to read past the end of the stream``: This error - happens when the driver cannot connect to the cluster nodes due to a - network error, misconfigured firewall, or other network issue. To - address this exception, ensure that all cluster nodes are reachable. - This error commonly occurs when the client machine’s IP address is - not configured in the Atlas IPs Access List, which can be found under - the :guilabel:`Network Access` tab for your Atlas Project. - * ``The remote certificate is invalid according to the validation - procedure``: This error typically indicates a TLS/SSL-related problem - such as an expired/invalid certificate or an untrusted root CA. You - can use tools like ``openssl s_client`` to debug TLS/SSL-related - certificate problems. \ No newline at end of file +.. _csharp-server-selection: + +========================== +Customize Server Selection +========================== + +.. contents:: On this page + :local: + :backlinks: none + :depth: 1 + :class: singlecol + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: code example, read preference, write + +Overview +-------- + +All MongoDB drivers follow a defined algorithm when selecting a server to read or write +from. By using the ``ClusterConfigurator`` property of a ``MongoClient`` object, you can +customize this algorithm to choose the server that works best for your application. + +.. important:: + + Customizing the server selection algorithm might have unintended consequences, + such as degraded read or write performance. + +Default Algorithm +----------------- + +When the {+driver-short+} executes a read operation, it performs the following steps, +in order, to select a MongoDB deployment: + +1. Selects all servers that match the active read preference from the list of known servers. + +#. If at least one readable server exists, the driver calls the user-defined + server-selector function and passes in the list from the previous step. + +#. Applies the ``LocalThreshold`` connection setting to the list of + servers returned from the function. + +#. Selects a server at random from the servers still on the list and + executes the operation against this server. + +When the {+driver-short+} executes a write operation, it begins by selecting all writeable +servers, not just those that match the active read preference. The remaining steps are +identical. + +To learn more about the default server selection algorithm, which the driver follows +when you don't specify any custom server selection logic, see +:manual:`Server Selection Algorithm ` in the +{+mdb-server+} manual. + +.. _csharp-server-selection-algorithm: + +Specifying Other Server Selection Algorithms +-------------------------------------------- + +You can specify different server selection logic by passing an instance of a server selector +class to the ``PreServerSelector`` or ``PostServerSelector`` property of the ``ClusterConfigurator``. The +``PreServerSelector`` property specifies a server selector that runs before the +standard server selection logic runs, while the ``PostServerSelector`` property +specifies a server selector that runs after standard server selection logic runs. You can +then pass your ``ClusterConfigurator`` instance to the ``MongoClientSettings`` object when you create a +``MongoClient`` instance to apply your custom server selection logic. + +The following table lists the different types of server selectors you can pass to the +``ClusterConfigurator`` property: + +.. list-table:: + :header-rows: 1 + :widths: 40 60 + + * - Server Selector + - Description + + * - ``CompositeServerSelector`` + - Selects servers based on multiple partial selectors + + * - ``DelegateServerSelector`` + - Wraps a delegate server selector + + * - ``EndPointServerSelector`` + - Selects servers based on their endpoint + + * - ``LatencyLimitingServerSelector`` + - Selects servers within an acceptable latency range + + * - ``PriorityServerSelector`` + - Selects a server based on a collection of servers to deprioritize + + * - ``RandomServerSelector`` + - Selects a random server + + * - ``ReadPreferenceServerSelector`` + - Selects servers based on a specified read preference + +The following example instructs a ``MongoClient`` to use the ``RandomServerSelector`` +class to select a server at random before the standard server selection logic runs: + +.. literalinclude:: /includes/fundamentals/code-examples/connection/ServerSelection.cs + :start-after: start-server-selector + :end-before: end-server-selector + :language: csharp + :dedent: + +To learn more about the different server selector classes, see the +`ServerSelectors API documentation <{+new-api-root+}/MongoDB.Driver/MongoDB.Driver.Core.Clusters.ServerSelectors.html>`__. + +Implementing Custom Server Selection Logic +------------------------------------------ + +You can implement your own custom server selection logic by creating a class that +inherits from the ``IServerSelector`` interface and overrides the +``SelectServers()`` method. The following example shows a simple custom server +selection class that selects servers with a ``ServerType`` of +``ServerType.ReplicaSetSecondary``: + +.. literalinclude:: /includes/fundamentals/code-examples/connection/ServerSelection.cs + :start-after: start-custom-class + :end-before: end-custom-class + :language: csharp + :dedent: + +You can then pass an instance of this class to the ``PreServerSelector`` or +``PostServerSelector`` property of a ``ClusterConfigurator`` instance, as shown in the +:ref:`csharp-server-selection-algorithm` section. + +Using Settings to Configure Server Selection +-------------------------------------------- + +You can specify the following server selection settings in your ``MongoClient`` object or +in your connection URI: + +.. list-table:: + :widths: 30 70 + :header-rows: 1 + + * - Setting + - Description + + * - ``LocalThreshold`` + - | The latency window for server eligibility. If a server's round trip takes longer + | than the fastest server's round-trip time plus this value, the server isn't + | eligible for selection. + | + | **Data Type**: ``TimeSpan`` + | **Default**: 15 milliseconds + | **Connection URI Example**: ``localThresholdMS=0`` + + * - ``ReadPreference`` + - | The client's default read-preference settings. ``MaxStaleness`` represents the + | longest replication lag (in real time) that a secondary can experience and + | still be eligible for server selection. Specifying ``-1`` means no maximum. + | See :ref:`read preference ` for more information. + | + | **Data Type**: `ReadPreference <{+new-api-root+}/MongoDB.Driver/MongoDB.Driver.ReadPreference.html>`__ + | **Default**: ``ReadPreference.Primary`` + | **Connection URI Example**: + + .. code-block:: none + :copyable: false + + readPreference=primaryPreferred + &maxStalenessSeconds=90 + &readPreferenceTags=dc:ny,rack:1 + + * - ``ServerSelectionTimeout`` + - | The length of time the driver tries to select a server before timing out. + | + | **Data Type**: ``TimeSpan`` + | **Default**: 30 seconds + | **Connection URI Example**: ``serverSelectionTimeoutMS=15000`` + +Troubleshooting +--------------- + +.. include:: /includes/troubleshooting/server-selection-timeout.rst + +API Documentation +----------------- + +To learn more about the classes and methods used in this guide, see the following API +documentation: + +- `MongoClient <{+new-api-root+}/MongoDB.Driver/MongoDB.Driver.MongoClient.html>`__ +- `MongoClientSettings <{+new-api-root+}/MongoDB.Driver/MongoDB.Driver.MongoClientSettings.html>`__ +- `ClusterConfigurator <{+new-api-root+}/MongoDB.Driver/MongoDB.Driver.MongoClientSettings.ClusterConfigurator.html>`__ +- `ServerSelectors <{+new-api-root+}/MongoDB.Driver/MongoDB.Driver.Core.Clusters.ServerSelectors.html>`__ +- `IServerSelector <{+new-api-root+}/MongoDB.Driver/MongoDB.Driver.Core.Clusters.ServerSelectors.IServerSelector.html>`__ +- `SelectServer() <{+new-api-root+}/MongoDB.Driver/MongoDB.Driver.Core.Clusters.ServerSelectors.IServerSelector.SelectServers.html>`__ \ No newline at end of file diff --git a/source/includes/fundamentals/code-examples/connection/ServerSelection.cs b/source/includes/fundamentals/code-examples/connection/ServerSelection.cs new file mode 100644 index 00000000..eaaabe17 --- /dev/null +++ b/source/includes/fundamentals/code-examples/connection/ServerSelection.cs @@ -0,0 +1,38 @@ +using MongoDB.Driver; +using MongoDB.Driver.Core.Clusters; +using MongoDB.Driver.Core.Clusters.ServerSelectors; +using MongoDB.Driver.Core.Servers; + +public class ServerSelection +{ + // Replace with your connection string + private const string MongoConnectionString = ""; + + public static void Main(string[] args) + { + { + // start-server-selector + var settings = MongoClientSettings.FromConnectionString(""); + var clusterConfigurator = builder => + { + builder.ConfigureCluster(c => + c.With(PreServerSelector: new RandomServerSelector())); + }; + + settings.ClusterConfigurator = clusterConfigurator; + var client = new MongoClient(settings); + // end-server-selector + } + } +} + +// start-custom-class +public class CustomServerSelector : IServerSelector +{ + public IEnumerable SelectServers(ClusterDescription cluster, + IEnumerable servers) + { + return servers.Where(server => server.Type == ServerType.ReplicaSetSecondary); + } +} +// end-custom-class diff --git a/source/includes/troubleshooting/server-selection-timeout.rst b/source/includes/troubleshooting/server-selection-timeout.rst new file mode 100644 index 00000000..1f1b67f9 --- /dev/null +++ b/source/includes/troubleshooting/server-selection-timeout.rst @@ -0,0 +1,66 @@ +Driver Throws a Timeout During Server Selection +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Each driver operation requires that you choose a server that +satisfies the :manual:`server selection criteria +`. If you do not select an appropriate +server within the `server selection timeout <{+new-api-root+}/MongoDB.Driver/MongoDB.Driver.MongoClientSettings.ServerSelectionTimeout.html>`__, the driver throws a +server selection timeout exception. The exception looks similar to the +following: + +.. code-block:: none + + A timeout occurred after 30000ms selecting a server using CompositeServerSelector{ Selectors = MongoDB.Driver.MongoClient+AreSessionsSupportedServerSelector, LatencyLimitingServerSelector{ AllowedLatencyRange = 00:00:00.0150000 }, OperationsCountServerSelector }. + Client view of cluster state is + { + ClusterId : "1", + Type : "Unknown", + State : "Disconnected", + Servers : + [{ + ServerId: "{ ClusterId : 1, EndPoint : "Unspecified/localhost:27017" }", + EndPoint: "Unspecified/localhost:27017", + ReasonChanged: "Heartbeat", + State: "Disconnected", + ServerVersion: , + TopologyVersion: , + Type: "Unknown", + HeartbeatException: "" + }] + }. + +The error message consists of multiple parts: + +1. The server selection timeout (30000 ms). +#. The server selectors considered (``CompositeServerSelector`` + containing ``AreSessionsSupportedServerSelector``, + ``LatencyLimitingServerSelector``, and + ``OperationsCountServerSelector``). +#. The driver’s current view of the cluster topology. The list of + servers that the driver is aware of is a key part of this view. Each + server description contains an exhaustive description of its current + state including information about an endpoint, a server version, a + server type, and its current health state. If the server encounters issues in + reporting its health, ``HeartbeatException`` contains the exception from the + last failed heartbeat. Analyzing the ``HeartbeatException`` on each + cluster node can assist in diagnosing most server selection issues. + The following heartbeat exceptions are common: + + * ``No connection could be made because the target machine actively + refused it``: The driver cannot see this cluster node. This might be + because the cluster node has crashed, a firewall is preventing + network traffic from reaching the cluster node or port, or some other + network error is preventing traffic from being successfully routed to + the cluster node. + * ``Attempted to read past the end of the stream``: This error + happens when the driver cannot connect to the cluster nodes due to a + network error, misconfigured firewall, or other network issue. To + address this exception, ensure that all cluster nodes are reachable. + This error commonly occurs when the client machine’s IP address is + not configured in the Atlas IPs Access List, which you can find under + the :guilabel:`Network Access` tab for your Atlas Project. + * ``The remote certificate is invalid according to the validation + procedure``: This error typically indicates a TLS/SSL-related problem + such as an expired/invalid certificate or an untrusted root CA. You + can use tools like ``openssl s_client`` to debug TLS/SSL-related + certificate problems.