Package com.netflix.astyanax.thrift

Source Code of com.netflix.astyanax.thrift.ThriftClusterImpl

/*******************************************************************************
* Copyright 2011 Netflix
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.netflix.astyanax.thrift;

import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.concurrent.ConcurrentMap;

import org.apache.cassandra.thrift.Cassandra;
import org.apache.cassandra.thrift.Cassandra.Client;
import org.apache.cassandra.thrift.CfDef;
import org.apache.cassandra.thrift.KsDef;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Function;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.netflix.astyanax.AstyanaxConfiguration;
import com.netflix.astyanax.Cluster;
import com.netflix.astyanax.Keyspace;
import com.netflix.astyanax.CassandraOperationType;
import com.netflix.astyanax.KeyspaceTracerFactory;
import com.netflix.astyanax.connectionpool.ConnectionPool;
import com.netflix.astyanax.connectionpool.OperationResult;
import com.netflix.astyanax.connectionpool.ConnectionContext;
import com.netflix.astyanax.connectionpool.exceptions.BadRequestException;
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.exceptions.SchemaDisagreementException;
import com.netflix.astyanax.ddl.ColumnDefinition;
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.retry.RunOnce;
import com.netflix.astyanax.thrift.ddl.*;

public class ThriftClusterImpl implements Cluster {
    private static final Logger LOG = LoggerFactory.getLogger(ThriftClusterImpl.class);
   
    private static final int MAX_SCHEMA_CHANGE_ATTEMPTS = 6;
    private static final int SCHEMA_DISAGREEMENT_BACKOFF = 10000;

    private final ConnectionPool<Cassandra.Client> connectionPool;
    private final ConcurrentMap<String, Keyspace> keyspaces;
    private final AstyanaxConfiguration config;
    private final KeyspaceTracerFactory tracerFactory;

    public ThriftClusterImpl(
            AstyanaxConfiguration config,
            ConnectionPool<Cassandra.Client> connectionPool,
            KeyspaceTracerFactory tracerFactory) {
        this.config         = config;
        this.connectionPool = connectionPool;
        this.tracerFactory  = tracerFactory;
        this.keyspaces      = Maps.newConcurrentMap();
    }

    @Override
    public String describeClusterName() throws ConnectionException {
        return connectionPool.executeWithFailover(
                new AbstractOperationImpl<String>(tracerFactory.newTracer(CassandraOperationType.DESCRIBE_CLUSTER)) {
                    @Override
                    public String internalExecute(Client client, ConnectionContext context) throws Exception {
                        return client.describe_cluster_name();
                    }

                }, config.getRetryPolicy().duplicate()).getResult();
    }

    @Override
    public String describeSnitch() throws ConnectionException {
        return connectionPool.executeWithFailover(
                new AbstractOperationImpl<String>(tracerFactory.newTracer(CassandraOperationType.DESCRIBE_SNITCH)) {
                    @Override
                    public String internalExecute(Client client, ConnectionContext context) throws Exception {
                        return client.describe_snitch();
                    }
                }, config.getRetryPolicy().duplicate()).getResult();
    }

    @Override
    public String describePartitioner() throws ConnectionException {
        return connectionPool
                .executeWithFailover(
                        new AbstractOperationImpl<String>(
                                tracerFactory.newTracer(CassandraOperationType.DESCRIBE_PARTITIONER)) {
                            @Override
                            public String internalExecute(Client client, ConnectionContext context) throws Exception {
                                return client.describe_partitioner();
                            }
                        }, config.getRetryPolicy().duplicate()).getResult();
    }

    @Override
    public Map<String, List<String>> describeSchemaVersions() throws ConnectionException {
        return connectionPool.executeWithFailover(
                new AbstractOperationImpl<Map<String, List<String>>>(
                        tracerFactory.newTracer(CassandraOperationType.DESCRIBE_SCHEMA_VERSION)) {
                    @Override
                    public Map<String, List<String>> internalExecute(Client client, ConnectionContext context) throws Exception {
                        return client.describe_schema_versions();
                    }
                }, config.getRetryPolicy().duplicate()).getResult();
    }

    /**
     * Get the version from the cluster
     *
     * @return
     * @throws OperationException
     */
    @Override
    public String getVersion() throws ConnectionException {
        return connectionPool.executeWithFailover(
                new AbstractOperationImpl<String>(tracerFactory.newTracer(CassandraOperationType.GET_VERSION)) {
                    @Override
                    public String internalExecute(Client client, ConnectionContext state) throws Exception {
                        return client.describe_version();
                    }
                }, config.getRetryPolicy().duplicate()).getResult();
    }

    private <K> OperationResult<K> executeSchemaChangeOperation(AbstractOperationImpl<K> op) throws OperationException,
            ConnectionException {
        int attempt = 0;
        do {
            try {
                return connectionPool.executeWithFailover(op, config.getRetryPolicy().duplicate());
            }
            catch (SchemaDisagreementException e) {
                if (++attempt >= MAX_SCHEMA_CHANGE_ATTEMPTS) {
                    throw e;
                }
                try {
                    Thread.sleep(SCHEMA_DISAGREEMENT_BACKOFF);
                }
                catch (InterruptedException e1) {
                    Thread.interrupted();
                    throw new RuntimeException(e1);
                }
            }
        } while (true);
    }

    @Override
    public List<KeyspaceDefinition> describeKeyspaces() throws ConnectionException {
        return connectionPool.executeWithFailover(
                new AbstractOperationImpl<List<KeyspaceDefinition>>(
                        tracerFactory.newTracer(CassandraOperationType.DESCRIBE_KEYSPACES)) {
                    @Override
                    public List<KeyspaceDefinition> internalExecute(Client client, ConnectionContext context) throws Exception {
                        List<KsDef> ksDefs = client.describe_keyspaces();
                        return Lists.transform(ksDefs, new Function<KsDef, KeyspaceDefinition>() {
                            @Override
                            public KeyspaceDefinition apply(KsDef ksDef) {
                                return new ThriftKeyspaceDefinitionImpl(ksDef);
                            }

                        });
                    }
                }, config.getRetryPolicy().duplicate()).getResult();
    }

    @Override
    public KeyspaceDefinition describeKeyspace(String ksName) throws ConnectionException {
        List<KeyspaceDefinition> ksDefs = describeKeyspaces();
        for (KeyspaceDefinition ksDef : ksDefs) {
            if (ksDef.getName().equals(ksName)) {
                return ksDef;
            }
        }
        return null;
    }

    @Override
    public Keyspace getKeyspace(String ksName) {
        Keyspace keyspace = keyspaces.get(ksName);
        if (keyspace == null) {
            synchronized (this) {
                Keyspace newKeyspace = new ThriftKeyspaceImpl(ksName, this.connectionPool, this.config, tracerFactory);
                keyspace = keyspaces.putIfAbsent(ksName, newKeyspace);
                if (keyspace == null) {
                    keyspace = newKeyspace;
                }
            }
        }
        return keyspace;
    }

    @Override
    public ColumnFamilyDefinition makeColumnFamilyDefinition() {
        return new ThriftColumnFamilyDefinitionImpl();
    }

    @Override
    public OperationResult<SchemaChangeResult> addColumnFamily(final ColumnFamilyDefinition def) throws ConnectionException {
        return internalCreateColumnFamily(((ThriftColumnFamilyDefinitionImpl) def)
                .getThriftColumnFamilyDefinition());
    }
   
    @Override
    public OperationResult<SchemaChangeResult> createColumnFamily(final Map<String, Object> options) throws ConnectionException {
        final ThriftColumnFamilyDefinitionImpl def = new ThriftColumnFamilyDefinitionImpl();
        def.setFields(options);
       
        return internalCreateColumnFamily(def.getThriftColumnFamilyDefinition());
    }
   
    @Override
    public OperationResult<SchemaChangeResult> createColumnFamily(final Properties props) throws ConnectionException {
        final CfDef def;
        try {
            def = ThriftUtils.getThriftObjectFromProperties(CfDef.class, props);
        } catch (Exception e) {
            throw new BadRequestException("Error converting properties to CfDef", e);
        }
       
        return internalCreateColumnFamily(def);
    }
   
    private OperationResult<SchemaChangeResult> internalCreateColumnFamily(final CfDef def) throws ConnectionException {
        return executeSchemaChangeOperation(new AbstractKeyspaceOperationImpl<SchemaChangeResult>(
                tracerFactory.newTracer(CassandraOperationType.ADD_COLUMN_FAMILY), def.getKeyspace()) {
            @Override
            public SchemaChangeResult internalExecute(Client client, ConnectionContext context) throws Exception {
                precheckSchemaAgreement(client);
                return new SchemaChangeResponseImpl()
                    .setSchemaId(client.system_add_column_family(def));
            }
        });
    }

    @Override
    public OperationResult<SchemaChangeResult>  updateColumnFamily(final ColumnFamilyDefinition def) throws ConnectionException {
        return internalColumnFamily(((ThriftColumnFamilyDefinitionImpl) def).getThriftColumnFamilyDefinition());
    }
   
    @Override
    public OperationResult<SchemaChangeResult> updateColumnFamily(final Map<String, Object> options) throws ConnectionException  {
        final ThriftColumnFamilyDefinitionImpl def = new ThriftColumnFamilyDefinitionImpl();
        def.setFields(options);
       
        return internalColumnFamily(def.getThriftColumnFamilyDefinition());
    }

    @Override
    public OperationResult<SchemaChangeResult> updateColumnFamily(final Properties props) throws ConnectionException {
        final CfDef def;
        try {
            def = ThriftUtils.getThriftObjectFromProperties(CfDef.class, props);
        } catch (Exception e) {
            throw new BadRequestException("Error converting properties to CfDef", e);
        }
       
        return internalColumnFamily(def);
    }
   
    private OperationResult<SchemaChangeResult>  internalColumnFamily(final CfDef def) throws ConnectionException {
        return executeSchemaChangeOperation(new AbstractKeyspaceOperationImpl<SchemaChangeResult>(
                tracerFactory.newTracer(CassandraOperationType.UPDATE_COLUMN_FAMILY), def.getKeyspace()) {
            @Override
            public SchemaChangeResult internalExecute(Client client, ConnectionContext context) throws Exception {
                precheckSchemaAgreement(client);
                return new SchemaChangeResponseImpl().setSchemaId(client.system_update_column_family(def));
            }
        });
    }

    @Override
    public KeyspaceDefinition makeKeyspaceDefinition() {
        return new ThriftKeyspaceDefinitionImpl();
    }

    @Override
    public OperationResult<SchemaChangeResult>  addKeyspace(final KeyspaceDefinition def) throws ConnectionException {
        return internalCreateKeyspace(((ThriftKeyspaceDefinitionImpl) def).getThriftKeyspaceDefinition());
    }
   
    @Override
    public OperationResult<SchemaChangeResult> createKeyspace(final Map<String, Object> options) throws ConnectionException  {
        final ThriftKeyspaceDefinitionImpl def = new ThriftKeyspaceDefinitionImpl();
        def.setFields(options);
       
        return internalCreateKeyspace(def.getThriftKeyspaceDefinition());
    }

    @Override
    public OperationResult<SchemaChangeResult> createKeyspace(final Properties props) throws ConnectionException {
        final KsDef def;
        try {
            def = ThriftUtils.getThriftObjectFromProperties(KsDef.class, props);
            if (def.getCf_defs() == null) {
                def.setCf_defs(Lists.<CfDef>newArrayList());
            }
        } catch (Exception e) {
            throw new BadRequestException("Error converting properties to KsDef", e);
        }
       
        return internalCreateKeyspace(def);
    }
   
    private OperationResult<SchemaChangeResult> internalCreateKeyspace(final KsDef def) throws ConnectionException {
        return executeSchemaChangeOperation(new AbstractOperationImpl<SchemaChangeResult>(
                tracerFactory.newTracer(CassandraOperationType.ADD_KEYSPACE)) {
            @Override
            public SchemaChangeResult internalExecute(Client client, ConnectionContext context) throws Exception {
                precheckSchemaAgreement(client);
                return new SchemaChangeResponseImpl()
                    .setSchemaId(client.system_add_keyspace(def));
            }
        });
    }

    @Override
    public OperationResult<SchemaChangeResult>  updateKeyspace(final KeyspaceDefinition def) throws ConnectionException {
        return internalUpdateKeyspace(((ThriftKeyspaceDefinitionImpl) def).getThriftKeyspaceDefinition());
    }
   
    @Override
    public OperationResult<SchemaChangeResult> updateKeyspace(final Map<String, Object> options) throws ConnectionException  {
        final ThriftKeyspaceDefinitionImpl def = new ThriftKeyspaceDefinitionImpl();
        try {
            def.setFields(options);
        } catch (Exception e) {
            throw new BadRequestException("Error converting properties to KsDef", e);
        }
       
        return internalUpdateKeyspace(def.getThriftKeyspaceDefinition());
    }

    @Override
    public OperationResult<SchemaChangeResult> updateKeyspace(final Properties props) throws ConnectionException {
        final KsDef def;
        try {
            def = ThriftUtils.getThriftObjectFromProperties(KsDef.class, props);
            if (def.getCf_defs() == null) {
                def.setCf_defs(Lists.<CfDef>newArrayList());
            }
        } catch (Exception e) {
            throw new BadRequestException("Error converting properties to KsDef", e);
        }
        return internalUpdateKeyspace(def);
    }

    private OperationResult<SchemaChangeResult> internalUpdateKeyspace(final KsDef def) throws ConnectionException {
        return executeSchemaChangeOperation(new AbstractOperationImpl<SchemaChangeResult>(
                tracerFactory.newTracer(CassandraOperationType.UPDATE_KEYSPACE)) {
            @Override
            public SchemaChangeResult internalExecute(Client client, ConnectionContext context) throws Exception {
                precheckSchemaAgreement(client);
                return new SchemaChangeResponseImpl().setSchemaId(client.system_update_keyspace(def));
            }
        });
    }

    @Override
    public ColumnDefinition makeColumnDefinition() {
        return new ThriftColumnDefinitionImpl();
    }

    @Override
    public AstyanaxConfiguration getConfig() {
        return config;
    }
   
    @Override
    public OperationResult<SchemaChangeResult> dropColumnFamily(final String keyspaceName, final String columnFamilyName) throws ConnectionException  {
        return connectionPool
                .executeWithFailover(
                        new AbstractKeyspaceOperationImpl<SchemaChangeResult>(
                                tracerFactory.newTracer(CassandraOperationType.DROP_COLUMN_FAMILY), keyspaceName) {
                            @Override
                            public SchemaChangeResult internalExecute(Client client, ConnectionContext context) throws Exception {
                                precheckSchemaAgreement(client);
                                return new SchemaChangeResponseImpl().setSchemaId(client.system_drop_column_family(columnFamilyName));
                            }
                        }, RunOnce.get());
    }

    @Override
    public OperationResult<SchemaChangeResult> dropKeyspace(final String keyspaceName) throws ConnectionException  {
        return connectionPool
                .executeWithFailover(
                        new AbstractKeyspaceOperationImpl<SchemaChangeResult>(
                                tracerFactory.newTracer(CassandraOperationType.DROP_KEYSPACE), keyspaceName) {
                            @Override
                            public SchemaChangeResult internalExecute(Client client, ConnectionContext context) throws Exception {
                                precheckSchemaAgreement(client);
                                return new SchemaChangeResponseImpl().setSchemaId(client.system_drop_keyspace(keyspaceName));
                            }
                        }, RunOnce.get());
    }

    @Override
    public Properties getAllKeyspaceProperties() throws ConnectionException {
        List<KeyspaceDefinition> keyspaces = this.describeKeyspaces();
        Properties props = new Properties();
        for (KeyspaceDefinition ksDef : keyspaces) {
            ThriftKeyspaceDefinitionImpl thriftKsDef = (ThriftKeyspaceDefinitionImpl)ksDef;
            try {
                for (Entry<Object, Object> prop : thriftKsDef.getProperties().entrySet()) {
                    props.setProperty(ksDef.getName() + "." + prop.getKey(), (String) prop.getValue());
                }
            } catch (Exception e) {
            }
        }
        return props;
    }

    @Override
    public Properties getKeyspaceProperties(String keyspace) throws ConnectionException {
        KeyspaceDefinition ksDef = this.describeKeyspace(keyspace);
        if (ksDef == null)
            throw new NotFoundException(String.format("Keyspace '%s' not found", keyspace));
       
        Properties props = new Properties();
        ThriftKeyspaceDefinitionImpl thriftKsDef = (ThriftKeyspaceDefinitionImpl)ksDef;
        try {
            for (Entry<Object, Object> prop : thriftKsDef.getProperties().entrySet()) {
                props.setProperty((String)prop.getKey(), (String) prop.getValue());
            }
        } catch (Exception e) {
            LOG.error(String.format("Error fetching properties for keyspace '%s'", keyspace));
        }
        return props;
    }

    @Override
    public Properties getColumnFamilyProperties(String keyspace, String columnFamily) throws ConnectionException {
        KeyspaceDefinition ksDef = this.describeKeyspace(keyspace);
        ColumnFamilyDefinition cfDef = ksDef.getColumnFamily(columnFamily);
        if (cfDef == null)
            throw new NotFoundException(String.format("Column family '%s' in keyspace '%s' not found", columnFamily, keyspace));
       
        Properties props = new Properties();
        ThriftColumnFamilyDefinitionImpl thriftCfDef = (ThriftColumnFamilyDefinitionImpl)cfDef;
        try {
            for (Entry<Object, Object> prop : thriftCfDef.getProperties().entrySet()) {
                props.setProperty((String)prop.getKey(), (String) prop.getValue());
            }
        } catch (Exception e) {
            LOG.error("Error processing column family properties");
        }
        return props;
    }
   
    /**
     * Do a quick check to see if there is a schema disagreement.  This is done as an extra precaution
     * to reduce the chances of putting the cluster into a bad state.  This will not gurantee however, that
     * by the time a schema change is made the cluster will be in the same state.
     * @param client
     * @throws Exception
     */
    private void precheckSchemaAgreement(Client client) throws Exception {
        Map<String, List<String>> schemas = client.describe_schema_versions();
        if (schemas.size() > 1) {
            throw new SchemaDisagreementException("Can't change schema due to pending schema agreement");
        }
    }
}
TOP

Related Classes of com.netflix.astyanax.thrift.ThriftClusterImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.