Skip to content

Commit 6529f5c

Browse files
author
aleksey7sidor
committed
feat userver: add dynamic configuration for postgres distlock
Dynamic config @ref POSTGRES_DISTLOCK_SETTINGS now allows to dynamically change some interval settings for components inherited from @ref storages::postgres::DistLockComponentBase. There is also `run_on_hosts` setting that allows to manually distribute distlocks to hosts, as well as to dynamically stop a failing distlock. Tests: a test added to `uservice-sample` commit_hash:2f0b3e1fdb6fb94005df1576c8498ba14957347a
1 parent a133f30 commit 6529f5c

File tree

5 files changed

+152
-9
lines changed

5 files changed

+152
-9
lines changed

.mapping.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3285,6 +3285,7 @@
32853285
"postgresql/dynamic_configs/POSTGRES_CONNLIMIT_MODE_AUTO_ENABLED.yaml":"taxi/uservices/userver/postgresql/dynamic_configs/POSTGRES_CONNLIMIT_MODE_AUTO_ENABLED.yaml",
32863286
"postgresql/dynamic_configs/POSTGRES_DEADLINE_PROPAGATION_VERSION.yaml":"taxi/uservices/userver/postgresql/dynamic_configs/POSTGRES_DEADLINE_PROPAGATION_VERSION.yaml",
32873287
"postgresql/dynamic_configs/POSTGRES_DEFAULT_COMMAND_CONTROL.yaml":"taxi/uservices/userver/postgresql/dynamic_configs/POSTGRES_DEFAULT_COMMAND_CONTROL.yaml",
3288+
"postgresql/dynamic_configs/POSTGRES_DISTLOCK_SETTINGS.yaml":"taxi/uservices/userver/postgresql/dynamic_configs/POSTGRES_DISTLOCK_SETTINGS.yaml",
32883289
"postgresql/dynamic_configs/POSTGRES_HANDLERS_COMMAND_CONTROL.yaml":"taxi/uservices/userver/postgresql/dynamic_configs/POSTGRES_HANDLERS_COMMAND_CONTROL.yaml",
32893290
"postgresql/dynamic_configs/POSTGRES_OMIT_DESCRIBE_IN_EXECUTE.yaml":"taxi/uservices/userver/postgresql/dynamic_configs/POSTGRES_OMIT_DESCRIBE_IN_EXECUTE.yaml",
32903291
"postgresql/dynamic_configs/POSTGRES_QUERIES_COMMAND_CONTROL.yaml":"taxi/uservices/userver/postgresql/dynamic_configs/POSTGRES_QUERIES_COMMAND_CONTROL.yaml",
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
default: {}
2+
description: |
3+
Configuration of periodic tasks executed with a distributed lock.
4+
A dictionary where the keys are task names.
5+
If hosts are specified in run_on_hosts, the task will switch to
6+
any of them at the first opportunity and will keep running
7+
there until a new preferred host is specified or a failover occurs due to
8+
an error. By adjusting the timing settings,
9+
you can override the lock acquisition policy.
10+
schema:
11+
type: object
12+
additionalProperties:
13+
$ref: '#/definitions/TaskSettings'
14+
definitions:
15+
TaskSettings:
16+
type: object
17+
description: Periodic task policy settings
18+
additionalProperties: false
19+
properties:
20+
run_on_hosts:
21+
description: |
22+
USE IsCancelAdvised() OR IT DOESNT WORK
23+
List of hosts on which the task
24+
is allowed to run. The task will start on
25+
any of the specified hosts—without
26+
prioritization.
27+
type: array
28+
x-usrv-cpp-type: std::unordered_set
29+
items:
30+
type: string
31+
acquire_interval:
32+
description: |
33+
How often to try to acquire the lock.
34+
type: integer
35+
x-usrv-cpp-type: std::chrono::milliseconds
36+
prolong_interval:
37+
description: |
38+
How often to attempt to renew
39+
the lock while holding it.
40+
type: integer
41+
x-usrv-cpp-type: std::chrono::milliseconds
42+
lock_ttl:
43+
description: Duration of lock
44+
type: integer
45+
x-usrv-cpp-type: std::chrono::milliseconds
46+
forced_stop_margin:
47+
description: |
48+
How long to allow the component to
49+
finish work when it cannot acquire the lock.
50+
type: integer
51+
x-usrv-cpp-type: std::chrono::milliseconds
52+
worker_func_restart_delay:
53+
description: |
54+
Delay before attempting to restart a
55+
crashed component.
56+
type: integer
57+
x-usrv-cpp-type: std::chrono::milliseconds

postgresql/include/userver/storages/postgres/dist_lock_component_base.hpp

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
#include <userver/components/component_base.hpp>
77
#include <userver/dist_lock/dist_locked_worker.hpp>
8+
#include <userver/dynamic_config/snapshot.hpp>
9+
#include <userver/dynamic_config/source.hpp>
810
#include <userver/storages/postgres/dist_lock_strategy.hpp>
911
#include <userver/utils/statistics/entry.hpp>
1012

