/*
* The Alluxio Open Foundation licenses this work under the Apache License, version 2.0
* (the "License"). You may not use this work except in compliance with the License, which is
* available at www.apache.org/licenses/LICENSE-2.0
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied, as more fully set forth in the License.
*
* See the NOTICE file distributed with this work for information regarding copyright ownership.
*/
package alluxio.client.block.policy;
import alluxio.client.block.BlockWorkerInfo;
import alluxio.client.block.policy.options.GetWorkerOptions;
import alluxio.wire.WorkerNetAddress;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Random;
import javax.annotation.concurrent.NotThreadSafe;
/**
* This policy maps blockId to several deterministic Alluxio workers. The number of workers a block
* can be mapped to can be passed through the constructor. The default is 1. It skips the workers
* that do not have enough capacity to hold the block.
*/
@NotThreadSafe
public final class DeterministicHashPolicy implements BlockLocationPolicy {
/** The default number of shards to serve a block. */
private static final int DEFAULT_NUM_SHARDS = 1;
private final int mShards;
private final Random mRandom = new Random();
/**
* Constructs a new {@link DeterministicHashPolicy}.
*/
public DeterministicHashPolicy() {
this(DEFAULT_NUM_SHARDS);
}
/**
* Constructs a new {@link DeterministicHashPolicy}.
*
* @param numShards the number of shards a block's traffic can be sharded to
*/
public DeterministicHashPolicy(Integer numShards) {
Preconditions.checkArgument(numShards >= 1);
mShards = numShards;
}
@Override
public WorkerNetAddress getWorker(GetWorkerOptions options) {
List<BlockWorkerInfo> workerInfos = Lists.newArrayList(options.getBlockWorkerInfos());
Collections.sort(workerInfos, new Comparator<BlockWorkerInfo>() {
@Override
public int compare(BlockWorkerInfo o1, BlockWorkerInfo o2) {
return o1.getNetAddress().toString().compareToIgnoreCase(o2.getNetAddress().toString());
}
});
HashMap<WorkerNetAddress, BlockWorkerInfo> blockWorkerInfoMap = new HashMap<>();
for (BlockWorkerInfo workerInfo : options.getBlockWorkerInfos()) {
blockWorkerInfoMap.put(workerInfo.getNetAddress(), workerInfo);
}
List<WorkerNetAddress> workers = new ArrayList<>();
// Try the next one if the worker mapped from the blockId doesn't work until all the workers
// are examined.
int index = (int) (options.getBlockId() % (long) workerInfos.size());
for (BlockWorkerInfo blockWorkerInfoUnused : workerInfos) {
WorkerNetAddress candidate = workerInfos.get(index).getNetAddress();
BlockWorkerInfo workerInfo = blockWorkerInfoMap.get(candidate);
if (workerInfo != null && workerInfo.getCapacityBytes() >= options.getBlockSize()) {
workers.add(candidate);
if (workers.size() >= mShards) {
break;
}
}
index = (index + 1) % workerInfos.size();
}
return workers.isEmpty() ? null : workers.get(mRandom.nextInt(workers.size()));
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof DeterministicHashPolicy)) {
return false;
}
DeterministicHashPolicy that = (DeterministicHashPolicy) o;
return Objects.equal(mShards, that.mShards);
}
@Override
public int hashCode() {
return Objects.hashCode(mShards);
}
@Override
public String toString() {
return Objects.toStringHelper(this).add("shards", mShards).toString();
}
}