Skip to content

Commit 4cd030c

Browse files
author
romankoshelev
committed
fix userver: storages::postgres::DistLockStrategy::Acquire
commit_hash:8f7c0fffb8318b58a5615b7eea61674cde8ade73
1 parent f37faff commit 4cd030c

File tree

3 files changed

+30
-17
lines changed

3 files changed

+30
-17
lines changed

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <userver/engine/deadline.hpp>
99
#include <userver/rcu/rcu.hpp>
1010
#include <userver/storages/postgres/options.hpp>
11+
#include <userver/storages/postgres/query.hpp>
1112

1213
USERVER_NAMESPACE_BEGIN
1314

@@ -32,8 +33,8 @@ class DistLockStrategy final : public dist_lock::DistLockStrategyBase {
3233
private:
3334
ClusterPtr cluster_;
3435
rcu::Variable<CommandControl> cc_;
35-
const std::string acquire_query_;
36-
const std::string release_query_;
36+
const Query acquire_query_;
37+
const Query release_query_;
3738
const std::string lock_name_;
3839
const std::string owner_prefix_;
3940
};

postgresql/src/storages/postgres/dist_lock_strategy.cpp

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ namespace {
1717
// key - $1
1818
// owner - $2
1919
// timeout in seconds - $3
20-
std::string MakeAcquireQuery(const std::string& table) {
20+
Query MakeAcquireQuery(const std::string& table) {
2121
static constexpr std::string_view kAcquireQueryFmt = R"(
2222
INSERT INTO {} AS t (key, owner, expiration_time) SELECT
2323
$1, $2, current_timestamp + make_interval(secs => $3)
@@ -35,19 +35,19 @@ std::string MakeAcquireQuery(const std::string& table) {
3535
WHERE (t.owner = $2) OR
3636
(t.expiration_time <= current_timestamp) RETURNING 1;
3737
)";
38-
return fmt::format(FMT_COMPILE(kAcquireQueryFmt), table, table);
38+
return {fmt::format(FMT_COMPILE(kAcquireQueryFmt), table, table), Query::Name{"dist_lock_acquire"}};
3939
}
4040

4141
// key - $1
4242
// owner - $2
43-
std::string MakeReleaseQuery(const std::string& table) {
43+
Query MakeReleaseQuery(const std::string& table) {
4444
static constexpr std::string_view kReleaseQueryFmt = R"(
4545
DELETE FROM {}
4646
WHERE key = $1
4747
AND owner = $2
4848
RETURNING 1;
4949
)";
50-
return fmt::format(FMT_COMPILE(kReleaseQueryFmt), table);
50+
return {fmt::format(FMT_COMPILE(kReleaseQueryFmt), table), Query::Name{"dist_lock_release"}};
5151
}
5252

5353
std::string MakeOwnerId(const std::string& prefix, const std::string& locker) {
@@ -79,17 +79,28 @@ void DistLockStrategy::UpdateCommandControl(CommandControl cc) {
7979
void DistLockStrategy::Acquire(std::chrono::milliseconds lock_ttl, const std::string& locker_id) {
8080
const double timeout_seconds = lock_ttl.count() / 1000.0;
8181
auto cc_ptr = cc_.Read();
82-
auto result = cluster_->Execute(
83-
ClusterHostType::kMaster,
84-
*cc_ptr,
85-
acquire_query_,
86-
lock_name_,
87-
MakeOwnerId(owner_prefix_, locker_id),
88-
timeout_seconds
89-
);
90-
91-
if (result.IsEmpty()) {
92-
throw dist_lock::LockIsAcquiredByAnotherHostException();
82+
83+
try {
84+
auto result = cluster_->Execute(
85+
ClusterHostType::kMaster,
86+
*cc_ptr,
87+
acquire_query_,
88+
lock_name_,
89+
MakeOwnerId(owner_prefix_, locker_id),
90+
timeout_seconds
91+
);
92+
93+
if (result.IsEmpty()) {
94+
throw dist_lock::LockIsAcquiredByAnotherHostException();
95+
}
96+
} catch (const TransactionRollback& exc) {
97+
if (exc.GetSqlState() == SqlState::kSerializationFailure) {
98+
// Looks like the default transaction isolation is 'repeatable read' or 'serializable' and we were hit by
99+
// "could not serialize access due to concurrent update"
100+
throw dist_lock::LockIsAcquiredByAnotherHostException();
101+
} else {
102+
throw;
103+
}
93104
}
94105
}
95106

postgresql/src/storages/postgres/sql_state.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,7 @@ constexpr utils::TrivialSet kStateWhitelist = [](auto selector) {
424424
/** @name Misc errors that are logged to LOG_WARNING instead of LOG_ERROR */
425425
.Case(SqlState::kUniqueViolation)
426426
.Case(SqlState::kForeignKeyViolation)
427+
.Case(SqlState::kSerializationFailure)
427428
.Case(SqlState::kDuplicatePreparedStatement)
428429
//@}
429430
;

0 commit comments

Comments
 (0)