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.
23///
24/// The class must be used for infinite loop jobs. If you want a distributed
25/// periodic, you should look at locked_periodiccomponents::PgLockedPeriodic.
26///
27/// @see dist_lock::DistLockedTask
28/// @see locked_periodiccomponents::PgLockedPeriodic
29///
30/// ## Static configuration example:
31///
32/// ```yaml
33/// example-distlock:
34/// cluster: postgresql-service
35/// table: service.distlocks
36/// lockname: master
37/// pg-timeout: 1s
38/// lock-ttl: 10s
39/// autostart: true
40/// ```
41/// See config `POSTGRES_DISTLOCK_SETTINGS`, some of parameters can be dynamically overridden.
42///
43/// ## Static options of storages::postgres::DistLockComponentBase :
44/// @include{doc} scripts/docs/en/components_schema/postgresql/src/storages/postgres/dist_lock_component_base.md
45///
46/// Options inherited from @ref components::ComponentBase :
47/// @include{doc} scripts/docs/en/components_schema/core/src/components/impl/component_base.md
48///
49/// ## Migration example
50///
51/// You have to create a SQL table for distlocks. An example of the migration
52/// script is as following:
53///
54/// ```SQL
55/// CREATE TABLE service.distlocks
56/// (
57/// key TEXT PRIMARY KEY,
58/// owner TEXT,
59/// expiration_time TIMESTAMPTZ
60/// );
61/// ```
62///
63/// @see @ref scripts/docs/en/userver/periodics.md
64class DistLockComponentBase : public components::ComponentBase {
65public:
66 DistLockComponentBase(const components::ComponentConfig&, const components::ComponentContext&);
67
68 ~DistLockComponentBase() override;
69
70 dist_lock::DistLockedWorker& GetWorker();
71
72 /// @note In testsuite always returns `true`, because there is only one host.
73 bool OwnsLock() const noexcept;
74
75 static yaml_config::Schema GetStaticConfigSchema();
76
77protected:
78 /// Override this function with anything that must be done under the pg lock.
79 ///
80 /// ## Example implementation
81 ///
82 /// ```cpp
83 /// void MyDistLockComponent::DoWork()
84 /// {
85 /// // `IsCancelAdvised` is advisory/soft signal to stop the task.
86 /// // Check it in every independent processing iteration.
87 /// // Whereas @ref engine::current_task::ShouldCancel() checks as frequently,
88 /// // as you can to honor low-level task cancellation.
89 /// while (!IsCancelAdvised())
90 /// {
91 /// // Start a new trace_id
92 /// auto span = tracing::Span::MakeRootSpan("my-dist-lock");
93 ///
94 /// // If Foo() or other function in DoWork() throws an exception,
95 /// // DoWork() will be restarted in `restart-delay` seconds.
96 /// Foo();
97 ///
98 /// // Check for cancellation after cpu-intensive Foo().
99 /// // You must check for cancellation at least every `lock-ttl`
100 /// // seconds to have time to notice lock prolongation failure.
101 /// if (engine::ShouldCancel()) break;
102 ///
103 /// Bar();
104 /// }
105 /// }
106 /// ```
107 ///
108 /// @note `DoWork` must honour task cancellation and stop ASAP when
109 /// it is cancelled, otherwise brain split is possible (IOW, two different
110 /// users do work assuming both of them hold the lock, which is not true).
111 virtual void DoWork() = 0;
112
113 /// Override this function to provide custom testsuite handler.
114 virtual void DoWorkTestsuite() { DoWork(); }
115
116 /// Must be called in ctr
118
119 /// Must be called in dtr
121
122 /// Check this method when going for the next independent processing
123 /// iteration. Whereas @ref engine::current_task::ShouldCancel()
124 /// checks as frequently, as you can to honor low-level task cancellation.
125 bool IsCancelAdvised() const;
126
127private:
128 bool ShouldRunOnHost(const dynamic_config::Snapshot& config) const;
129 void OnConfigUpdate(const dynamic_config::Diff& diff);
130
131 dynamic_config::Source config_;
132 const std::string name_;
133 const std::string real_host_name_;
134 std::unique_ptr<dist_lock::DistLockedWorker> worker_;
135 bool autostart_;
136 bool testsuite_enabled_{false};
137 dist_lock::DistLockSettings default_settings_;
138
139 concurrent::AsyncEventSubscriberScope subscription_token_;
140};
141
142} // namespace storages::postgres
143
144USERVER_NAMESPACE_END