Skip to content

Commit 6e3fc50

Browse files
committed
feat docs: document the long transaction detection
Tests: на прод не влияет commit_hash:60bf568b8b4533be246d4a25774c122ac8a55160
1 parent 2ad1a8c commit 6e3fc50

File tree

10 files changed

+151
-20
lines changed

10 files changed

+151
-20
lines changed

.mapping.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4412,6 +4412,7 @@
44124412
"scripts/docs/en/userver/libraries/s3api.md":"taxi/uservices/userver/scripts/docs/en/userver/libraries/s3api.md",
44134413
"scripts/docs/en/userver/log_level_running_service.md":"taxi/uservices/userver/scripts/docs/en/userver/log_level_running_service.md",
44144414
"scripts/docs/en/userver/logging.md":"taxi/uservices/userver/scripts/docs/en/userver/logging.md",
4415+
"scripts/docs/en/userver/long_transactions.md":"taxi/uservices/userver/scripts/docs/en/userver/long_transactions.md",
44154416
"scripts/docs/en/userver/lru_cache.md":"taxi/uservices/userver/scripts/docs/en/userver/lru_cache.md",
44164417
"scripts/docs/en/userver/memory_profile_running_service.md":"taxi/uservices/userver/scripts/docs/en/userver/memory_profile_running_service.md",
44174418
"scripts/docs/en/userver/mongodb.md":"taxi/uservices/userver/scripts/docs/en/userver/mongodb.md",

core/include/userver/utils/trx_tracker.hpp

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#pragma once
22

3-
/// @file
3+
/// @file userver/utils/trx_tracker.hpp
44
/// @brief Tracking for heavy operations while having active transactions.
55

66
#include <optional>
@@ -13,16 +13,16 @@ USERVER_NAMESPACE_BEGIN
1313

