in src/prod/src/Reliability/LoadBalancing/PlacementAndLoadBalancing.cpp [1980:2677]
pair<ErrorCode, bool> PlacementAndLoadBalancing::InternalUpdateService(ServiceDescription && serviceDescription, bool forceUpdate, bool traceDetail, bool updateMetricConnections, bool skipServicePackageUpdate)
{
auto itServiceIdMap = serviceToIdMap_.find(serviceDescription.Name);
if (serviceDescription.ServiceId == 0 && itServiceIdMap == serviceToIdMap_.end())
{
// This is a new service, we need to assign next service ID to it.
serviceDescription.ServiceId = nextServiceId_++;
serviceToIdMap_.insert(make_pair(serviceDescription.Name, serviceDescription.ServiceId));
plbStatistics_.AddService(serviceDescription);
}
else
{
if (itServiceIdMap == serviceToIdMap_.end())
{
return make_pair(ErrorCodeValue::ServiceNotFound, false);
}
// Service already exists, reuse the existing service ID.
serviceDescription.ServiceId = itServiceIdMap->second;
}
Uint64UnorderedMap<Application>::iterator appIt = applicationTable_.end();
bool appHasScaleoutOrCapacity = false;
if (serviceDescription.ApplicationName != L"")
{
auto appIter = applicationToIdMap_.find(serviceDescription.ApplicationName);
if (appIter == applicationToIdMap_.end())
{
// In case when service gets created before the application (possible for application created before v4.5)
// we need to keep track of this application as well, so we assign new AppID to it.
appIter = applicationToIdMap_.insert(make_pair(serviceDescription.ApplicationName, nextApplicationId_++)).first;
ApplicationDescription appDescription(move(wstring(serviceDescription.ApplicationName)));
appDescription.ApplicationId = appIter->second;
appIt = applicationTable_.insert(make_pair(appDescription.ApplicationId, Application(move(appDescription)))).first;
if (applicationTable_.size() == 1)
{
nextApplicationToBeTraced_ = applicationTable_.begin()->first;
for (auto it = serviceDomainTable_.begin(); it != serviceDomainTable_.end(); it++)
{
it->second.LastApplicationTraced = applicationTable_.begin()->first;
}
}
}
else
{
// If application already exists, we just need to find it.
appIt = applicationTable_.find(appIter->second);
// As we never remove applicaiton from applicationToIdMap_ there is a possibility that we already deleted the application
// In this case we should not allow service creation
if (appIt == applicationTable_.end())
{
Trace.ApplicationNotFound(serviceDescription.ApplicationName, serviceDescription.ApplicationId, serviceDescription.Name);
return make_pair(ErrorCodeValue::ApplicationInstanceDeleted, false);
}
appHasScaleoutOrCapacity = appIt->second.ApplicationDesc.HasScaleoutOrCapacity();
}
serviceDescription.ApplicationId = appIter->second;
}
auto itServicePackage = servicePackageTable_.end();
auto itServicePackageId = servicePackageToIdMap_.find(serviceDescription.ServicePackageIdentifier);
if (itServicePackageId != servicePackageToIdMap_.end())
{
itServicePackage = servicePackageTable_.find(itServicePackageId->second);
serviceDescription.ServicePackageId = itServicePackageId->second;
//in case we skip this it means that the service package descritpion was already adjusted with appropriate rg metrics
if (!skipServicePackageUpdate)
{
if (itServicePackage != servicePackageTable_.end())
{
PLBConfig const& config = PLBConfig::GetConfig();
bool isSingleReplicaPerHost = serviceDescription.ServicePackageActivationMode == ServiceModel::ServicePackageActivationMode::Enum::ExclusiveProcess;
// Always add default metrics.
serviceDescription.AddDefaultMetrics();
// Resources should be added as metrics in case that SP is governed.
for (auto & rgResource : itServicePackage->second.Description.CorrectedRequiredResources)
{
double rgMetricWeight = (rgResource.first == ServiceModel::Constants::SystemMetricNameCpuCores) ?
config.CpuCoresMetricWeight :
config.MemoryInMBMetricWeight;
if (isSingleReplicaPerHost)
{
serviceDescription.AddRGMetric(rgResource.first, rgMetricWeight, rgResource.second, rgResource.second);
}
else
{
serviceDescription.AddRGMetric(rgResource.first, rgMetricWeight, 0, 0);
}
}
}
}
}
//this is a service package created before 5.6 and we do not have info about it so we will create a new one so that we can keep track of all the services it has
//so we can do proper safety check when this service package starts using rg
else
{
if (appIt != applicationTable_.end())
{
ServicePackageDescription description(
ServiceModel::ServicePackageIdentifier(serviceDescription.ServicePackageIdentifier),
std::map<std::wstring, double>(),
std::vector<std::wstring>());
auto servicePackageId = nextServicePackageIndex_;
serviceDescription.ServicePackageId = servicePackageId;
servicePackageToIdMap_.insert(make_pair(description.ServicePackageIdentifier, nextServicePackageIndex_++));
itServicePackage = servicePackageTable_.insert(make_pair(servicePackageId, ServicePackage(move(description)))).first;
appIt->second.AddServicePackage(itServicePackage->second.Description);
plbStatistics_.AddServicePackage(itServicePackage->second.Description);
Trace.UpdateServicePackage(itServicePackage->second.Description.Name, itServicePackage->second.Description);
}
}
// This will add default metrics in case that service does not report any other metric.
// In case that service uses RG, default metrics were added above.
serviceDescription.AddDefaultMetrics();
// If service is stateful singleton replica, align primary and secondary default loads
// This is needed to avoid issues during singleton replica movement,
// as first new secondary is created, and then promoted to primary
if (serviceDescription.IsStateful && serviceDescription.TargetReplicaSetSize == 1)
{
serviceDescription.AlignSinletonReplicaDefLoads();
}
//is this a new service create, or an update for an existing service...
bool isNewService = serviceToDomainTable_.find(serviceDescription.ServiceId) == serviceToDomainTable_.end();
//Affinty relationship is not allowed to have chains...
if (IsAffinityChain(serviceDescription))
{
Trace.AffinityChainDetected(serviceDescription.Name, serviceDescription.AffinitizedService);
if (forceUpdate)
{
// Report health error on this service
plbDiagnosticsSPtr_->ReportServiceCorrelationError(serviceDescription);
// Remove affinity from service description and proceed
serviceDescription.ClearAffinitizedService();
}
else
{
return make_pair(ErrorCodeValue::ServiceAffinityChainNotSupported, false);
}
}
if (!forceUpdate)
{
if (!CheckClusterResource(serviceDescription))
{
return make_pair(ErrorCodeValue::InsufficientClusterCapacity, false);
}
if (PLBConfig::GetConfig().ValidatePlacementConstraint && !IsMaster && !serviceDescription.PlacementConstraints.empty())
{
if (!CheckConstraintKeyDefined(serviceDescription))
{
Trace.ConstraintKeyUndefined(serviceDescription.Name, serviceDescription.PlacementConstraints);
return make_pair(ErrorCodeValue::ConstraintKeyUndefined, false);
}
else
{
if (serviceNameToPlacementConstraintTable_.size() < PLBConfig::GetConfig().PlacementConstraintValidationCacheSize)
{
//Adds this to the table only if the CreateServiceCall it will be successful
serviceNameToPlacementConstraintTable_.insert(std::make_pair(serviceDescription.Name, expressionCache_));
}
expressionCache_ = nullptr;
}
}
}
PLBConfig & config = PLBConfig::GetConfig();
// we take a copy of the metrics instead of reference since
// we can use it later for reattaching FT's in case the service is being redefined.
vector<ServiceMetric> metrics = serviceDescription.Metrics;
ASSERT_IF(metrics.empty(), "Service metrics should not be empty");
//store a copy of existing failoverUnits and original metrics
vector<FailoverUnit> relatedFailoverUnits;
vector<ServiceMetric> originalMetrics;
//domains to be merged: we only use this when we aren't supposed to update metric connections...(i.e during domain split)
set<ServiceDomain::DomainId> domains;
bool areRelatedFailoverUnitsStateful = false;
//freshly affinitized as child, but parent hasn't been created yet.
bool isParentAbsent = false;
// This is used for scaling based on number of partitions
Common::StopwatchTime serviceScalingExpiry = Common::StopwatchTime::Zero;
int partitionCountChange = 0;
auto isAutoScalingPolicyValid = [](ServiceDescription const& serviceDescription)
{
int minCount = 0;
auto autoScalingPolicy = serviceDescription.AutoScalingPolicy;
if (autoScalingPolicy.Mechanism->Kind == ScalingMechanismKind::PartitionInstanceCount)
{
InstanceCountScalingMechanismSPtr mechanism = static_pointer_cast<InstanceCountScalingMechanism>(serviceDescription.AutoScalingPolicy.Mechanism);
minCount = mechanism->MinimumInstanceCount;
}
if (autoScalingPolicy.Mechanism->Kind == ScalingMechanismKind::AddRemoveIncrementalNamedPartition)
{
AddRemoveIncrementalNamedPartitionScalingMechanismSPtr mechanism = static_pointer_cast<AddRemoveIncrementalNamedPartitionScalingMechanism>(serviceDescription.AutoScalingPolicy.Mechanism);
minCount = mechanism->MinimumPartitionCount;
}
if (minCount == 0)
{
return false;
}
else
{
return true;
}
};
if (isNewService)
{
if (!forceUpdate && serviceDescription.IsAutoScalingDefined)
{
//New auto scaling policy should be created with new service, so checking is mincount > 0
if (!isAutoScalingPolicyValid(serviceDescription))
{
Trace.InvalidAutoScaleMinCount(serviceDescription.Name);
return make_pair(ErrorCodeValue::InvalidServiceScalingPolicy, false);
}
}
}
bool isScalingPolicyChanged = false;
//In case of redefinition, we will execute a deleteService() to clear all info. This deleteservice won't split any domains though.
if (!isNewService)
{
ASSERT_IF(!updateMetricConnections, "Metric Connections should always be updated for a non-new service");
//Get all failover unit associated with that service
auto itServiceToDomain = serviceToDomainTable_.find(serviceDescription.ServiceId);
// No need to check itServiceToDomain, because isNewService is true if element is not found
auto itServiceDomain = serviceDomainTable_.find((itServiceToDomain->second)->first);
if (itServiceDomain != serviceDomainTable_.end())
{
// store original metrics related in original service description
auto itService = itServiceDomain->second.Services.find(serviceDescription.ServiceId);
if (itService != itServiceDomain->second.Services.end())
{
serviceScalingExpiry = itService->second.NextAutoScaleCheck;
ServiceDescription const& originalServiceDescription = itService->second.ServiceDesc;
partitionCountChange = serviceDescription.PartitionCount - originalServiceDescription.PartitionCount;
originalMetrics = originalServiceDescription.Metrics;
plbStatistics_.UpdateService(originalServiceDescription, serviceDescription);
// Check if FTs will have to be checked and updated for AS
if (serviceDescription.IsAutoScalingDefined != originalServiceDescription.IsAutoScalingDefined)
{
isScalingPolicyChanged = true;
}
else if (serviceDescription.IsAutoScalingDefined)
{
isScalingPolicyChanged = serviceDescription.AutoScalingPolicy != originalServiceDescription.AutoScalingPolicy;
}
if (!forceUpdate && serviceDescription.IsAutoScalingDefined && isScalingPolicyChanged)
{
// Autoscaling policy is changed so check is it still valid
if (!isAutoScalingPolicyValid(serviceDescription))
{
Trace.InvalidAutoScaleMinCount(serviceDescription.Name);
return make_pair(ErrorCodeValue::InvalidServiceScalingPolicy, false);
}
}
//inquires whether the affinity relationship was changed
areRelatedFailoverUnitsStateful = originalServiceDescription.IsStateful;
}
GuidUnorderedMap<FailoverUnit> const& failoverUnits = (itServiceDomain->second).FailoverUnits;
for (auto iter = failoverUnits.begin(); iter != failoverUnits.end(); ++iter)
{
if (iter->second.FuDescription.ServiceName == serviceDescription.Name)
{
relatedFailoverUnits.push_back(iter->second);
}
}
for (auto iter = relatedFailoverUnits.begin(); iter != relatedFailoverUnits.end(); ++iter)
{
wstring serviceName = serviceDescription.Name;
// Delete failover unit by id and name
FailoverUnitDescription fuDescription(iter->FUId, move(serviceName), serviceDescription.ServiceId);
itServiceDomain->second.UpdateFailoverUnit(move(fuDescription), Common::Stopwatch::Now(), false);
}
}
if (!forceUpdate)
{
// Error is reported when affinity chain is detected and service update is required to remove affinity chain.
// Clear the health error only if this is user-initiated service update (!force and !new)
plbDiagnosticsSPtr_->ReportServiceDescriptionOK(serviceDescription);
}
bool deleted = InternalDeleteService(serviceDescription.Name, false, false);
ASSERT_IFNOT(deleted, "Failed to delete a service for service update.");
}
//in case its new service, just make sure its not being re-created due to a splitdomain.
//if it is being created due to a splitdomain, we will take care of metric merges later.
if (updateMetricConnections)
{
metricConnections_.AddOrRemoveMetricConnectionsForService(metrics, true);
}
//add this service to the service package
if (itServicePackage != servicePackageTable_.end())
{
itServicePackage->second.Services.insert(serviceDescription.ServiceId);
}
//in case service is part of an application with capacity
if (!serviceDescription.ApplicationName.empty())
{
appIt->second.AddService(serviceDescription.Name);
if (appHasScaleoutOrCapacity)
{
if (updateMetricConnections)
{
ChangeServiceMetricsForApplication(
serviceDescription.ApplicationId, serviceDescription.Name, metrics);
}
else
{
//in case the application already has a Domain...
auto appDomainIt = applicationToDomainTable_.find(serviceDescription.ApplicationId);
if (appDomainIt != applicationToDomainTable_.end())
{
domains.insert(appDomainIt->second->second.Id);
}
else
//in case application metrics can connect Domains...
{
auto itAppById = applicationTable_.find(serviceDescription.ApplicationId);
DBGASSERT_IF(itAppById == applicationTable_.end(), "Undefined Application {0}", serviceDescription.ApplicationName);
if (itAppById != applicationTable_.end() && itAppById->second.ApplicationDesc.HasScaleoutOrCapacity())
{
auto capacities = itAppById->second.ApplicationDesc.AppCapacities;
for (auto capacityIt = capacities.begin(); capacityIt != capacities.end(); capacityIt++)
{
auto metricDomainIt = metricToDomainTable_.find(capacityIt->second.MetricName);
if (metricDomainIt != metricToDomainTable_.end())
{
domains.insert(metricDomainIt->second->second.Id);
}
}
}
}
}
}
}
//in case service was affinitized as child...
if (!serviceDescription.AffinitizedService.empty())
{
auto const& itId = serviceToIdMap_.find(serviceDescription.AffinitizedService);
if (itId != serviceToIdMap_.end())
{
auto itDomain = serviceToDomainTable_.find(itId->second);
if (itDomain != serviceToDomainTable_.end())
{
auto itService = itDomain->second->second.Services.find(itId->second);
if (itService != itDomain->second->second.Services.end())
{
if (isNewService && itService->second.ServiceDesc.PartitionCount > 1)
{
Trace.ParentServiceMultipleParitions(itService->second.ServiceDesc);
}
}
//make sure its not being re-created due to a splitdomain
if (updateMetricConnections)
{
auto itSecondService = itDomain->second->second.Services.find(itId->second);
if (itSecondService != itDomain->second->second.Services.end())
{
metricConnections_.AddOrRemoveMetricAffinity(metrics, itSecondService->second.ServiceDesc.Metrics, true);
}
}
else
{
domains.insert(itDomain->second->second.Id);
}
}
else
{
isParentAbsent = true;
}
}
else
{
isParentAbsent = true;
}
}
//in case it's affinitized as parent
else
{
bool hasChildren = false;
auto it = dependedServiceToDomainTable_.find(serviceDescription.Name);
//parent created after child...
if (it != dependedServiceToDomainTable_.end())
{
DBGASSERT_IF(it->second.empty(), "dependedServiceToDomainTable entry shouldn't contain 0 domains for service {0}", serviceDescription.Name);
vector<wstring> dependentDomains;
dependentDomains.reserve(it->second.size());
for (auto domainIt = it->second.begin(); domainIt != it->second.end(); ++domainIt)
{
dependentDomains.push_back((*domainIt)->second.Id);
auto childServices = (*domainIt)->second.ChildServices.find(serviceDescription.Name);
if (childServices != (*domainIt)->second.ChildServices.end())
{
for (auto itChild = childServices->second.begin(); itChild != childServices->second.end(); itChild++)
{
uint64 childServiceId = GetServiceId(*itChild);
if (childServiceId != 0)
{
auto itService = (*domainIt)->second.Services.find(childServiceId);
hasChildren = true;
if (updateMetricConnections)
{
auto itSecondService = (*domainIt)->second.Services.find(childServiceId);
if (itSecondService != (*domainIt)->second.Services.end())
{
metricConnections_.AddOrRemoveMetricAffinity(metrics, itSecondService->second.ServiceDesc.Metrics, true);
}
}
else
{
auto itDomain = serviceToDomainTable_.find(childServiceId);
if (itDomain != serviceToDomainTable_.end())
{
domains.insert(itDomain->second->second.Id);
}
}
}
}
}
}
dependedServiceToDomainTable_.erase(it);
}
if (isNewService && hasChildren && serviceDescription.PartitionCount > 1)
{
Trace.ParentServiceMultipleParitions(serviceDescription);
}
}
if (updateMetricConnections)
{
//If it is a new service, we should not bother with trying to Split Domains...
ExecuteDomainChange(true);
}
for (auto itDomain = serviceDomainTable_.begin(); itDomain != serviceDomainTable_.end(); itDomain++)
{
DBGASSERT_IF(!metricConnections_.AreMetricsConnected(itDomain->second.Metrics) && isNewService && PLBConfig::GetConfig().SplitDomainEnabled, "Metrics aren't connected before adding service: {0}", serviceDescription.Name);
}
ServiceDomainTable::iterator itNewDomain = serviceDomainTable_.end();
for (auto itMetric = metrics.begin(); itMetric != metrics.end(); ++itMetric)
{
auto itMetricToDomain = metricToDomainTable_.find(itMetric->Name);
if (itMetricToDomain != metricToDomainTable_.end())
{
if (updateMetricConnections)
{
DBGASSERT_IF(itNewDomain != itMetricToDomain->second && itNewDomain != serviceDomainTable_.end(), "Domains we not properly merged for {0}", metrics);
//we decided the domain from its metrics.
itNewDomain = itMetricToDomain->second;
}
else
{
domains.insert(itMetricToDomain->second->second.Id);
}
}
if (!itMetric->IsBuiltIn && !itMetric->IsMetricOfDefaultServices() &&
(config.MetricBalancingThresholds.find(itMetric->Name) == config.MetricBalancingThresholds.end() ||
config.MetricActivityThresholds.find(itMetric->Name) == config.MetricActivityThresholds.end()))
{
// Trace warning that metric does not have activity or balancing threshold
if (traceDetail)
{
Trace.MetricDoesNotHaveThreshold(itMetric->Name, serviceDescription.Name);
}
}
}
//if we're not performing metric connection operations, we get the domain from it's dependencies.
if (!domains.empty() && !updateMetricConnections)
{
itNewDomain = MergeDomains(domains);
}
//if we couldn't find any domain, we will create a new one.
if (itNewDomain == serviceDomainTable_.end())
{
ServiceDomain::DomainId newDomainId = GenerateUniqueDomainId(serviceDescription);
ServiceDomain newDomain(ServiceDomain::DomainId(newDomainId), *this);
std::wstring prefix = ServiceDomain::GetDomainIdPrefix(newDomainId);
itNewDomain = serviceDomainTable_.insert(make_pair(move(prefix), move(newDomain))).first;
if (traceDetail)
{
Trace.AddServiceDomain(itNewDomain->second.Id);
}
}
//Various domain addition operations(app+dependedservice+service)...
if (appHasScaleoutOrCapacity)
{
// No need to check if application exists because it is added above and we're under lock
AddApplicationToDomain(applicationTable_.find(serviceDescription.ApplicationId)->second.ApplicationDesc, itNewDomain, vector<wstring>());
}
//if paren't hasnt been created yet, we add depended service to itNewDomain
if (isParentAbsent)
{
AddDependedServiceToDomain(serviceDescription.AffinitizedService, itNewDomain);
}
auto serviceId = serviceDescription.ServiceId;
if (isScalingPolicyChanged || isNewService)
{
if (serviceDescription.IsAutoScalingDefined)
{
if (serviceDescription.AutoScalingPolicy.IsServiceScaled())
{
serviceScalingExpiry = Stopwatch::Now() + serviceDescription.AutoScalingPolicy.GetScalingInterval();
}
else
{
serviceScalingExpiry = Common::StopwatchTime::Zero;
}
}
else
{
serviceScalingExpiry = Common::StopwatchTime::Zero;
}
}
AddServiceToDomain(move(serviceDescription), itNewDomain);
AutoScaler & autoScaler = itNewDomain->second.AutoScalerComponent;
autoScaler.AddService(serviceId, serviceScalingExpiry);
autoScaler.UpdateServicePartitionCount(serviceId, partitionCountChange);
auto serviceIt = itNewDomain->second.Services.find(serviceId);
// Keep this updated
if (serviceIt != itNewDomain->second.Services.end())
{
serviceIt->second.NextAutoScaleCheck = serviceScalingExpiry;
}
DBGASSERT_IF(itNewDomain->second.CheckMetrics() && PLBConfig::GetConfig().SplitDomainEnabled, "Found metrics with No services and No Applications in {0}", itNewDomain->second.Id);
//reattach the failover units...
if (!isNewService)
{
auto itServiceToDomain = serviceToDomainTable_.find(serviceId);
if (itServiceToDomain != serviceToDomainTable_.end())
{
auto & service = itServiceToDomain->second->second.GetService(serviceId);
// add back failover units when update service
for (auto iter = relatedFailoverUnits.begin(); iter != relatedFailoverUnits.end(); ++iter)
{
FailoverUnit ft = *iter;
ASSERT_IFNOT(originalMetrics.size() == ft.PrimaryEntries.size() &&
originalMetrics.size() == ft.SecondaryEntries.size(),
"original metrics size does not match with load entry size in failover unit. MetricsSize:{0}, PrimaryEntry:{1}, SecondaryEntry:{2}",
originalMetrics.size(), ft.PrimaryEntries.size(), ft.SecondaryEntries.size());
vector<uint> primaryEntries;
vector<uint> secondaryEntries;
map<Federation::NodeId, std::vector<uint>> newSecondaryEntriesMap;
for (int j = 0; j < metrics.size(); ++j)
{
primaryEntries.push_back(service.GetDefaultMetricLoad(j, ReplicaRole::Primary));
secondaryEntries.push_back(service.GetDefaultMetricLoad(j, ReplicaRole::Secondary));
}
for (auto it = ft.SecondaryEntriesMap.begin(); it != ft.SecondaryEntriesMap.end(); ++it)
{
newSecondaryEntriesMap.insert(std::make_pair(it->first, secondaryEntries));
}
StopwatchTime scalingExpiry = ft.NextScalingCheck;
//if the scaling policy has changed we need to recompute the next interval to look at it
//if we have removed the policy now we should just set the interval to zero
//else we calculate it from this point + scale interval
if (isScalingPolicyChanged)
{
if (service.ServiceDesc.IsAutoScalingDefined)
{
if (service.ServiceDesc.AutoScalingPolicy.IsPartitionScaled())
{
scalingExpiry = Stopwatch::Now() + service.ServiceDesc.AutoScalingPolicy.GetScalingInterval();
}
else
{
scalingExpiry = StopwatchTime::Zero;
}
}
else
{
scalingExpiry = StopwatchTime::Zero;
}
}
FailoverUnitDescription newFTDescription(ft.FuDescription);
//if no scaling policy, or if scaling is not per partition, target is always equal to the one from service description
if (!service.ServiceDesc.IsAutoScalingDefined || !service.ServiceDesc.AutoScalingPolicy.IsPartitionScaled())
{
newFTDescription.TargetReplicaSetSize = service.ServiceDesc.TargetReplicaSetSize;
}
//We create FailoverUnit with all default values for Move and Load costs
FailoverUnit newFt(move(newFTDescription), move(primaryEntries), move(secondaryEntries),
move(newSecondaryEntriesMap), service.GetDefaultMoveCost(ReplicaRole::Primary), service.GetDefaultMoveCost(ReplicaRole::Secondary),
serviceDescription.OnEveryNode, move(ft.ResourceLoadMap), scalingExpiry);
//Recostructing FailoverUnit by updating with original primary and secondary load costs
for (int j = 0; j < metrics.size(); ++j)
{
wstring const & metricName = metrics[j].Name;
for (size_t k = 0; k < originalMetrics.size(); ++k)
{
// If the metric also defined before and it is not default value (updated by service report load),
// we should keep the reported load
// otherwise we will still use the new default load.
if (originalMetrics[k].Name == metricName)
{
if (areRelatedFailoverUnitsStateful && ft.IsPrimaryLoadReported[k])
{
newFt.UpdateLoad(ReplicaRole::Primary, static_cast<size_t>(j), ft.PrimaryEntries[k], this->Settings);
}
if (!this->Settings.UseSeparateSecondaryLoad)
{
if (ft.IsSecondaryLoadReported[k])
{
newFt.UpdateLoad(ReplicaRole::Secondary, static_cast<size_t>(j), ft.SecondaryEntries[k], this->Settings);
}
}
else
{
for (auto it = ft.IsSecondaryLoadMapReported.begin(); it != ft.IsSecondaryLoadMapReported.end(); ++it)
{
if (it->second[k])
{
auto itMap = ft.SecondaryEntriesMap.find(it->first);
if (itMap != ft.SecondaryEntriesMap.end())
{
newFt.UpdateLoad(ReplicaRole::Secondary, static_cast<size_t>(j), itMap->second[k], this->Settings, true, it->first);
}
}
}
}
break;
}
}
}
// Recostructing FailoverUnit by updating with original primary and secondary move costs
if (ft.IsMoveCostReported(ReplicaRole::Primary))
{
newFt.UpdateMoveCost(ReplicaRole::Primary, ft.PrimaryMoveCost);
}
if (ft.IsMoveCostReported(ReplicaRole::Secondary))
{
newFt.UpdateMoveCost(ReplicaRole::Secondary, ft.SecondaryMoveCost);
}
auto itServiceToDomain2 = serviceToDomainTable_.find(newFt.FuDescription.ServiceId);
// Failover units that were deleted for update are reattached to service here. If service is missing in serviceToDomainTable_
// assert is neccessary, because PLB would lose track of service failover units completely.
ASSERT_IF(itServiceToDomain2 == serviceToDomainTable_.end(), "Service {0} doesn't exist in service to domain id table", newFt.FuDescription.ServiceId);
itServiceToDomain2->second->second.AddFailoverUnit(move(newFt), Stopwatch::Now());
}
}
}
//Split at the end(we check !isNewService, because new services cannot cause a domain split.)...
if (updateMetricConnections && !isNewService)
{
ExecuteDomainChange();
}
for (auto itDomain = serviceDomainTable_.begin(); itDomain != serviceDomainTable_.end(); itDomain++)
{
DBGASSERT_IF(!metricConnections_.AreMetricsConnected(itDomain->second.Metrics) && PLBConfig::GetConfig().SplitDomainEnabled, "Metrics aren't connected for: {0}", itDomain->second.Id);
}
return make_pair(ErrorCodeValue::Success, false);
}