void NodeCapacitySubspace::GetTargetNodesForReplicas( TempSolution const& tempSolution, std::vector<PlacementReplica const*> const& replicas, NodeSet & candidateNodes, bool useNodeBufferCapacity, NodeToConstraintDiagnosticsDataMapSPtr const nodeToConstraintDiagnosticsDataMapSPtr /* = nullptr */) const { ASSERT_IF(replicas.size() == 0, "Replica empty."); NodeEntry const* currentNode = replicas.size() == 1 ? tempSolution.GetReplicaLocation(replicas[0]) : nullptr; //Choose the function here to avoid the runtime speeed hit of boolean checks for every node in the filter auto FilterCandidateNodesForCapacityConstraintsWithOrWithoutDiagnostics = (nodeToConstraintDiagnosticsDataMapSPtr == nullptr) ? static_cast<std::function<bool(NodeEntry const *)>>( [&](NodeEntry const *node) -> bool { return FilterCandidateNodesForCapacityConstraints(node, tempSolution, replicas, useNodeBufferCapacity, false); } ): static_cast<std::function<bool(NodeEntry const *)>>( [&](NodeEntry const *node) -> bool { NodeCapacityViolationDiagnosticsDataSPtr constraintDiagnosticsDataSPtr = make_shared<NodeCapacityViolationDiagnosticsData>(L"", 0, 0, 0, 0, 0, 0, false); return FilterCandidateNodesForCapacityConstraints(node, tempSolution, replicas, useNodeBufferCapacity, constraintDiagnosticsDataSPtr, false) ? true : [&node, &nodeToConstraintDiagnosticsDataMapSPtr, &constraintDiagnosticsDataSPtr]() -> bool { nodeToConstraintDiagnosticsDataMapSPtr->insert(move(std::pair<Federation::NodeId, NodeCapacityViolationDiagnosticsDataSPtr>(node->NodeId, move(constraintDiagnosticsDataSPtr)))); // with diagnostics return false; }(); } ); candidateNodes.Filter([&](NodeEntry const *node) -> bool { if (!node->HasCapacity) { return true; } else if (node == currentNode) { auto itNode = nodeExtraLoads_.find(currentNode); if (itNode != nodeExtraLoads_.end()) { auto itReplica = itNode->second.find(replicas[0]); if (itReplica != itNode->second.end()) { // if the replica is one of invalid replicas on this node return false; } } return true; } else if (replicas[0]->Partition->Service->Application && replicas[0]->Partition->Service->Application->HasReservation) { // If replica was already on this node, it will be allowed to return because it will be evaluated in the previous // else-if branch above. Here we check if application was on this node, and we don't allow new replicas from that app. // This won't happen during search for upgrade (replicas.size() > 1), so we're safe checking replicas[0] auto const& nodeIt = nodeExtraApplications_.find(node); if ( nodeIt != nodeExtraApplications_.end() && nodeIt->second.find(replicas[0]->Partition->Service->Application) != nodeIt->second.end()) { // We are moving the entire application from this node, so it's not allowed as target. If we had allowed it, and if // replica load was below reservation then there is no other mechanism for ensuring that application will be moved. return false; } return FilterCandidateNodesForCapacityConstraintsWithOrWithoutDiagnostics(node); } else if (replicas[0]->Partition->Service->ServicePackage) { // This won't happen during search for upgrade (replicas.size() > 1), so we're safe checking replicas[0] auto const& nodeIt = nodeExtraServicePackages_.find(node); if ( nodeIt != nodeExtraServicePackages_.end() && nodeIt->second.find(replicas[0]->Partition->Service->ServicePackage) != nodeIt->second.end()) { // We are moving the entire service package from this node, so it's not allowed as target. If we had allowed it, and if // replica load was below reservation then there is no other mechanism for ensuring that application will be moved. return false; } return FilterCandidateNodesForCapacityConstraintsWithOrWithoutDiagnostics(node); } else { return FilterCandidateNodesForCapacityConstraintsWithOrWithoutDiagnostics(node); } }); }
void NodeCapacitySubspace::PromoteSecondaryForPartitions(TempSolution const& tempSolution, std::vector<PartitionEntry const*> const& partitions, NodeSet & candidateNodes) const { size_t globalMetricCount = tempSolution.OriginalPlacement->GlobalMetricCount; size_t totalMetricCount = tempSolution.OriginalPlacement->TotalMetricCount; ASSERT_IFNOT(totalMetricCount >= globalMetricCount, "Invalid metric count"); size_t globalMetricStartIndex = totalMetricCount - globalMetricCount; bool applyOnlyMoveInChanges = tempSolution.OriginalPlacement->Settings.PreventTransientOvercommit; candidateNodes.Filter([&](NodeEntry const *node) -> bool { if (!node->IsUp) { return false; } if (!node->HasCapacity) { return true; } else { LoadEntry const& originalLoads = node->Loads; LoadEntry const& shouldDisappearLoads = node->ShouldDisappearLoads; LoadEntry const& baseLoads = tempSolution.BaseSolution.NodeChanges[node]; LoadEntry const& tempLoads = applyOnlyMoveInChanges ? tempSolution.NodeMovingInChanges[node] : tempSolution.NodeChanges[node]; ASSERT_IFNOT(totalMetricCount == originalLoads.Length && totalMetricCount == tempLoads.Length && totalMetricCount == baseLoads.Length, "Invalid temp loads or base loads"); bool ret = true; LoadEntry swapLoadDiffEntry(globalMetricCount); // We rely on static subspace to filter out nodes without secondaries, so we assume that there are replicas on the node. std::map<ApplicationEntry const*, LoadEntry> appLoadOnNode; for (auto pIt = partitions.begin(); pIt != partitions.end(); ++pIt) { auto partition = *pIt; ServiceEntry const* replicaService = partition->Service; ApplicationEntry const* application = replicaService->Application; bool hasReservation = application && application->HasReservation; std::map<ApplicationEntry const*, LoadEntry>::iterator appLoadOnNodeIt; if (hasReservation) { appLoadOnNodeIt = appLoadOnNode.find(application); if (appLoadOnNodeIt == appLoadOnNode.end()) { appLoadOnNodeIt = appLoadOnNode.insert(make_pair(application, ((NodeMetrics const&)tempSolution.ApplicationNodeLoad[application])[node])).first; } } LoadEntry const& primaryEntry = partition->GetReplicaEntry(ReplicaRole::Primary); LoadEntry const& secondaryEntry = partition->GetReplicaEntry(ReplicaRole::Secondary, true, node->NodeId); for (size_t metricIndex = 0; metricIndex < replicaService->MetricCount; metricIndex++) { ASSERT_IFNOT(metricIndex < secondaryEntry.Length, "Metric index should be < replica entry size: {0} {1}", metricIndex, secondaryEntry.Length); int64 swapLoadDiffValue = primaryEntry.Values[metricIndex] - secondaryEntry.Values[metricIndex]; size_t globalMetricIndex = replicaService->GlobalMetricIndices[metricIndex]; size_t capacityIndex = globalMetricIndex - globalMetricStartIndex; if (hasReservation) { // There are already replicas on the node: this replica may fit into reservation that already exists auto applicationLoadOnNode = appLoadOnNodeIt->second.Values[capacityIndex]; appLoadOnNodeIt->second.AddLoad(capacityIndex, swapLoadDiffValue); auto applicationReservation = application->Reservation.Values[capacityIndex]; auto remainingReservation = applicationLoadOnNode < applicationReservation ? applicationReservation - applicationLoadOnNode : 0; swapLoadDiffValue = swapLoadDiffValue < remainingReservation ? 0 : swapLoadDiffValue - remainingReservation; } swapLoadDiffEntry.AddLoad(capacityIndex, swapLoadDiffValue); } } for (size_t i = 0; i < globalMetricCount; ++i) { int64 partitionsSwapDiffValue = swapLoadDiffEntry.Values[i]; int64 capacity = node->TotalCapacities.Values[i]; if (partitionsSwapDiffValue <= 0 || capacity < 0) { continue; } size_t totalIndex = globalMetricStartIndex + i; int64 totalLoadWithPartitionsSwap = originalLoads.Values[totalIndex] + tempSolution.GetApplicationReservedLoad(node, i) + shouldDisappearLoads.Values[totalIndex] + tempLoads.Values[totalIndex] + partitionsSwapDiffValue; if (totalLoadWithPartitionsSwap > capacity) { ret = false; break; } } return ret; } }); }