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