step.grid.tokenpool.affinityevaluator.capacityaware.CapacityAwareTokenWrapperAffinityEvaluatorImpl Maven / Gradle / Ivy
/*******************************************************************************
* Copyright (C) 2020, exense GmbH
*
* This file is part of STEP
*
* STEP is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* STEP is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with STEP. If not, see .
******************************************************************************/
package step.grid.tokenpool.affinityevaluator.capacityaware;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import ch.exense.commons.io.FileWatchService;
import step.grid.TokenWrapper;
import step.grid.TokenWrapperState;
import step.grid.tokenpool.AffinityEvaluator;
import step.grid.tokenpool.Identity;
import step.grid.tokenpool.TokenPool;
import step.grid.tokenpool.affinityevaluator.TokenPoolAware;
import step.grid.tokenpool.affinityevaluator.TokenWrapperAffinityEvaluatorImpl;
import step.grid.tokenpool.affinityevaluator.capacityaware.CapacityAwareTokenWrapperAffinityEvaluatorConf.Agent;
/**
* An {@link AffinityEvaluator} that takes the token usage per agent host into account and thus
* gives priority to tokens located on agent hosts with the lowest token usage.
*
* This {@link AffinityEvaluator} makes it also possible to define a maximal capacity per agent host.
*
*/
public class CapacityAwareTokenWrapperAffinityEvaluatorImpl extends TokenWrapperAffinityEvaluatorImpl implements TokenPoolAware, Closeable {
private TokenPool tokenPool;
private CapacityAwareTokenWrapperAffinityEvaluatorConf conf;
private FileWatchService fileWatchService;
private static final int MAX_SCORE = 1000;
public CapacityAwareTokenWrapperAffinityEvaluatorImpl() {
super();
}
@Override
@SuppressWarnings("unchecked")
public void setTokenPool(TokenPool, ?> tokenPool) {
this.tokenPool = (TokenPool) tokenPool;
}
@Override
public void setProperties(Map properties) {
super.setProperties(properties);
if(properties!=null) {
String configuration = properties.get("configuration");
if(configuration != null) {
File configurationFile = new File(configuration);
init(configurationFile);
} else {
throw new RuntimeException("Error while initializing "+CapacityAwareTokenWrapperAffinityEvaluatorImpl.class.getSimpleName()+": Missing property 'configuration'");
}
} else {
throw new RuntimeException("Error while initializing "+CapacityAwareTokenWrapperAffinityEvaluatorImpl.class.getSimpleName()+": Missing properties");
}
}
protected void init(File configurationFile) {
parseConf(configurationFile);
fileWatchService = new FileWatchService();
fileWatchService.register(configurationFile, () -> {
parseConf(configurationFile);
});
}
protected void parseConf(File configurationFile) {
CapacityAwareTokenWrapperAffinityEvaluatorConfParser confParser = new CapacityAwareTokenWrapperAffinityEvaluatorConfParser();
try {
conf = confParser.parse(configurationFile);
} catch (Exception e) {
throw new RuntimeException("Error while parsing configuration file "+configurationFile.getAbsolutePath(), e);
}
}
public int getAffinityScore(Identity i1, TokenWrapper i2) {
final String agentUrl = i2.getAgent().getAgentUrl();
// parse the agentUrl to get the agent host on which the agent host usage calculation is based
String agentHost = getHost(agentUrl);
/// the token usage per agent host
AtomicInteger agentHostUsage = new AtomicInteger(0);
// calculate the token usage per agent host
tokenPool.getTokens().forEach(tokenWrapper->{
if(tokenWrapper.getState() == TokenWrapperState.IN_USE && getHost(tokenWrapper.getAgent().getAgentUrl()).equals(agentHost)) {
agentHostUsage.incrementAndGet();
}
});
// get the configuration for this agent host
Agent agentHostConf = conf.getAgents().stream().filter(a->a.getHostname().equals(agentHost)).findFirst().orElse(null);
int currentAgentUsage = agentHostUsage.get();
if(agentHostConf != null && agentHostConf.getCapacity() != -1 && currentAgentUsage>=agentHostConf.getCapacity()) {
// the token usage of this agent host reached the maximal defined capacity => thus return -1
return -1;
} else {
int defaultAffinityScore = super.getAffinityScore(i1, i2);
// only the sign of the default affinity evaluator is considered here i.e. if it is matching or not.
int signumOfDefaultAffinityScore = Integer.signum(defaultAffinityScore);
// returning a score which is inversely proportional to the token usage or -1 if the default affinity score is equal to -1
return Math.max(-1, Math.max(1, (MAX_SCORE-currentAgentUsage))*signumOfDefaultAffinityScore);
}
}
protected String getHost(final String agentUrl) {
URL url;
try {
url = new URL(agentUrl);
} catch (MalformedURLException e) {
throw new RuntimeException("Error while parsing url "+agentUrl, e);
}
return url.getHost();
}
@Override
public void close() throws IOException {
fileWatchService.close();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy