All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.netflix.astyanax.cql.CqlKeyspaceImpl Maven / Gradle / Ivy

package com.netflix.astyanax.cql;

import static com.datastax.driver.core.querybuilder.QueryBuilder.eq;

import java.nio.ByteBuffer;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Callable;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.codahale.metrics.MetricRegistryListener;
import com.datastax.driver.core.Cluster;
import com.datastax.driver.core.Configuration;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.Row;
import com.datastax.driver.core.Session;
import com.datastax.driver.core.Statement;
import com.datastax.driver.core.querybuilder.QueryBuilder;
import com.google.common.base.Function;
import com.google.common.collect.Lists;
import com.netflix.astyanax.AstyanaxConfiguration;
import com.netflix.astyanax.Clock;
import com.netflix.astyanax.ColumnMutation;
import com.netflix.astyanax.Keyspace;
import com.netflix.astyanax.KeyspaceTracerFactory;
import com.netflix.astyanax.MutationBatch;
import com.netflix.astyanax.SerializerPackage;
import com.netflix.astyanax.clock.MicrosecondsAsyncClock;
import com.netflix.astyanax.connectionpool.ConnectionPool;
import com.netflix.astyanax.connectionpool.ConnectionPoolConfiguration;
import com.netflix.astyanax.connectionpool.ConnectionPoolMonitor;
import com.netflix.astyanax.connectionpool.ConnectionPoolProxy.SeedHostListener;
import com.netflix.astyanax.connectionpool.Host;
import com.netflix.astyanax.connectionpool.Operation;
import com.netflix.astyanax.connectionpool.OperationResult;
import com.netflix.astyanax.connectionpool.TokenRange;
import com.netflix.astyanax.connectionpool.exceptions.ConnectionException;
import com.netflix.astyanax.connectionpool.exceptions.NotFoundException;
import com.netflix.astyanax.connectionpool.exceptions.OperationException;
import com.netflix.astyanax.connectionpool.impl.OperationResultImpl;
import com.netflix.astyanax.cql.direct.DirectCqlStatement;
import com.netflix.astyanax.cql.reads.CqlColumnFamilyQueryImpl;
import com.netflix.astyanax.cql.schema.CqlColumnFamilyDefinitionImpl;
import com.netflix.astyanax.cql.schema.CqlKeyspaceDefinitionImpl;
import com.netflix.astyanax.cql.util.CFQueryContext;
import com.netflix.astyanax.cql.writes.CqlColumnMutationImpl;
import com.netflix.astyanax.cql.writes.CqlMutationBatchImpl;
import com.netflix.astyanax.ddl.ColumnFamilyDefinition;
import com.netflix.astyanax.ddl.KeyspaceDefinition;
import com.netflix.astyanax.ddl.SchemaChangeResult;
import com.netflix.astyanax.ddl.impl.SchemaChangeResponseImpl;
import com.netflix.astyanax.model.ColumnFamily;
import com.netflix.astyanax.model.CfSplit;
import com.netflix.astyanax.partitioner.BigInteger127Partitioner;
import com.netflix.astyanax.partitioner.Murmur3Partitioner;
import com.netflix.astyanax.partitioner.Partitioner;
import com.netflix.astyanax.query.ColumnFamilyQuery;
import com.netflix.astyanax.retry.RetryPolicy;
import com.netflix.astyanax.serializers.SerializerPackageImpl;
import com.netflix.astyanax.serializers.UnknownComparatorException;

/**
 * Java Driver based impl of {@link Keyspace} that implements ddl operations as well as row queries and mutation batches.
 * The class encapsulates a java driver cluster and session object to provide all the functionality. 
 *  
 * Note that due to the way the object is setup via AstyanaxContext and CqlFamilyFactory, it needs to implements 
 * a {@link SeedHostListener} so that it can construct the cluster and session object appropriately once the seed hosts
 * have been provided by the {link HostSupplier} object.
 *  
 * @author poberai
 */
