userver: userver/storages/postgres/dist_lock_component_base.hpp Source File
Loading...
Searching...
No Matches
dist_lock_component_base.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/storages/postgres/dist_lock_component_base.hpp
4/// @brief @copybrief storages::postgres::DistLockComponentBase
5
6#include <userver/components/component_base.hpp>
7#include <userver/dist_lock/dist_locked_worker.hpp>
8#include <userver/dynamic_config/snapshot.hpp>
9#include <userver/dynamic_config/source.hpp>
10#include <userver/storages/postgres/dist_lock_strategy.hpp>
11
12USERVER_NAMESPACE_BEGIN
13
14namespace storages::postgres {
15
16/// @ingroup userver_components userver_base_classes
17///
18/// @brief Base class for postgres-based distlock worker components
19///
20/// A component that implements a distlock with lock in Postgres. Inherit from
21/// DistLockComponentBase and implement DoWork(). Lock options are configured
22/// in static config. To customize a distlock for testsuite override DoWorkTestsuite().
23///
24/// By default the distlock is started and stopped automatically by the framework
25/// in all environments except testsuite. If manual control over the distlock startup
26/// and shutdown is needed pass DisableAutostartAtBase{} to the DistLockComponentBase .ctor.
27///
28/// The class must be used for infinite loop jobs. If you want a distributed
29/// periodic, you should look at locked_periodiccomponents::PgLockedPeriodic.
30///
31/// @see dist_lock::DistLockedTask
32/// @see locked_periodiccomponents::PgLockedPeriodic
33///
34/// ## Static configuration example:
35///
36/// ```yaml
37/// example-distlock:
38/// cluster: postgresql-service
39/// table: service.distlocks
40/// lockname: master
41/// pg-timeout: 1s
42/// lock-ttl: 10s
43/// autostart: true
44/// ```
45/// See config `POSTGRES_DISTLOCK_SETTINGS`, some of parameters can be dynamically overridden.
46///
47/// ## Static options of storages::postgres::DistLockComponentBase :
48/// @include{doc} scripts/docs/en/components_schema/postgresql/src/storages/postgres/dist_lock_component_base.md
49///
50/// Options inherited from @ref components::ComponentBase :
51/// @include{doc} scripts/docs/en/components_schema/core/src/components/impl/component_base.md
52///
53/// ## Migration example
54///
55/// You have to create a SQL table for distlocks. An example of the migration
56/// script is as following:
57///
58/// ```SQL
59/// CREATE TABLE service.distlocks
60/// (
61/// key TEXT PRIMARY KEY,
62/// owner TEXT,
63/// expiration_time TIMESTAMPTZ
64/// );
65/// ```
66///
67/// @see @ref scripts/docs/en/userver/periodics.md
69public:
71
72 /// @brief Constructs the distlock base and enables automatic startup and shutdown of the distlock.
74 const components::ComponentConfig& component_config,
75 const components::ComponentContext& component_context
76 );
77
78 /// @brief Constructs the distlock base and disables automatic startup and shutdown of the distlock.
79 /// The dislock should be started and stopped manually via AutostartDistlock() and StopDistLock() calls.
81 const components::ComponentConfig& component_config,
82 const components::ComponentContext& component_context,
84 );
85
86 ~DistLockComponentBase() override;
87
88 dist_lock::DistLockedWorker& GetWorker();
89
90 /// @note In testsuite always returns `true`, because there is only one host.
91 bool OwnsLock() const noexcept;
92
93 static yaml_config::Schema GetStaticConfigSchema();
94
95protected:
96 /// Override this function with anything that must be done under the pg lock.
97 ///
98 /// ## Example implementation
99 ///
100 /// ```cpp
101 /// void MyDistLockComponent::DoWork()
102 /// {
103 /// // `IsCancelAdvised` is advisory/soft signal to stop the task.
104 /// // Check it in every independent processing iteration.
105 /// // Whereas @ref engine::current_task::ShouldCancel() checks as frequently,
106 /// // as you can to honor low-level task cancellation.
107 /// while (!IsCancelAdvised())
108 /// {
109 /// // Start a new trace_id
110 /// auto span = tracing::Span::MakeRootSpan("my-dist-lock");
111 ///
112 /// // If Foo() or other function in DoWork() throws an exception,
113 /// // DoWork() will be restarted in `restart-delay` seconds.
114 /// Foo();
115 ///
116 /// // Check for cancellation after cpu-intensive Foo().
117 /// // You must check for cancellation at least every `lock-ttl`
118 /// // seconds to have time to notice lock prolongation failure.
119 /// if (engine::ShouldCancel()) break;
120 ///
121 /// Bar();
122 /// }
123 /// }
124 /// ```
125 ///
126 /// @note `DoWork` must honour task cancellation and stop ASAP when
127 /// it is cancelled, otherwise brain split is possible (IOW, two different
128 /// users do work assuming both of them hold the lock, which is not true).
129 virtual void DoWork() = 0;
130
131 /// Override this function to provide custom testsuite handler.
132 virtual void DoWorkTestsuite() { DoWork(); }
133
134 /// Should be called in a .ctor of a derived class iff DisableAutostartAtBase{}
135 /// has been passed to the DistLockComponentBase .ctor.
137
138 /// Should be called in a .dtor of a derived class iff DisableAutostartAtBase{}
139 /// has been passed to the DistLockComponentBase .ctor.
141
142 /// Check this method when going for the next independent processing
143 /// iteration. Whereas @ref engine::current_task::ShouldCancel()
144 /// checks as frequently, as you can to honor low-level task cancellation.
145 bool IsCancelAdvised() const;
146
147private:
148 enum class AutostartDistlock : bool { kNo = false, kYes = true };
149
150 DistLockComponentBase(
151 const components::ComponentConfig& component_config,
152 const components::ComponentContext& component_context,
153 AutostartDistlock enable_autostart_at_base
154 );
155
156 bool ShouldRunOnHost(const dynamic_config::Snapshot& config) const;
157 void OnConfigUpdate(const dynamic_config::Diff& diff);
158
159 dynamic_config::Source config_;
160 const std::string name_;
161 const std::string real_host_name_;
162 std::unique_ptr<dist_lock::DistLockedWorker> worker_;
163 bool autostart_;
164 bool testsuite_enabled_{false};
165 AutostartDistlock enable_autostart_at_base_;
166 dist_lock::DistLockSettings default_settings_;
167
168 concurrent::AsyncEventSubscriberScope subscription_token_;
169};
170
171} // namespace storages::postgres
172
173USERVER_NAMESPACE_END