package edu.brown.catalog;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import org.apache.commons.collections15.set.ListOrderedSet;
import org.apache.log4j.Logger;
import org.voltdb.compiler.ClusterConfig;
import edu.brown.hstore.HStoreThreadManager;
import edu.brown.logging.LoggerUtil;
import edu.brown.logging.LoggerUtil.LoggerBoolean;
import edu.brown.utils.CollectionUtil;
import edu.brown.utils.FileUtil;
import edu.brown.utils.PartitionSet;
import edu.brown.utils.StringUtil;
/**
* This is a more useful extension of VoltDB's ClusterConfig
* @author pavlo
*/
public class ClusterConfiguration extends ClusterConfig {
private static final Logger LOG = Logger.getLogger(ClusterConfiguration.class);
private static final LoggerBoolean debug = new LoggerBoolean();
private static final LoggerBoolean trace = new LoggerBoolean();
static {
LoggerUtil.attachObserver(LOG, debug, trace);
}
private static final Pattern COLON_SPLIT = Pattern.compile(":");
/**
* Host -> SiteId -> Set<PartitionConfiguration>
*/
private final Map<String, Map<Integer, Set<PartitionConfiguration>>> host_sites = new HashMap<String, Map<Integer, Set<PartitionConfiguration>>>();
private final Set<Integer> all_partitions = new HashSet<Integer>();
/**
* PartitionConfiguration
*/
private class PartitionConfiguration {
private final String host;
private final int site;
private final int partition;
public PartitionConfiguration(String host, int site, int partition) {
this.host = host;
this.site = site;
this.partition = partition;
}
@Override
public String toString() {
return String.format("%s - %s", this.host,
HStoreThreadManager.getThreadName(this.site, this.partition));
}
}
public ClusterConfiguration(String hostname_format, int num_hosts, int num_sites_per_host, int num_partitions_per_site) {
super();
int siteid = 0;
int partitionid = 0;
final boolean use_format = hostname_format.contains("%");
for (int host = 0; host < num_hosts; host++) {
String hostname = (use_format ? String.format(hostname_format, host) : hostname_format);
for (int site = 0; site < num_sites_per_host; site++) {
for (int partition = 0; partition < num_partitions_per_site; partition++) {
this.addPartition(hostname, siteid, partitionid++);
} // FOR (partitions)
siteid++;
} // FOR (sites)
} // FOR (hosts)
}
public ClusterConfiguration() {
super();
}
public ClusterConfiguration(Collection<String> host_triplets) {
super();
for (String host_info : host_triplets) {
this.addPartition(host_info);
} // FOR
}
public ClusterConfiguration(String hosts) {
List<String> host_triplets = new ArrayList<String>();
if (FileUtil.exists(hosts)) {
String contents = FileUtil.readFile(hosts);
CollectionUtil.addAll(host_triplets, contents.split("\n"));
} else {
CollectionUtil.addAll(host_triplets, hosts.split(";"));
}
for (String host_info : host_triplets) {
this.addPartition(host_info);
} // FOR
}
public void clear() {
this.host_sites.clear();
this.all_partitions.clear();
}
@Override
public boolean validate() {
return (this.host_sites.isEmpty() == false);
}
public boolean isEmpty() {
return (this.host_sites.isEmpty());
}
public void addPartition(String host_info) {
host_info = host_info.trim();
if (host_info.isEmpty())
return;
String data[] = COLON_SPLIT.split(host_info);
assert (data.length == 3) : "Invalid host information '" + host_info + "'";
String host = data[0];
if (host.startsWith("#"))
return;
int site = Integer.parseInt(data[1]);
// Partition Ranges
PartitionSet partitions = PartitionSet.parse(data[2]);
for (int partition : partitions) {
this.addPartition(host, site, partition);
} // FOR
}
public synchronized void addPartition(String host, int site, int partition) {
if (this.all_partitions.contains(partition)) {
throw new IllegalArgumentException("Duplicate partition id #" + partition + " for host '" + host + "'");
}
if (debug.val)
LOG.info(String.format("Adding Partition: %s:%d:%d", host, site, partition));
PartitionConfiguration pc = new PartitionConfiguration(host, site, partition);
this.all_partitions.add(partition);
// Host -> Sites
if (!this.host_sites.containsKey(host)) {
this.host_sites.put(host, new HashMap<Integer, Set<PartitionConfiguration>>());
}
if (!this.host_sites.get(host).containsKey(site)) {
this.host_sites.get(host).put(site, new HashSet<PartitionConfiguration>());
}
this.host_sites.get(host).get(site).add(pc);
if (debug.val)
LOG.debug("New PartitionConfiguration: " + pc);
}
public Collection<String> getHosts() {
return (this.host_sites.keySet());
}
public Collection<Integer> getSites(String host) {
return (this.host_sites.get(host).keySet());
}
private Collection<PartitionConfiguration> getPartitions(String host, int site) {
return (this.host_sites.get(host).get(site));
}
public Collection<Integer> getPartitionIds(String host, int site) {
Set<Integer> ids = new ListOrderedSet<Integer>();
for (PartitionConfiguration pc : this.getPartitions(host, site)) {
ids.add(pc.partition);
} // FOR
return (ids);
}
@Override
public String toString() {
return StringUtil.formatMaps(this.host_sites);
}
}