public class CqlKeyspaceImpl implements Keyspace, SeedHostListener {

	private static final Logger Logger = LoggerFactory.getLogger(CqlKeyspaceImpl.class);
	
	private final Clock clock;
	
	public volatile Cluster cluster;
	public volatile Session session;
	
	private final KeyspaceContext ksContext;
	private final String keyspaceName;
	private final AstyanaxConfiguration astyanaxConfig;
	private final KeyspaceTracerFactory tracerFactory; 
	private final Configuration javaDriverConfig;
	private final ConnectionPoolMonitor cpMonitor;
	private final MetricRegistryListener metricsRegListener;

	public CqlKeyspaceImpl(String ksName, AstyanaxConfiguration asConfig, KeyspaceTracerFactory tracerFactory, ConnectionPoolConfiguration cpConfig, ConnectionPoolMonitor cpMonitor) {
		this(null, ksName, asConfig, tracerFactory, cpConfig,cpMonitor);
	}

	public CqlKeyspaceImpl(KeyspaceContext ksContext) {
		this(ksContext.getSession(), ksContext.getKeyspace(), ksContext.getConfig(), ksContext.getTracerFactory(), null, ksContext.getConnectionPoolMonitor());
	}

	CqlKeyspaceImpl(Session session, String ksName, AstyanaxConfiguration asConfig, KeyspaceTracerFactory tracerFactory, ConnectionPoolMonitor cpMonitor) {
		this(session, ksName, asConfig, tracerFactory, null,cpMonitor);
	}

	private CqlKeyspaceImpl(Session session, String ksName, AstyanaxConfiguration asConfig, KeyspaceTracerFactory tracerFactory, ConnectionPoolConfiguration cpConfig, ConnectionPoolMonitor cpMonitor) {
		this.session = session;
		this.keyspaceName = ksName.toLowerCase();
		this.astyanaxConfig = asConfig;
		this.tracerFactory = tracerFactory;
		this.cpMonitor = cpMonitor;
		this.metricsRegListener = ((JavaDriverConnectionPoolMonitorImpl)cpMonitor).getMetricsRegistryListener();
		this.ksContext = new KeyspaceContext(this);
		
		if (asConfig.getClock() != null) {
			clock = asConfig.getClock();
		} else {
			clock = new MicrosecondsAsyncClock();
		}
		
		if (cpConfig != null) {
			javaDriverConfig = ((JavaDriverConnectionPoolConfigurationImpl)cpConfig).getJavaDriverConfig();
		} else {
			javaDriverConfig = null;
		}
	}
	
	
	@Override
	public AstyanaxConfiguration getConfig() {
		return astyanaxConfig;
	}

	@Override
	public String getKeyspaceName() {
		return keyspaceName;
	}

	@Override
	public Partitioner getPartitioner() throws ConnectionException {
		String pName = describePartitioner();
		if (pName.contains("Murmur3Partitioner")) {
			return Murmur3Partitioner.get();
		} else if (pName.contains("RandomPartitioner")) {
			return BigInteger127Partitioner.get();
		} else {
			throw new RuntimeException("Unrecognized partitioner: " + pName);
		}
	}

	@Override
	public String describePartitioner() throws ConnectionException {
		Statement q = QueryBuilder.select("partitioner").from("system", "local");
		ResultSet result = session.execute(q);
		com.datastax.driver.core.Row row = result.one();
		if (row == null) {
			throw new RuntimeException("Missing paritioner");
		}
		String pName = row.getString(0);
		return pName;
	}

	@Override
	public List describeRing() throws ConnectionException {
		return CqlRingDescriber.getInstance().getTokenRanges(session, false);
	}

	@Override
	public List describeRing(String dc) throws ConnectionException {
		return CqlRingDescriber.getInstance().getTokenRanges(session, dc, null);
	}

	@Override
	public List describeRing(String dc, String rack) throws ConnectionException {
		return CqlRingDescriber.getInstance().getTokenRanges(session, dc, rack);
	}

