@@ -197,6 +197,7 @@ class TPQTabletFixture : public NUnitTest::TBaseFixture {
197197
198198 void WaitReadSetAck (NHelpers::TPQTabletMock& tablet, const TReadSetAckMatcher& matcher);
199199 void SendReadSetAck (NHelpers::TPQTabletMock& tablet);
200+ void WaitForNoReadSetAck (NHelpers::TPQTabletMock& tablet);
200201
201202 void SendDropTablet (const TDropTabletParams& params);
202203 void WaitDropTabletReply (const TDropTabletReplyMatcher& matcher);
@@ -206,7 +207,7 @@ class TPQTabletFixture : public NUnitTest::TBaseFixture {
206207
207208 void SendCancelTransactionProposal (const TCancelTransactionProposalParams& params);
208209
209- void StartPQWriteTxsObserver ();
210+ void StartPQWriteTxsObserver (TAutoPtr<IEventHandle>* ev = nullptr );
210211 void WaitForPQWriteTxs ();
211212
212213 template <class T > void WaitForEvent (size_t count);
@@ -215,7 +216,7 @@ class TPQTabletFixture : public NUnitTest::TBaseFixture {
215216
216217 void TestWaitingForTEvReadSet (size_t senders, size_t receivers);
217218
218- void StartPQWriteObserver (bool & flag, unsigned cookie);
219+ void StartPQWriteObserver (bool & flag, unsigned cookie, TAutoPtr<IEventHandle>* ev = nullptr );
219220 void WaitForPQWriteComplete (bool & flag);
220221
221222 bool FoundPQWriteState = false ;
@@ -241,6 +242,9 @@ class TPQTabletFixture : public NUnitTest::TBaseFixture {
241242 void WaitForTxState (ui64 txId, NKikimrPQ::TTransaction::EState state);
242243 void WaitForExecStep (ui64 step);
243244
245+ void InterceptSaveTxState (TAutoPtr<IEventHandle>& event);
246+ void SendSaveTxState (TAutoPtr<IEventHandle>& event);
247+
244248 //
245249 // TODO(abcdef): для тестирования повторных вызовов нужны примитивы Send+Wait
246250 //
@@ -522,6 +526,17 @@ void TPQTabletFixture::WaitReadSetAck(NHelpers::TPQTabletMock& tablet, const TRe
522526 }
523527}
524528
529+ void TPQTabletFixture::WaitForNoReadSetAck (NHelpers::TPQTabletMock& tablet)
530+ {
531+ TDispatchOptions options;
532+ options.CustomFinalCondition = [&]() {
533+ return tablet.ReadSetAck .Defined ();
534+ };
535+ Ctx->Runtime ->DispatchEvents (options, TDuration::Seconds (2 ));
536+
537+ UNIT_ASSERT (!tablet.ReadSetAck .Defined ());
538+ }
539+
525540void TPQTabletFixture::SendDropTablet (const TDropTabletParams& params)
526541{
527542 auto event = MakeHolder<TEvPersQueue::TEvDropTablet>();
@@ -745,18 +760,21 @@ void TPQTabletFixture::WaitWriteResponse(const TWriteResponseMatcher& matcher)
745760 Ctx->Runtime ->SetObserverFunc (prev);
746761}
747762
748- void TPQTabletFixture::StartPQWriteObserver (bool & flag, unsigned cookie)
763+ void TPQTabletFixture::StartPQWriteObserver (bool & flag, unsigned cookie, TAutoPtr<IEventHandle>* ev )
749764{
750765 flag = false ;
751766
752- auto observer = [&flag, cookie](TAutoPtr<IEventHandle>& event) {
767+ auto observer = [&flag, cookie, ev ](TAutoPtr<IEventHandle>& event) {
753768 if (auto * kvResponse = event->CastAsLocal <TEvKeyValue::TEvResponse>()) {
754- if (kvResponse->Record .HasCookie ()) {
755- }
756769 if ((event->Sender == event->Recipient ) &&
757770 kvResponse->Record .HasCookie () &&
758771 (kvResponse->Record .GetCookie () == cookie)) {
759772 flag = true ;
773+
774+ if (ev) {
775+ *ev = event;
776+ return TTestActorRuntimeBase::EEventAction::DROP;
777+ }
760778 }
761779 }
762780
@@ -793,9 +811,9 @@ void TPQTabletFixture::SendCancelTransactionProposal(const TCancelTransactionPro
793811 event.Release ());
794812}
795813
796- void TPQTabletFixture::StartPQWriteTxsObserver ()
814+ void TPQTabletFixture::StartPQWriteTxsObserver (TAutoPtr<IEventHandle>* event )
797815{
798- StartPQWriteObserver (FoundPQWriteTxs, 5 ); // TPersQueue::WRITE_TX_COOKIE
816+ StartPQWriteObserver (FoundPQWriteTxs, 5 , event ); // TPersQueue::WRITE_TX_COOKIE
799817}
800818
801819void TPQTabletFixture::WaitForPQWriteTxs ()
@@ -1030,6 +1048,40 @@ void TPQTabletFixture::WaitForExecStep(ui64 step)
10301048 UNIT_FAIL (" expected execution step " << step);
10311049}
10321050
1051+ void TPQTabletFixture::InterceptSaveTxState (TAutoPtr<IEventHandle>& ev)
1052+ {
1053+ bool found = false ;
1054+
1055+ TTestActorRuntimeBase::TEventFilter prev;
1056+ auto filter = [&](TTestActorRuntimeBase&, TAutoPtr<IEventHandle>& event) -> bool {
1057+ if (auto * msg = event->CastAsLocal <TEvKeyValue::TEvRequest>()) {
1058+ if (msg->Record .HasCookie () && (msg->Record .GetCookie () == 5 )) { // WRITE_TX_COOKIE
1059+ ev = event;
1060+ found = true ;
1061+ return true ;
1062+ }
1063+ }
1064+
1065+ return false ;
1066+ };
1067+ prev = Ctx->Runtime ->SetEventFilter (filter);
1068+
1069+ TDispatchOptions options;
1070+ options.CustomFinalCondition = [&found]() {
1071+ return found;
1072+ };
1073+
1074+ UNIT_ASSERT (Ctx->Runtime ->DispatchEvents (options));
1075+ UNIT_ASSERT (found);
1076+
1077+ Ctx->Runtime ->SetEventFilter (prev);
1078+ }
1079+
1080+ void TPQTabletFixture::SendSaveTxState (TAutoPtr<IEventHandle>& event)
1081+ {
1082+ Ctx->Runtime ->Send (event);
1083+ }
1084+
10331085Y_UNIT_TEST_F (Parallel_Transactions_1, TPQTabletFixture)
10341086{
10351087 TestParallelTransactions (" consumer" , " consumer" );
@@ -1926,6 +1978,45 @@ Y_UNIT_TEST_F(After_Restarting_The_Tablet_Sends_A_TEvReadSet_For_Transactions_In
19261978 WaitReadSetEx (*tablet, {.Step =100 , .TxId =txId_1, .Decision =NKikimrTx::TReadSetData::DECISION_COMMIT, .Count =2 });
19271979}
19281980
1981+ Y_UNIT_TEST_F (TEvReadSet_Is_Not_Sent_Ahead_Of_Time, TPQTabletFixture)
1982+ {
1983+ const ui64 txId = 67890 ;
1984+ const ui64 mockTabletId = 22222 ;
1985+
1986+ NHelpers::TPQTabletMock* tablet = CreatePQTabletMock (mockTabletId);
1987+ PQTabletPrepare ({.partitions =1 }, {}, *Ctx);
1988+
1989+ SendProposeTransactionRequest ({.TxId =txId,
1990+ .Senders ={mockTabletId}, .Receivers ={mockTabletId},
1991+ .TxOps ={
1992+ {.Partition =0 , .Consumer =" user" , .Begin =0 , .End =0 , .Path =" /topic" },
1993+ }});
1994+ WaitProposeTransactionResponse ({.TxId =txId,
1995+ .Status =NKikimrPQ::TEvProposeTransactionResult::PREPARED});
1996+
1997+ SendPlanStep ({.Step =100 , .TxIds ={txId}});
1998+
1999+ WaitForCalcPredicateResult ();
2000+
2001+ tablet->SendReadSet (*Ctx->Runtime , {.Step =100 , .TxId =txId, .Target =Ctx->TabletId , .Decision =NKikimrTx::TReadSetData::DECISION_COMMIT});
2002+
2003+ // WaitProposeTransactionResponse({.TxId=txId,
2004+ // .Status=NKikimrPQ::TEvProposeTransactionResult::COMPLETE});
2005+
2006+ TAutoPtr<IEventHandle> kvRequest;
2007+ InterceptSaveTxState (kvRequest);
2008+
2009+ tablet->SendReadSet (*Ctx->Runtime , {.Step =100 , .TxId =txId, .Target =Ctx->TabletId , .Decision =NKikimrTx::TReadSetData::DECISION_COMMIT});
2010+
2011+ WaitForNoReadSetAck (*tablet);
2012+
2013+ SendSaveTxState (kvRequest);
2014+
2015+ WaitForTxState (txId, NKikimrPQ::TTransaction::EXECUTED);
2016+
2017+ WaitReadSetAck (*tablet, {.Step =100 , .TxId =txId, .Source =22222 , .Target =Ctx->TabletId , .Consumer =Ctx->TabletId });
2018+ }
2019+
19292020}
19302021
19312022}
0 commit comments