|
| 1 | +// |
| 2 | +// Copyright (c) 2025 Marcelo Zimbres Silva (mzimbres@gmail.com), |
| 3 | +// Ruben Perez Hidalgo (rubenperez038 at gmail dot com) |
| 4 | +// |
| 5 | +// Distributed under the Boost Software License, Version 1.0. (See accompanying |
| 6 | +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) |
| 7 | +// |
| 8 | + |
| 9 | += Sentinel |
| 10 | + |
| 11 | +Boost.Redis supports Redis Sentinel deployments. Sentinel handling |
| 12 | +in `connection` is built-in: xref:reference:boost/redis/basic_connection/async_run-04.adoc[`async_run`] |
| 13 | +automatically connects to Sentinels, resolves the master's address, and connects to the master. |
| 14 | + |
| 15 | +Configuration is done using xref:reference:boost/redis/sentinel_config.adoc[`config::sentinel`]: |
| 16 | + |
| 17 | +[source,cpp] |
| 18 | +---- |
| 19 | +config cfg; |
| 20 | +
|
| 21 | +// To enable Sentinel, set this field to a non-empty list |
| 22 | +// of (hostname, port) pairs where Sentinels are listening |
| 23 | +cfg.sentinel.addresses = { |
| 24 | + {"sentinel1.example.com", "26379"}, |
| 25 | + {"sentinel2.example.com", "26379"}, |
| 26 | + {"sentinel3.example.com", "26379"}, |
| 27 | +}; |
| 28 | +
|
| 29 | +// Set master_name to the identifier that you configured |
| 30 | +// in the "sentinel monitor" statement of your sentinel.conf file |
| 31 | +cfg.sentinel.master_name = "mymaster"; |
| 32 | +---- |
| 33 | + |
| 34 | +Once set, the connection object can be used normally. See our |
| 35 | +our {site-url}/example/cpp20_sentinel.cpp[Sentinel example] |
| 36 | +for a full program. |
| 37 | + |
| 38 | +== Connecting to replicas |
| 39 | + |
| 40 | +By default, the library connects to the Redis master. |
| 41 | +You can connect to one of its replicas by using |
| 42 | +xref:reference:boost/redis/sentinel_config/server_role.adoc[`config::sentinel::server_role`]. |
| 43 | +This can be used to balance load, if all your commands read data from |
| 44 | +the server and never write to it. The particular replica will be chosen randomly. |
| 45 | + |
| 46 | +[source,cpp] |
| 47 | +---- |
| 48 | +config cfg; |
| 49 | +
|
| 50 | +// Set up Sentinel |
| 51 | +cfg.sentinel.addresses = { |
| 52 | + {"sentinel1.example.com", "26379"}, |
| 53 | + {"sentinel2.example.com", "26379"}, |
| 54 | + {"sentinel3.example.com", "26379"}, |
| 55 | +}; |
| 56 | +cfg.sentinel.master_name = "mymaster"; |
| 57 | +
|
| 58 | +// Ask the library to connect to a random replica of 'mymaster', rather than the master node |
| 59 | +cfg.sentinel.server_role = role::replica; |
| 60 | +---- |
| 61 | + |
| 62 | + |
| 63 | +== Sentinel authentication |
| 64 | + |
| 65 | +If your Sentinels require authentication, |
| 66 | +you can use xref:reference:boost/redis/sentinel_config/setup.adoc[`config::sentinel::setup`] |
| 67 | +to provide credentials. |
| 68 | +This request is executed immediately after connecting to Sentinels, and |
| 69 | +before any other command: |
| 70 | + |
| 71 | +[source,cpp] |
| 72 | +---- |
| 73 | +// Set up Sentinel |
| 74 | +config cfg; |
| 75 | +cfg.sentinel.addresses = { |
| 76 | + {"sentinel1.example.com", "26379"}, |
| 77 | + {"sentinel2.example.com", "26379"}, |
| 78 | + {"sentinel3.example.com", "26379"}, |
| 79 | +}; |
| 80 | +cfg.sentinel.master_name = "mymaster"; |
| 81 | +
|
| 82 | +// By default, setup contains a 'HELLO 3' command. |
| 83 | +// Override it to add an AUTH clause to it with out credentials. |
| 84 | +cfg.sentinel.setup.clear(); |
| 85 | +cfg.sentinel.setup.push("HELLO", 3, "AUTH", "sentinel_user", "sentinel_password"); |
| 86 | +
|
| 87 | +// cfg.sentinel.setup applies to Sentinels, only. |
| 88 | +// Use cfg.setup to authenticate to masters/replicas. |
| 89 | +cfg.use_setup = true; // Required for cfg.setup to be used, for historic reasons |
| 90 | +cfg.setup.clear(); |
| 91 | +cfg.setup.push("HELLO", 3, "AUTH", "master_user", "master_password"); |
| 92 | +---- |
| 93 | + |
| 94 | +== Using TLS with Sentinels |
| 95 | + |
| 96 | +You might use TLS with Sentinels only, masters/replicas only, or both by adjusting |
| 97 | +xref:reference:boost/redis/sentinel_config/use_ssl.adoc[`config::sentinel::use_ssl`] |
| 98 | +and xref:reference:boost/redis/config/use_ssl.adoc[`config::use_ssl`]: |
| 99 | + |
| 100 | +[source,cpp] |
| 101 | +---- |
| 102 | +// Set up Sentinel |
| 103 | +config cfg; |
| 104 | +cfg.sentinel.addresses = { |
| 105 | + {"sentinel1.example.com", "26379"}, |
| 106 | + {"sentinel2.example.com", "26379"}, |
| 107 | + {"sentinel3.example.com", "26379"}, |
| 108 | +}; |
| 109 | +cfg.sentinel.master_name = "mymaster"; |
| 110 | +
|
| 111 | +// Adjust these switches to enable/disable TLS |
| 112 | +cfg.use_ssl = true; // Applies to masters and replicas |
| 113 | +cfg.sentinel.use_ssl = true; // Applies to Sentinels |
| 114 | +---- |
| 115 | + |
| 116 | +== Sentinel algorithm |
| 117 | + |
| 118 | +This section details how `async_run` interacts with Sentinel. |
| 119 | +Most of the algorithm follows |
| 120 | +https://redis.io/docs/latest/develop/reference/sentinel-clients/[the official Sentinel client guidelines]. |
| 121 | +Some of these details may vary between library versions. |
| 122 | + |
| 123 | +* Connections maintain an internal list of Sentinels, bootstrapped from |
| 124 | + xref:reference:boost/redis/sentinel_config/addresses.adoc[`config::sentinel::addresses`]. |
| 125 | +* The first Sentinel in the list is contacted by performing the following: |
| 126 | +** A physical connection is established. |
| 127 | +** The setup request is executed. |
| 128 | +** The master's address is resolved using |
| 129 | + https://redis.io/docs/latest/operate/oss_and_stack/management/sentinel/#sentinel-api[`SENTINEL GET-MASTER-NAME-BY-ADDR`]. |
| 130 | +** If `config::sentinel::server_role` is `role::replica`, replica addresses are obtained using |
| 131 | + https://redis.io/docs/latest/operate/oss_and_stack/management/sentinel/#sentinel-api[`SENTINEL REPLICAS`]. |
| 132 | + One replica is chosen randomly. |
| 133 | +** The address of other Sentinels also monitoring this master are retrieved using |
| 134 | + https://redis.io/docs/latest/operate/oss_and_stack/management/sentinel/#sentinel-api[`SENTINEL SENTINELS`]. |
| 135 | +* If a Sentinel is unreachable, doesn't know about the configured master, |
| 136 | + or returns an error while executing the above requests, the next Sentinel in the list is tried. |
| 137 | +* If all Sentinels have been tried without success, `config::reconnect_wait_interval` |
| 138 | + is waited, and the process starts again. |
| 139 | +* After a successful Sentinel response, the internal Sentinel list is updated |
| 140 | + with any newly discovered Sentinels. |
| 141 | + Sentinels in `config::sentinel::addresses` are always kept in the list, |
| 142 | + even if they weren't present in the output of `SENTINEL SENTINELS`. |
| 143 | +* The retrieved address is used |
| 144 | + to establish a connection with the master or replica. |
| 145 | + A `ROLE` command is added at the end of the setup request. |
| 146 | + This is used to detect situations where a Sentinel returns outdated |
| 147 | + information due to a failover in process. If `ROLE` doesn't output |
| 148 | + the expected role (`"master"` or `"slave"`, depending on `config::sentinel::server_role`) |
| 149 | + `config::reconnect_wait_interval` is waited and Sentinel is contacted again. |
| 150 | +* The connection to the master/replica is run like any other connection. |
| 151 | + If network errors or timeouts happen, `config::reconnect_wait_interval` |
| 152 | + is waited and Sentinel is contacted again. |
0 commit comments