@@ -39,6 +41,7 @@ namespace storages::postgres {
3941
/// lock-ttl: 10s
4042
/// autostart: true
4143
/// ```
44+
/// See config `POSTGRES_DISTLOCK_SETTINGS`, some of parameters can be dynamically overridden.
4245
///
4346
/// ## Static options:
4447
/// name | Description | Default value
@@ -92,7 +95,11 @@ class DistLockComponentBase : public components::ComponentBase {
9295
/// ```cpp
9396
/// void MyDistLockComponent::DoWork()
9497
/// {
95-
/// while (!engine::ShouldCancel())
98+
/// // `IsCancelAdvised` is advisory/soft signal to stop the task.
99+
/// // Check it in every independent processing iteration.
100+
/// // Whereas @ref engine::current_task::ShouldCancel() checks as frequently,
101+
/// // as you can to honor low-level task cancellation.
102+
/// while (!IsCancelAdvised())
96103
/// {
97104
/// // Start a new trace_id
98105
/// auto span = tracing::Span::MakeRootSpan("my-dist-lock");
@@ -125,10 +132,24 @@ class DistLockComponentBase : public components::ComponentBase {
125132
/// Must be called in dtr
126133
void StopDistLock();
127134

135+
/// Check this method when going for the next independent processing
136+
/// iteration. Whereas @ref engine::current_task::ShouldCancel()
137+
/// checks as frequently, as you can to honor low-level task cancellation.
138+
bool IsCancelAdvised() const;
139+
128140
private:
141+
bool ShouldRunOnHost(const dynamic_config::Snapshot& config) const;
142+
void OnConfigUpdate(const dynamic_config::Diff& diff);
143+
144+
dynamic_config::Source config_;
145+
const std::string name_;
146+
const std::string real_host_name_;
129147
std::unique_ptr<dist_lock::DistLockedWorker> worker_;
130148
bool autostart_;
131149
bool testsuite_enabled_{false};
150+
dist_lock::DistLockSettings default_settings_;
151+
152+
concurrent::AsyncEventSubscriberScope subscription_token_;
132153

133154
// Subscriptions must be the last fields.
134155
USERVER_NAMESPACE::utils::statistics::Entry statistics_holder_;

postgresql/library.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,4 @@ configs:
2222
- POSTGRES_OMIT_DESCRIBE_IN_EXECUTE
2323
- POSTGRES_QUERIES_COMMAND_CONTROL
2424
- POSTGRES_STATEMENT_METRICS_SETTINGS
25+
- POSTGRES_DISTLOCK_SETTINGS

postgresql/src/storages/postgres/dist_lock_component_base.cpp

Lines changed: 71 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,19 @@
33
#include <userver/components/component.hpp>
44
#include <userver/components/statistics_storage.hpp>
55
#include <userver/dist_lock/dist_lock_settings.hpp>
6+
#include <userver/dynamic_config/storage/component.hpp>
7+
#include <userver/hostinfo/blocking/get_hostname.hpp>
68
#include <userver/storages/postgres/component.hpp>
79
#include <userver/testsuite/tasks.hpp>
10+
#include <userver/utils/algo.hpp>
811
#include <userver/yaml_config/merge_schemas.hpp>
912

1013
#ifndef ARCADIA_ROOT
1114
#include "generated/src/storages/postgres/dist_lock_component_base.yaml.hpp" // Y_IGNORE
1215
#endif
1316

17+
#include <dynamic_config/variables/POSTGRES_DISTLOCK_SETTINGS.hpp>
18+
1419
USERVER_NAMESPACE_BEGIN
1520

1621
namespace storages::postgres {
@@ -19,7 +24,11 @@ DistLockComponentBase::DistLockComponentBase(
1924
const components::ComponentConfig& component_config,
2025
const components::ComponentContext& component_context
2126
)
22-
: components::ComponentBase(component_config, component_context) {
27+
: components::ComponentBase(component_config, component_context),
28+
config_(component_context.FindComponent<components::DynamicConfig>().GetSource()),
29+
name_(component_config.Name()),
30+
real_host_name_(hostinfo::blocking::GetRealHostName())
31+
{
2332
auto cluster =
2433
component_context.FindComponent<components::Postgres>(component_config["cluster"].As<std::string>())
2534
.GetCluster();
@@ -38,29 +47,32 @@ DistLockComponentBase::DistLockComponentBase(
3847
settings.worker_func_restart_delay =
3948
component_config["restart-delay"].As<std::chrono::milliseconds>(settings.worker_func_restart_delay);
4049

50+
default_settings_ = settings;
4151
auto strategy = std::make_shared<DistLockStrategy>(std::move(cluster), table, lock_name, settings);
4252

4353
auto task_processor_name = component_config["task-processor"].As<std::optional<std::string>>();
4454
auto* task_processor =
4555
task_processor_name ? &component_context.GetTaskProcessor(task_processor_name.value()) : nullptr;
4656

4757
auto locker_log_level = logging::LevelFromString(component_config["locker-log-level"].As<std::string>("info"));
48-
4958
worker_ = std::make_unique<dist_lock::DistLockedWorker>(
5059
lock_name,
5160
[this]() {
52-
if (testsuite_enabled_) {
53-
DoWorkTestsuite();
54-
} else {
55-
DoWork();
61+
const auto snapshot = config_.GetSnapshot();
62+
if (ShouldRunOnHost(snapshot)) {
63+
if (testsuite_enabled_) {
64+
DoWorkTestsuite();
65+
} else {
66+
DoWork();
67+
}
5668
}
5769
},
5870
std::move(strategy),
5971
settings,
6072
task_processor,
6173
locker_log_level
6274
);
63-
75+
subscription_token_ = config_.UpdateAndListen(this, name_, &DistLockComponentBase::OnConfigUpdate);
6476
autostart_ = component_config["autostart"].As<bool>(false);
6577

6678
auto& statistics_storage = component_context.FindComponent<components::StatisticsStorage>();
@@ -80,7 +92,10 @@ DistLockComponentBase::DistLockComponentBase(
8092
}
8193
}
8294

83-
DistLockComponentBase::~DistLockComponentBase() { statistics_holder_.Unregister(); }
95+
DistLockComponentBase::~DistLockComponentBase() {
96+
statistics_holder_.Unregister();
97+
subscription_token_.Unsubscribe();
98+
}
8499

85100
dist_lock::DistLockedWorker& DistLockComponentBase::GetWorker() { return *worker_; }
86101

@@ -97,6 +112,54 @@ void DistLockComponentBase::AutostartDistLock() {
97112

98113
void DistLockComponentBase::StopDistLock() { worker_->Stop(); }
99114

115+
void DistLockComponentBase::OnConfigUpdate(const dynamic_config::Diff& diff) {
116+
const auto& old_snapshot_opt = diff.previous;
117+
const auto& new_snapshot = diff.current;
118+
119+
const auto& new_settings = new_snapshot[::dynamic_config::POSTGRES_DISTLOCK_SETTINGS];
120+
const auto* new_overrides = utils::FindOrNullptr(new_settings.extra, name_);
121+
122+
if (old_snapshot_opt.has_value()) {
123+
const auto& old_settings = (*old_snapshot_opt)[::dynamic_config::POSTGRES_DISTLOCK_SETTINGS];
124+
const auto* old_overrides = utils::FindOrNullptr(old_settings.extra, name_);
125+
126+
if (!old_overrides && !new_overrides) {
127+
return;
128+
}
129+
if (old_overrides && new_overrides && *old_overrides == *new_overrides) {
130+
return;
131+
}
132+
}
133+
auto& worker = GetWorker();
134+
if (new_overrides) {
135+
dist_lock::DistLockSettings settings;
136+
settings.acquire_interval = new_overrides->acquire_interval.value_or(default_settings_.acquire_interval);
137+
settings.prolong_interval = new_overrides->prolong_interval.value_or(default_settings_.prolong_interval);
138+
settings.lock_ttl = new_overrides->lock_ttl.value_or(default_settings_.lock_ttl);
139+
settings.forced_stop_margin = new_overrides->forced_stop_margin.value_or(default_settings_.forced_stop_margin);
140+
settings.worker_func_restart_delay =
141+
new_overrides->worker_func_restart_delay.value_or(default_settings_.worker_func_restart_delay);
142+
143+
worker.UpdateSettings(settings);
144+
} else {
145+
worker.UpdateSettings(default_settings_);
146+
}
147+
}
148+
bool DistLockComponentBase::ShouldRunOnHost(const dynamic_config::Snapshot& config) const {
149+
const auto& worker_settings_config = config[::dynamic_config::POSTGRES_DISTLOCK_SETTINGS];
150+
if (const auto* settings = utils::FindOrNullptr(worker_settings_config.extra, name_)) {
151+
if (const auto& hosts = settings->run_on_hosts) {
152+
return hosts->count(real_host_name_);
153+
}
154+
}
155+
return true;
156+
}
157+
158+
bool DistLockComponentBase::IsCancelAdvised() const {
159+
const auto snapshot = config_.GetSnapshot();
160+
return !ShouldRunOnHost(snapshot);
161+
}
162+
100163
yaml_config::Schema DistLockComponentBase::GetStaticConfigSchema() {
101164
return yaml_config::MergeSchemasFromResource<
102165
components::ComponentBase>("src/storages/postgres/dist_lock_component_base.yaml");

0 commit comments

Comments
 (0)