/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.usergrid.persistence.qakka.serialization.sharding.impl;
import com.datastax.driver.core.BatchStatement;
import com.datastax.driver.core.Row;
import com.datastax.driver.core.Statement;
import com.datastax.driver.core.querybuilder.Assignment;
import com.datastax.driver.core.querybuilder.Clause;
import com.datastax.driver.core.querybuilder.QueryBuilder;
import com.google.common.collect.Lists;
import com.google.inject.Inject;
import org.apache.usergrid.persistence.core.CassandraConfig;
import org.apache.usergrid.persistence.core.astyanax.MultiTenantColumnFamilyDefinition;
import org.apache.usergrid.persistence.core.datastax.TableDefinition;
import org.apache.usergrid.persistence.core.datastax.impl.TableDefinitionStringImpl;
import org.apache.usergrid.persistence.qakka.core.CassandraClient;
import org.apache.usergrid.persistence.qakka.serialization.sharding.Shard;
import org.apache.usergrid.persistence.qakka.serialization.sharding.ShardSerialization;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collection;
import java.util.Collections;
import java.util.UUID;
public class ShardSerializationImpl implements ShardSerialization {
private static final Logger logger = LoggerFactory.getLogger( ShardSerializationImpl.class );
private final CassandraClient cassandraClient;
private final CassandraConfig cassandraConfig;
public final static String COLUMN_QUEUE_NAME = "queue_name";
public final static String COLUMN_REGION = "region";
public final static String COLUMN_SHARD_ID = "shard_id";
public final static String COLUMN_ACTIVE = "active";
public final static String COLUMN_POINTER = "pointer";
public final static String TABLE_SHARDS_MESSAGES_AVAILABLE = "shards_messages_available";
public final static String TABLE_SHARDS_MESSAGES_INFLIGHT = "shards_messages_inflight";
static final String SHARDS_MESSAGES_AVAILABLE =
"CREATE TABLE IF NOT EXISTS shards_messages_available ( " +
"queue_name text, " +
"region text, " +
"shard_id bigint, " +
"active int, " +
"pointer timeuuid, " +
"PRIMARY KEY ((queue_name, region), active, shard_id) " +
") WITH CLUSTERING ORDER BY (active DESC, shard_id ASC); ";
static final String SHARDS_MESSAGES_AVAILABLE_INFLIGHT =
"CREATE TABLE IF NOT EXISTS shards_messages_inflight ( " +
"queue_name text, " +
"region text, " +
"shard_id bigint, " +
"active int, " +
"pointer timeuuid, " +
"PRIMARY KEY ((queue_name, region), active, shard_id) " +
") WITH CLUSTERING ORDER BY (active DESC, shard_id ASC); ";
@Inject
public ShardSerializationImpl( CassandraConfig cassandraConfig, CassandraClient cassandraClient ) {
this.cassandraConfig = cassandraConfig;
this.cassandraClient = cassandraClient;
}
public void createShard(final Shard shard){
Statement insert = QueryBuilder.insertInto(getTableName(shard.getType()))
.value(COLUMN_QUEUE_NAME, shard.getQueueName())
.value(COLUMN_REGION, shard.getRegion())
.value(COLUMN_SHARD_ID, shard.getShardId())
.value(COLUMN_ACTIVE, 1)
.value(COLUMN_POINTER, shard.getPointer());
cassandraClient.getQueueMessageSession().execute(insert);
}
public Shard loadShard(final Shard shard){
Clause queueNameClause = QueryBuilder.eq(COLUMN_QUEUE_NAME, shard.getQueueName());
Clause regionClause = QueryBuilder.eq(COLUMN_REGION, shard.getRegion());
Clause activeClause = QueryBuilder.eq(COLUMN_ACTIVE, 1);
Clause shardIdClause = QueryBuilder.eq(COLUMN_SHARD_ID, shard.getShardId());
Statement select = QueryBuilder.select().from(getTableName(shard.getType()))
.where(queueNameClause)
.and(regionClause)
.and(activeClause)
.and(shardIdClause);
Row row = cassandraClient.getQueueMessageSession().execute(select).one();
if (row == null){
return null;
}
final String queueName = row.getString(COLUMN_QUEUE_NAME);
final String region = row.getString(COLUMN_REGION);
final long shardId = row.getLong(COLUMN_SHARD_ID);
final UUID pointer = row.getUUID(COLUMN_POINTER);
return new Shard(queueName, region, shard.getType(), shardId, pointer);
}
public void deleteShard(final Shard shard){
Clause queueNameClause = QueryBuilder.eq(COLUMN_QUEUE_NAME, shard.getQueueName());
Clause regionClause = QueryBuilder.eq(COLUMN_REGION, shard.getRegion());
Clause activeClause = QueryBuilder.eq(COLUMN_ACTIVE, 1);
Clause shardIdClause = QueryBuilder.eq(COLUMN_SHARD_ID, shard.getShardId());
Statement delete = QueryBuilder.delete().from(getTableName(shard.getType()))
.where(queueNameClause)
.and(regionClause)
.and(activeClause)
.and(shardIdClause);
cassandraClient.getQueueMessageSession().execute(delete);
}
@Override
public void deleteAllShards(String queueName, String region) {
BatchStatement batch = new BatchStatement();
Shard.Type[] shardTypes = new Shard.Type[]{Shard.Type.DEFAULT, Shard.Type.INFLIGHT};
for (Shard.Type shardType : shardTypes) {
Statement delete = QueryBuilder.delete().from( getTableName( shardType ) )
.where( QueryBuilder.eq(COLUMN_QUEUE_NAME, queueName) )
.and( QueryBuilder.eq(COLUMN_REGION, region) );
logger.trace("Removing shards for queue {} region {} shardType {} query {}",
queueName, region, shardType, batch.toString());
batch.add( delete );
}
cassandraClient.getQueueMessageSession().execute( batch );
}
public void updateShardPointer(final Shard shard){
Assignment assignment = QueryBuilder.set(COLUMN_POINTER, shard.getPointer());
Clause queueNameClause = QueryBuilder.eq(COLUMN_QUEUE_NAME, shard.getQueueName());
Clause regionClause = QueryBuilder.eq(COLUMN_REGION, shard.getRegion());
Clause activeClause = QueryBuilder.eq(COLUMN_ACTIVE, 1);
Clause shardIdClause = QueryBuilder.eq(COLUMN_SHARD_ID, shard.getShardId());
Statement update = QueryBuilder.update(getTableName(shard.getType()))
.with(assignment)
.where(queueNameClause)
.and(regionClause)
.and(activeClause)
.and(shardIdClause);
cassandraClient.getQueueMessageSession().execute(update);
}
public static String getTableName(Shard.Type shardType){
String table;
if( shardType.equals(Shard.Type.DEFAULT)) {
table = TABLE_SHARDS_MESSAGES_AVAILABLE;
}else if (shardType.equals(Shard.Type.INFLIGHT)) {
table = TABLE_SHARDS_MESSAGES_INFLIGHT;
}else{
throw new IllegalArgumentException("Unknown ShardType");
}
return table;
}
@Override
public Collection<MultiTenantColumnFamilyDefinition> getColumnFamilies() {
return Collections.EMPTY_LIST;
}
@Override
public Collection<TableDefinition> getTables() {
return Lists.newArrayList(
new TableDefinitionStringImpl( cassandraConfig.getApplicationLocalKeyspace(),
TABLE_SHARDS_MESSAGES_AVAILABLE, SHARDS_MESSAGES_AVAILABLE ),
new TableDefinitionStringImpl( cassandraConfig.getApplicationLocalKeyspace(),
TABLE_SHARDS_MESSAGES_INFLIGHT, SHARDS_MESSAGES_AVAILABLE_INFLIGHT )
);
}
}