@@ -48,6 +48,11 @@ class MockDistLockStrategy final : public dist_lock::DistLockStrategyBase {
4848 *locked_by = locker_id;
4949 }
5050
51+ void Prolong (std::chrono::milliseconds lock_ttl, const std::string& locker_id) override {
52+ Acquire (lock_ttl, locker_id);
53+ prolongs_++;
54+ }
55+
5156 void Release (const std::string& locker_id) override {
5257 auto locked_by = locked_by_var_.Lock ();
5358 if (*locked_by == locker_id) {
@@ -69,15 +74,51 @@ class MockDistLockStrategy final : public dist_lock::DistLockStrategyBase {
6974
7075 size_t GetAttemptsCount () const { return attempts_; }
7176
77+ size_t GetProlongsCount () const { return prolongs_; }
78+
7279private:
7380 concurrent::Variable<std::string> locked_by_var_;
7481 std::atomic<bool > allowed_{false };
7582 std::atomic<size_t > attempts_{0 };
83+ std::atomic<size_t > prolongs_{0 };
7684};
7785// / [Sample distlock strategy]
7886
7987auto MakeMockStrategy () { return std::make_shared<MockDistLockStrategy>(); }
8088
89+ // A strategy that intentionally does NOT override Prolong(), so refreshes fall
90+ // back to the default DistLockStrategyBase::Prolong() (which calls Acquire()).
91+ class DefaultProlongStrategy final : public dist_lock::DistLockStrategyBase {
92+ public:
93+ ~DefaultProlongStrategy () override {
94+ auto locked_by = locked_by_var_.Lock ();
95+ EXPECT_TRUE (locked_by->empty ());
96+ }
97+
98+ void Acquire (std::chrono::milliseconds, const std::string& locker_id) override {
99+ UASSERT (!locker_id.empty ());
100+ acquires_++;
101+ auto locked_by = locked_by_var_.Lock ();
102+ if (!locked_by->empty () && *locked_by != locker_id) {
103+ throw dist_lock::LockIsAcquiredByAnotherHostException ();
104+ }
105+ *locked_by = locker_id;
106+ }
107+
108+ void Release (const std::string& locker_id) override {
109+ auto locked_by = locked_by_var_.Lock ();
110+ if (*locked_by == locker_id) {
111+ locked_by->clear ();
112+ }
113+ }
114+
115+ size_t GetAcquiresCount () const { return acquires_; }
116+
117+ private:
118+ concurrent::Variable<std::string> locked_by_var_;
119+ std::atomic<size_t > acquires_{0 };
120+ };
121+
81122class DistLockWorkload {
82123public:
83124 explicit DistLockWorkload (bool abort_on_cancel = false )
@@ -470,4 +511,40 @@ UTEST_MT(LockedTask, SingleAttemptExample, 3) {
470511 // / [Sample distributed locked task SingleAttempt]
471512}
472513
514+ UTEST_MT (LockedWorker, ProlongUsedForRefresh, 3 ) {
515+ auto strategy = MakeMockStrategy ();
516+ DistLockWorkload work;
517+ dist_lock::DistLockedWorker locked_worker (kWorkerName , [&] { work.Work (); }, strategy, MakeSettings ());
518+
519+ locked_worker.Start ();
520+ strategy->Allow (true );
521+ EXPECT_TRUE (work.WaitForLocked (true , utest::kMaxTestWaitTime ));
522+
523+ for (int i = 0 ; i < 100 && strategy->GetProlongsCount () < 2 ; ++i) {
524+ engine::InterruptibleSleepFor (kAttemptInterval );
525+ }
526+
527+ EXPECT_LE (2U , strategy->GetProlongsCount ());
528+ EXPECT_LT (strategy->GetProlongsCount (), strategy->GetAttemptsCount ());
529+
530+ locked_worker.Stop ();
531+ }
532+
533+ UTEST_MT (LockedWorker, DefaultProlongRoutesThroughAcquire, 3 ) {
534+ auto strategy = std::make_shared<DefaultProlongStrategy>();
535+ DistLockWorkload work;
536+ dist_lock::DistLockedWorker locked_worker (kWorkerName , [&] { work.Work (); }, strategy, MakeSettings ());
537+
538+ locked_worker.Start ();
539+ EXPECT_TRUE (work.WaitForLocked (true , utest::kMaxTestWaitTime ));
540+
541+ const auto acquires_after_take = strategy->GetAcquiresCount ();
542+ for (int i = 0 ; i < 100 && strategy->GetAcquiresCount () <= acquires_after_take; ++i) {
543+ engine::InterruptibleSleepFor (kAttemptInterval );
544+ }
545+ EXPECT_LT (acquires_after_take, strategy->GetAcquiresCount ());
546+
547+ locked_worker.Stop ();
548+ }
549+
473550USERVER_NAMESPACE_END
0 commit comments