/*
* Copyright 2016 Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hadoop.yarn.server.resourcemanager.quota;
import io.hops.exception.StorageException;
import io.hops.metadata.yarn.dal.quota.PriceMultiplicatorDataAccess;
import io.hops.metadata.yarn.dal.util.YARNOperationType;
import io.hops.metadata.yarn.entity.quota.PriceMultiplicator;
import io.hops.transaction.handler.LightWeightRequestHandler;
import io.hops.util.RMStorageFactory;
import java.io.IOException;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.service.AbstractService;
import org.apache.hadoop.yarn.conf.YarnConfiguration;
import org.apache.hadoop.yarn.server.resourcemanager.RMContext;
import org.apache.hadoop.yarn.server.resourcemanager.scheduler.QueueMetrics;
public class PriceMultiplicatiorService extends AbstractService {
private static final Log LOG = LogFactory.getLog(PriceMultiplicatiorService.class);
private Configuration conf;
private final RMContext rmcontext;
private volatile boolean stopped = false;
private Thread priceCalculationThread;
private float tippingPoint;
private float incrementFactor;
private long priceMultiplicationFactorCalculationInterval;
private float currentMultiplicator;
private PriceMultiplicatorDataAccess priceMultiplicatorDA;
public PriceMultiplicatiorService(RMContext rmctx) {
super("Price multiplication factor service");
rmcontext = rmctx;
}
@Override
public void serviceInit(Configuration conf) throws Exception {
LOG.info("Initializing price estimation service");
this.conf = conf;
// Initialize config parameters
this.tippingPoint = this.conf.getFloat(
YarnConfiguration.QUOTA_MULTIPLICATOR_THRESHOLD,
YarnConfiguration.DEFAULT_QUOTA_MULTIPLICATOR_THRESHOLD);
this.incrementFactor = this.conf.getFloat(
YarnConfiguration.QUOTA_INCREMENT_FACTOR,
YarnConfiguration.DEFAULT_QUOTA_INCREMENT_FACTOR);
this.priceMultiplicationFactorCalculationInterval = this.conf.getLong(
YarnConfiguration.QUOTA_PRICE_MULTIPLICATOR_INTERVAL,
YarnConfiguration.DEFAULT_QUOTA_PRICE_MULTIPLICATOR_INTERVAL);
// Initialize DataAccesses
this.priceMultiplicatorDA = (PriceMultiplicatorDataAccess) RMStorageFactory.
getDataAccess(PriceMultiplicatorDataAccess.class);
currentMultiplicator = 1;
recover();
}
private void recover() throws IOException {
Map<PriceMultiplicator.MultiplicatorType, PriceMultiplicator> currentMultiplicators
= getCurrentMultiplicator();
if (currentMultiplicators.get(PriceMultiplicator.MultiplicatorType.VARIABLE)
!= null) {
currentMultiplicator = currentMultiplicators.get(
PriceMultiplicator.MultiplicatorType.VARIABLE).getValue();
}
}
private Map<PriceMultiplicator.MultiplicatorType, PriceMultiplicator> getCurrentMultiplicator()
throws IOException {
LightWeightRequestHandler currentPriceHandler
= new LightWeightRequestHandler(YARNOperationType.TEST) {
@Override
public Object performTask() throws StorageException {
connector.beginTransaction();
connector.readCommitted();
Map<PriceMultiplicator.MultiplicatorType, PriceMultiplicator> currentPrices
= priceMultiplicatorDA.getAll();
connector.commit();
return currentPrices;
}
};
return (Map<PriceMultiplicator.MultiplicatorType, PriceMultiplicator>) currentPriceHandler.
handle();
}
@Override
protected void serviceStart() throws Exception {
assert !stopped : "starting when already stopped";
LOG.info("Starting a new price estimation service.");
priceCalculationThread = new Thread(new WorkingThread());
priceCalculationThread.setName("Price estimation service");
priceCalculationThread.setDaemon(true);
priceCalculationThread.start();
super.serviceStart();
}
@Override
protected void serviceStop() throws Exception {
stopped = true;
if (priceCalculationThread != null) {
priceCalculationThread.interrupt();
}
super.serviceStop();
LOG.info("Stopping the price estimation service.");
}
private class WorkingThread implements Runnable {
@Override
public void run() {
LOG.info("Price estimation service started");
while (!stopped && !Thread.currentThread().isInterrupted()) {
try {
// Calculate the price based on resource usage
CalculateNewPrice();
persistMultiplicator();
// Pass the latest price to the Containers Log Service
ContainersLogsService cl = rmcontext.getContainersLogsService();
if (cl != null) {
cl.setCurrentPrice(currentMultiplicator);
}
Thread.sleep(priceMultiplicationFactorCalculationInterval);
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
} catch (IOException ex) {
LOG.error(ex, ex);
}
}
LOG.info("Quota scheduler thread is exiting gracefully");
}
}
protected void CalculateNewPrice() throws IOException {
QueueMetrics metrics = rmcontext.getScheduler().
getRootQueueMetrics();
int totalMB = metrics.getAllocatedMB() + metrics.getAvailableMB();
int totalCores = metrics.getAllocatedVirtualCores() + metrics.
getAvailableVirtualCores();
int usedMB = metrics.getAllocatedMB() + metrics.getPendingMB();
float persentUsedMB = (float) usedMB / totalMB;
int usedCores = metrics.getAllocatedVirtualCores() + metrics.
getPendingVirtualCores();
float persentUsedCores = (float) usedCores / totalCores;
float incrementBase = Math.max(persentUsedCores, persentUsedMB)
- tippingPoint;
incrementBase = Math.max(incrementBase, 0);
currentMultiplicator = 1 + incrementBase * incrementFactor;
LOG.debug("New multiplicator: " + currentMultiplicator + " (mem: "
+ persentUsedMB + ", vcores: " + persentUsedCores + ")");
}
private void persistMultiplicator() throws IOException {
LightWeightRequestHandler prepareHandler;
prepareHandler = new LightWeightRequestHandler(YARNOperationType.TEST) {
@Override
public Object performTask() throws IOException {
connector.beginTransaction();
connector.writeLock();
priceMultiplicatorDA.add(new PriceMultiplicator(
PriceMultiplicator.MultiplicatorType.VARIABLE,
currentMultiplicator));
connector.commit();
return null;
}
};
prepareHandler.handle();
}
}