package edu.berkeley.thebes.common.config; import java.io.FileNotFoundException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import javax.naming.ConfigurationException; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.yammer.metrics.reporting.ConsoleReporter; import com.yammer.metrics.reporting.GraphiteReporter; import edu.berkeley.thebes.common.config.ConfigParameterTypes.AtomicityLevel; import edu.berkeley.thebes.common.config.ConfigParameterTypes.IsolationLevel; import edu.berkeley.thebes.common.config.ConfigParameterTypes.PersistenceEngine; import edu.berkeley.thebes.common.config.ConfigParameterTypes.RoutingMode; import edu.berkeley.thebes.common.config.ConfigParameterTypes.SessionLevel; import edu.berkeley.thebes.common.config.ConfigParameterTypes.TransactionMode; import edu.berkeley.thebes.common.persistence.IPersistenceEngine; import edu.berkeley.thebes.common.persistence.disk.BDBPersistenceEngine; import edu.berkeley.thebes.common.persistence.disk.LevelDBPersistenceEngine; import edu.berkeley.thebes.common.persistence.memory.MemoryPersistenceEngine; import edu.berkeley.thebes.common.thrift.ServerAddress; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Config { private static TransactionMode txnMode; private static List<ServerAddress> clusterServers; private static List<ServerAddress> siblingServers = null; private static List<ServerAddress> masterServers; private static Logger logger = LoggerFactory.getLogger(Config.class); private static String HOST_NAME; private static AtomicBoolean initialized = new AtomicBoolean(false); private synchronized static void initialize(List<ConfigParameters> requiredParams) throws FileNotFoundException, ConfigurationException { if (initialized.getAndSet(true)) { return; } YamlConfig.initialize((String) getOptionNoYaml(ConfigParameters.CONFIG_FILE)); List<ConfigParameters> missingFields = Lists.newArrayList(); for(ConfigParameters param : requiredParams) { if(getOption(param) == null) missingFields.add(param); } if(missingFields.size() > 0) throw new ConfigurationException("missing required configuration options: "+missingFields); try { HOST_NAME = InetAddress.getLocalHost().getHostName(); } catch (UnknownHostException e) { HOST_NAME = "unknown-host"; e.printStackTrace(); } if (txnMode == null) txnMode = getThebesTxnMode(); clusterServers = getServersInCluster(getClusterID()); masterServers = getMasterServers(); configureGraphite(); if(getMetricsToConsole()) { ConsoleReporter reporter = new ConsoleReporter(System.err); reporter.start(5, TimeUnit.SECONDS); } new IOReporter().start(); } private static Boolean getMetricsToConsole() { return getOption(ConfigParameters.METRICS_TO_CONSOLE); } private static void configureGraphite() { String graphiteIP = getOption(ConfigParameters.GRAPHITE_IP); if (graphiteIP.equals("")) { return; } logger.debug("Connecting to graphite on host "+graphiteIP); GraphiteReporter.enable(20, TimeUnit.SECONDS, graphiteIP, 2003, HOST_NAME); } public static void initializeClient() throws FileNotFoundException, ConfigurationException { initialize(RequirementLevel.CLIENT_COMMON.getRequiredParameters()); } public static void initializeServer(TransactionMode mode) throws FileNotFoundException, ConfigurationException { txnMode = mode; initialize(RequirementLevel.SERVER_COMMON.getRequiredParameters()); siblingServers = getSiblingServers(getClusterID(), getServerID()); } public static void initializeTwoPLTransactionManager() throws FileNotFoundException, ConfigurationException { txnMode = TransactionMode.TWOPL; initialize(RequirementLevel.TWOPL_TM.getRequiredParameters()); } public Config() throws FileNotFoundException, ConfigurationException { clusterServers = getServersInCluster(getClusterID()); } private static Object getOptionNoYaml(ConfigParameters option) { Object ret = System.getProperty(option.getTextName()); if (ret != null) return option.castValue(ret); return option.getDefaultValue(); } public static <T> T getOption(ConfigParameters option) { Object ret = System.getProperty(option.getTextName()); if (ret != null) { return (T) option.castValue(ret); } ret = YamlConfig.getOption(option.getTextName()); if (ret != null) return (T) option.castValue(ret); ret = option.getDefaultValue(); if (ret != null) return (T) ret; else throw new IllegalStateException("No configuration for " + option); } public static PersistenceEngine getPersistenceType() { return getOption(ConfigParameters.PERSISTENCE_ENGINE); } public static Integer getServerPort() { return getOption(ConfigParameters.SERVER_PORT); } public static Integer getAntiEntropyServerPort() { return getOption(ConfigParameters.ANTI_ENTROPY_PORT); } public static Integer getTwoPLServerPort() { return getOption(ConfigParameters.TWOPL_PORT); } private static Integer getTwoPLTransactionManagerPort() { return getOption(ConfigParameters.TWOPL_TM_PORT); } public static Integer getClusterID() { return getOption(ConfigParameters.CLUSTERID); } /** Returns the cluster map (based on the current transaction mode). */ public static Map<Integer, List<String>> getClusterMap() { return getOption(txnMode.getClusterConfigParam()); } private static Map<Integer, List<String>> getMasterClusterMap() { return getOption(ConfigParameters.TWOPL_CLUSTER_CONFIG); } public static List<ServerAddress> getServersInCluster(int clusterID) { List<String> serverIPs = getClusterMap().get(clusterID); List<ServerAddress> servers = Lists.newArrayList(); for (int serverID = 0; serverID < serverIPs.size(); serverID ++) { String ip = serverIPs.get(serverID); if (ip.endsWith("*")) { ip = ip.substring(0, ip.length()-1); } servers.add(new ServerAddress(clusterID, serverID, ip, getServerPort())); } return servers; } public static Integer getServerID() { return getOption(ConfigParameters.SERVERID); } public static Short getClientID() { return getOption(ConfigParameters.CLIENTID); } private static List<ServerAddress> getSiblingServers(int clusterID, int serverID) { List<ServerAddress> ret = Lists.newArrayList(); Map<Integer, List<String>> clusterMap = getClusterMap(); for (int clusterKey : clusterMap.keySet()) { if (clusterKey == clusterID) continue; String server = clusterMap.get(clusterKey).get(serverID); if (txnMode == TransactionMode.TWOPL && server.endsWith("*")) { server = server.substring(0, server.length()-1); } ret.add(new ServerAddress(clusterKey, serverID, server, getServerPort())); } return ret; } /** * Returns the ordered list of Master servers for each serverId. * This returns null in HAT mode. */ public static List<ServerAddress> getMasterServers() { if (masterServers != null) { return masterServers; } Map<Integer, ServerAddress> masterMap = Maps.newHashMap(); Map<Integer, List<String>> clusterMap = getMasterClusterMap(); for (int clusterID : clusterMap.keySet()) { for (int serverID = 0; serverID < clusterMap.get(clusterID).size(); serverID ++) { String server = clusterMap.get(clusterID).get(serverID); if (server.endsWith("*")) { assert !masterMap.containsKey(serverID) : "2 masters for serverID " + serverID; masterMap.put(serverID, new ServerAddress(clusterID, serverID, server.substring(0, server.length()-1), getServerPort())); } } } List<ServerAddress> masters = Lists.newArrayListWithCapacity(clusterServers.size()); for (int i = 0; i < clusterServers.size(); i ++) { assert masterMap.containsKey(i) : "Missing master for replica set " + i; masters.add(masterMap.get(i)); } return masters; } public static Integer getSocketTimeout() { return getOption(ConfigParameters.SOCKET_TIMEOUT); } public static InetSocketAddress getServerBindIP() { return new InetSocketAddress(getServerIP(), getServerPort()); } public static InetSocketAddress getAntiEntropyServerBindIP() { return new InetSocketAddress(getServerIP(), getAntiEntropyServerPort()); } public static InetSocketAddress getTwoPLServerBindIP() { return new InetSocketAddress(getServerIP(), getServerPort()); } /** Returns the TM bind ip for the TM in *this* cluster. */ public static InetSocketAddress getTwoPLTransactionManagerBindIP() { Map<Integer, String> tmConfig = getOption(ConfigParameters.TWOPL_TM_CONFIG); String myIP = tmConfig.get(getClusterID()); return new InetSocketAddress(myIP, getTwoPLTransactionManagerPort()); } public static ServerAddress getTwoPLTransactionManagerByCluster(int clusterID) { Map<Integer, String> tmConfig = getOption(ConfigParameters.TWOPL_TM_CONFIG); return new ServerAddress(clusterID, -1, tmConfig.get(clusterID), getTwoPLTransactionManagerPort()); } public static Boolean shouldReplicateToTwoPLSlaves() { return getOption(ConfigParameters.TWOPL_REPLICATE_TO_SLAVES); } public static Boolean shouldUseTwoPLTM() { return getOption(ConfigParameters.TWOPL_USE_TM); } public static List<ServerAddress> getServersInCluster() { return clusterServers; } public static List<ServerAddress> getSiblingServers() { return siblingServers; } public static String getPrettyServerID() { return String.format("C%d:S%d", getClusterID(), getServerID()); } public static Boolean isStandaloneServer() { return getOption(ConfigParameters.STANDALONE); } public static Integer getNumAntiEntropyThreads() { return getOption(ConfigParameters.ANTI_ENTROPY_THREADS); } public static Integer getNumTAAntiEntropyThreads() { return getOption(ConfigParameters.TA_ANTI_ENTROPY_THREADS); } public static Integer getNumQuorumThreads() { return getOption(ConfigParameters.QUORUM_THREADS); } public static TransactionMode getThebesTxnMode() { return getOption(ConfigParameters.TXN_MODE); } public static IsolationLevel getThebesIsolationLevel() { return getOption(ConfigParameters.HAT_ISOLATION_LEVEL); } public static AtomicityLevel getThebesAtomicityLevel() { return getOption(ConfigParameters.ATOMICITY_LEVEL); } public static SessionLevel getThebesSessionLevel() { return getOption(ConfigParameters.SESSION_LEVEL); } public static Integer getAntiEntropyBootstrapTime() { return getOption(ConfigParameters.ANTIENTROPY_BOOTSTRAP_TIME); } public static String getLoggerLevel() { return getOption(ConfigParameters.LOGGER_LEVEL); } public static String getDiskDatabaseFile() { return getOption(ConfigParameters.DISK_DATABASE_FILE); } public static Boolean doCleanDatabaseFile() { return getOption(ConfigParameters.DO_CLEAN_DATABASE_FILE); } public static IPersistenceEngine getPersistenceEngine() throws ConfigurationException { PersistenceEngine engineType = Config.getPersistenceType(); switch (engineType) { case MEMORY: return new MemoryPersistenceEngine(); case LEVELDB: return new LevelDBPersistenceEngine(Config.getDiskDatabaseFile()); case BDB: return new BDBPersistenceEngine(); default: throw new ConfigurationException("unexpected persistency type: " + engineType); } } /** Returns true if this server is the Master of a 2PL replica set. */ public static Boolean isMaster() { return txnMode == TransactionMode.TWOPL && masterServers.get(getServerID()).getIP().equals(getServerIP()); } public static RoutingMode getRoutingMode() { return getOption(ConfigParameters.ROUTING_MODE); } /** Returns the IP for this server, based on our clusterid and serverid. */ private static String getServerIP() { String ip = getClusterMap().get(getClusterID()).get(getServerID()); if (ip.endsWith("*")) { return ip.substring(0, ip.length()-1); } else { return ip; } } public static int getNumClusters() { return getClusterMap().size(); } public static Integer getDatabaseCacheSize() { return getOption(ConfigParameters.DATABASE_CACHE_SIZE); } public static Boolean shouldStorePendingInMemory() { return getOption(ConfigParameters.STORE_PENDING_IN_MEMORY); } public static Integer getTABatchTime() { return getOption(ConfigParameters.TA_BATCH_TIME); } public static String getPendingWritesDB() { return getOption(ConfigParameters.PENDING_WRITES_DB); } }