Skip to content

Commit 0cf7163

Browse files
committed
BUG31315173: Added documentation for multi-host and failover
Routers or multi-host and failover in mysqlx lacks of documentation his patch adds some example of use.
1 parent bf900b2 commit 0cf7163

File tree

2 files changed

+229
-0
lines changed

2 files changed

+229
-0
lines changed

docs/mysqlx/tutorials.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Tutorials
66

77
tutorials/getting_started
88
tutorials/collections
9+
tutorials/connection_routers
910
tutorials/connection_pooling
1011
tutorials/transactions
1112
tutorials/creating_indexes
Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
Connection Routers
2+
==================
3+
4+
*Connection Routers* is a technique used for connecting to one of multiple hosts using connection failover, which attempts to connect to the next endpoint if the current endpoint is not available before raising an error. For this technique, define multiple hosts by specifying a URI-like string containing multiple hosts, ports, and an optional priority or using the ``routers`` option when invoking :func:`mysqlx.get_client()`.
5+
6+
This technique enables the connector to perform automatic connection failover selection when an endpoint are not available. When multiple endpoints are available, the chosen server used for the session depends on the ``priority`` option. If a priority is set, it must be defined for each endpoint. The available endpoint with the highest :data:`priority` is prioritized first. If not specified, a randomly available endpoint is used.
7+
8+
Here's an example of how to specify multiple hosts `URI-like` :data:`string` and without priority when calling the :func:`mysqlx.get_client()`. The URI-like connection string is formatted as:
9+
10+
.. code-block:: python
11+
12+
import mysqlx
13+
14+
connection_str = 'mysqlx://root:@[(address=unreachable_host),(address=127.0.0.1:33060)]?connect-timeout=2000'
15+
options_string = '{}' # An empty document
16+
17+
client = mysqlx.get_client(connection_str, options_string)
18+
session = client.get_session()
19+
20+
# (...)
21+
22+
session.close()
23+
client.close()
24+
25+
26+
The multiple hosts can also be given as a :data:`list` with the ``routers`` in the connection settings:
27+
28+
.. code-block:: python
29+
30+
import mysqlx
31+
32+
routers = [
33+
{"host": "unreachable_host"}, # default port is 33060
34+
{"host": "127.0.0.1", "port": 33060},
35+
]
36+
37+
connection_dict = {
38+
'routers': routers,
39+
'port': 33060,
40+
'user': 'mike',
41+
'password': 's3cr3t!'
42+
'connect_timeout': 2000,
43+
}
44+
options_dict = {} # empty dict object
45+
46+
client = mysqlx.get_client(connection_dict, options_dict)
47+
session = client.get_session()
48+
49+
# (...)
50+
51+
session.close()
52+
client.close()
53+
54+
55+
The above examples have two hosts but many more hosts and ports can be defined, and it's important to understand that the supplied MySQL user and password supplied (in either the URI-like string or in the user and password options) applies to all of the possible endpoints. Therefore the same MySQL account must exist on each of the endpoints.
56+
57+
.. note:: Because of the failover, a connection attempt for establishing a connection on all the given hosts will occur before an error is raised, so using the ``connect_timeout`` option is recommended when a large number of hosts could be down. The order for the connection attempts occur randomly unless the ``priority`` option is defined.
58+
59+
.. note:: The ``connect_timeout`` option's value must be a positive integer.
60+
61+
Specifying multiple hosts with a priority in the `URI-like` :data:`string` is formatted as such:
62+
63+
.. code-block:: python
64+
65+
import mysqlx
66+
67+
connection_str = 'mysqlx://root:@[(address=unreachable_host, priority=100),(address=127.0.0.1:33060, priority=90)]?connect-timeout=2000'
68+
options_string = '{}' # An empty dictionary object
69+
70+
client = mysqlx.get_client(connection_str, options_string)
71+
session = client.get_session()
72+
73+
# (...)
74+
75+
session.close()
76+
client.close()
77+
78+
79+
Specifying multiple hosts with a priority in the connection settings is formatted as such:
80+
81+
.. code-block:: python
82+
83+
import mysqlx
84+
85+
routers = [{"host": "unreachable_host", "priority": 100}, # default port is 33060
86+
{"host": "127.0.0.1", "port": 33060, "priority": 90}
87+
]
88+
89+
connection_dict = {
90+
'routers': routers,
91+
'port': 33060,
92+
'user': 'mike',
93+
'password': 's3cr3t!',
94+
'connect_timeout': 2000
95+
}
96+
options_dict = {}
97+
98+
client = mysqlx.get_client(connection_dict, options_dict)
99+
session = client.get_session()
100+
101+
# (...)
102+
103+
session.close()
104+
client.close()
105+
106+
.. note:: Valid values for the ``priority`` option are values :data:`1` to :data:`100``, where 100 is the highest priority value. Priority determines the connection order with highest priority value being first. If priority is given for one host, then a priority value must be given for all the hosts.
107+
108+
The Routers technique can be combined with the pooling technique by passing a pooling configuration for :class:`mysqlx.Client`. Set the pooling options by passing a :data:`dict` or a JSON document string in the second parameter.
109+
110+
The following example uses the same MySQL as in previous examples, but with different hostnames to emulate two other servers, and the ``options_dict`` is a dictionary with the settings for each pool. Notice that with ``max_size`` option set to 5, we can get up to 10 sessions because a connection pool is created for each server with 5 connections.
111+
112+
.. code-block:: python
113+
114+
import mysqlx
115+
116+
routers = [{"host": "localhost", "priority": 100}, # default port is 33060
117+
{"host": "127.0.0.1", "port": 33060, "priority": 90}
118+
]
119+
120+
connection_dict = {
121+
'routers': routers,
122+
'port': 33060,
123+
'user': 'root',
124+
'password': '',
125+
'connect_timeout':2000
126+
}
127+
128+
options_dict = {'pooling':{'max_size': 5, 'queue_timeout': 1000}}
129+
130+
client = mysqlx.get_client(connection_dict, options_dict)
131+
132+
# We can get 5 sessions from each pool.
133+
134+
for n in range(5):
135+
print(f"session: {n}")
136+
session = client.get_session()
137+
res = session.sql("select connection_id()").execute().fetch_all()
138+
for row in res:
139+
print(f"connection id: {row[0]}")
140+
141+
for n in range(5):
142+
print(f"session: {n}")
143+
session = client.get_session()
144+
res = session.sql("select connection_id()").execute().fetch_all()
145+
for row in res:
146+
print(f"connection id: {row[0]}")
147+
148+
149+
The output:
150+
151+
.. code-block:: python
152+
153+
session: 0
154+
connection id: 603
155+
session: 1
156+
connection id: 604
157+
session: 2
158+
connection id: 605
159+
session: 3
160+
connection id: 606
161+
session: 4
162+
connection id: 607
163+
session: 0
164+
connection id: 608
165+
session: 1
166+
connection id: 609
167+
session: 2
168+
connection id: 610
169+
session: 3
170+
connection id: 611
171+
session: 4
172+
connection id: 612
173+
174+
175+
The following example shows using Multi-host and failover while using a pool. In this example, the “unreachable_host” has higher priority than the second host :data:`"127.0.0.1"`, so the connection is attempted to :data:`"unreachable_host"` first but it will fail. However, this does not raise an error and the connection attempt to the host :data:`"127.0.0.1"` that's available will succeed. However, an error is raised when the pool is maxed out.
176+
177+
.. code-block:: python
178+
179+
import mysqlx
180+
181+
routers = [{"host": "unreachable_host", "priority": 100},
182+
{"host": "127.0.0.1", "port": 33060, "priority": 90}
183+
]
184+
185+
connection_dict = {
186+
'routers': routers,
187+
'port': 33060,
188+
'user': 'mike',
189+
'password': 's3cr3t!',
190+
'connect_timeout': 2000
191+
}
192+
193+
options_dict = {'pooling':{'max_size': 5, 'queue_timeout': 1000}}
194+
195+
client = mysqlx.get_client(connection_dict, options_dict)
196+
197+
for n in range(5):
198+
print(f"session: {n}")
199+
session = client.get_session()
200+
res = session.sql("select connection_id()").execute().fetch_all()
201+
for row in res:
202+
print(f"connection id: {row[0]}")
203+
204+
# Since the "unreachable_host" is unavailable and the max_size option for
205+
# the pools is set to 5, we can only get 5 sessions prior to get an error.
206+
# By requiring another session a mysqlx.errors.PoolError error is raised.
207+
client.get_session() # This line raises an PoolError
208+
209+
210+
The code above will give an output similar to the following:
211+
212+
.. code-block:: python
213+
214+
session: 0
215+
connection id: 577
216+
session: 1
217+
connection id: 578
218+
session: 2
219+
connection id: 579
220+
session: 3
221+
connection id: 580
222+
session: 4
223+
connection id: 581
224+
225+
mysqlx.errors.PoolError: Unable to connect to any of the target hosts: [
226+
pool: 127.0.0.1_33060_... error: pool max size has been reached
227+
pool: unreachable_host_33060_... error: Cannot connect to host: [Errno 11001] getaddrinfo failed
228+
]

0 commit comments

Comments
 (0)