package org.apache.fullmatix.mysql; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.apache.helix.HelixAdmin; import org.apache.helix.HelixManager; import org.apache.helix.HelixManagerFactory; import org.apache.helix.HelixProperty; import org.apache.helix.InstanceType; import org.apache.helix.NotificationContext; import org.apache.helix.PropertyKey; import org.apache.helix.ScopedConfigChangeListener; import org.apache.helix.model.HelixConfigScope; import org.apache.helix.model.IdealState; import org.apache.helix.model.InstanceConfig; import org.apache.helix.model.HelixConfigScope.ConfigScopeProperty; import org.apache.helix.model.builder.HelixConfigScopeBuilder; import org.apache.helix.spectator.RoutingTableProvider; /** * Given the database, tablename and partitionId - tells the node that is currently hosting it. */ public class ConnectionURLProvider implements ScopedConfigChangeListener { private String _zkAddress; private String _cluster; private HelixManager _manager; RoutingTableProvider _routingTableProvider = new RoutingTableProvider(); Map<String, Integer> _dbPartitionToSliceMapping = new ConcurrentHashMap<String, Integer>(); public ConnectionURLProvider(String zkAddress, String clusterName) { _zkAddress = zkAddress; _cluster = clusterName; } public void start() throws Exception { _manager = HelixManagerFactory.getZKHelixManager(_cluster, "connectionURLProvider", InstanceType.SPECTATOR, _zkAddress); _manager.connect(); _manager.addExternalViewChangeListener(_routingTableProvider); _manager.addConfigChangeListener(this, ConfigScopeProperty.RESOURCE); } public void stop() { _manager.disconnect(); } /** * @param database * @param table * @param partitionId * @return Returns the configuration of the master. instanceconfig.getRecord().getSimpleField(key) * provides the required info to create a jdbc connection. key can be * "mysql_port, mysql_host" */ public InstanceConfig getMaster(String database, String table, int partitionId) { String dbPartitionName = database + "_" + partitionId; Integer sliceId = _dbPartitionToSliceMapping.get(dbPartitionName); if (sliceId != null) { List<InstanceConfig> instances = _routingTableProvider.getInstances(MySQLConstants.MASTER_SLAVE_RESOURCE_NAME, MySQLConstants.MASTER_SLAVE_RESOURCE_NAME + "_" + sliceId, "MASTER"); if (instances.size() == 1) { // TODO: verify that this instance contains this table. return instances.get(0); } } return null; } /** * @param database * @param table * @param partitionId * @return Returns the configuration of the master. instanceconfig.getRecord().getSimpleField(key) * provides the required info to create a jdbc connection. key can be * "mysql_port, mysql_host" */ public InstanceConfig getMaster(String database, int partitionId) { String dbPartitionName = database + "_" + partitionId; Integer sliceId = _dbPartitionToSliceMapping.get(dbPartitionName); if (sliceId != null) { List<InstanceConfig> instances = _routingTableProvider.getInstances(MySQLConstants.MASTER_SLAVE_RESOURCE_NAME, MySQLConstants.MASTER_SLAVE_RESOURCE_NAME + "_" + sliceId, "MASTER"); if (instances.size() == 1) { // TODO: verify that this instance contains this table. return instances.get(0); } } return null; } /** * @param database * @param table * @param partitionId * @return Returns the configuration of the master. instanceconfig.getRecord().getSimpleField(key) * provides the required info to create a jdbc connection. key can be * "mysql_port, mysql_host" */ public List<InstanceConfig> getSlaves(String database, String table, int partitionId) { String dbPartitionName = database + "_" + partitionId; Integer sliceId = _dbPartitionToSliceMapping.get(dbPartitionName); if (sliceId != null) { List<InstanceConfig> instances = _routingTableProvider.getInstances(MySQLConstants.MASTER_SLAVE_RESOURCE_NAME, MySQLConstants.MASTER_SLAVE_RESOURCE_NAME + "_" + sliceId, "SLAVE"); return instances; } return null; } @Override public void onConfigChange(List<HelixProperty> configs, NotificationContext context) { List<String> databases = new ArrayList<String>(); Map<String, List<String>> databaseTablesMap = new HashMap<String, List<String>>(); PropertyKey key = new PropertyKey.Builder(_cluster).resourceConfigs(); Map<String, HelixProperty> resourceConfigs = _manager.getHelixDataAccessor().getChildValuesMap(key); HelixAdmin helixAdmin = _manager.getClusterManagmentTool(); HelixConfigScopeBuilder scopeBuilder = new HelixConfigScopeBuilder(ConfigScopeProperty.PARTITION); for (String resource : resourceConfigs.keySet()) { HelixProperty resourceConfig = resourceConfigs.get(resource); String resourceType = resourceConfig.getRecord().getSimpleField("type"); if ("DATABASE".equalsIgnoreCase(resourceType)) { IdealState resourceIdealState = helixAdmin.getResourceIdealState(_cluster, resource); for (String dbPartitionName : resourceIdealState.getPartitionSet()) { HelixConfigScope scope = scopeBuilder.forCluster(_cluster).forResource(resource).forPartition(dbPartitionName) .build(); String sliceId = _manager.getConfigAccessor().get(scope, "sliceId"); _dbPartitionToSliceMapping.put(dbPartitionName, Integer.parseInt(sliceId)); } databases.add(resource); databaseTablesMap.put(resource, new ArrayList<String>()); } } } public static void main(String[] args) throws Exception { ConnectionURLProvider provider = new ConnectionURLProvider("localhost:2181", "mysql-cluster-test"); provider.start(); while (true) { int partitionId = (int) (Math.random() * 10000) % 6; InstanceConfig master = provider.getMaster("MyDB", "MyTable", partitionId); if (master != null) { System.out.println("Master for partition:" + partitionId + " is " + master.getId()); } else { System.err.println("No master available"); } Thread.sleep(10000); } } }