@@ -283,7 +283,7 @@ class TPartitionFixture : public NUnitTest::TBaseFixture {
283283 void SendReserveBytes (const ui64 cookie, const ui32 size, const TString& ownerCookie, const ui64 messageNo, bool lastRequest = false );
284284 void SendChangeOwner (const ui64 cookie, const TString& owner, const TActorId& pipeClient, const bool force = true );
285285 void SendWrite (const ui64 cookie, const ui64 messageNo, const TString& ownerCookie, const TMaybe<ui64> offset, const TString& data,
286- bool ignoreQuotaDeadline = false , ui64 seqNo = 0 );
286+ bool ignoreQuotaDeadline = false , ui64 seqNo = 0 , bool isDirectWrite = false );
287287 void SendGetWriteInfo ();
288288 void ShadowPartitionCountersTest (bool isFirstClass);
289289
@@ -298,6 +298,14 @@ class TPartitionFixture : public NUnitTest::TBaseFixture {
298298 void SendEvent (IEventBase* event);
299299 void SendEvent (IEventBase* event, const TActorId& from, const TActorId& to);
300300
301+ THolder<TEvPQ::TEvApproveWriteQuota> WaitForRequestQuotaAndHoldApproveWriteQuota ();
302+ void SendDeletePartition ();
303+ void WaitForDeletePartitionDoneTimeout ();
304+ void SendApproveWriteQuota (THolder<TEvPQ::TEvApproveWriteQuota>&& event);
305+ void WaitForQuotaConsumed ();
306+ void WaitForWriteError (ui64 cookie, NPersQueue::NErrorCode::EErrorCode errorCode);
307+ void WaitForDeletePartitionDone ();
308+
301309 TMaybe<TTestContext> Ctx;
302310 TMaybe<TFinalizer> Finalizer;
303311
@@ -635,7 +643,7 @@ void TPartitionFixture::SendReserveBytes(const ui64 cookie, const ui32 size, con
635643
636644void TPartitionFixture::SendWrite
637645 (const ui64 cookie, const ui64 messageNo, const TString& ownerCookie, const TMaybe<ui64> offset, const TString& data,
638- bool ignoreQuotaDeadline, ui64 seqNo
646+ bool ignoreQuotaDeadline, ui64 seqNo, bool isDirectWrite
639647) {
640648 TEvPQ::TEvWrite::TMsg msg;
641649 msg.SourceId = " SourceId" ;
@@ -657,7 +665,7 @@ void TPartitionFixture::SendWrite
657665 TVector<TEvPQ::TEvWrite::TMsg> msgs;
658666 msgs.push_back (msg);
659667
660- auto event = MakeHolder<TEvPQ::TEvWrite>(cookie, messageNo, ownerCookie, offset, std::move (msgs), false , std::nullopt );
668+ auto event = MakeHolder<TEvPQ::TEvWrite>(cookie, messageNo, ownerCookie, offset, std::move (msgs), isDirectWrite , std::nullopt );
661669 Ctx->Runtime ->SingleSys ()->Send (new IEventHandle (ActorId, Ctx->Edge , event.Release ()));
662670}
663671
@@ -1274,6 +1282,92 @@ void TPartitionFixture::TestWriteSubDomainOutOfSpace(TDuration quotaWaitDuration
12741282 }
12751283}
12761284
1285+ THolder<TEvPQ::TEvApproveWriteQuota> TPartitionFixture::WaitForRequestQuotaAndHoldApproveWriteQuota ()
1286+ {
1287+ THolder<TEvPQ::TEvApproveWriteQuota> approveWriteQuota;
1288+
1289+ auto observer = [&approveWriteQuota](TAutoPtr<IEventHandle>& ev) mutable {
1290+ if (auto * event = ev->CastAsLocal <TEvPQ::TEvApproveWriteQuota>()) {
1291+ approveWriteQuota = MakeHolder<TEvPQ::TEvApproveWriteQuota>(event->Cookie ,
1292+ event->AccountQuotaWaitTime ,
1293+ event->PartitionQuotaWaitTime );
1294+ return TTestActorRuntimeBase::EEventAction::DROP;
1295+ }
1296+ return TTestActorRuntimeBase::EEventAction::PROCESS;
1297+ };
1298+ auto prevObserver = Ctx->Runtime ->SetObserverFunc (observer);
1299+
1300+ TDispatchOptions options;
1301+ options.CustomFinalCondition = [&]() {
1302+ return approveWriteQuota != nullptr ;
1303+ };
1304+ UNIT_ASSERT (Ctx->Runtime ->DispatchEvents (options));
1305+
1306+ Ctx->Runtime ->SetObserverFunc (prevObserver);
1307+
1308+ UNIT_ASSERT (approveWriteQuota != nullptr );
1309+
1310+ return approveWriteQuota;
1311+ }
1312+
1313+ void TPartitionFixture::SendDeletePartition ()
1314+ {
1315+ auto event = MakeHolder<TEvPQ::TEvDeletePartition>();
1316+ Ctx->Runtime ->SingleSys ()->Send (new IEventHandle (ActorId, Ctx->Edge , event.Release ()));
1317+ }
1318+
1319+ void TPartitionFixture::WaitForDeletePartitionDoneTimeout ()
1320+ {
1321+ auto event = Ctx->Runtime ->GrabEdgeEvent <TEvPQ::TEvDeletePartitionDone>(TDuration::Seconds (3 ));
1322+ UNIT_ASSERT_VALUES_EQUAL (event, nullptr );
1323+ }
1324+
1325+ void TPartitionFixture::SendApproveWriteQuota (THolder<TEvPQ::TEvApproveWriteQuota>&& event)
1326+ {
1327+ Ctx->Runtime ->SingleSys ()->Send (new IEventHandle (ActorId, Ctx->Edge , event.Release ()));
1328+ event = nullptr ;
1329+ }
1330+
1331+ void TPartitionFixture::WaitForQuotaConsumed ()
1332+ {
1333+ bool hasQuotaConsumed = false ;
1334+
1335+ auto observer = [&hasQuotaConsumed](TAutoPtr<IEventHandle>& ev) mutable {
1336+ if (auto * event = ev->CastAsLocal <TEvPQ::TEvConsumed>()) {
1337+ hasQuotaConsumed = true ;
1338+ }
1339+ return TTestActorRuntimeBase::EEventAction::PROCESS;
1340+ };
1341+ auto prevObserver = Ctx->Runtime ->SetObserverFunc (observer);
1342+
1343+ TDispatchOptions options;
1344+ options.CustomFinalCondition = [&]() {
1345+ return hasQuotaConsumed;
1346+ };
1347+ UNIT_ASSERT (Ctx->Runtime ->DispatchEvents (options));
1348+
1349+ Ctx->Runtime ->SetObserverFunc (prevObserver);
1350+
1351+ UNIT_ASSERT (hasQuotaConsumed);
1352+ }
1353+
1354+ void TPartitionFixture::WaitForWriteError (ui64 cookie, NPersQueue::NErrorCode::EErrorCode errorCode)
1355+ {
1356+ auto event = Ctx->Runtime ->GrabEdgeEvent <TEvPQ::TEvError>();
1357+
1358+ UNIT_ASSERT (event != nullptr );
1359+
1360+ UNIT_ASSERT_VALUES_EQUAL (cookie, event->Cookie );
1361+ UNIT_ASSERT_C (errorCode == event->ErrorCode , " extected: " << (int )errorCode << " , accepted: " << (int )event->ErrorCode );
1362+ }
1363+
1364+ void TPartitionFixture::WaitForDeletePartitionDone ()
1365+ {
1366+ auto event = Ctx->Runtime ->GrabEdgeEvent <TEvPQ::TEvDeletePartitionDone>();
1367+
1368+ UNIT_ASSERT (event != nullptr );
1369+ }
1370+
12771371struct TTestUserAct {
12781372 TSrcIdMap SourceIds = {};
12791373 TString ClientId = {};
@@ -3270,6 +3364,36 @@ Y_UNIT_TEST_F(EndWriteTimestamp_HeadKeys, TPartitionFixture) {
32703364 UNIT_ASSERT_C (now - TDuration::Seconds (2 ) < endWriteTimestamp && endWriteTimestamp < now, " " << (now - TDuration::Seconds (2 )) << " < " << endWriteTimestamp << " < " << now );
32713365} // EndWriteTimestamp_FromMeta
32723366
3367+ Y_UNIT_TEST_F (The_DeletePartition_Message_Arrives_Before_The_ApproveWriteQuota_Message, TPartitionFixture)
3368+ {
3369+ // create a supportive partition
3370+ const TPartitionId partitionId{1 , TWriteId{2 , 3 }, 4 };
3371+ CreatePartition ({.Partition =partitionId});
3372+
3373+ // write 2 messages in it
3374+ SendWrite (1 , 0 , " owner" , 0 , " message #1" , false , 1 , true );
3375+ SendWrite (2 , 1 , " owner" , 1 , " message #2" , false , 2 , true );
3376+
3377+ // delay the response from the quoter
3378+ auto approveWriteQuota = WaitForRequestQuotaAndHoldApproveWriteQuota ();
3379+
3380+ // Send a `TEvDeletePartition`. The partition will wait for the response from the quoter to arrive.
3381+ SendDeletePartition ();
3382+ WaitForDeletePartitionDoneTimeout ();
3383+
3384+ // The answer is from the quoter
3385+ SendApproveWriteQuota (std::move (approveWriteQuota));
3386+ WaitForQuotaConsumed ();
3387+
3388+ WaitCmdWrite ();
3389+ SendCmdWriteResponse (NMsgBusProxy::MSTATUS_OK);
3390+
3391+ // Write operations fail with an error
3392+ WaitForWriteError (1 , NPersQueue::NErrorCode::ERROR);
3393+ WaitForDeletePartitionDone ();
3394+ WaitForWriteError (2 , NPersQueue::NErrorCode::ERROR);
3395+ }
3396+
32733397} // End of suite
32743398
32753399} // namespace
0 commit comments