diff --git a/cloud/filestore/libs/storage/service/service_ut_sharding.cpp b/cloud/filestore/libs/storage/service/service_ut_sharding.cpp index b07afc106b5..bbb3e7f683b 100644 --- a/cloud/filestore/libs/storage/service/service_ut_sharding.cpp +++ b/cloud/filestore/libs/storage/service/service_ut_sharding.cpp @@ -3855,6 +3855,158 @@ Y_UNIT_TEST_SUITE(TStorageServiceShardingTest) Sort(ids); UNIT_ASSERT_VALUES_EQUAL(expected, ids); } + + void CheckPendingCreateNodeInShards(bool withCreateHandle, bool withTabletReboot) + { + NProto::TStorageConfig config; + TTestEnv env({}, config); + env.CreateSubDomain("nfs"); + + ui32 nodeIdx = env.CreateNode("nfs"); + + const TString fsId = "test"; + const auto shard1Id = fsId + "-f1"; + const auto shard2Id = fsId + "-f2"; + + TServiceClient service(env.GetRuntime(), nodeIdx); + service.CreateFileStore(fsId, 1'000); + service.CreateFileStore(shard1Id, 1'000); + service.CreateFileStore(shard2Id, 1'000); + + ConfigureShards(service, fsId, shard1Id, shard2Id); + + auto headers = service.InitSession(fsId, "client"); + + bool createNodeObserved = false; + bool createHandleObserved = false; + TAutoPtr createNodeOnShard; + ui64 tabletId = -1; + + auto& runtime = env.GetRuntime(); + runtime.SetEventFilter( + [&](auto& runtime, TAutoPtr& event) + { + Y_UNUSED(runtime); + switch (event->GetTypeRewrite()) { + case TEvService::EvCreateHandleRequest: + createHandleObserved = true; + break; + case TEvService::EvCreateNodeRequest: { + const auto* msg = + event->Get(); + if (msg->Record.GetShardFileSystemId().empty()) { + createNodeOnShard = event.Release(); + return true; + } + createNodeObserved = true; + + break; + } + case TEvSSProxy::EvDescribeFileStoreResponse: { + using TDesc = TEvSSProxy::TEvDescribeFileStoreResponse; + const auto* msg = event->Get(); + const auto& desc = + msg->PathDescription.GetFileStoreDescription(); + if (desc.GetConfig().GetFileSystemId() == fsId) { + tabletId = desc.GetIndexTabletId(); + } + break; + } + } + return false; + }); + + auto checkListNodes = [&](auto expectedNames) { + auto listNodesRsp = + service.ListNodes(headers, fsId, RootNodeId)->Record; + + UNIT_ASSERT_VALUES_EQUAL(expectedNames.size(), listNodesRsp.NamesSize()); + UNIT_ASSERT_VALUES_EQUAL(expectedNames.size(), listNodesRsp.NodesSize()); + + const auto& names = listNodesRsp.GetNames(); + TVector listedNames(names.begin(), names.end()); + Sort(listedNames); + UNIT_ASSERT_VALUES_EQUAL(expectedNames, listedNames); + }; + + // send create node/handle, delay response in shard and make sure node is not + // listed + if (!withCreateHandle) { + service.SendCreateNodeRequest( + headers, + TCreateNodeArgs::File( + RootNodeId, + "file1", + 0, // mode + shard1Id)); + } else { + service.SendCreateHandleRequest( + headers, + fsId, + RootNodeId, + "file1", + TCreateHandleArgs::CREATE, + shard1Id); + } + runtime.DispatchEvents(TDispatchOptions(), TDuration::Seconds(1)); + + if (withCreateHandle) { + UNIT_ASSERT(!createNodeObserved); + UNIT_ASSERT(createHandleObserved); + } else { + UNIT_ASSERT(createNodeObserved); + UNIT_ASSERT(!createHandleObserved); + } + UNIT_ASSERT(createNodeOnShard); + + if (withTabletReboot) { + TIndexTabletClient tablet(env.GetRuntime(), nodeIdx, tabletId); + tablet.RebootTablet(); + + headers = service.InitSession(fsId, "client", {}, true); + } + + checkListNodes(TVector{}); + + // send delayed response in shard and make sure node is listed + auto createNodeRsp = + std::make_unique(); + runtime.Send( + new IEventHandle( + createNodeOnShard->Sender, + createNodeOnShard->Recipient, + createNodeRsp.release(), + 0, // flags + createNodeOnShard->Cookie), + nodeIdx); + runtime.DispatchEvents(TDispatchOptions(), TDuration::Seconds(1)); + + checkListNodes(TVector{"file1"}); + } + + Y_UNIT_TEST(ShouldNotListPendingCreateNodeInShards) + { + CheckPendingCreateNodeInShards( + false, // don't create handle + false // don't reboot tablet + ); + CheckPendingCreateNodeInShards( + false, // don't create handle + true // reboot tablet + ); + } + + Y_UNIT_TEST(ShouldNotListPendingCreateHandleInShards) + { + CheckPendingCreateNodeInShards( + true, // create handle + false // don't reboot tablet + ); + CheckPendingCreateNodeInShards( + true, // create handle + true // reboot tablet + ); + } } } // namespace NCloud::NFileStore::NStorage diff --git a/cloud/filestore/libs/storage/tablet/tablet_actor_createnode.cpp b/cloud/filestore/libs/storage/tablet/tablet_actor_createnode.cpp index c5fa5944a7b..e59a159c787 100644 --- a/cloud/filestore/libs/storage/tablet/tablet_actor_createnode.cpp +++ b/cloud/filestore/libs/storage/tablet/tablet_actor_createnode.cpp @@ -78,7 +78,7 @@ class TCreateNodeInShardActor final const TString LogTag; TRequestInfoPtr RequestInfo; const TActorId ParentId; - const NProto::TCreateNodeRequest Request; + NProto::TCreateNodeRequest Request; const ui64 RequestId; const ui64 OpLogEntryId; TCreateNodeInShardResult Result; @@ -263,6 +263,7 @@ void TCreateNodeInShardActor::ReplyAndDie( Request.GetHeaders().GetSessionId(), RequestId, OpLogEntryId, + std::move(*Request.MutableName()), std::move(Result))); Die(ctx); @@ -649,6 +650,8 @@ void TIndexTabletActor::HandleNodeCreatedInShard( WorkerActors.erase(ev->Sender); + EndNodeCreateInShard(msg->NodeName); + if (auto* x = std::get_if(&res)) { if (msg->RequestInfo) { auto response = @@ -759,6 +762,8 @@ void TIndexTabletActor::RegisterCreateNodeInShardActor( ui64 opLogEntryId, TCreateNodeInShardResult result) { + StartNodeCreateInShard(request.GetName()); + auto actor = std::make_unique( LogTag, std::move(requestInfo), diff --git a/cloud/filestore/libs/storage/tablet/tablet_actor_listnodes.cpp b/cloud/filestore/libs/storage/tablet/tablet_actor_listnodes.cpp index aed05db6090..fc6ebf463ff 100644 --- a/cloud/filestore/libs/storage/tablet/tablet_actor_listnodes.cpp +++ b/cloud/filestore/libs/storage/tablet/tablet_actor_listnodes.cpp @@ -195,11 +195,14 @@ void TIndexTabletActor::CompleteTx_ListNodes( const auto& ref = args.ChildRefs[i]; requestBytes += ref.Name.size(); if (ref.ShardId) { - AddExternalNode( - record, - ref.Name, - ref.ShardId, - ref.ShardNodeName); + if (!HasPendingNodeCreateInShard(ref.ShardNodeName)) { + AddExternalNode( + record, + ref.Name, + ref.ShardId, + ref.ShardNodeName); + + } continue; } diff --git a/cloud/filestore/libs/storage/tablet/tablet_private.h b/cloud/filestore/libs/storage/tablet/tablet_private.h index 43cf53aacce..99f62eb4be8 100644 --- a/cloud/filestore/libs/storage/tablet/tablet_private.h +++ b/cloud/filestore/libs/storage/tablet/tablet_private.h @@ -576,6 +576,7 @@ struct TEvIndexTabletPrivate const TString SessionId; const ui64 RequestId; const ui64 OpLogEntryId; + const TString NodeName; TCreateNodeInShardResult Result; TNodeCreatedInShard( @@ -583,11 +584,13 @@ struct TEvIndexTabletPrivate TString sessionId, ui64 requestId, ui64 opLogEntryId, + TString nodeName, TCreateNodeInShardResult result) : RequestInfo(std::move(requestInfo)) , SessionId(std::move(sessionId)) , RequestId(requestId) , OpLogEntryId(opLogEntryId) + , NodeName(std::move(nodeName)) , Result(std::move(result)) { } diff --git a/cloud/filestore/libs/storage/tablet/tablet_state.h b/cloud/filestore/libs/storage/tablet/tablet_state.h index f8ff7221669..e37f9063ad3 100644 --- a/cloud/filestore/libs/storage/tablet/tablet_state.h +++ b/cloud/filestore/libs/storage/tablet/tablet_state.h @@ -439,6 +439,12 @@ FILESTORE_FILESYSTEM_STATS(FILESTORE_DECLARE_COUNTER) const TString& message, ui64 nodeId); + bool HasPendingNodeCreateInShard(const TString& nodeName) const; + + void StartNodeCreateInShard(const TString& nodeName); + + void EndNodeCreateInShard(const TString& nodeName); + private: void UpdateUsedBlocksCount( TIndexTabletDatabase& db, diff --git a/cloud/filestore/libs/storage/tablet/tablet_state_impl.h b/cloud/filestore/libs/storage/tablet/tablet_state_impl.h index e4847015859..019a867a339 100644 --- a/cloud/filestore/libs/storage/tablet/tablet_state_impl.h +++ b/cloud/filestore/libs/storage/tablet/tablet_state_impl.h @@ -60,6 +60,7 @@ struct TIndexTabletState::TImpl TNodeIndexCache NodeIndexCache; TInMemoryIndexState InMemoryIndexState; TSet OrphanNodeIds; + TSet PendingNodeCreateInShardNames; TCheckpointStore Checkpoints; TChannels Channels; diff --git a/cloud/filestore/libs/storage/tablet/tablet_state_nodes.cpp b/cloud/filestore/libs/storage/tablet/tablet_state_nodes.cpp index 1c683c36662..fcf694d571f 100644 --- a/cloud/filestore/libs/storage/tablet/tablet_state_nodes.cpp +++ b/cloud/filestore/libs/storage/tablet/tablet_state_nodes.cpp @@ -254,6 +254,22 @@ void TIndexTabletState::WriteOrphanNode( Impl->OrphanNodeIds.insert(nodeId); } +bool TIndexTabletState::HasPendingNodeCreateInShard(const TString& nodeName) const +{ + return Impl->PendingNodeCreateInShardNames.contains(nodeName); +} + +void TIndexTabletState::StartNodeCreateInShard(const TString& nodeName) +{ + Impl->PendingNodeCreateInShardNames.insert(nodeName); +} + +void TIndexTabletState::EndNodeCreateInShard(const TString& nodeName) +{ + Impl->PendingNodeCreateInShardNames.erase(nodeName); +} + + //////////////////////////////////////////////////////////////////////////////// // NodeAttrs