	@Override
	public List describeRing(boolean cached) throws ConnectionException {
		return CqlRingDescriber.getInstance().getTokenRanges(session, cached);
	}

	@Override
	public KeyspaceDefinition describeKeyspace() throws ConnectionException {
		
		Statement query = QueryBuilder.select().from("system", "schema_keyspaces").where(eq("keyspace_name", keyspaceName));
		Row row = session.execute(query).one();
		if (row == null) {
			throw new RuntimeException("Keyspace not found: " + keyspaceName);
		}
		return (new CqlKeyspaceDefinitionImpl(session, row));
	}

	@Override
	public Properties getKeyspaceProperties() throws ConnectionException {
		try {
			return describeKeyspace().getProperties();
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	@Override
	public Properties getColumnFamilyProperties(String columnFamily) throws ConnectionException {
        KeyspaceDefinition ksDef = this.describeKeyspace();
        ColumnFamilyDefinition cfDef = ksDef.getColumnFamily(columnFamily);
        if (cfDef == null)
            throw new NotFoundException(String.format("Column family '%s' in keyspace '%s' not found", columnFamily, getKeyspaceName()));
        try {
			return cfDef.getProperties();
		} catch (Exception e) {
			throw new RuntimeException();
		}
	}

	@Override
	public SerializerPackage getSerializerPackage(String cfName, boolean ignoreErrors) throws ConnectionException, UnknownComparatorException {
		
		ColumnFamilyDefinition cfDef = describeKeyspace().getColumnFamily(cfName);
		return new SerializerPackageImpl(cfDef, ignoreErrors);
	}

	@Override
	public MutationBatch prepareMutationBatch() {
		return new CqlMutationBatchImpl(ksContext, clock, astyanaxConfig.getDefaultWriteConsistencyLevel(), astyanaxConfig.getRetryPolicy());
	}

	@Override
	public  ColumnMutation prepareColumnMutation(ColumnFamily columnFamily, K rowKey, C column) {
		return new CqlColumnMutationImpl(ksContext, new CFQueryContext(columnFamily, rowKey), column);
	}

	@Override
	public  ColumnFamilyQuery prepareQuery(ColumnFamily cf) {
		return new CqlColumnFamilyQueryImpl(ksContext, cf);
	}

	@Override
	public OperationResult createKeyspace(Map options) throws ConnectionException {
		return new CqlKeyspaceDefinitionImpl(session, options).setName(keyspaceName).execute();
	}

	@Override
	public OperationResult createKeyspace(Properties properties) throws ConnectionException {
		return new CqlKeyspaceDefinitionImpl(session, properties).setName(keyspaceName).execute();
	}

	@SuppressWarnings("rawtypes")
	@Override
	public OperationResult createKeyspace(Map options, Map> cfs) throws ConnectionException {
		
		CqlKeyspaceDefinitionImpl ksDef = new CqlKeyspaceDefinitionImpl(session, options);
		if (ksDef.getName() == null) {
			ksDef.setName(keyspaceName);
		}
		
		OperationResult result = ksDef.execute();
		
		for (ColumnFamily cf : cfs.keySet()) {
			CqlColumnFamilyDefinitionImpl cfDef = new CqlColumnFamilyDefinitionImpl(session, ksDef.getName(), cf, cfs.get(cf));
			ksDef.addColumnFamily(cfDef);
		}
		
		return result;
	}

	@Override
	public OperationResult updateKeyspace(Map options) throws ConnectionException {
		return new CqlKeyspaceDefinitionImpl(session, options).setName(keyspaceName).alterKeyspace().execute();
	}

	@Override
	public OperationResult updateKeyspace(Properties props) throws ConnectionException {
		return new CqlKeyspaceDefinitionImpl(session, props).setName(keyspaceName).alterKeyspace().execute();
	}

	@Override
	public OperationResult dropKeyspace() throws ConnectionException {
		return new CqlOperationResultImpl(session.execute("DROP KEYSPACE " + keyspaceName), null);
	}
	
	@Override
	public  OperationResult truncateColumnFamily(ColumnFamily columnFamily) throws OperationException, ConnectionException {
		ResultSet result = session.execute("TRUNCATE " + keyspaceName + "." + columnFamily.getName());
		return new CqlOperationResultImpl(result, null);
	}

	@Override
	public OperationResult truncateColumnFamily(String columnFamily) throws ConnectionException {
		ResultSet result = session.execute("TRUNCATE " + keyspaceName + "." + columnFamily);
		return new CqlOperationResultImpl(result, null);
	}

	@Override
	public  OperationResult createColumnFamily(ColumnFamily columnFamily, Map options) throws ConnectionException {
		return new CqlColumnFamilyDefinitionImpl(session, keyspaceName, columnFamily, options).execute();
	}

	@Override
	public OperationResult createColumnFamily(Properties props) throws ConnectionException {
		return new CqlColumnFamilyDefinitionImpl(session, keyspaceName, props).execute();
	}

	@Override
	public OperationResult createColumnFamily(Map options) throws ConnectionException {
		return new CqlColumnFamilyDefinitionImpl(session, keyspaceName, options).execute();
	}

	@Override
	public  OperationResult updateColumnFamily(ColumnFamily columnFamily, Map options) throws ConnectionException {
		return new CqlColumnFamilyDefinitionImpl(session, keyspaceName, columnFamily, options).alterTable().execute();
	}

	@Override
	public OperationResult updateColumnFamily(Properties props) throws ConnectionException {
		return new CqlColumnFamilyDefinitionImpl(session, keyspaceName, props).alterTable().execute();
	}

	@Override
	public OperationResult updateColumnFamily(Map options) throws ConnectionException {
		return new CqlColumnFamilyDefinitionImpl(session, keyspaceName, options).alterTable().execute();
	}

	@Override
	public OperationResult dropColumnFamily(String columnFamilyName) throws ConnectionException {
		return new CqlOperationResultImpl(session.execute("DROP TABLE " + keyspaceName + "." + columnFamilyName), null);
	}

	@Override
	public  OperationResult dropColumnFamily(ColumnFamily columnFamily) throws ConnectionException {
		return dropColumnFamily(columnFamily.getName());
	}

	@Override
	public Map> describeSchemaVersions() throws ConnectionException {
		return new CqlSchemaVersionReader(session).exec();
	}

	@Override
	public CqlStatement prepareCqlStatement() {
		return new DirectCqlStatement(session);
	}

	@Override
	public ConnectionPool getConnectionPool() throws ConnectionException {
		throw new UnsupportedOperationException("Operation not supported");
	}


	@Override
	public OperationResult testOperation(Operation operation) throws ConnectionException {
		throw new UnsupportedOperationException("Operation not supported");
	}

	@Override
	public OperationResult testOperation(Operation operation, RetryPolicy retry) throws ConnectionException {
		throw new UnsupportedOperationException("Operation not supported");
	}

	@Override
	public void setHosts(Collection hosts, int port) {

		try {
			if (session != null) {
				Logger.info("Session has already been set, SKIPPING SET HOSTS");
				return;
			}
			List hostList = Lists.newArrayList(hosts);

			List contactPoints = Lists.transform(hostList, new Function() {
				@Override
				public String apply(Host input) {
					if (input != null) {
						return input.getHostName(); 
					}
					return null;
				}
			});

			Configuration config = javaDriverConfig;
			
			// We really need a mechanism to easily override Configuration on the builder
			Logger.info("Using port: " + port);
			
			Cluster.Builder builder = Cluster.builder()
					.addContactPoints(contactPoints.toArray(new String[0]))
					.withPort(port)
					.withLoadBalancingPolicy(config.getPolicies().getLoadBalancingPolicy())
					.withReconnectionPolicy(config.getPolicies().getReconnectionPolicy())
					.withRetryPolicy(config.getPolicies().getRetryPolicy())
					.withCompression(config.getProtocolOptions().getCompression())
					.withPoolingOptions(config.getPoolingOptions())
					.withSocketOptions(config.getSocketOptions())
					.withQueryOptions(config.getQueryOptions());
			
			if (config.getMetricsOptions() == null) {
				builder.withoutMetrics();
			} else if (!config.getMetricsOptions().isJMXReportingEnabled()) {
				builder.withoutJMXReporting();
			}
					
			cluster = builder.build();
			if (!(this.cpMonitor instanceof JavaDriverConnectionPoolMonitorImpl))
				this.cluster.getMetrics().getRegistry().addListener((MetricRegistryListener) this.metricsRegListener);
			
			Logger.info("Connecting to cluster");
			session = cluster.connect();
			Logger.info("Done connecting to cluster, session object created");

		} catch (RuntimeException e) {
			Logger.error("Failed to set hosts for keyspace impl", e);
			
		} catch (Exception e) {
			Logger.error("Failed to set hosts for keyspace impl", e);
		}
	}
	
	@Override
	public void shutdown() {
		cluster.close();
	}


	public class KeyspaceContext {
		
		private final Keyspace ks; 
		
		public KeyspaceContext(Keyspace keyspaceCtx) {
			this.ks = keyspaceCtx;
		}
		public Session getSession() {
			return session;
		}
		public String getKeyspace() {
			return keyspaceName;
		}
		public AstyanaxConfiguration getConfig() {
			return astyanaxConfig;
		}
		public KeyspaceTracerFactory getTracerFactory() {
			return tracerFactory;
		}
		
		public Keyspace getKeyspaceContext() {
			return ks;
		}
		public ConnectionPoolMonitor getConnectionPoolMonitor(){
			return cpMonitor;
		}
	}


	@Override
	public OperationResult createKeyspaceIfNotExists(final Map options) throws ConnectionException {

		return createKeyspaceIfNotExists(new Callable>() {
			@Override
			public OperationResult call() throws Exception {
				return createKeyspace(options);
			}
		});
	}

	@Override
	public OperationResult createKeyspaceIfNotExists(final Properties properties) throws ConnectionException {
		
		return createKeyspaceIfNotExists(new Callable>() {
			@Override
			public OperationResult call() throws Exception {
				return createKeyspace(properties);
			}
		});
	}

	@Override
	public OperationResult createKeyspaceIfNotExists(final Map options, final Map> cfs) throws ConnectionException {
		
		return createKeyspaceIfNotExists(new Callable>() {
			@Override
			public OperationResult call() throws Exception {
				return createKeyspace(options, cfs);
			}
		});
	}

    @Override
    public List describeSplitsEx(String cfName, String startToken, String endToken, int keysPerSplit)
            throws ConnectionException {
        return null;
    }

    @Override
    public List describeSplitsEx(String cfName, String startToken, String endToken, int keysPerSplit,
                                   ByteBuffer rowKey)
            throws ConnectionException {
        return null;
    }

    @Override
    public List describeSplits(String cfName, String startToken, String endToken, int keysPerSplit)
            throws ConnectionException {
        return null;
    }
	
    
    private OperationResult createKeyspaceIfNotExists(Callable> createKeyspace) throws ConnectionException {
        
    	// Check if keyspace exists
    	ResultSet result = session.execute("select * from system.local where keyspace_name = '" + keyspaceName + "'");
    	List rows = result.all();
    	if (rows != null && rows.isEmpty()) {
    		return new OperationResultImpl(Host.NO_HOST, new SchemaChangeResponseImpl().setSchemaId("no-op"), 0);
    	}

    	try {
    		return createKeyspace.call();
    	} catch (ConnectionException e) {
    		throw e;
    	} catch (Exception e) {
    		throw new RuntimeException(e);
    	}
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy