/* dCache - http://www.dcache.org/
*
* Copyright (C) 2014 Deutsches Elektronen-Synchrotron
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.dcache.poolmanager;
import com.google.common.collect.ImmutableMap;
import java.security.SecureRandom;
import java.util.List;
import java.util.Map;
import diskCacheV111.poolManager.CostModule;
import diskCacheV111.pools.PoolCostInfo;
import diskCacheV111.util.CacheException;
import org.dcache.pool.assumption.AvailableSpaceAssumption;
import org.dcache.vehicles.FileAttributes;
/**
* This partition type is for pools that are used as temporary transfer buffers
* rather than for long term storage.
*
* Pool selection does not take free space into account (with the exception of not
* selecting full pools for writes). Pools are selected by load only, using a
* randomized weighted selection with 1/2^(cc * mc * n) as the weigth, where n is
* the number of movers on the pool, mc is the mover cost factor of the pool and
* cc is the cpucostfactor as configured in the partition. Mover queue limits do
* not influence pool selection.
*
* Buffer pools do not support hot spot replication, cost based link fallbacks,
* replication limits, same host replication protection, nor idle cost cuts.
* Buffer pools do support pool to pool transfers as well as stage from tape,
* although the current implementation may not be well suited for tape pools.
*/
public class BufferPartition extends Partition
{
private static final long serialVersionUID = 437942839152498525L;
private static final SecureRandom RANDOM = new SecureRandom();
static final String TYPE = "buffer";
private static final Map<String,String> DEFAULTS =
ImmutableMap.of("cpucostfactor", "1.0");
private final double costFactor;
public BufferPartition(Map<String, String> inherited)
{
this(inherited, Partition.NO_PROPERTIES);
}
public BufferPartition(Map<String, String> inherited, Map<String, String> defined)
{
super(DEFAULTS, inherited, defined);
costFactor = getDouble("cpucostfactor");
}
@Override
protected Partition create(Map<String, String> inherited, Map<String, String> defined)
{
return new BufferPartition(inherited, defined);
}
@Override
public String getType()
{
return TYPE;
}
private double random()
{
return RANDOM.nextDouble();
}
private double getWeight(PoolCostInfo cost)
{
int movers = 0;
for (PoolCostInfo.PoolQueueInfo queue : cost.getExtendedMoverHash().values()) {
movers += queue.getActive() + queue.getQueued();
}
return 1.0 / (Math.pow(2.0, (cost.getMoverCostFactor() * costFactor * movers)));
}
private SelectedPool selectPool(List<PoolInfo> pools)
{
double[] available = new double[pools.size()];
double sum = 0.0;
for (int i = 0; i < available.length; i++) {
sum += getWeight(pools.get(i).getCostInfo());
available[i] = sum;
}
double threshold = random() * sum;
for (int i = 0; i < available.length; i++) {
if (threshold < available[i]) {
return new SelectedPool(pools.get(i));
}
}
return null;
}
private SelectedPool selectPool(List<PoolInfo> pools, final long preallocated)
{
double[] available = new double[pools.size()];
double sum = 0.0;
for (int i = 0; i < available.length; i++) {
PoolInfo pool = pools.get(i);
PoolCostInfo.PoolSpaceInfo space = pool.getCostInfo().getSpaceInfo();
if (space.getFreeSpace() + space.getRemovableSpace() - space.getGap() > preallocated) {
sum += getWeight(pool.getCostInfo());
available[i] = sum;
}
}
double threshold = random() * sum;
for (int i = 0; i < available.length; i++) {
if (threshold < available[i]) {
return new SelectedPool(pools.get(i), new AvailableSpaceAssumption(preallocated));
}
}
return null;
}
@Override
public SelectedPool selectWritePool(CostModule cm, List<PoolInfo> pools, FileAttributes attributes,
long preallocated) throws CacheException
{
return selectPool(pools, preallocated);
}
@Override
public SelectedPool selectReadPool(CostModule cm, List<PoolInfo> pools, FileAttributes attributes) throws CacheException
{
return selectPool(pools);
}
@Override
public P2pPair selectPool2Pool(CostModule cm, List<PoolInfo> src, List<PoolInfo> dst,
FileAttributes attributes, boolean force) throws CacheException
{
return new P2pPair(selectPool(src), selectPool(dst, attributes.getSize()));
}
@Override
public SelectedPool selectStagePool(CostModule cm, List<PoolInfo> pools, String previousPool, String previousHost,
FileAttributes attributes) throws CacheException
{
return selectPool(pools, attributes.getSize());
}
}