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/storages/postgres/dist_lock_strategy.hpp>
9#include <userver/utils/statistics/entry.hpp>
10
11USERVER_NAMESPACE_BEGIN
12
13namespace storages::postgres {
14
15// clang-format off
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///
43/// ## Static options:
44/// name | Description | Default value
45/// -------------- | ------------ | -------------
46/// cluster | postgres cluster name | --
47/// table | table name to store distlocks | --
48/// lockname | name of the lock | --
49/// lock-ttl | TTL of the lock; must be at least as long as the duration between subsequent cancellation checks, otherwise brain split is possible | --
50/// pg-timeout | timeout, must be less than lock-ttl/2 | --
51/// restart-delay | how much time to wait after failed task restart | 100ms
52/// autostart | if true, start automatically after component load | false
53/// task-processor | the name of the TaskProcessor for running DoWork | main-task-processor
54/// testsuite-support | Enable testsuite support | false
55///
56/// ## Migration example
57///
58/// You have to create a SQL table for distlocks. An example of the migration
59/// script is as following:
60///
61/// ```SQL
62/// CREATE TABLE service.distlocks
63/// (
64/// key TEXT PRIMARY KEY,
65/// owner TEXT,
66/// expiration_time TIMESTAMPTZ
67/// );
68/// ```
69///
70/// @see @ref scripts/docs/en/userver/periodics.md
71
72// clang-format on
73
75public:
76 DistLockComponentBase(const components::ComponentConfig&, const components::ComponentContext&);
77
78 ~DistLockComponentBase() override;
79
80 dist_lock::DistLockedWorker& GetWorker();
81
82 bool OwnsLock() const noexcept;
83
84 static yaml_config::Schema GetStaticConfigSchema();
85
86protected:
87 /// Override this function with anything that must be done under the pg lock.
88 ///
89 /// ## Example implementation
90 ///
91 /// ```cpp
92 /// void MyDistLockComponent::DoWork()
93 /// {
94 /// while (!engine::ShouldCancel())
95 /// {
96 /// // Start a new trace_id
97 /// auto span = tracing::Span::MakeRootSpan("my-dist-lock");
98 ///
99 /// // If Foo() or other function in DoWork() throws an exception,
100 /// // DoWork() will be restarted in `restart-delay` seconds.
101 /// Foo();
102 ///
103 /// // Check for cancellation after cpu-intensive Foo().
104 /// // You must check for cancellation at least every `lock-ttl`
105 /// // seconds to have time to notice lock prolongation failure.
106 /// if (engine::ShouldCancel()) break;
107 ///
108 /// Bar();
109 /// }
110 /// }
111 /// ```
112 ///
113 /// @note `DoWork` must honour task cancellation and stop ASAP when
114 /// it is cancelled, otherwise brain split is possible (IOW, two different
115 /// users do work assuming both of them hold the lock, which is not true).
116 virtual void DoWork() = 0;
117
118 /// Override this function to provide custom testsuite handler.
119 virtual void DoWorkTestsuite() { DoWork(); }
120
121 /// Must be called in ctr
123
124 /// Must be called in dtr
126
127private:
128 std::unique_ptr<dist_lock::DistLockedWorker> worker_;
129 bool autostart_;
130 bool testsuite_enabled_{false};
131
132 // Subscriptions must be the last fields.
133 USERVER_NAMESPACE::utils::statistics::Entry statistics_holder_;
134};
135
136} // namespace storages::postgres
137
138USERVER_NAMESPACE_END