use of org.apache.pulsar.policies.data.loadbalancer.NamespaceBundleStats in project incubator-pulsar by apache.
the class ModularLoadManagerImplTest method testLoadShedding.
// Test that load shedding works
@Test
public void testLoadShedding() throws Exception {
final NamespaceBundleStats stats1 = new NamespaceBundleStats();
final NamespaceBundleStats stats2 = new NamespaceBundleStats();
stats1.msgRateIn = 100;
stats2.msgRateIn = 200;
final Map<String, NamespaceBundleStats> statsMap = new ConcurrentHashMap<>();
statsMap.put(mockBundleName(1), stats1);
statsMap.put(mockBundleName(2), stats2);
final LocalBrokerData localBrokerData = new LocalBrokerData();
localBrokerData.update(new SystemResourceUsage(), statsMap);
final Namespaces namespacesSpy1 = spy(pulsar1.getAdminClient().namespaces());
AtomicReference<String> bundleReference = new AtomicReference<>();
doAnswer(invocation -> {
bundleReference.set(invocation.getArguments()[0].toString() + '/' + invocation.getArguments()[1]);
return null;
}).when(namespacesSpy1).unloadNamespaceBundle(Mockito.anyString(), Mockito.anyString());
setField(pulsar1.getAdminClient(), "namespaces", namespacesSpy1);
pulsar1.getConfiguration().setLoadBalancerEnabled(true);
final LoadData loadData = (LoadData) getField(primaryLoadManager, "loadData");
final Map<String, BrokerData> brokerDataMap = loadData.getBrokerData();
final BrokerData brokerDataSpy1 = spy(brokerDataMap.get(primaryHost));
when(brokerDataSpy1.getLocalData()).thenReturn(localBrokerData);
brokerDataMap.put(primaryHost, brokerDataSpy1);
// Need to update all the bundle data for the shedder to see the spy.
primaryLoadManager.onUpdate(null, null, null);
Thread.sleep(100);
localBrokerData.setCpu(new ResourceUsage(80, 100));
primaryLoadManager.doLoadShedding();
// 80% is below overload threshold: verify nothing is unloaded.
verify(namespacesSpy1, Mockito.times(0)).unloadNamespaceBundle(Mockito.anyString(), Mockito.anyString());
localBrokerData.getCpu().usage = 90;
primaryLoadManager.doLoadShedding();
// Most expensive bundle will be unloaded.
verify(namespacesSpy1, Mockito.times(1)).unloadNamespaceBundle(Mockito.anyString(), Mockito.anyString());
assertEquals(bundleReference.get(), mockBundleName(2));
primaryLoadManager.doLoadShedding();
// Now less expensive bundle will be unloaded (normally other bundle would move off and nothing would be
// unloaded, but this is not the case due to the spy's behavior).
verify(namespacesSpy1, Mockito.times(2)).unloadNamespaceBundle(Mockito.anyString(), Mockito.anyString());
assertEquals(bundleReference.get(), mockBundleName(1));
primaryLoadManager.doLoadShedding();
// Now both are in grace period: neither should be unloaded.
verify(namespacesSpy1, Mockito.times(2)).unloadNamespaceBundle(Mockito.anyString(), Mockito.anyString());
}
use of org.apache.pulsar.policies.data.loadbalancer.NamespaceBundleStats in project incubator-pulsar by apache.
the class SimpleLoadManagerImplTest method testNamespaceBundleStats.
@Test
public void testNamespaceBundleStats() {
NamespaceBundleStats nsb1 = new NamespaceBundleStats();
nsb1.msgRateOut = 10000;
nsb1.producerCount = 1;
nsb1.consumerCount = 1;
nsb1.cacheSize = 4;
nsb1.msgRateIn = 500;
nsb1.msgThroughputIn = 30;
nsb1.msgThroughputOut = 30;
NamespaceBundleStats nsb2 = new NamespaceBundleStats();
nsb2.msgRateOut = 20000;
nsb2.producerCount = 300;
nsb2.consumerCount = 300;
nsb2.cacheSize = 110000;
nsb2.msgRateIn = 5000;
nsb2.msgThroughputIn = 110000.0;
nsb2.msgThroughputOut = 110000.0;
assertTrue(nsb1.compareTo(nsb2) == -1);
assertTrue(nsb1.compareByMsgRate(nsb2) == -1);
assertTrue(nsb1.compareByTopicConnections(nsb2) == -1);
assertTrue(nsb1.compareByCacheSize(nsb2) == -1);
assertTrue(nsb1.compareByBandwidthOut(nsb2) == -1);
assertTrue(nsb1.compareByBandwidthIn(nsb2) == -1);
}
use of org.apache.pulsar.policies.data.loadbalancer.NamespaceBundleStats in project incubator-pulsar by apache.
the class LoadSimulationController method initializeBundleData.
// Initialize a BundleData from a resource quota and configurations and modify the quota accordingly.
private BundleData initializeBundleData(final ResourceQuota quota, final ShellArguments arguments) {
final double messageRate = (quota.getMsgRateIn() + quota.getMsgRateOut()) / 2;
final int messageSize = (int) Math.ceil((quota.getBandwidthIn() + quota.getBandwidthOut()) / (2 * messageRate));
arguments.rate = messageRate * arguments.rateMultiplier;
arguments.size = messageSize;
final NamespaceBundleStats startingStats = new NamespaceBundleStats();
// Modify the original quota so that new rates are set.
final double modifiedRate = messageRate * arguments.rateMultiplier;
final double modifiedBandwidth = (quota.getBandwidthIn() + quota.getBandwidthOut()) * arguments.rateMultiplier / 2;
quota.setMsgRateIn(modifiedRate);
quota.setMsgRateOut(modifiedRate);
quota.setBandwidthIn(modifiedBandwidth);
quota.setBandwidthOut(modifiedBandwidth);
// Assume modified memory usage is comparable to the rate multiplier times the original usage.
quota.setMemory(quota.getMemory() * arguments.rateMultiplier);
startingStats.msgRateIn = quota.getMsgRateIn();
startingStats.msgRateOut = quota.getMsgRateOut();
startingStats.msgThroughputIn = quota.getBandwidthIn();
startingStats.msgThroughputOut = quota.getBandwidthOut();
final BundleData bundleData = new BundleData(10, 1000, startingStats);
// Assume there is ample history for the bundle.
bundleData.getLongTermData().setNumSamples(1000);
bundleData.getShortTermData().setNumSamples(10);
return bundleData;
}
use of org.apache.pulsar.policies.data.loadbalancer.NamespaceBundleStats in project incubator-pulsar by apache.
the class SimpleLoadManagerImpl method doLoadShedding.
@Override
public void doLoadShedding() {
long overloadThreshold = this.getLoadBalancerBrokerOverloadedThresholdPercentage();
long comfortLoadLevel = this.getLoadBalancerBrokerComfortLoadThresholdPercentage();
log.info("Running load shedding task as leader broker, overload threshold {}, comfort loadlevel {}", overloadThreshold, comfortLoadLevel);
// overloadedRU --> bundleName
Map<ResourceUnit, String> namespaceBundlesToBeUnloaded = new HashMap<>();
synchronized (currentLoadReports) {
for (Map.Entry<ResourceUnit, LoadReport> entry : currentLoadReports.entrySet()) {
ResourceUnit overloadedRU = entry.getKey();
LoadReport lr = entry.getValue();
if (isAboveLoadLevel(lr.getSystemResourceUsage(), overloadThreshold)) {
ResourceType bottleneckResourceType = lr.getBottleneckResourceType();
Map<String, NamespaceBundleStats> bundleStats = lr.getSortedBundleStats(bottleneckResourceType);
if (bundleStats == null) {
log.warn("Null bundle stats for bundle {}", lr.getName());
continue;
}
// 1. owns only one namespace
if (bundleStats.size() == 1) {
// can't unload one namespace, just issue a warning message
String bundleName = lr.getBundleStats().keySet().iterator().next();
log.warn("HIGH USAGE WARNING : Sole namespace bundle {} is overloading broker {}. " + "No Load Shedding will be done on this broker", bundleName, overloadedRU.getResourceId());
continue;
}
for (Map.Entry<String, NamespaceBundleStats> bundleStat : bundleStats.entrySet()) {
String bundleName = bundleStat.getKey();
NamespaceBundleStats stats = bundleStat.getValue();
// We need at least one underloaded RU from list of candidates that can host this bundle
if (isBrokerAvailableForRebalancing(bundleStat.getKey(), comfortLoadLevel)) {
log.info("Namespace bundle {} will be unloaded from overloaded broker {}, bundle stats (topics: {}, producers {}, " + "consumers {}, bandwidthIn {}, bandwidthOut {})", bundleName, overloadedRU.getResourceId(), stats.topics, stats.producerCount, stats.consumerCount, stats.msgThroughputIn, stats.msgThroughputOut);
namespaceBundlesToBeUnloaded.put(overloadedRU, bundleName);
} else {
log.info("Unable to shed load from broker {}, no brokers with enough capacity available " + "for re-balancing {}", overloadedRU.getResourceId(), bundleName);
}
break;
}
}
}
}
unloadNamespacesFromOverLoadedBrokers(namespaceBundlesToBeUnloaded);
}
use of org.apache.pulsar.policies.data.loadbalancer.NamespaceBundleStats in project incubator-pulsar by apache.
the class SimpleLoadManagerImpl method doNamespaceBundleSplit.
/**
* Detect and split hot namespace bundles
*/
@Override
public void doNamespaceBundleSplit() throws Exception {
int maxBundleCount = pulsar.getConfiguration().getLoadBalancerNamespaceMaximumBundles();
long maxBundleTopics = pulsar.getConfiguration().getLoadBalancerNamespaceBundleMaxTopics();
long maxBundleSessions = pulsar.getConfiguration().getLoadBalancerNamespaceBundleMaxSessions();
long maxBundleMsgRate = pulsar.getConfiguration().getLoadBalancerNamespaceBundleMaxMsgRate();
long maxBundleBandwidth = pulsar.getConfiguration().getLoadBalancerNamespaceBundleMaxBandwidthMbytes() * MBytes;
log.info("Running namespace bundle split with thresholds: topics {}, sessions {}, msgRate {}, bandwidth {}, maxBundles {}", maxBundleTopics, maxBundleSessions, maxBundleMsgRate, maxBundleBandwidth, maxBundleCount);
if (this.lastLoadReport == null || this.lastLoadReport.getBundleStats() == null) {
return;
}
Map<String, NamespaceBundleStats> bundleStats = this.lastLoadReport.getBundleStats();
Set<String> bundlesToBeSplit = new HashSet<>();
for (Map.Entry<String, NamespaceBundleStats> statsEntry : bundleStats.entrySet()) {
String bundleName = statsEntry.getKey();
NamespaceBundleStats stats = statsEntry.getValue();
long totalSessions = stats.consumerCount + stats.producerCount;
double totalMsgRate = stats.msgRateIn + stats.msgRateOut;
double totalBandwidth = stats.msgThroughputIn + stats.msgThroughputOut;
boolean needSplit = false;
if (stats.topics > maxBundleTopics || totalSessions > maxBundleSessions || totalMsgRate > maxBundleMsgRate || totalBandwidth > maxBundleBandwidth) {
if (stats.topics <= 1) {
log.info("Unable to split hot namespace bundle {} since there is only one topic.", bundleName);
} else {
NamespaceName namespaceName = NamespaceName.get(LoadManagerShared.getNamespaceNameFromBundleName(bundleName));
int numBundles = pulsar.getNamespaceService().getBundleCount(namespaceName);
if (numBundles >= maxBundleCount) {
log.info("Unable to split hot namespace bundle {} since the namespace has too many bundles.", bundleName);
} else {
needSplit = true;
}
}
}
if (needSplit) {
if (this.getLoadBalancerAutoBundleSplitEnabled()) {
log.info("Will split hot namespace bundle {}, topics {}, producers+consumers {}, msgRate in+out {}, bandwidth in+out {}", bundleName, stats.topics, totalSessions, totalMsgRate, totalBandwidth);
bundlesToBeSplit.add(bundleName);
} else {
log.info("DRY RUN - split hot namespace bundle {}, topics {}, producers+consumers {}, msgRate in+out {}, bandwidth in+out {}", bundleName, stats.topics, totalSessions, totalMsgRate, totalBandwidth);
}
}
}
if (bundlesToBeSplit.size() > 0) {
for (String bundleName : bundlesToBeSplit) {
try {
pulsar.getAdminClient().namespaces().splitNamespaceBundle(LoadManagerShared.getNamespaceNameFromBundleName(bundleName), LoadManagerShared.getBundleRangeFromBundleName(bundleName), pulsar.getConfiguration().isLoadBalancerAutoUnloadSplitBundlesEnabled());
log.info("Successfully split namespace bundle {}", bundleName);
} catch (Exception e) {
log.error("Failed to split namespace bundle {}", bundleName, e);
}
}
this.setLoadReportForceUpdateFlag();
}
}
Aggregations