@@ -50,7 +50,7 @@ class QuiesceAgentTest : public testing::Test {
5050 QuiesceDbVersion get_latest_version ()
5151 {
5252 std::lock_guard l (agent_mutex);
53- return std::max ({ current.db_version , working. db_version , pending.db_version } );
53+ return std::max (current.db_version , pending.db_version );
5454 }
5555 TrackedRoots& mutable_tracked_roots () {
5656 return current.roots ;
@@ -60,6 +60,16 @@ class QuiesceAgentTest : public testing::Test {
6060 std::unique_lock l (agent_mutex);
6161 return await_idle_locked (l);
6262 }
63+
64+ using TRV = TrackedRootsVersion;
65+ std::optional<std::function<void (TRV& pending, TRV& current)>> before_work;
66+
67+ void _agent_thread_will_work () {
68+ auto f = before_work;
69+ if (f) {
70+ (*f)(pending, current);
71+ }
72+ }
6373 };
6474 QuiesceMap latest_ack;
6575 std::unordered_map<QuiesceRoot, QuiescingRoot> quiesce_requests;
@@ -98,19 +108,12 @@ class QuiesceAgentTest : public testing::Test {
98108 auto [it, inserted] = quiesce_requests.try_emplace (r, req_id, c);
99109
100110 if (!inserted) {
101- // we must update the request id so that old one can't cancel this request.
102- it->second .first = req_id;
103- if (it->second .second ) {
104- it->second .second ->complete (-EINTR);
105- it->second .second = c;
106- } else {
107- // if we have no context, it means we've completed it
108- // since we weren't inserted, we must have successfully quiesced
109- c->complete (0 );
110- }
111+ // it's a conflict that MDCache doesn't deal with
112+ c->complete (-EINPROGRESS);
113+ return req_id;
114+ } else {
115+ return it->second .first ;
111116 }
112-
113- return it->second .first ;
114117 };
115118
116119 ci.cancel_request = [this ](RequestHandle h) {
@@ -177,10 +180,10 @@ class QuiesceAgentTest : public testing::Test {
177180 }
178181
179182 template <class _Rep = std::chrono::seconds::rep, class _Period = std::chrono::seconds::period, typename D = std::chrono::duration<_Rep, _Period>>
180- bool await_idle_v (QuiesceDbVersion version , D timeout = std::chrono::duration_cast<D>(std::chrono::seconds(10 )))
183+ bool await_idle_v (QuiesceSetVersion v , D timeout = std::chrono::duration_cast<D>(std::chrono::seconds(10 )))
181184 {
182- return timed_run (timeout, [this , version ] {
183- while (version > agent->await_idle ()) { };
185+ return timed_run (timeout, [this , v ] {
186+ while (QuiesceDbVersion { 1 , v} > agent->await_idle ()) { };
184187 });
185188 }
186189
@@ -481,32 +484,35 @@ TEST_F(QuiesceAgentTest, DuplicateQuiesceRequest) {
481484
482485 EXPECT_TRUE (await_idle ());
483486
484- // now we should have seen the ack with root2 quiesced
487+ // root1 and root2 are still registered internally
488+ // so it should result in a failure to quiesce them again
485489 EXPECT_EQ (3 , latest_ack.db_version );
486- EXPECT_EQ (1 , latest_ack.roots .size ());
487- EXPECT_EQ (QS_QUIESCED, latest_ack.roots .at (" root1" ).state );
490+ EXPECT_EQ (2 , latest_ack.roots .size ());
491+ EXPECT_EQ (QS_FAILED, latest_ack.roots .at (" root1" ).state );
492+ EXPECT_EQ (QS_FAILED, latest_ack.roots .at (" root2" ).state );
488493
489494 // the actual state of the pinned objects shouldn't have changed
490495 EXPECT_EQ (QS_QUIESCED, pinned1->get_actual_state ());
491- EXPECT_EQ (QS_FAILED , pinned2->get_actual_state ());
496+ EXPECT_EQ (QS_QUIESCING , pinned2->get_actual_state ());
492497
493498 EXPECT_EQ (0 , *pinned1->quiesce_result );
494- EXPECT_EQ (-EINTR, * pinned2->quiesce_result );
499+ EXPECT_FALSE ( pinned2->quiesce_result . has_value () );
495500
496- // releasing the pinned objects will attempt to cancel, but that shouldn't interfere with the current state
501+ // releasing the pinned objects should cancel and remove from internal requests
497502 pinned1.reset ();
498503 pinned2.reset ();
499504
500- EXPECT_TRUE (quiesce_requests.contains (" root1" ));
501- EXPECT_TRUE (quiesce_requests.contains (" root2" ));
505+ EXPECT_FALSE (quiesce_requests.contains (" root1" ));
506+ EXPECT_FALSE (quiesce_requests.contains (" root2" ));
502507
503- EXPECT_TRUE (complete_quiesce (" root2 " ));
508+ EXPECT_TRUE (complete_quiesce (" root3 " ));
504509
505510 EXPECT_TRUE (await_idle ());
506511 EXPECT_EQ (3 , latest_ack.db_version );
507- EXPECT_EQ (2 , latest_ack.roots .size ());
508- EXPECT_EQ (QS_QUIESCED, latest_ack.roots .at (" root1" ).state );
509- EXPECT_EQ (QS_QUIESCED, latest_ack.roots .at (" root2" ).state );
512+ EXPECT_EQ (3 , latest_ack.roots .size ());
513+ EXPECT_EQ (QS_FAILED, latest_ack.roots .at (" root1" ).state );
514+ EXPECT_EQ (QS_FAILED, latest_ack.roots .at (" root2" ).state );
515+ EXPECT_EQ (QS_QUIESCED, latest_ack.roots .at (" root3" ).state );
510516}
511517
512518TEST_F (QuiesceAgentTest, TimeoutBeforeComplete)
@@ -549,3 +555,51 @@ TEST_F(QuiesceAgentTest, TimeoutBeforeComplete)
549555 EXPECT_EQ (0 , tracked.size ());
550556 }
551557}
558+
559+
560+ TEST_F (QuiesceAgentTest, RapidDbUpdates)
561+ {
562+ // This validates that the same new root that happens to be reported
563+ // more than once before we have chance to process it is not submitted
564+ // multiple times
565+
566+ // set a handler that will post v2 whlie we're working on v1
567+ agent->before_work = [this ](TestQuiesceAgent::TRV& p, TestQuiesceAgent::TRV& c) {
568+ if (c.db_version .set_version != 1 ) {
569+ return ;
570+ }
571+ agent->before_work .reset ();
572+ auto ack = update (2 , {
573+ { " root1" , QS_QUIESCING },
574+ { " root2" , QS_QUIESCING },
575+ });
576+
577+ ASSERT_TRUE (ack.has_value ());
578+ EXPECT_EQ (2 , ack->db_version );
579+ EXPECT_EQ (0 , ack->roots .size ());
580+ };
581+
582+ {
583+ auto ack = update (1 , {
584+ { " root1" , QS_QUIESCING },
585+ });
586+
587+ ASSERT_TRUE (ack.has_value ());
588+ EXPECT_EQ (1 , ack->db_version );
589+ EXPECT_EQ (0 , ack->roots .size ());
590+ }
591+
592+ EXPECT_TRUE (await_idle_v (2 ));
593+
594+ // nothing should be in the ack
595+ // if we incorrectly submit root1 twice
596+ // then it should be repored here as FAILED
597+ EXPECT_EQ (2 , latest_ack.db_version );
598+ EXPECT_EQ (0 , latest_ack.roots .size ());
599+
600+ {
601+ auto tracked = agent->tracked_roots ();
602+ EXPECT_EQ (2 , tracked.size ());
603+ }
604+ }
605+
0 commit comments