use of com.yahoo.pulsar.common.policies.data.loadbalancer.LoadReport in project pulsar by yahoo.
the class LoadBalancerTest method testLoadReportsWrittenOnZK.
/*
* tests that load manager creates its node and writes the initial load report a /loadbalance/brokers tests that
* those load reports can be deserialized and are in valid format tests if the rankings are populated from the load
* reports are not, both broker will have zero rank
*/
@Test
public void testLoadReportsWrittenOnZK() throws Exception {
ZooKeeper zkc = bkEnsemble.getZkClient();
try {
for (int i = 0; i < BROKER_COUNT; i++) {
String znodePath = String.format("%s/%s", SimpleLoadManagerImpl.LOADBALANCE_BROKERS_ROOT, lookupAddresses[i]);
byte[] loadReportData = zkc.getData(znodePath, false, null);
assert (loadReportData.length > 0);
log.info("LoadReport {}, {}", lookupAddresses[i], new String(loadReportData));
LoadReport loadReport = objectMapper.readValue(loadReportData, LoadReport.class);
assert (loadReport.getName().equals(lookupAddresses[i]));
assertTrue(loadReport.isUnderLoaded());
assertFalse(loadReport.isOverLoaded());
// Check Initial Ranking is populated in both the brokers
Field ranking = ((SimpleLoadManagerImpl) pulsarServices[i].getLoadManager()).getClass().getDeclaredField("sortedRankings");
ranking.setAccessible(true);
AtomicReference<Map<Long, Set<ResourceUnit>>> sortedRanking = (AtomicReference<Map<Long, Set<ResourceUnit>>>) ranking.get(pulsarServices[i].getLoadManager());
printSortedRanking(sortedRanking);
// all brokers have same rank to it would be 0 --> set-of-all-the-brokers
int brokerCount = 0;
for (Map.Entry<Long, Set<ResourceUnit>> entry : sortedRanking.get().entrySet()) {
brokerCount += entry.getValue().size();
}
assertEquals(brokerCount, BROKER_COUNT);
DestinationName fqdn = DestinationName.get("persistent://pulsar/use/primary-ns/test-topic");
ResourceUnit found = pulsarServices[i].getLoadManager().getLeastLoaded(pulsarServices[i].getNamespaceService().getBundle(fqdn));
assertTrue(found != null);
}
} catch (InterruptedException | KeeperException e) {
fail("Unable to read the data from Zookeeper - [{}]", e);
}
}
use of com.yahoo.pulsar.common.policies.data.loadbalancer.LoadReport in project pulsar by yahoo.
the class SimpleLoadManagerImpl method start.
@Override
public void start() throws PulsarServerException {
try {
// Register the brokers in zk list
ServiceConfiguration conf = pulsar.getConfiguration();
if (pulsar.getZkClient().exists(LOADBALANCE_BROKERS_ROOT, false) == null) {
try {
ZkUtils.createFullPathOptimistic(pulsar.getZkClient(), LOADBALANCE_BROKERS_ROOT, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
} catch (KeeperException.NodeExistsException e) {
// ignore the exception, node might be present already
}
}
String lookupServiceAddress = pulsar.getAdvertisedAddress() + ":" + conf.getWebServicePort();
brokerZnodePath = LOADBALANCE_BROKERS_ROOT + "/" + lookupServiceAddress;
LoadReport loadReport = null;
try {
loadReport = generateLoadReport();
this.lastResourceUsageTimestamp = loadReport.getTimestamp();
} catch (Exception e) {
log.warn("Unable to get load report to write it on zookeeper [{}]", e);
}
String loadReportJson = "";
if (loadReport != null) {
loadReportJson = ObjectMapperFactory.getThreadLocal().writeValueAsString(loadReport);
}
try {
ZkUtils.createFullPathOptimistic(pulsar.getZkClient(), brokerZnodePath, loadReportJson.getBytes(Charsets.UTF_8), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
} catch (Exception e) {
// Catching excption here to print the right error message
log.error("Unable to create znode - [{}] for load balance on zookeeper ", brokerZnodePath, e);
throw e;
}
// first time, populate the broker ranking
updateRanking();
log.info("Created broker ephemeral node on {}", brokerZnodePath);
// load default resource quota
this.realtimeAvgResourceQuota = pulsar.getLocalZkCacheService().getResourceQuotaCache().getDefaultQuota();
this.lastResourceQuotaUpdateTimestamp = System.currentTimeMillis();
this.realtimeCpuLoadFactor = getDynamicConfigurationDouble(LOADBALANCER_DYNAMIC_SETTING_LOAD_FACTOR_CPU_ZPATH, SETTING_NAME_LOAD_FACTOR_CPU, this.realtimeCpuLoadFactor);
this.realtimeMemoryLoadFactor = getDynamicConfigurationDouble(LOADBALANCER_DYNAMIC_SETTING_LOAD_FACTOR_MEM_ZPATH, SETTING_NAME_LOAD_FACTOR_MEM, this.realtimeMemoryLoadFactor);
} catch (Exception e) {
log.error("Unable to create znode - [{}] for load balance on zookeeper ", brokerZnodePath, e);
throw new PulsarServerException(e);
}
}
use of com.yahoo.pulsar.common.policies.data.loadbalancer.LoadReport in project pulsar by yahoo.
the class SimpleLoadManagerImpl method writeLoadReportOnZookeeper.
@Override
public void writeLoadReportOnZookeeper() throws Exception {
// update average JVM heap usage to average value of the last 120 seconds
long realtimeJvmHeapUsage = getRealtimeJvmHeapUsageMBytes();
if (this.avgJvmHeapUsageMBytes <= 0) {
this.avgJvmHeapUsageMBytes = realtimeJvmHeapUsage;
} else {
long weight = Math.max(1, TimeUnit.SECONDS.toMillis(120) / LOAD_REPORT_UPDATE_MIMIMUM_INTERVAL);
this.avgJvmHeapUsageMBytes = ((weight - 1) * this.avgJvmHeapUsageMBytes + realtimeJvmHeapUsage) / weight;
}
// Update LoadReport in below situations:
// 1) This is the first time to update LoadReport
// 2) The last LoadReport is 5 minutes ago
// 3) There is more than 10% change on number of bundles assigned comparing with broker's maximum capacity
// 4) There is more than 10% change on resource usage comparing with broker's resource limit
boolean needUpdate = false;
if (lastLoadReport == null || this.forceLoadReportUpdate == true) {
needUpdate = true;
this.forceLoadReportUpdate = false;
} else {
long timestampNow = System.currentTimeMillis();
long timeElapsedSinceLastReport = timestampNow - lastLoadReport.getTimestamp();
int maxUpdateIntervalInMinutes = pulsar.getConfiguration().getLoadBalancerReportUpdateMaxIntervalMinutes();
if (timeElapsedSinceLastReport > TimeUnit.MINUTES.toMillis(maxUpdateIntervalInMinutes)) {
needUpdate = true;
} else if (timeElapsedSinceLastReport > LOAD_REPORT_UPDATE_MIMIMUM_INTERVAL) {
// check number of bundles assigned, comparing with last LoadReport
long oldBundleCount = lastLoadReport.getNumBundles();
long newBundleCount = pulsar.getBrokerService().getNumberOfNamespaceBundles();
long bundleCountChange = Math.abs(oldBundleCount - newBundleCount);
long maxCapacity = ResourceUnitRanking.calculateBrokerMaxCapacity(lastLoadReport.getSystemResourceUsage(), pulsar.getLocalZkCacheService().getResourceQuotaCache().getDefaultQuota());
double bundlePercentageChange = (maxCapacity > 0) ? (bundleCountChange * 100 / maxCapacity) : 0;
if (newBundleCount < oldBundleCount || bundlePercentageChange > pulsar.getConfiguration().getLoadBalancerReportUpdateThresholdPercentage()) {
needUpdate = true;
}
// check resource usage comparing with last LoadReport
if (!needUpdate && timestampNow - this.lastResourceUsageTimestamp > TimeUnit.MINUTES.toMillis(pulsar.getConfiguration().getLoadBalancerHostUsageCheckIntervalMinutes())) {
SystemResourceUsage oldUsage = lastLoadReport.getSystemResourceUsage();
SystemResourceUsage newUsage = this.getSystemResourceUsage();
this.lastResourceUsageTimestamp = timestampNow;
// calculate percentage of change
double cpuChange = (newUsage.cpu.limit > 0) ? ((newUsage.cpu.usage - oldUsage.cpu.usage) * 100 / newUsage.cpu.limit) : 0;
double memChange = (newUsage.memory.limit > 0) ? ((newUsage.memory.usage - oldUsage.memory.usage) * 100 / newUsage.memory.limit) : 0;
double directMemChange = (newUsage.directMemory.limit > 0) ? ((newUsage.directMemory.usage - oldUsage.directMemory.usage) * 100 / newUsage.directMemory.limit) : 0;
double bandwidthOutChange = (newUsage.bandwidthOut.limit > 0) ? ((newUsage.bandwidthOut.usage - oldUsage.bandwidthOut.usage) * 100 / newUsage.bandwidthOut.limit) : 0;
double bandwidthInChange = (newUsage.bandwidthIn.limit > 0) ? ((newUsage.bandwidthIn.usage - oldUsage.bandwidthIn.usage) * 100 / newUsage.bandwidthIn.limit) : 0;
long resourceChange = (long) Math.min(100.0, Math.max(Math.abs(cpuChange), Math.max(Math.abs(directMemChange), Math.max(Math.abs(memChange), Math.max(Math.abs(bandwidthOutChange), Math.abs(bandwidthInChange))))));
if (resourceChange > pulsar.getConfiguration().getLoadBalancerReportUpdateThresholdPercentage()) {
needUpdate = true;
log.info("LoadReport update triggered by change on resource usage, detal ({}).", String.format("cpu: %.1f%%, mem: %.1f%%, directMemory: %.1f%%, bandwidthIn: %.1f%%, bandwidthOut: %.1f%%)", cpuChange, memChange, directMemChange, bandwidthInChange, bandwidthOutChange));
}
}
}
}
if (needUpdate) {
LoadReport lr = generateLoadReport();
pulsar.getZkClient().setData(brokerZnodePath, ObjectMapperFactory.getThreadLocal().writeValueAsBytes(lr), -1);
this.lastLoadReport = lr;
this.lastResourceUsageTimestamp = lr.getTimestamp();
// split-bundle if requires
doNamespaceBundleSplit();
}
}
use of com.yahoo.pulsar.common.policies.data.loadbalancer.LoadReport in project pulsar by yahoo.
the class AdminTest method brokerStats.
@Test
void brokerStats() throws Exception {
doReturn("client-id").when(brokerStats).clientAppId();
Collection<Metrics> metrics = brokerStats.getMetrics();
assertNotNull(metrics);
LoadReport loadReport = brokerStats.getLoadReport();
assertNotNull(loadReport);
assertEquals(loadReport.isOverLoaded(), false);
Collection<Metrics> mBeans = brokerStats.getMBeans();
assertTrue(!mBeans.isEmpty());
AllocatorStats allocatorStats = brokerStats.getAllocatorStats("default");
assertNotNull(allocatorStats);
Map<String, Map<String, PendingBookieOpsStats>> bookieOpsStats = brokerStats.getPendingBookieOpsStats();
assertTrue(bookieOpsStats.isEmpty());
StreamingOutput destination = brokerStats.getDestinations2();
assertNotNull(destination);
Map<Long, Collection<ResourceUnit>> resource = brokerStats.getBrokerResourceAvailability("prop", "use", "ns2");
// size should be 1 with default resourceUnit
assertTrue(resource.size() == 1);
}
use of com.yahoo.pulsar.common.policies.data.loadbalancer.LoadReport in project pulsar by yahoo.
the class DiscoveryServiceServlet method redirect.
private void redirect(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
LoadReport broker = nextBroker();
URI brokerURI;
if (request.getScheme().equals("http")) {
// Use normal HTTP url
brokerURI = new URI(broker.getWebServiceUrl());
} else {
brokerURI = new URI(broker.getWebServiceUrlTls());
}
StringBuilder location = new StringBuilder();
location.append(brokerURI.getScheme()).append("://").append(brokerURI.getHost()).append(':').append(brokerURI.getPort()).append(request.getRequestURI());
if (request.getQueryString() != null) {
location.append('?').append(request.getQueryString());
}
if (log.isDebugEnabled()) {
log.info("Redirecting to {}", location);
}
response.setStatus(HttpServletResponse.SC_TEMPORARY_REDIRECT);
response.setHeader("Location", location.toString());
} catch (URISyntaxException e) {
log.warn("No broker found in zookeeper {}", e.getMessage(), e);
throw new RestException(Status.SERVICE_UNAVAILABLE, "Broker is not available");
}
}
Aggregations