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/loggable_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
75 public:
76 DistLockComponentBase(const components::ComponentConfig&,
77 const components::ComponentContext&);
78
79 ~DistLockComponentBase() override;
80
81 dist_lock::DistLockedWorker& GetWorker();
82
83 static yaml_config::Schema GetStaticConfigSchema();
84
85 protected:
86 /// Override this function with anything that must be done under the pg lock.
87 ///
88 /// ## Example implementation
89 ///
90 /// ```cpp
91 /// void MyDistLockComponent::DoWork()
92 /// {
93 /// while (!engine::ShouldCancel())
94 /// {
95 /// // Start a new trace_id
96 /// auto span = tracing::Span::MakeRootSpan("my-dist-lock");
97 ///
98 /// // If Foo() or other function in DoWork() throws an exception,
99 /// // DoWork() will be restarted in `restart-delay` seconds.
100 /// Foo();
101 ///
102 /// // Check for cancellation after cpu-intensive Foo().
103 /// // You must check for cancellation at least every `lock-ttl`
104 /// // seconds to have time to notice lock prolongation failure.
105 /// if (engine::ShouldCancel()) break;
106 ///
107 /// Bar();
108 /// }
109 /// }
110 /// ```
111 ///
112 /// @note `DoWork` must honour task cancellation and stop ASAP when
113 /// it is cancelled, otherwise brain split is possible (IOW, two different
114 /// users do work assuming both of them hold the lock, which is not true).
115 virtual void DoWork() = 0;
116
117 /// Override this function to provide custom testsuite handler.
118 virtual void DoWorkTestsuite() { DoWork(); }
119
120 /// Must be called in ctr
122
123 /// Must be called in dtr
125
126 private:
127 std::unique_ptr<dist_lock::DistLockedWorker> worker_;
128 bool autostart_;
129 bool testsuite_enabled_{false};
130
131 // Subscriptions must be the last fields.
132 USERVER_NAMESPACE::utils::statistics::Entry statistics_holder_;
133};
134
135} // namespace storages::postgres
136
137USERVER_NAMESPACE_END