1414
/// @brief Tracking for heavy operations while having active transactions.
1515
///
16-
/// Some operations, like http requests, are heavy and can take
17-
/// too long during an incident. If they are called during an active
18-
/// database transaction, connection will be held for longer and
19-
/// connection pool will be exhausted. Transaction tracker prevents this
20-
/// by holding counter of active transactions in TaskLocalVariable
16+
/// Some operations, like HTTP requests, are heavy and can take too long during an incident. If they are called during
17+
/// an active database transaction, connection will be held for longer and connection pool will be exhausted.
18+
/// Transaction tracker prevents this by holding counter of active transactions in TaskLocalVariable
2119
/// and checking for active transactions in heavy operations.
2220
///
2321
/// ## Example usage:
2422
///
2523
/// @snippet utils/trx_tracker_test.cpp Sample TransactionTracker usage
24+
///
25+
/// @see @ref scripts/docs/en/userver/long_transactions.md
2626
namespace utils::trx_tracker {
2727

2828
namespace impl {
@@ -42,9 +42,8 @@ bool IsEnabled() noexcept;
4242

4343
/// @brief Unique ID for every task.
4444
///
45-
/// Sometimes transactions start and end in different coroutines.
46-
/// To prevent transaction from incrementing and decrementing different
47-
/// transaction counters, TransactionLock stores TaskId on Lock and
45+
/// Sometimes transactions start and end in different coroutines. To prevent transaction from incrementing and
46+
/// decrementing different transaction counters, TransactionLock stores TaskId on Lock and
4847
/// checks that stored TaskId is the same as current TaskId in Unlock.
4948
class TaskId final {
5049
public:
@@ -90,10 +89,10 @@ void CheckNoTransactions(std::string_view location);
9089

9190
/// @brief Disable check for active transactions.
9291
///
93-
/// To consciously call a heavy operation in active transaction,
94-
/// check can be disabled by creating an instance of this class.
95-
/// Checks will be disabled until every instance either has
96-
/// Reenable() method called or is destroyed.
92+
/// To consciously call a heavy operation in active transaction, check can be disabled by creating an instance of this
93+
/// class. Checks will be disabled until every instance either has Reenable() method called or is destroyed.
94+
///
95+
/// @snippet utils/trx_tracker_test.cpp Sample CheckDisabler usage
9796
class CheckDisabler final {
9897
public:
9998
/// @brief Disable check for active transactions.

core/src/utils/trx_tracker_test.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,26 @@ utils::statistics::Rate GetTriggers() { return utils::trx_tracker::GetStatistics
1616

1717
using TrxTrackerFixture = utest::LogCaptureFixture<>;
1818

19+
class FakeTransaction {
20+
public:
21+
FakeTransaction() { lock_.Lock(); }
22+
23+
void Commit() { lock_.Unlock(); }
24+
25+
private:
26+
utils::trx_tracker::TransactionLock lock_{};
27+
};
28+
29+
class FakeCluster {
30+
public:
31+
FakeTransaction Begin() { return {}; }
32+
};
33+
34+
class FakeClient {
35+
public:
36+
void SomeHandle(int) { utils::trx_tracker::CheckNoTransactions(); }
37+
};
38+
1939
} // namespace
2040

2141
UTEST_F(TrxTrackerFixture, AssertInTransaction) {
@@ -189,6 +209,27 @@ UTEST(TrxTracker, CopyAssignmentSelf) {
189209
EXPECT_EQ(GetTriggers(), 1);
190210
}
191211

212+
UTEST(TrxTracker, CheckDisablerSample) {
213+
utils::trx_tracker::ResetStatistics();
214+
const utils::trx_tracker::impl::GlobalEnabler enabler;
215+
216+
FakeCluster cluster;
217+
FakeClient client;
218+
int request = 42;
219+
220+
/// [Sample CheckDisabler usage]
221+
auto trx = cluster.Begin();
222+
{
223+
utils::trx_tracker::CheckDisabler disabler;
224+
client.SomeHandle(request); // calls utils::trx_tracker::CheckNoTransactions()
225+
}
226+
227+
trx.Commit();
228+
/// [Sample CheckDisabler usage]
229+
230+
EXPECT_EQ(GetTriggers(), 0);
231+
}
232+
192233
UTEST(TrxTracker, AssertWithDisabler) {
193234
utils::trx_tracker::ResetStatistics();
194235
const utils::trx_tracker::impl::GlobalEnabler enabler;

scripts/docs/en/index.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ and make sure that it builds and passes tests.
134134
* @ref scripts/docs/en/userver/congestion_control.md
135135
* @ref scripts/docs/en/userver/stack.md
136136
* @ref scripts/docs/en/userver/dump_coroutines.md
137+
* @ref scripts/docs/en/userver/long_transactions.md
137138

138139

139140
## Caches
@@ -185,6 +186,7 @@ and make sure that it builds and passes tests.
185186
* @ref scripts/docs/en/userver/libraries/easy.md
186187
* @ref scripts/docs/en/userver/libraries/s3api.md
187188
* @ref scripts/docs/en/userver/libraries/grpc-reflection.md
189+
* @ref scripts/docs/en/userver/libraries/multi_index_lru.md
188190

189191
## Opensource
190192
* @ref scripts/docs/en/userver/development/stability.md

scripts/docs/en/userver/caches.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,5 +227,5 @@ Each cache automatically collects metrics. See
227227
----------
228228

229229
@htmlonly <div class="bottom-nav"> @endhtmlonly
230-
@ref scripts/docs/en/userver/dump_coroutines.md | @ref scripts/docs/en/userver/cache_dumps.md ⇨
230+
@ref scripts/docs/en/userver/long_transactions.md | @ref scripts/docs/en/userver/cache_dumps.md ⇨
231231
@htmlonly </div> @endhtmlonly

scripts/docs/en/userver/development/stability.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,5 +69,5 @@ There are tiers to differentiate technologies:
6969
----------
7070

7171
@htmlonly <div class="bottom-nav"> @endhtmlonly
72-
@ref scripts/docs/en/userver/libraries/grpc-reflection.md | @ref scripts/docs/en/userver/driver_guide.md ⇨
72+
@ref scripts/docs/en/userver/libraries/multi_index_lru.md | @ref scripts/docs/en/userver/driver_guide.md ⇨
7373
@htmlonly </div> @endhtmlonly

scripts/docs/en/userver/dump_coroutines.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,5 @@ json
100100
----------
101101

102102
@htmlonly <div class="bottom-nav"> @endhtmlonly
103-
@ref scripts/docs/en/userver/stack.md |
104-
@ref scripts/docs/en/userver/caches.md ⇨
103+
@ref scripts/docs/en/userver/stack.md | @ref scripts/docs/en/userver/long_transactions.md ⇨
105104
@htmlonly </div> @endhtmlonly

scripts/docs/en/userver/libraries/grpc-reflection.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ components_manager:
4444
4545
4. Thats it. Reflection component will start automatically and expose every gRPC entity in your service.
4646
47+
48+
----------
49+
4750
@htmlonly <div class="bottom-nav"> @endhtmlonly
48-
⇦ @ref scripts/docs/en/userver/libraries/s3api.md | @ref scripts/docs/en/userver/development/stability.md ⇨
51+
⇦ @ref scripts/docs/en/userver/libraries/s3api.md | @ref scripts/docs/en/userver/libraries/multi_index_lru.md ⇨
4952
@htmlonly </div> @endhtmlonly

scripts/docs/en/userver/libraries/multi_index_lru.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,19 @@
22

33
## Introduction
44

5-
Generic LRU (Least Recently Used) cache container implementation that combines Boost.MultiIndex for flexible indexing with Boost.Intrusive for efficient LRU tracking. It uses `namespace multi_index_lru`.
5+
Generic LRU (Least Recently Used) cache container implementation that combines Boost.MultiIndex for flexible indexing
6+
for efficient LRU tracking. It uses @ref multi_index_lru.
67

7-
The container maintains elements in access order while supporting multiple indexing strategies through Boost.MultiIndex. The LRU eviction policy automatically removes the least recently accessed items when capacity is reached.
8+
The container maintains elements in access order while supporting multiple indexing strategies through Boost.MultiIndex.
9+
The LRU eviction policy automatically removes the least recently accessed items when capacity is reached.
810

911
## Usage
1012

1113
@snippet libraries/multi-index-lru/src/main_test.cpp Usage
1214

15+
16+
----------
17+
18+
@htmlonly <div class="bottom-nav"> @endhtmlonly
19+
@ref scripts/docs/en/userver/libraries/grpc-reflection.md | @ref scripts/docs/en/userver/development/stability.md ⇨
20+
@htmlonly </div> @endhtmlonly
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# Long Transactions Detection
2+
3+
When working with databases, 🐙 userver provides the ability to create a transaction in code and execute database
4+
queries within it. From creation to commit/rollback, a transaction holds one of the database connections entirely.
5+
If a heavy operation is called during the transaction’s lifetime (e.g., an HTTP request), then in case of an incident
6+
(e.g., the service being called via HTTP goes down), the execution time of that operation will increase.
7+
Consequently, the transaction’s lifetime will also increase while the connection is hold, which may cause the entire
8+
database to fail.
9+
10+
Consequently there is a built-in mechanism for long transactions detection. See also @ref utils::trx_tracker.
11+
12+
13+
## Drawbacks of Long Transactions
14+
15+
* **Exhaustion of the connection pool**. Each transaction occupies a database connection for its entire duration.
16+
Prolonged transactions tie up more connections, risking exhaustion of the connection pool.
17+
* **Exhaustion of database resources**. To ensure transaction isolation between data writing and completion, the
18+
database must maintain two versions of each row (old and new). With many concurrent transactions, the number of row
19+
duplicates grows significantly, leading to substantial increases in table storage usage.
20+
* **Execution linearization**. Only one operation can modify a specific database row at a time; others must wait.
21+
If a writing transaction becomes blocked, all subsequent write operations are also halt prepared to proceed.
22+
23+
24+
## Where the Long Transactions Detection check works
25+
26+
To detect long operations within transactions, we insert a check for active transactions into every heavy operation
27+
in userver. When triggered, this check writes a log and a metric.
28+
29+
The check is inserted into:
30+
31+
+ HTTP requests:
32+
+ Synchronous code‑generated requests;
33+
+ Waiting for asynchronous code‑generated requests;
34+
+ Working with STQ (CallLater, Reschedule, ...);
35+
+ gRPC requests.
36+
37+
Transactions from the following databases are monitored:
38+
39+
+ MySQL;
40+
+ PostgreSQL;
41+
+ SQLite;
42+
+ YDB.
43+
44+
The check works in testsuite tests but is disabled in gtest and gbenchmark.
45+
46+
47+
## How the Long Transactions Detection check failure is displayed?
48+
49+
**Logs**:
50+
51+
When the check is triggered, the service will spam `WARN` logs with the following text:
52+
`Long call while having active transactions`. The `meta.location` field displays the name of the method that calls the
53+
endpoint where the check was triggered.
54+
55+
**Metrics**:
56+
57+
Metric: `engine.heavy-operations-in-transactions` is incremented in case of heavy operation in transaction.
58+
59+
60+
## How to fix the Long Transactions Detection check failure
61+
62+
Move the heavy request out from the transaction.
63+
64+
If the bahavior is understood and it is fine to have a long transaction in that particular case, the check could be
65+
disabled for a scope via @ref utils::trx_tracker::CheckDisabler :
66+
67+
@snippet utils/trx_tracker_test.cpp Sample CheckDisabler usage
68+
69+
To disable the check for the whole service use the `enable_trx_tracker` static configuration option of
70+
@ref components::ManagerControllerComponent. Note that this is **not** recommended.
71+
72+
73+
----------
74+
75+
@htmlonly <div class="bottom-nav"> @endhtmlonly
76+
@ref scripts/docs/en/userver/dump_coroutines.md | @ref scripts/docs/en/userver/caches.md ⇨
77+
@htmlonly </div> @endhtmlonly
78+

0 commit comments

Comments
 (0)