/**
* Copyright 2016 LinkedIn Corp. All rights reserved.
*
* 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.
*/
package com.github.ambry.tools.admin;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
class Disk {
public long capacity;
public long freeCapacity;
public int id;
public Node node;
public Disk(int id, long capacity, Node node) {
this.capacity = capacity;
this.freeCapacity = capacity;
this.id = id;
this.node = node;
}
}
class Node {
public List<Disk> disks;
public int id;
public Node(int id, int numberOfDisks, long capacity) {
disks = new ArrayList<Disk>(numberOfDisks);
for (int i = 0; i < numberOfDisks; i++) {
disks.add(new Disk(i, capacity, this));
}
this.id = id;
}
public long getFreeCapacity() {
long freeCapacity = 0;
for (Disk disk : disks) {
freeCapacity += disk.freeCapacity;
}
return freeCapacity;
}
public Disk getDiskWithMostCapacity(long replicaSize) {
Disk minDisk = null;
for (Disk disk : disks) {
if ((minDisk == null || minDisk.freeCapacity < disk.freeCapacity) && disk.freeCapacity >= replicaSize) {
minDisk = disk;
}
}
return minDisk;
}
}
class Datacenter {
public List<Node> nodes;
public Datacenter(int numberOfNodes, int numberOfDisks, long capacity) {
nodes = new ArrayList<Node>(numberOfNodes);
for (int i = 0; i < numberOfNodes; i++) {
nodes.add(new Node(i, numberOfDisks, capacity));
}
}
public Node getNodeWithMostFreeCapacity() {
Node minNode = null;
for (Node node : nodes) {
if (minNode == null || minNode.getFreeCapacity() < node.getFreeCapacity()) {
minNode = node;
}
}
return minNode;
}
}
class Partition {
List<Disk> disks;
public Partition(List<Disk> disks) {
this.disks = disks;
}
}
public class TestPartitionAlloc {
public static void main(String args[]) {
// 1. strategy 1
Datacenter dc = new Datacenter(12, 14, 1099511627776L);
int numberOfPartitions = 100;
int numberOfReplicas = 3;
long replicaSize = 107374182400L;
List<Partition> partitions = new ArrayList<Partition>(numberOfPartitions);
Strategy1(dc, partitions, numberOfPartitions, numberOfReplicas, replicaSize);
System.out.println("Strategy 1");
Output(dc, partitions);
partitions.clear();
dc = new Datacenter(12, 14, 1099511627776L);
Strategy2(dc, partitions, numberOfPartitions, numberOfReplicas, replicaSize);
System.out.println("Strategy 2");
Output(dc, partitions);
partitions.clear();
dc = new Datacenter(12, 14, 1099511627776L);
Strategy3(dc, partitions, numberOfPartitions, numberOfReplicas, replicaSize);
System.out.println("Strategy 3");
Output(dc, partitions);
partitions.clear();
// This is the best strategy with static allocation map. Any two disks differ by atmost 1 replica
dc = new Datacenter(12, 14, 1099511627776L);
Strategy4(dc, partitions, numberOfPartitions, numberOfReplicas, replicaSize);
System.out.println("Strategy 4");
Output(dc, partitions);
partitions.clear();
}
// shuffle nodes and disks and choose the first for each replica
public static void Strategy1(Datacenter dc, List<Partition> partitions, int numberOfPartitions, int numberOfReplicas,
long replicaSize) {
for (int i = 0; i < numberOfPartitions; i++) {
List<Node> nodes = dc.nodes;
Collections.shuffle(nodes);
List<Disk> allocatedDisks = new ArrayList<Disk>();
for (Node dataNode : nodes) {
if (allocatedDisks.size() == numberOfReplicas) {
break;
}
List<Disk> shuffledDisks = dataNode.disks;
Collections.shuffle(shuffledDisks);
for (Disk disk : shuffledDisks) {
if (disk.freeCapacity >= replicaSize) {
allocatedDisks.add(disk);
disk.freeCapacity = disk.freeCapacity - replicaSize;
break; // Only one replica per DataNodeId.
}
}
}
partitions.add(new Partition(allocatedDisks));
}
}
// choose a random node and disk for each replica
public static void Strategy2(Datacenter dc, List<Partition> partitions, int numberOfPartitions, int numberOfReplicas,
long replicaSize) {
Random rand = new Random(System.currentTimeMillis());
for (int i = 0; i < numberOfPartitions; i++) {
List<Node> nodes = dc.nodes;
List<Disk> allocatedDisks = new ArrayList<Disk>();
int j = 0;
while (j < numberOfReplicas) {
int index = rand.nextInt(nodes.size());
Set<Integer> checkedDisks = new HashSet<Integer>();
while (true) {
List<Disk> shuffledDisks = nodes.get(index).disks;
int diskIndex = rand.nextInt(shuffledDisks.size());
Disk disk = shuffledDisks.get(diskIndex);
if (!checkedDisks.contains(disk.id)) {
if (disk.freeCapacity >= replicaSize) {
allocatedDisks.add(disk);
disk.freeCapacity = disk.freeCapacity - replicaSize;
j++;
break;
}
checkedDisks.add(disk.id);
}
}
}
partitions.add(new Partition(allocatedDisks));
}
}
// shuffle nodes and pick the first while disk is chosen based on most capacity left
public static void Strategy3(Datacenter dc, List<Partition> partitions, int numberOfPartitions, int numberOfReplicas,
long replicaSize) {
for (int i = 0; i < numberOfPartitions; i++) {
List<Node> nodes = dc.nodes;
Collections.shuffle(nodes);
List<Disk> allocatedDisks = new ArrayList<Disk>();
for (Node dataNode : nodes) {
if (allocatedDisks.size() == numberOfReplicas) {
break;
}
Disk disk = dataNode.getDiskWithMostCapacity(replicaSize);
allocatedDisks.add(disk);
disk.freeCapacity = disk.freeCapacity - replicaSize;
}
partitions.add(new Partition(allocatedDisks));
}
}
// choose node and disk based on most capacity left
public static void Strategy4(Datacenter dc, List<Partition> partitions, int numberOfPartitions, int numberOfReplicas,
long replicaSize) {
for (int i = 0; i < numberOfPartitions; i++) {
List<Disk> allocatedDisks = new ArrayList<Disk>();
for (int j = 0; j < numberOfReplicas; j++) {
Node node = dc.getNodeWithMostFreeCapacity();
Disk disk = node.getDiskWithMostCapacity(replicaSize);
allocatedDisks.add(disk);
disk.freeCapacity = disk.freeCapacity - replicaSize;
}
partitions.add(new Partition(allocatedDisks));
}
}
public static void Output(Datacenter dc, List<Partition> partitions) {
Map<String, List<Integer>> diskmap = new HashMap<String, List<Integer>>();
for (int i = 0; i < dc.nodes.size(); i++) {
for (int j = 0; j < dc.nodes.get(i).disks.size(); j++) {
diskmap.put(dc.nodes.get(i).disks.get(j).id + ":" + dc.nodes.get(i).id, new ArrayList<Integer>());
}
}
System.out.println("Partitions");
for (int i = 0; i < partitions.size(); i++) {
System.out.print("Partition Id " + i);
for (int j = 0; j < partitions.get(i).disks.size(); j++) {
List<Integer> replicas =
diskmap.get(partitions.get(i).disks.get(j).id + ":" + partitions.get(i).disks.get(j).node.id);
replicas.add(i);
diskmap.put(partitions.get(i).disks.get(j).id + ":" + partitions.get(i).disks.get(j).node.id, replicas);
System.out.print(
" ReplicaId " + partitions.get(i).disks.get(j).id + ":" + partitions.get(i).disks.get(j).node.id + " - "
+ partitions.get(i).disks.get(j).freeCapacity);
}
System.out.println();
}
System.out.println("Distribution by disks");
for (Map.Entry<String, List<Integer>> entry : diskmap.entrySet()) {
System.out.print(" diskId " + entry.getKey());
for (int i = 0; i < entry.getValue().size(); i++) {
System.out.print(" partitionId " + entry.getValue().get(i) + " ");
}
System.out.println();
}
}
}