diff options
| -rw-r--r-- | ydb/core/mind/dynamic_nameserver.cpp | 31 | ||||
| -rw-r--r-- | ydb/core/mind/dynamic_nameserver_impl.h | 6 | ||||
| -rw-r--r-- | ydb/core/mind/dynamic_nameserver_mon.cpp | 4 | ||||
| -rw-r--r-- | ydb/core/mind/lease_holder.cpp | 9 | ||||
| -rw-r--r-- | ydb/core/mind/node_broker.cpp | 23 | ||||
| -rw-r--r-- | ydb/core/mind/node_broker_impl.h | 1 | ||||
| -rw-r--r-- | ydb/core/mind/node_broker_ut.cpp | 186 | ||||
| -rw-r--r-- | ydb/core/protos/counters_node_broker.proto | 1 | ||||
| -rw-r--r-- | ydb/core/protos/node_broker.proto | 9 | ||||
| -rw-r--r-- | ydb/library/actors/core/interconnect.h | 6 |
10 files changed, 253 insertions, 23 deletions
diff --git a/ydb/core/mind/dynamic_nameserver.cpp b/ydb/core/mind/dynamic_nameserver.cpp index c23a3a79aa2..a30a975ccc1 100644 --- a/ydb/core/mind/dynamic_nameserver.cpp +++ b/ydb/core/mind/dynamic_nameserver.cpp @@ -291,8 +291,9 @@ void TDynamicNameserver::ReplaceNameserverSetup(TIntrusivePtr<TTableNameserverSe if (StaticConfig->StaticNodeTable != newStaticConfig->StaticNodeTable) { StaticConfig = std::move(newStaticConfig); InvalidateListNodesCache(); - for (const auto& subscriber : StaticNodeChangeSubscribers) { - TActivationContext::Send(new IEventHandle(SelfId(), subscriber, new TEvInterconnect::TEvListNodes)); + for (const auto& [subscriber, onlyAliveDynamicNodes] : StaticNodeChangeSubscribers) { + TActivationContext::Send(new IEventHandle(SelfId(), subscriber, + new TEvInterconnect::TEvListNodes(false, onlyAliveDynamicNodes))); } UpdateCounters(); } @@ -403,10 +404,10 @@ void TDynamicNameserver::InvalidateListNodesCache() ListNodesCacheAlive->Invalidate(); } -void TDynamicNameserver::SendNodesList(TActorId recipient, bool onlyAliveNodes, const TActorContext &ctx) +void TDynamicNameserver::SendNodesList(TActorId recipient, bool onlyAliveDynamicNodes, const TActorContext &ctx) { auto now = ctx.Now(); - auto& cache = onlyAliveNodes ? ListNodesCacheAlive : ListNodesCacheAll; + auto& cache = onlyAliveDynamicNodes ? ListNodesCacheAlive : ListNodesCacheAll; if (cache->NeedUpdate(now)) { auto newNodes = MakeIntrusive<TIntrusiveVector<TEvInterconnect::TNodeInfo>>(); @@ -440,10 +441,14 @@ void TDynamicNameserver::SendNodesList(TActorId recipient, bool onlyAliveNodes, for (auto &config : DynamicConfigs) { for (auto &pr : config->DynamicNodes) { if (EnableLongLease) { - if (onlyAliveNodes && pr.second.Liveness != ENodeLiveness::Alive) { - continue; // dead nodes are not included in alive-only list + if (onlyAliveDynamicNodes) { + if (pr.second.Liveness != ENodeLiveness::Alive) { + continue; // dead nodes are not included in alive-only list + } } - } else if (pr.second.Expire <= now) { + } + + if (pr.second.EffectiveExpire(EnableLongLease) <= now) { continue; // expired nodes are not included } @@ -475,8 +480,8 @@ void TDynamicNameserver::SendNodesList(TActorId recipient, bool onlyAliveNodes, void TDynamicNameserver::SendNodesList(const TActorContext &ctx) { - for (auto &[sender, onlyAliveNodes] : ListNodesQueue) { - SendNodesList(sender, onlyAliveNodes, ctx); + for (auto &[sender, onlyAliveDynamicNodes] : ListNodesQueue) { + SendNodesList(sender, onlyAliveDynamicNodes, ctx); } ListNodesQueue.clear(); } @@ -627,10 +632,10 @@ void TDynamicNameserver::Handle(TEvInterconnect::TEvListNodes::TPtr &ev, YDB_LOG_DEBUG("TDynamicNameserver::Handle TEvInterconnect::TEvListNodes", {"eventString", ev->Get()->ToString()}); - const bool onlyAliveNodes = ev->Get()->OnlyAliveNodes; + const bool onlyAliveDynamicNodes = ev->Get()->OnlyAliveDynamicNodes; if (ProtocolState == EProtocolState::Connecting) { - SendNodesList(ev->Sender, onlyAliveNodes, ctx); + SendNodesList(ev->Sender, onlyAliveDynamicNodes, ctx); } else { if (ListNodesQueue.empty()) { auto dinfo = AppData(ctx)->DomainsInfo; @@ -646,11 +651,11 @@ void TDynamicNameserver::Handle(TEvInterconnect::TEvListNodes::TPtr &ev, PendingRequests.Set(domain); } } - ListNodesQueue.emplace_back(ev->Sender, onlyAliveNodes); + ListNodesQueue.emplace_back(ev->Sender, onlyAliveDynamicNodes); } if (ev->Get()->SubscribeToStaticNodeChanges) { - StaticNodeChangeSubscribers.insert(ev->Sender); + StaticNodeChangeSubscribers[ev->Sender] = onlyAliveDynamicNodes; } } diff --git a/ydb/core/mind/dynamic_nameserver_impl.h b/ydb/core/mind/dynamic_nameserver_impl.h index d403d01d259..c62988bbe49 100644 --- a/ydb/core/mind/dynamic_nameserver_impl.h +++ b/ydb/core/mind/dynamic_nameserver_impl.h @@ -247,7 +247,7 @@ private: const TActorContext &ctx); void ResolveStaticNode(ui32 nodeId, TActorId sender, TMonotonic deadline, const TActorContext &ctx); void ResolveDynamicNode(ui32 nodeId, TAutoPtr<IEventHandle> ev, TMonotonic deadline, const TActorContext &ctx); - void SendNodesList(TActorId recipient, bool onlyAliveNodes, const TActorContext &ctx); + void SendNodesList(TActorId recipient, bool onlyAliveDynamicNodes, const TActorContext &ctx); void SendNodesList(const TActorContext &ctx); void InvalidateListNodesCache(); void PendingRequestAnswered(ui32 domain, const TActorContext &ctx); @@ -296,7 +296,9 @@ private: // Domain -> Epoch ID. THashMap<ui32, ui64> EpochUpdates; ui32 ResolvePoolId; - THashSet<TActorId> StaticNodeChangeSubscribers; + // Subscriber -> its OnlyAliveDynamicNodes preference, so re-notifications + // respect the filter the subscriber originally requested. + THashMap<TActorId, bool> StaticNodeChangeSubscribers; bool SubscribedToConsoleNSConfig = false; bool EnableLongLease = false; diff --git a/ydb/core/mind/dynamic_nameserver_mon.cpp b/ydb/core/mind/dynamic_nameserver_mon.cpp index 36267c13d88..cf55a1c1f61 100644 --- a/ydb/core/mind/dynamic_nameserver_mon.cpp +++ b/ydb/core/mind/dynamic_nameserver_mon.cpp @@ -215,6 +215,10 @@ void TDynamicNameserver::Handle(NMon::TEvHttpInfo::TPtr &ev, const TActorContext << " <td class='right-align'>Protocol state:</td>" << Endl << " <td>" << ToString(ProtocolState) << "</td>" << Endl << " </tr>" << Endl + << " <tr>" << Endl + << " <td class='right-align'>EnableNodeBrokerLongLease:</td>" << Endl + << " <td>" << (EnableLongLease ? "true" : "false") << "</td>" << Endl + << " </tr>" << Endl << " </tbody>" << Endl << "</table></div>" << Endl; diff --git a/ydb/core/mind/lease_holder.cpp b/ydb/core/mind/lease_holder.cpp index 4dd60552cd4..2678358486f 100644 --- a/ydb/core/mind/lease_holder.cpp +++ b/ydb/core/mind/lease_holder.cpp @@ -2,6 +2,7 @@ #include "node_broker.h" #include <ydb/core/base/appdata.h> +#include <ydb/core/base/counters.h> #include <ydb/core/base/tablet_pipe.h> #include <ydb/core/cms/console/configs_dispatcher.h> #include <ydb/core/cms/console/console.h> @@ -63,6 +64,9 @@ public: EnableLongLease = AppData()->FeatureFlags.GetEnableNodeBrokerLongLease(); + auto counters = GetServiceCounters(AppData(ctx)->Counters, "utils")->GetSubgroup("component", "lease_holder"); + PingErrorsCounter = counters->GetCounter("PingErrors", true); + const ui32 featureFlagsItem = NKikimrConsole::TConfigItem::FeatureFlagsItem; Send(NConsole::MakeConfigsDispatcherID(SelfId().NodeId()), new NConsole::TEvConfigsDispatcher::TEvSetConfigSubscriptionRequest(featureFlagsItem)); @@ -141,6 +145,7 @@ private: { NTabletPipe::CloseClient(ctx, NodeBrokerPipe); NodeBrokerPipe = TActorId(); + PingErrorsCounter->Inc(); Ping(ctx); } @@ -185,6 +190,7 @@ private: if (rec.GetStatus().GetCode() != NKikimrNodeBroker::TStatus::OK) { YDB_LOG_ERROR_CTX(ctx, "TLeaseHolder::Handle TEvNodeBroker::TEvExtendLeaseResponse: cannot extend lease", {"reason", rec.GetStatus().GetReason().data()}); + PingErrorsCounter->Inc(); return; } @@ -234,7 +240,7 @@ private: << "Epoch end: " << ToString(EpochEnd) << Endl << "Next epoch end: " << ToString(NextEpochEnd) << Endl << "Next ping at: " << ToString(NextPing) << Endl - << "Enable long lease: " << EnableLongLease << Endl + << "EnableNodeBrokerLongLease: " << (EnableLongLease ? "true" : "false") << Endl << "Expire: " << ToString(Expire) << Endl << "ExpireV2: " << ToString(ExpireV2) << Endl; } @@ -289,6 +295,7 @@ private: TInstant Expire; TInstant ExpireV2; NActors::TSchedulerCookieHolder ExpireCookieHolder; + ::NMonitoring::TDynamicCounters::TCounterPtr PingErrorsCounter; }; IActor *CreateLeaseHolder(TInstant expire) diff --git a/ydb/core/mind/node_broker.cpp b/ydb/core/mind/node_broker.cpp index 44851258486..ac41e0902c6 100644 --- a/ydb/core/mind/node_broker.cpp +++ b/ydb/core/mind/node_broker.cpp @@ -151,6 +151,8 @@ bool TNodeBroker::OnRenderAppHtmlPage(NMon::TEvRemoteHttpInfo::TPtr ev, << " MaxStaticNodeId: " << MaxStaticId << Endl << " MaxDynamicNodeId: " << MaxDynamicId << Endl << " EpochDuration: " << Committed.EpochDuration << Endl + << " LeaseDuration: " << Committed.LeaseDuration << Endl + << " EnableNodeBrokerLongLease: " << (EnableLongLease ? "true" : "false") << Endl << " StableNodeNamePrefix: " << Committed.StableNodeNamePrefix << Endl << " BannedIds:"; for (auto &pr : Committed.BannedIds) @@ -232,6 +234,7 @@ void TNodeBroker::TState::ClearState() Nodes.clear(); ExpiredNodes.clear(); RemovedNodes.clear(); + DeadNodesCount = 0; Hosts.clear(); RecomputeFreeIds(); @@ -295,6 +298,9 @@ void TNodeBroker::TState::AddNode(const TNodeInfo &info) } Hosts.emplace(std::make_tuple(info.Host, info.Address, info.Port), info.NodeId); Nodes.emplace(info.NodeId, info); + if (info.Liveness == ENodeLiveness::Dead) { + ++DeadNodesCount; + } break; case ENodeState::Expired: YDB_LOG_DEBUG_CTX(TActorContext::AsActorContext(), "TNodeBroker::AddNode: added expired node", @@ -329,6 +335,9 @@ void TNodeBroker::TState::ExtendLease(TNodeInfo &node) node.ExpireV2 = Epoch.NextEnd + LeaseDuration; node.AliveUntil = Epoch.NextEnd; + if (node.Liveness == ENodeLiveness::Dead) { + --DeadNodesCount; + } node.Liveness = ENodeLiveness::Alive; YDB_LOG_DEBUG_CTX(TActorContext::AsActorContext(), "TNodeBroker::ExtendLease: extended node lease", @@ -348,6 +357,9 @@ void TNodeBroker::TState::FixNodeId(TNodeInfo &node) node.ExpireV2 = TInstant::Max(); node.AliveUntil = TInstant::Max(); + if (node.Liveness == ENodeLiveness::Dead) { + --DeadNodesCount; + } node.Liveness = ENodeLiveness::Alive; YDB_LOG_DEBUG_CTX(TActorContext::AsActorContext(), "TNodeBroker::FixNodeId: fixed node ID", @@ -506,7 +518,7 @@ void TNodeBroker::FillNodeInfo(const TNodeInfo &node, info.SetAddress(node.Address); info.SetExpire(node.Expire.GetValue()); info.SetExpireV2(node.ExpireV2.GetValue()); - info.SetLiveness(static_cast<ui32>(node.Liveness)); + info.SetLiveness(static_cast<NKikimrNodeBroker::ELiveness>(node.Liveness)); node.Location.Serialize(info.MutableLocation(), false); FillNodeName(node.SlotIndex, info); } @@ -559,6 +571,9 @@ void TNodeBroker::TState::ApplyStateDiff(const TStateDiff &diff) {"nodeId", it->second.IdString()}); Hosts.erase(std::make_tuple(it->second.Host, it->second.Address, it->second.Port)); + if (it->second.Liveness == ENodeLiveness::Dead) { + --DeadNodesCount; + } it->second.State = ENodeState::Expired; it->second.Version = diff.NewEpoch.Version; ExpiredNodes.emplace(id, std::move(it->second)); @@ -572,6 +587,9 @@ void TNodeBroker::TState::ApplyStateDiff(const TStateDiff &diff) LOG_DEBUG_S(TActorContext::AsActorContext(), NKikimrServices::NODE_BROKER, LogPrefix() << " Node " << it->second.IdString() << " is marked as dead"); + if (it->second.Liveness != ENodeLiveness::Dead) { + ++DeadNodesCount; + } it->second.Liveness = ENodeLiveness::Dead; it->second.Version = diff.NewEpoch.Version; } @@ -843,6 +861,7 @@ void TNodeBroker::UpdateCommittedStateCounters() { TabletCounters->Simple()[COUNTER_ACTIVE_NODES].Set(Committed.Nodes.size()); TabletCounters->Simple()[COUNTER_EXPIRED_NODES].Set(Committed.ExpiredNodes.size()); TabletCounters->Simple()[COUNTER_REMOVED_NODES].Set(Committed.RemovedNodes.size()); + TabletCounters->Simple()[COUNTER_DEAD_NODES].Set(Committed.DeadNodesCount); TabletCounters->Simple()[COUNTER_EPOCH_VERSION].Set(Committed.Epoch.Version); } @@ -1954,7 +1973,7 @@ TNodeInfoSchema TNodeBroker::TNodeInfo::SerializeToSchema() const { serialized.SetExpire(Expire.MicroSeconds()); serialized.SetExpireV2(ExpireV2.MicroSeconds()); serialized.SetAliveUntil(AliveUntil.MicroSeconds()); - serialized.SetLiveness(static_cast<ui32>(Liveness)); + serialized.SetLiveness(static_cast<NKikimrNodeBroker::ELiveness>(Liveness)); Location.Serialize(serialized.MutableLocation(), false); serialized.MutableServicedSubDomain()->CopyFrom(ServicedSubDomain); if (SlotIndex.has_value()) { diff --git a/ydb/core/mind/node_broker_impl.h b/ydb/core/mind/node_broker_impl.h index b932a7d0337..4d5fe31818c 100644 --- a/ydb/core/mind/node_broker_impl.h +++ b/ydb/core/mind/node_broker_impl.h @@ -404,6 +404,7 @@ private: THashMap<ui32, TNodeInfo> Nodes; THashMap<ui32, TNodeInfo> ExpiredNodes; THashMap<ui32, TNodeInfo> RemovedNodes; + ui64 DeadNodesCount = 0; // Maps <Host/Addr:Port> to NodeID. THashMap<std::tuple<TString, TString, ui16>, ui32> Hosts; // Bitmap with free Node IDs (with no lower 5 bits). diff --git a/ydb/core/mind/node_broker_ut.cpp b/ydb/core/mind/node_broker_ut.cpp index 41c7db5a91b..120bfc19d40 100644 --- a/ydb/core/mind/node_broker_ut.cpp +++ b/ydb/core/mind/node_broker_ut.cpp @@ -19,6 +19,7 @@ #include <ydb/core/blobstorage/pdisk/blobstorage_pdisk_tools.h> #include <ydb/core/protos/schemeshard/operations.pb.h> #include <ydb/core/protos/tx_proxy.pb.h> +#include <ydb/core/protos/blobstorage_distributed_config.pb.h> #include <ydb/core/tablet_flat/shared_cache_events.h> #include <ydb/core/tablet_flat/shared_sausagecache.h> #include <ydb/core/tx/schemeshard/schemeshard.h> @@ -1053,6 +1054,50 @@ THashSet<ui32> GetNameserverDynamicNodeIds(TTestActorRuntime &runtime, TActorId return ids; } +// Subscribe to static node table changes with the given onlyAliveDynamicNodes +// preference. The nameservice remembers the preference per subscriber and +// re-notifies with a fresh nodes list (respecting the filter) whenever the +// static node table changes. +void SubscribeToStaticNodeChanges(TTestActorRuntime &runtime, TActorId subscriber, + bool onlyAliveDynamicNodes) +{ + runtime.Send(new IEventHandle(GetNameserviceActorId(), subscriber, + new TEvInterconnect::TEvListNodes(/* subscribeToStaticNodeChanges */ true, + onlyAliveDynamicNodes))); +} + +// Grab the next nodes-list event delivered to a specific subscriber and return +// the set of dynamic (non-static) node ids in it. Used to observe the list the +// nameservice pushes to static-node-change subscribers. +THashSet<ui32> GrabNameserverDynamicNodeIds(TTestActorRuntime &runtime, TActorId subscriber) +{ + ui32 maxStaticNodeId = runtime.GetAppData().DynamicNameserviceConfig->MaxStaticNodeId; + auto ev = runtime.GrabEdgeEventRethrow<TEvInterconnect::TEvNodesInfo>(subscriber); + THashSet<ui32> ids; + for (auto &node : ev->Get()->Nodes) + if (node.NodeId > maxStaticNodeId) + ids.insert(node.NodeId); + return ids; +} + +// Push a new static node table to the node-0 nameservice as if the node warden +// delivered a self-managed storage config. When the table differs from the +// current one the nameservice invalidates its caches and re-notifies every +// static-node-change subscriber. +void SetNameserverStaticNodes(TTestActorRuntime &runtime, TActorId sender, + const TVector<ui32> &staticNodeIds) +{ + auto config = std::make_shared<NKikimrBlobStorage::TStorageConfig>(); + for (ui32 nodeId : staticNodeIds) { + auto *node = config->AddAllNodes(); + node->SetNodeId(nodeId); + node->SetHost(Sprintf("static%u.local", nodeId)); + node->SetPort(19000 + nodeId); + } + runtime.Send(new IEventHandle(GetNameserviceActorId(), sender, + new TEvNodeWardenStorageConfig(config, /* selfManagementEnabled */ true, nullptr))); +} + void GetNameserverNodesList(TTestActorRuntime &runtime, TActorId sender, THashMap<ui32, TEvInterconnect::TNodeInfo> &nodes, @@ -5395,6 +5440,147 @@ Y_UNIT_TEST_SUITE(TDynamicNameserverTest) { UNIT_ASSERT(GetNameserverDynamicNodeIds(runtime, sender, /* onlyAlive */ false).contains(NODE1)); UNIT_ASSERT(!GetNameserverDynamicNodeIds(runtime, sender, /* onlyAlive */ true).contains(NODE1)); } + + Y_UNIT_TEST(OnlyAliveDynamicNodesFiltersDeadNodes) + { + TTestBasicRuntime runtime(8, false); + Setup(runtime, 4, {}, false, /* enableNodeBrokerLongLease */ true); + TActorId sender = runtime.AllocateEdgeActor(); + + // Lease duration equal to a single (default) epoch: a node that stops + // pinging becomes Dead two epochs later (see + // TNodeBrokerTest::LongLeaseNodeBecomesDeadThenExpires). + SetLeaseDuration(runtime, sender, TDuration::Minutes(5)); + + // Register two nodes; both start Alive. + CheckRegistration(runtime, sender, "host1", 1001, "host1.host1.host1", "1.2.3.4", + 1, 2, 3, 4, TStatus::OK, NODE1); + CheckRegistration(runtime, sender, "host2", 1001, "host2.host2.host2", "1.2.3.5", + 1, 2, 3, 5, TStatus::OK, NODE2); + + // Keep NODE1 pinging every epoch so it stays Alive, while NODE2 goes + // silent and becomes Dead after two epochs. + auto epoch = WaitForEpochUpdate(runtime, sender); + CheckLeaseExtension(runtime, sender, NODE1, TStatus::OK, epoch); + epoch = WaitForEpochUpdate(runtime, sender); + CheckLeaseExtension(runtime, sender, NODE1, TStatus::OK, epoch); + + CheckNodeLiveness(runtime, sender, NODE1, ENodeLiveness::Alive); + CheckNodeLiveness(runtime, sender, NODE2, ENodeLiveness::Dead); + + // The full list contains both nodes; the alive-only list serves the + // Alive node and omits the Dead one (its own cache is built separately + // from the full-list cache). + auto all = GetNameserverDynamicNodeIds(runtime, sender, /* onlyAlive */ false); + UNIT_ASSERT(all.contains(NODE1)); + UNIT_ASSERT(all.contains(NODE2)); + + auto alive = GetNameserverDynamicNodeIds(runtime, sender, /* onlyAlive */ true); + UNIT_ASSERT(alive.contains(NODE1)); + UNIT_ASSERT(!alive.contains(NODE2)); + UNIT_ASSERT_VALUES_EQUAL(alive.size(), 1); + } + + Y_UNIT_TEST(OnlyAliveDynamicNodesNoopWhenLongLeaseDisabled) + { + TTestBasicRuntime runtime(8, false); + Setup(runtime, 4, {}, false, /* enableNodeBrokerLongLease */ false); + TActorId sender = runtime.AllocateEdgeActor(); + + SetLeaseDuration(runtime, sender, TDuration::Minutes(5)); + + CheckRegistration(runtime, sender, "host1", 1001, "host1.host1.host1", "1.2.3.4", + 1, 2, 3, 4, TStatus::OK, NODE1); + + // With the long lease feature disabled there is no Dead state and the + // onlyAliveDynamicNodes filter is a no-op: a registered, not-yet-expired + // node is served in both the full and the alive-only lists. + UNIT_ASSERT(GetNameserverDynamicNodeIds(runtime, sender, /* onlyAlive */ false).contains(NODE1)); + UNIT_ASSERT(GetNameserverDynamicNodeIds(runtime, sender, /* onlyAlive */ true).contains(NODE1)); + + // The node stops pinging but survives the current epoch, still in both lists. + WaitForEpochUpdate(runtime, sender); + UNIT_ASSERT(GetNameserverDynamicNodeIds(runtime, sender, /* onlyAlive */ false).contains(NODE1)); + UNIT_ASSERT(GetNameserverDynamicNodeIds(runtime, sender, /* onlyAlive */ true).contains(NODE1)); + + // It then expires (by Expire, never becoming Dead) and drops out of both + // lists identically. + WaitForEpochUpdate(runtime, sender); + UNIT_ASSERT(!GetNameserverDynamicNodeIds(runtime, sender, /* onlyAlive */ false).contains(NODE1)); + UNIT_ASSERT(!GetNameserverDynamicNodeIds(runtime, sender, /* onlyAlive */ true).contains(NODE1)); + } + + Y_UNIT_TEST(OnlyAliveDynamicNodesRevivedByRegistration) + { + TTestBasicRuntime runtime(8, false); + Setup(runtime, 4, {}, false, /* enableNodeBrokerLongLease */ true); + TActorId sender = runtime.AllocateEdgeActor(); + + SetLeaseDuration(runtime, sender, TDuration::Minutes(5)); + + CheckRegistration(runtime, sender, "host1", 1001, "host1.host1.host1", "1.2.3.4", + 1, 2, 3, 4, TStatus::OK, NODE1); + + // Let the node become Dead (but not expired). + WaitForEpochUpdate(runtime, sender); + WaitForEpochUpdate(runtime, sender); + CheckNodeLiveness(runtime, sender, NODE1, ENodeLiveness::Dead); + + // The dead node is hidden from the alive-only list but still in the full one. + UNIT_ASSERT(GetNameserverDynamicNodeIds(runtime, sender, /* onlyAlive */ false).contains(NODE1)); + UNIT_ASSERT(!GetNameserverDynamicNodeIds(runtime, sender, /* onlyAlive */ true).contains(NODE1)); + + // Re-registration revives the node (back to Alive), so it must reappear + // in the alive-only list once the nameserver refreshes its cache. + CheckRegistration(runtime, sender, "host1", 1001, "host1.host1.host1", "1.2.3.4", + 1, 2, 3, 4, TStatus::OK, NODE1); + CheckNodeLiveness(runtime, sender, NODE1, ENodeLiveness::Alive); + + UNIT_ASSERT(GetNameserverDynamicNodeIds(runtime, sender, /* onlyAlive */ false).contains(NODE1)); + UNIT_ASSERT(GetNameserverDynamicNodeIds(runtime, sender, /* onlyAlive */ true).contains(NODE1)); + } + + Y_UNIT_TEST(OnlyAliveDynamicNodesStaticNodeChangeSubscribers) + { + TTestBasicRuntime runtime(8, false); + Setup(runtime, 4, {}, false, /* enableNodeBrokerLongLease */ true); + TActorId sender = runtime.AllocateEdgeActor(); + + SetLeaseDuration(runtime, sender, TDuration::Minutes(5)); + + // Register a node and drive it into the Dead-but-not-expired window, so + // the alive-only and full lists disagree on whether it is served. + CheckRegistration(runtime, sender, "host1", 1001, "host1.host1.host1", "1.2.3.4", + 1, 2, 3, 4, TStatus::OK, NODE1); + WaitForEpochUpdate(runtime, sender); + WaitForEpochUpdate(runtime, sender); + CheckNodeLiveness(runtime, sender, NODE1, ENodeLiveness::Dead); + + // Establish a known static node table baseline. + SetNameserverStaticNodes(runtime, sender, {1, 2}); + + // Two subscribers to static node changes with opposite + // onlyAliveDynamicNodes preferences. + TActorId subAll = runtime.AllocateEdgeActor(); + TActorId subAlive = runtime.AllocateEdgeActor(); + SubscribeToStaticNodeChanges(runtime, subAll, /* onlyAlive */ false); + SubscribeToStaticNodeChanges(runtime, subAlive, /* onlyAlive */ true); + + // The initial list responses already respect each subscriber's filter: + // the full-list subscriber sees the dead node, the alive-only one does not. + UNIT_ASSERT(GrabNameserverDynamicNodeIds(runtime, subAll).contains(NODE1)); + UNIT_ASSERT(!GrabNameserverDynamicNodeIds(runtime, subAlive).contains(NODE1)); + + // Change the static node table; both subscribers must be re-notified. + SetNameserverStaticNodes(runtime, sender, {1, 2, 3}); + + // Each re-notification must respect the onlyAliveDynamicNodes preference + // the subscriber originally requested (stored in StaticNodeChangeSubscribers): + // the dead node stays in the full-list subscriber's view and is hidden + // from the alive-only subscriber's view. + UNIT_ASSERT(GrabNameserverDynamicNodeIds(runtime, subAll).contains(NODE1)); + UNIT_ASSERT(!GrabNameserverDynamicNodeIds(runtime, subAlive).contains(NODE1)); + } } Y_UNIT_TEST_SUITE(TSlotIndexesPoolTest) { diff --git a/ydb/core/protos/counters_node_broker.proto b/ydb/core/protos/counters_node_broker.proto index d3c774208c0..1b87c6be705 100644 --- a/ydb/core/protos/counters_node_broker.proto +++ b/ydb/core/protos/counters_node_broker.proto @@ -14,6 +14,7 @@ enum ESimpleCounters { COUNTER_ACTIVE_NODES = 4 [(CounterOpts) = {Name: "ActiveNodes"}]; COUNTER_EXPIRED_NODES = 5 [(CounterOpts) = {Name: "ExpiredNodes"}]; COUNTER_REMOVED_NODES = 6 [(CounterOpts) = {Name: "RemovedNodes"}]; + COUNTER_DEAD_NODES = 7 [(CounterOpts) = {Name: "DeadNodes"}]; } enum ECumulativeCounters { diff --git a/ydb/core/protos/node_broker.proto b/ydb/core/protos/node_broker.proto index 4240a396d9b..6c82e332e15 100644 --- a/ydb/core/protos/node_broker.proto +++ b/ydb/core/protos/node_broker.proto @@ -13,6 +13,11 @@ message TListNodes { } } +enum ELiveness { + ALIVE = 0; + DEAD = 1; +} + message TNodeInfo { optional uint32 NodeId = 1; optional string Host = 2; @@ -23,7 +28,7 @@ message TNodeInfo { optional uint64 Expire = 7; optional uint64 ExpireV2 = 9; optional string Name = 8; - optional uint32 Liveness = 10; + optional ELiveness Liveness = 10; } message TNodeInfoSchema { @@ -39,7 +44,7 @@ message TNodeInfoSchema { optional bool AuthorizedByCertificate = 10; optional uint64 ExpireV2 = 11; optional uint64 AliveUntil = 12; - optional uint32 Liveness = 13; + optional ELiveness Liveness = 13; } message TVersionInfo { diff --git a/ydb/library/actors/core/interconnect.h b/ydb/library/actors/core/interconnect.h index 33a99a62669..9291fe6d439 100644 --- a/ydb/library/actors/core/interconnect.h +++ b/ydb/library/actors/core/interconnect.h @@ -211,13 +211,13 @@ namespace NActors { struct TEvListNodes: public TEventLocal<TEvListNodes, EvListNodes> { const bool SubscribeToStaticNodeChanges = false; - const bool OnlyAliveNodes = true; + const bool OnlyAliveDynamicNodes = true; TEvListNodes() = default; - TEvListNodes(bool subscribeToStaticNodeChanges, bool onlyAliveNodes = true) + TEvListNodes(bool subscribeToStaticNodeChanges, bool onlyAliveDynamicNodes = true) : SubscribeToStaticNodeChanges(subscribeToStaticNodeChanges) - , OnlyAliveNodes(onlyAliveNodes) + , OnlyAliveDynamicNodes(onlyAliveDynamicNodes) {} }; |
