/*
* 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.
*/
/*
* 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.wizecommerce.hecuba.astyanax;
import java.nio.ByteBuffer;
import java.util.*;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.exception.ExceptionUtils;
import com.google.common.base.Joiner;
import com.netflix.astyanax.*;
import com.netflix.astyanax.clock.ClockType;
import com.netflix.astyanax.connectionpool.Host;
import com.netflix.astyanax.connectionpool.HostStats;
import com.netflix.astyanax.connectionpool.NodeDiscoveryType;
import com.netflix.astyanax.connectionpool.OperationResult;
import com.netflix.astyanax.connectionpool.exceptions.ConnectionException;
import com.netflix.astyanax.connectionpool.impl.*;
import com.netflix.astyanax.impl.AstyanaxConfigurationImpl;
import com.netflix.astyanax.model.*;
import com.netflix.astyanax.query.ColumnQuery;
import com.netflix.astyanax.query.PreparedIndexExpression;
import com.netflix.astyanax.query.RowQuery;
import com.netflix.astyanax.serializers.StringSerializer;
import com.netflix.astyanax.thrift.ThriftFamilyFactory;
import com.wizecommerce.hecuba.*;
import com.wizecommerce.hecuba.util.ClientManagerUtils;
import com.wizecommerce.hecuba.util.ConfigUtils;
/**
* @author Eran Chinthaka Withana
*/
public class AstyanaxBasedHecubaClientManager<K> extends HecubaClientManager<K> {
Keyspace keyspace;
ColumnFamily<K, String> columnFamily;
ColumnFamily<String, K> secondaryIndexColumnFamily;
AstyanaxConfigurationImpl astyanaxConfigurationImpl;
ConnectionPoolConfigurationImpl connectionPoolConfigurationImpl;
CountingConnectionPoolMonitor connectionPoolMonitor;
AstyanaxContext<Keyspace> context;
AstyanaxContext<Cluster> clusterContext;
Serializer<K> keySerializer;
private Clock clock = ClockType.ASYNC_MICRO.get();
public AstyanaxBasedHecubaClientManager() {
}
public AstyanaxBasedHecubaClientManager(CassandraParamsBean parameters, Serializer<K> keySerializer) {
super(parameters);
initialize(getClusterName(), getLocationURL(), getPort(), getKeyspace());
this.columnFamily = new ColumnFamily<K, String>(getColumnFamilyName(), keySerializer, StringSerializer.get());
this.keySerializer = keySerializer;
if (columnsToIndexOnColumnNameAndValue != null && columnsToIndexOnColumnNameAndValue.size() > 0) {
String secondaryIndexedColumnFamily = ConfigUtils.getInstance().getConfiguration().getString(
HecubaConstants.GLOBAL_PROP_NAME_PREFIX + "." + columnFamily + ".secondaryIndexCF",
columnFamily.getName() + HecubaConstants.SECONDARY_INDEX_CF_NAME_SUFFIX);
this.secondaryIndexColumnFamily = new ColumnFamily<String, K>(secondaryIndexedColumnFamily,
StringSerializer.get(), keySerializer);
}
}
public void initialize(String clusterName, String locationUrls, String port, String keyspaceName) {
astyanaxConfigurationImpl = new AstyanaxConfigurationImpl();
final Configuration configuration = ConfigUtils.getInstance().getConfiguration();
astyanaxConfigurationImpl.setDiscoveryType(NodeDiscoveryType.valueOf(configuration.getString(
HecubaConstants.ASTYANAX_NODE_DISCOVERY_TYPE, "RING_DESCRIBE")));
astyanaxConfigurationImpl.setConnectionPoolType(ConnectionPoolType.
valueOf(configuration.getString(
HecubaConstants.ASTYANAX_CONNECTION_POOL_TYPE,
"TOKEN_AWARE")));
connectionPoolConfigurationImpl = new ConnectionPoolConfigurationImpl("MyConnectionPool").setPort(
Integer.parseInt(port)).setSeeds(getListOfNodesAndPorts(locationUrls, port)).setMaxConnsPerHost(
configuration.getInteger(HecubaConstants.ASTYANAX_MAX_CONNS_PER_HOST, 3));
// Will resort hosts per token partition every 10 seconds
SmaLatencyScoreStrategyImpl smaLatencyScoreStrategyImpl = new SmaLatencyScoreStrategyImpl(
configuration.getInteger(HecubaConstants.ASTYANAX_LATENCY_AWARE_UPDATE_INTERVAL, 10000),
configuration.getInteger(HecubaConstants.ASTYANAX_LATENCY_AWARE_RESET_INTERVAL, 10000),
configuration.getInteger(HecubaConstants.ASTYANAX_LATENCY_AWARE_WINDOW_SIZE, 100),
configuration.getFloat(HecubaConstants.ASTYANAX_LATENCY_AWARE_BADNESS_INTERVAL, 0.5f));
// Enabled SMA. Omit this to use round robin with a token range.
connectionPoolConfigurationImpl.setLatencyScoreStrategy(smaLatencyScoreStrategyImpl);
if(StringUtils.isNotBlank(username) && StringUtils.isNotBlank(password)) {
SimpleAuthenticationCredentials simpleAuth =new SimpleAuthenticationCredentials(username, password);
connectionPoolConfigurationImpl.setAuthenticationCredentials(simpleAuth);
}
connectionPoolMonitor = new CountingConnectionPoolMonitor();
context = new AstyanaxContext.Builder().forCluster(clusterName).forKeyspace(keyspaceName)
.withAstyanaxConfiguration(astyanaxConfigurationImpl)
.withConnectionPoolConfiguration(connectionPoolConfigurationImpl)
.withConnectionPoolMonitor(connectionPoolMonitor).buildKeyspace(
ThriftFamilyFactory.getInstance());
context.start();
keyspace = context.getEntity();
if (clusterContext == null) {
initiateClusterContext(clusterName);
}
}
@Override
public void updateString(K key, String columnName, String value, long timestamp, int ttl) {
MutationBatch m = keyspace.prepareMutationBatch();
// set the timestamp, if set.
if (timestamp > 0) {
m.setTimestamp(timestamp);
}
updateSecondaryIndexes(key, columnName, value, ttl, m);
m.withRow(columnFamily, key).putColumn(columnName, value, ttl > 0 ? ttl : null);
try {
m.execute();
} catch (ConnectionException e) {
log.error(ExceptionUtils.getStackTrace(e));
}
}
private void updateSecondaryIndexes(K key, String columnName, String value, int ttl, MutationBatch mutationBatch) {
// first check whether we have secondary indexes enabled for this CF and the given column has a secondary
// index on it.
if (isSecondaryIndexByColumnNameAndValueEnabledForColumn(columnName)) {
// first retrieve the old value of this column to delete it from the secondary index.
final String oldValue = readString(key, columnName);
updateSecondaryIndexColumnFamily(key, columnName, value, ttl, mutationBatch, oldValue);
}
if (isSecondaryIndexByColumnNameEnabledForColumn(columnName)) {
updateSecondaryIndexColumnFamily(key, columnName, "", ttl, mutationBatch, "");
}
}
private void updateSecondaryIndexColumnFamily(K key, String columnName, String value, int ttl,
MutationBatch mutationBatch, String oldValue) {
// enqueue a deletion to the secondary index to remove the previous value of this column.
if (!StringUtils.isBlank(oldValue) && !"null".equalsIgnoreCase(oldValue)) {
mutationBatch.withRow(secondaryIndexColumnFamily, getSecondaryIndexKey(columnName, oldValue)).deleteColumn(
key);
}
// add the new value to the secondary index CF. Make sure to handle the TTLs properly.
mutationBatch.withRow(secondaryIndexColumnFamily, getSecondaryIndexKey(columnName, value)).putColumn(key, key,
keySerializer,
ttl > 0 ?
ttl :
null);
}
@Override
public void updateByteBuffer(K key, String columnName, ByteBuffer value) {
try {
MutationBatch m = keyspace.prepareMutationBatch();
m.withRow(columnFamily, key).putColumn(columnName, value, null);
m.execute();
} catch (ConnectionException e) {
log.warn("Couldn't update byte buffer ", e);
}
}
public AstyanaxContext<Cluster> initiateClusterContext(String clusterName) {
clusterContext = new AstyanaxContext.Builder().forCluster(clusterName).withAstyanaxConfiguration(
new AstyanaxConfigurationImpl()).withConnectionPoolConfiguration(new ConnectionPoolConfigurationImpl(
clusterName).setMaxConnsPerHost(1).setSeeds(getListOfNodesAndPorts(locationURLs, ports)).setPort(
Integer.parseInt(ports))).withConnectionPoolMonitor(new CountingConnectionPoolMonitor()).buildCluster(
ThriftFamilyFactory.getInstance());
clusterContext.start();
return clusterContext;
}
@Override
public void updateRow(K key, Map<String, Object> row, Map<String, Long> timestamps, Map<String, Integer> ttls) throws Exception {
// Inserting data
MutationBatch m = keyspace.prepareMutationBatch();
MutationBatch secondaryIndexMutation = keyspace.prepareMutationBatch();
ColumnListMutation<String> columnListMutation = m.withRow(columnFamily, key);
// check whether we have to set the timestamps.
final boolean timestampsDefined = timestamps != null;
for (String columnName : row.keySet()) {
final Object value = row.get(columnName);
// we will set the ttls if defined, if we pass that as null so that internally it will NOT set the ttls.
final Integer ttl = ttls == null ? null : ttls.get(columnName);
String valueToInsert = ClientManagerUtils.getInstance().convertValueForStorage(value);
// if timestamps are set, pass that on to the columnListMutation. The tricky thing here is once you set
// a timestamp that will be used from that point beyond. So if a timestamp is NOT defined at least for one
// column name then we need to set that timestamp to proper system timestamp.
if (timestampsDefined) {
columnListMutation.setTimestamp(timestamps.get(columnName) == null || timestamps.get(columnName) < 1 ?
clock.getCurrentTime() : timestamps.get(columnName));
}
columnListMutation.putColumn(columnName, valueToInsert, ttl);
updateSecondaryIndexes(key, columnName, valueToInsert, ttl == null ? -1 : ttl, secondaryIndexMutation);
}
try {
// first update secondary indexes.
if (!secondaryIndexMutation.isEmpty()) {
final OperationResult<Void> secondaryIndexMutationExecResult = secondaryIndexMutation.execute();
log.debug("Row Inserted into Cassandra secondary indexes. Exec Time = " +
secondaryIndexMutationExecResult.getLatency() + ", " +
"Host used = " + secondaryIndexMutationExecResult.getHost().getHostName());
}
OperationResult<Void> result = m.execute();
log.debug("Row Inserted into Cassandra. Exec Time = " + result.getLatency() + ", Host used = " +
result.getHost().getHostName());
} catch (ConnectionException e) {
if (log.isDebugEnabled()) {
log.debug("HecubaClientManager error while updating key " + key.toString());
}
throw e;
}
}
@Override
public String readString(K key, String columnName) {
Column<String> columnByName = readColumn(key, columnName);
return columnByName == null ? null : columnByName.getStringValue();
}
private Column<String> readColumn(K key, String columnName) {
try {
OperationResult<ColumnList<String>> result = keyspace.prepareQuery(columnFamily).getKey(key).execute();
return result.getResult().getColumnByName(columnName);
} catch (ConnectionException e) {
if (log.isDebugEnabled()) {
log.debug("HecubaClientManager error while reading key " + key.toString() + ". " +
ExceptionUtils.getStackTrace(e));
}
}
return null;
}
@Override
public CassandraColumn readColumnInfo(K key, String columnName) {
Column<String> columnByName = readColumn(key, columnName);
return columnByName == null ? null : new CassandraColumn(columnByName.getName(), columnByName.getStringValue(),
columnByName.getTimestamp(), columnByName.getTtl());
}
public CassandraResultSet<K, String> readAllColumns(K key) throws ConnectionException {
try {
OperationResult<ColumnList<String>> result = keyspace.prepareQuery(columnFamily).getKey(key).execute();
ColumnList<String> columns = result.getResult();
if (isClientAdapterDebugMessagesEnabled) {
log.info("Row retrieved from Cassandra. Exec Time (micro-sec) = " + result.getLatency() / 1000 +
", Host used = " + result.getHost() + ", Key = " + key);
}
return new AstyanaxResultSet<>(columns);
} catch (ConnectionException e) {
if (log.isDebugEnabled()) {
log.debug("HecubaClientManager error while reading key " + key.toString());
}
throw e;
}
}
@Override
public CassandraResultSet<K, String> readColumnSlice(K key, String start, String end, boolean reversed, int count) {
try {
final OperationResult<ColumnList<String>> executeResult = keyspace.prepareQuery(columnFamily).getKey(key)
.withColumnRange(start, end, reversed,
count).execute();
if (executeResult != null) {
return new AstyanaxResultSet<K, String>(executeResult.getResult());
}
} catch (ConnectionException e) {
log.warn("Couldn't execute column slice query ", e);
}
return null;
}
@Override
public CassandraResultSet readAllColumnsBySecondaryIndex(Map<String, String> parameters, int limit) {
List<PreparedIndexExpression<K, String>> clauses = new ArrayList<PreparedIndexExpression<K, String>>();
for (Map.Entry<String, String> entry : parameters.entrySet()) {
PreparedIndexExpression<K, String> clause = columnFamily.newIndexClause().whereColumn(entry.getKey())
.equals().value(entry.getValue());
clauses.add(clause);
}
OperationResult<Rows<K, String>> result;
try {
result = keyspace.prepareQuery(columnFamily).searchWithIndex()
// .setStartKey(0L)
.addPreparedExpressions(clauses).execute();
Rows<K, String> rowItems = result.getResult();
if (rowItems.size() > 0) {
Row<K, String> row = rowItems.getRowByIndex(0);
AstyanaxResultSet<K, String> resultSet = new AstyanaxResultSet<K, String>(row.getColumns());
return resultSet;
}
} catch (ConnectionException e) {
log.warn("HecubaClientManager error while reading secondary index key " + parameters.toString());
if (log.isDebugEnabled()) {
log.debug("Caught Exception", e);
}
}
return null;
}
@Override
public Long getCounterValue(K key, String counterColumnName) {
try {
RowQuery<K, String> row = keyspace.prepareQuery(columnFamily).getKey(key);
if (row != null) {
ColumnQuery<String> column = row.getColumn(counterColumnName);
if (column != null) {
OperationResult<Column<String>> executeResult = column.execute();
if (executeResult != null) {
Column<String> result = executeResult.getResult();
return result != null ? result.getLongValue() : 0L;
} else {
return 0L;
}
} else {
return 0L;
}
} else {
return 0L;
}
} catch (ConnectionException e) {
if (log.isDebugEnabled()) {
log.debug("HecubaClientManager error while reading key " + key.toString() + ". " +
ExceptionUtils.getStackTrace(e));
log.debug("Caught Exception", e);
}
}
return 0L;
}
@Override
public void updateCounter(K key, String counterColumnName, long value) {
try {
keyspace.prepareColumnMutation(columnFamily, key, counterColumnName).incrementCounterColumn(value)
.execute();
} catch (ConnectionException e) {
log.error(ExceptionUtils.getStackTrace(e));
}
}
@Override
public void incrementCounter(K key, String counterColumnName) {
this.updateCounter(key, counterColumnName, 1);
}
@Override
public void decrementCounter(K key, String counterColumnName) {
this.updateCounter(key, counterColumnName, -1);
}
@Override
public void deleteColumn(K key, String columnName) {
MutationBatch m = keyspace.prepareMutationBatch();
// first check whether this is a column we have a seconday index on.
if (isSecondaryIndexByColumnNameAndValueEnabledForColumn(columnName)) {
final Column<String> oldColumn = readColumn(key, columnName);
if (oldColumn != null) {
m.withRow(secondaryIndexColumnFamily, getSecondaryIndexKey(columnName, oldColumn.getStringValue()))
.deleteColumn(key);
}
}
if (isSecondaryIndexByColumnNameEnabledForColumn(columnName)) {
m.withRow(secondaryIndexColumnFamily, getSecondaryIndexKey(columnName, "")).deleteColumn(key);
}
// delete the column from the main CF
m.withRow(columnFamily, key).deleteColumn(columnName);
try {
m.execute();
} catch (ConnectionException e) {
log.error(ExceptionUtils.getStackTrace(e));
}
}
@Override
public void deleteRow(K key, long timestamp) {
try {
MutationBatch m = keyspace.prepareMutationBatch();
timestamp = timestamp > 0 ? timestamp : keyspace.getConfig().getClock().getCurrentTime();
// first delete all the secondary indexes.
if (isSecondaryIndexByColumnNameAndValueEnabled || isSecondaryIndexesByColumnNamesEnabled) {
// first read the old values of columns.
final CassandraResultSet<K, String> oldValues = readColumns(key, columnsToIndexOnColumnNameAndValue);
if (isSecondaryIndexByColumnNameAndValueEnabled) {
for (String columnName : columnsToIndexOnColumnNameAndValue) {
m.withRow(secondaryIndexColumnFamily, getSecondaryIndexKey(columnName, oldValues.getString(
columnName))).setTimestamp(timestamp).deleteColumn(key);
}
}
if (isSecondaryIndexesByColumnNamesEnabled) {
for (String columnName : oldValues.getColumnNames()) {
if (columnName.matches(secondaryIdxByColumnPattern)) {
m.withRow(secondaryIndexColumnFamily, getSecondaryIndexKey(columnName, "")).setTimestamp(
timestamp).deleteColumn(key);
}
}
}
}
// now delete the main row.
m.withRow(columnFamily, key).setTimestamp(timestamp).delete();
m.execute();
} catch (ConnectionException e) {
log.error(ExceptionUtils.getStackTrace(e));
} catch (Exception e) {
log.error(ExceptionUtils.getStackTrace(e));
}
}
@Override
public CassandraResultSet<K, String> retrieveBySecondaryIndex(String columnName, String columnValue) {
if (isSecondaryIndexByColumnNameAndValueEnabledForColumn(columnName)) {
return retrieveFromSecondaryIndex(columnName, columnValue);
}
return null;
}
@Override
public CassandraResultSet<K, String> retrieveBySecondaryIndex(String columnName, List<String> columnValues) {
if (isSecondaryIndexByColumnNameAndValueEnabledForColumn(columnName)) {
return retrieveFromSecondaryIndex(columnName, columnValues);
}
return null;
}
private CassandraResultSet<K, String> retrieveFromSecondaryIndex(String columnName, String columnValue) {
try {
List<K> mappingObjectIds = retrieveKeysFromSecondaryIndex(columnName, columnValue);
if (CollectionUtils.isNotEmpty(mappingObjectIds)) {
return readAllColumns(new HashSet<K>(mappingObjectIds));
}
} catch (Exception e) {
log.warn("Error executing retrieveBySecondaryIndex ", e);
}
return null;
}
private CassandraResultSet<K, String> retrieveFromSecondaryIndex(String columnName, List<String> columnValues) {
try {
Map<String, List<K>> keysMap = retrieveKeysFromSecondaryIndex(columnName, columnValues);
if (MapUtils.isNotEmpty(keysMap)) {
Set<K> keys = new HashSet<>();
for (List<K> keysForColValue : keysMap.values()) {
keys.addAll(keysForColValue);
}
return readAllColumns(keys);
}
} catch (Exception e) {
log.warn("Error executing retrieveBySecondaryIndex for multiple column values", e);
}
return null;
}
@Override
public CassandraResultSet<K, String> retrieveByColumnNameBasedSecondaryIndex(String columnName) {
if (isSecondaryIndexByColumnNameEnabledForColumn(columnName)) {
return retrieveFromSecondaryIndex(columnName, "");
}
return null;
}
@Override
public CassandraResultSet<K, String> readColumns(K key, List<String> columnName) throws Exception {
final OperationResult<ColumnList<String>> operationResult = keyspace.prepareQuery(columnFamily).getKey(key)
.withColumnSlice(columnName).execute();
if (operationResult != null) {
return new AstyanaxResultSet<K, String>(operationResult.getResult());
}
return null;
}
@Override
public CassandraResultSet<K, String> readColumns(Set<K> keys, List<String> columnNames) throws Exception {
if (CollectionUtils.isNotEmpty(columnNames)) {
final OperationResult<Rows<K, String>> result = keyspace.prepareQuery(columnFamily).getKeySlice(keys)
.withColumnSlice(columnNames).execute();
if (isClientAdapterDebugMessagesEnabled) {
log.info(columnNames.size() + " columns retrieved from Cassandra [Astyanax] for " + keys.size() +
" keys . Exec Time (micro-sec) = " + (result.getLatency() / 1000) + ", Host used = " +
result.getHost());
}
return new AstyanaxResultSet<K, String>(result);
} else {
return readAllColumns(keys);
}
}
@Override
public CassandraResultSet<K, String> readAllColumns(Set<K> keys) throws Exception {
// This method was added as part of multi-get feature for cache calls.
try {
OperationResult<Rows<K, String>> result = keyspace.prepareQuery(columnFamily).getKeySlice(keys).execute();
if (isClientAdapterDebugMessagesEnabled) {
log.info("Rows retrieved from Cassandra [Astyanax] (for " + keys.size() + " keys). Exec Time " +
"(micro-sec) = " + result.getLatency() / 1000 + ", Host used = " + result.getHost());
}
return new AstyanaxResultSet<K, String>(result);
} catch (ConnectionException e) {
log.warn("HecubaClientManager [Astyanax] error while reading multiple keys. Number of keys = " +
keys.size() + ", keys = " + keys.toString());
if (log.isDebugEnabled()) {
log.debug("Caught Exception while reading for multiple keys", e);
logDownedHosts();
}
throw e;
}
}
@Override
public CassandraResultSet<K, String> readColumnSlice(Set<K> keys, String start, String end, boolean reversed) {
try {
final OperationResult<Rows<K, String>> rowSliceQueryResult = keyspace.prepareQuery(columnFamily)
.getKeySlice(keys).withColumnRange(
start, end, reversed, Integer.MAX_VALUE).execute();
return new AstyanaxResultSet<K, String>(rowSliceQueryResult);
} catch (ConnectionException e) {
log.warn("error while executing a row slice query ", e);
if (log.isDebugEnabled()) {
logDownedHosts();
}
}
return null;
}
@Override
public void deleteColumns(K key, List<String> columnNameList) {
for (String columnName : columnNameList) {
deleteColumn(key, columnName);
}
}
@Override
public List<K> retrieveKeysBySecondaryIndex(String columnName, String columnValue) {
if (isSecondaryIndexByColumnNameAndValueEnabledForColumn(columnName)) {
return retrieveKeysFromSecondaryIndex(columnName, columnValue);
}
return null;
}
@Override
public Map<String, List<K>> retrieveKeysBySecondaryIndex(String columnName, List<String> columnValues) {
if (isSecondaryIndexByColumnNameAndValueEnabledForColumn(columnName)) {
return retrieveKeysFromSecondaryIndex(columnName, columnValues);
}
return null;
}
@Override
public List<K> retrieveKeysByColumnNameBasedSecondaryIndex(String columnName) {
if (isSecondaryIndexByColumnNameEnabledForColumn(columnName)) {
return retrieveKeysFromSecondaryIndex(columnName, "");
}
return null;
}
private List<K> retrieveKeysFromSecondaryIndex(String columnName, String columnValue) {
try {
String secondaryIndexKey = getSecondaryIndexKey(columnName, columnValue);
final OperationResult<ColumnList<K>> result = keyspace.prepareQuery(secondaryIndexColumnFamily).getKey(
secondaryIndexKey).execute();
if (isClientAdapterDebugMessagesEnabled) {
log.debug("Secondary Index Row for " + secondaryIndexKey + " retrieved from Cassandra. Exec Time = " +
result.getLatency() + ", Host used = " + result.getHost().getHostName());
}
if (result != null && result.getResult().size() > 0) {
final Iterator<Column<K>> iterator = result.getResult().iterator();
List<K> rowKeys = new ArrayList<K>();
while (iterator.hasNext()) {
rowKeys.add(iterator.next().getName());
}
return rowKeys;
}
} catch (Exception e) {
log.warn("Error executing retrieveKeysFromSecondaryIndex ", e);
if (log.isDebugEnabled()) {
logDownedHosts();
}
}
return null;
}
private Map<String, List<K>> retrieveKeysFromSecondaryIndex(String columnName, List<String> columnValues) {
try {
List<String> secondaryIndexKeys = getSecondaryIndexKeys(columnName, columnValues);
OperationResult<Rows<String, K>> result = keyspace.prepareQuery(secondaryIndexColumnFamily).getKeySlice(
secondaryIndexKeys).execute();
if (isClientAdapterDebugMessagesEnabled) {
log.debug("Secondary Index Row for " + Joiner.on(",").join(secondaryIndexKeys) +
" retrieved from Cassandra. Exec Time = " +
result.getLatency() + ", Host used = " + result.getHost().getHostName());
}
if (result != null && result.getResult().size() > 0) {
Map<String, List<K>> rowKeysMap = new HashMap<>();
Iterator<Row<String, K>> rowIterator = result.getResult().iterator();
while (rowIterator.hasNext()) {
Row<String, K> row = rowIterator.next();
Iterator<Column<K>> columnIterator = row.getColumns().iterator();
List<K> keys = new ArrayList<>();
while (columnIterator.hasNext()) {
keys.add(columnIterator.next().getName());
}
if (CollectionUtils.isNotEmpty(keys)) {
rowKeysMap.put(getSecondaryIndexedColumnValue(row.getKey()), keys);
}
}
return rowKeysMap;
}
} catch (Exception e) {
log.warn("Error executing retrieveKeysFromSecondaryIndex for multiple column values", e);
if (log.isDebugEnabled()) {
logDownedHosts();
}
}
return null;
}
@Override
protected void logDownedHosts() {
Map<Host, HostStats> hostStatsMap = connectionPoolMonitor.getHostStats();
StringBuilder allHostsSB = new StringBuilder("All Hosts = {");
StringBuilder downedHostsSB = new StringBuilder("Downed Hosts = {");
for (Host host : hostStatsMap.keySet()) {
allHostsSB.append(host.getHostName() + ", ");
HostStats stats = hostStatsMap.get(host);
if (stats != null && !stats.isUp()) {
downedHostsSB.append(host.getHostName() + ", ");
}
}
if (allHostsSB.toString().endsWith(", ")) {
allHostsSB.setLength(allHostsSB.length() - 2);
}
allHostsSB.append("}");
log.debug(allHostsSB.toString());
if (downedHostsSB.toString().endsWith(", ")) {
downedHostsSB.setLength(downedHostsSB.length() - 2);
}
downedHostsSB.append("}");
log.debug(downedHostsSB.toString());
}
@Override
public void shutDown() {
clusterContext.shutdown();
context.shutdown();
connectionPoolConfigurationImpl.shutdown();
}
}