userver: userver/storages/redis/component.hpp Source File
Loading...
Searching...
No Matches
component.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/storages/redis/component.hpp
4/// @brief @copybrief components::Redis
5
6#include <memory>
7#include <string>
8#include <unordered_map>
9
10#include <userver/components/component_base.hpp>
11#include <userver/components/component_fwd.hpp>
12#include <userver/dynamic_config/source.hpp>
13#include <userver/rcu/rcu.hpp>
14#include <userver/storages/redis/base.hpp>
15#include <userver/storages/redis/fwd.hpp>
16#include <userver/storages/redis/wait_connected_mode.hpp>
17#include <userver/storages/secdist/secdist.hpp>
18#include <userver/testsuite/redis_control.hpp>
19#include <userver/utils/statistics/entry.hpp>
20
21USERVER_NAMESPACE_BEGIN
22
23/// Components, clients and helpers for different databases and storages
24namespace storages {}
25
26/// Valkey and Redis client and helpers
27namespace storages::redis {
28
29class SubscribeClientImpl;
30
31namespace impl {
32class Sentinel;
33class ThreadPools;
34} // namespace impl
35} // namespace storages::redis
36
37namespace components {
38
39// clang-format off
40
41/// @ingroup userver_components
42///
43/// @brief Valkey and Redis client component
44///
45/// Provides access to a valkey or redis cluster.
46///
47/// ## Dynamic options:
48/// * @ref REDIS_COMMANDS_BUFFERING_SETTINGS
49/// * @ref REDIS_DEFAULT_COMMAND_CONTROL
50/// * @ref REDIS_METRICS_SETTINGS
51/// * @ref REDIS_PUBSUB_METRICS_SETTINGS
52/// * @ref REDIS_RETRY_BUDGET_SETTINGS
53/// * @ref REDIS_REPLICA_MONITORING_SETTINGS
54/// * @ref REDIS_SUBSCRIBER_DEFAULT_COMMAND_CONTROL
55/// * @ref REDIS_SUBSCRIPTIONS_REBALANCE_MIN_INTERVAL_SECONDS
56/// * @ref REDIS_WAIT_CONNECTED
57///
58/// ## Static options:
59/// Name | Description | Default value
60/// ---- | ----------- | -------------
61/// thread_pools.redis_thread_pool_size | thread count to serve Redis requests | -
62/// thread_pools.sentinel_thread_pool_size | thread count to serve sentinel requests. | -
63/// groups | array of redis clusters to work with excluding subscribers | -
64/// groups.[].config_name | key name in secdist with options for this cluster | -
65/// groups.[].db | name to refer to the cluster in components::Redis::GetClient() | -
66/// groups.[].sharding_strategy | one of RedisCluster, RedisStandalone, KeyShardCrc32, KeyShardTaximeterCrc32 or KeyShardGpsStorageDriver | "KeyShardTaximeterCrc32"
67/// groups.[].allow_reads_from_master | allows read requests from master instance | false
68/// subscribe_groups | array of redis clusters to work with in subscribe mode | -
69/// subscribe_groups.[].config_name | key name in secdist with options for this cluster | -
70/// subscribe_groups.[].db | name to refer to the cluster in components::Redis::GetSubscribeClient() | -
71/// subscribe_groups.[].sharding_strategy | either RedisCluster or KeyShardTaximeterCrc32 | "KeyShardTaximeterCrc32"
72///
73/// ## Static configuration example:
74///
75/// ```
76/// # yaml
77/// redis:
78/// groups:
79/// - config_name: cats
80/// db: hello_service_cats_catalogue
81/// sharding_strategy: RedisCluster
82/// - config_name: dogs
83/// db: hello_service_dogs_catalogue
84/// subscribe_groups:
85/// - config_name: food
86/// db: hello_service_pet_food_orders
87/// thread_pools:
88/// redis_thread_pool_size: 8
89/// sentinel_thread_pool_size: 1
90/// ```
91///
92/// ## Secdist format
93///
94/// If a `config_name` option is provided, for example
95/// `groups.some.config_name: some_name_of_your_database`, then the Secdist
96/// entry for that alias should look like following:
97/// @code{.json}
98/// {
99/// "redis_settings": {
100/// "some_name_of_your_database": {
101/// "password": "the_password_of_your_database",
102/// "sentinel_password": "the_password_for_sentinels_if_any",
103/// "secure_connection": false,
104/// "sentinels": [
105/// {"host": "the_host1_of_your_database", "port": 11564}
106/// ],
107/// "shards": [
108/// {"name": "test_master0"}
109/// ]
110/// }
111/// }
112/// }
113/// @endcode
114///
115/// For an example of Secdist setup for standalone, sentinel and cluster testsuite configurations see the snippet:
116///
117/// @snippet redis/functional_tests/integration_tests/tests/conftest.py Sample pytest redis configuration
118///
119/// Note that if the components::Secdist component has `update-period` other
120/// than 0, then new connections are created or gracefully closed as the secdist configuration change to new value.
121///
122/// ## Cluster Valkey or Cluster Redis setup
123///
124/// Valkey/Redis cluster is the new recommended way of setting up key-value datastores with improved stability.
125///
126/// To start, set `sharding_strategy: RedisCluster` in the static config
127/// as shown above.
128///
129/// Secdist configuration is simplified:
130///
131/// 1. `"shards"` field is ignored, you can specify an empty array there;
132/// 2. `"sentinels"` field should contain some of the cluster nodes. They are
133/// only used for topology discovery; it is not necessary to list all nodes.
134
135// clang-format on
136class Redis : public ComponentBase {
137public:
138 Redis(const ComponentConfig& config, const ComponentContext& component_context);
139
140 ~Redis() override;
141
142 /// @ingroup userver_component_names
143 /// @brief The default name of components::Redis
144 static constexpr std::string_view kName = "redis";
145
146 std::shared_ptr<storages::redis::Client>
147 GetClient(const std::string& name, storages::redis::RedisWaitConnected wait_connected = {}) const;
148 [[deprecated("use GetClient()")]] std::shared_ptr<storages::redis::impl::Sentinel> Client(const std::string& name
149 ) const;
150 std::shared_ptr<storages::redis::SubscribeClient>
151 GetSubscribeClient(const std::string& name, storages::redis::RedisWaitConnected wait_connected = {}) const;
152
153 static yaml_config::Schema GetStaticConfigSchema();
154
155private:
156 void OnConfigUpdate(const dynamic_config::Snapshot& cfg);
157 void OnSecdistUpdate(const storages::secdist::SecdistConfig& cfg);
158
159 void Connect(
160 const ComponentConfig& config,
161 const ComponentContext& component_context,
162 const testsuite::RedisControl& testsuite_redis_control
163 );
164
165 void WriteStatistics(utils::statistics::Writer& writer);
166 void WriteStatisticsPubsub(utils::statistics::Writer& writer);
167
168 std::shared_ptr<storages::redis::impl::ThreadPools> thread_pools_;
169 std::unordered_map<std::string, std::shared_ptr<storages::redis::impl::Sentinel>> sentinels_;
170 std::unordered_map<std::string, std::shared_ptr<storages::redis::Client>> clients_;
171 std::unordered_map<std::string, std::shared_ptr<storages::redis::SubscribeClientImpl>> subscribe_clients_;
172
173 dynamic_config::Source config_;
174 concurrent::AsyncEventSubscriberScope config_subscription_;
175 concurrent::AsyncEventSubscriberScope secdist_subscription_;
176
177 utils::statistics::Entry statistics_holder_;
178 utils::statistics::Entry subscribe_statistics_holder_;
179
180 storages::redis::MetricsSettings::StaticSettings static_metrics_settings_;
181 rcu::Variable<storages::redis::MetricsSettings> metrics_settings_;
182 rcu::Variable<storages::redis::PubsubMetricsSettings> pubsub_metrics_settings_;
183};
184
185template <>
186inline constexpr bool kHasValidate<Redis> = true;
187
188} // namespace components
189
190USERVER_NAMESPACE_END