/*
*
* * 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.graph.serialization.impl.shard.impl;
import java.nio.ByteBuffer;
import java.util.Collection;
import java.util.Iterator;
import java.util.UUID;
import javax.annotation.Nullable;
import javax.inject.Inject;
import com.google.common.base.Optional;
import org.apache.usergrid.persistence.core.CassandraConfig;
import org.apache.usergrid.persistence.core.astyanax.MultiTenantColumnFamily;
import org.apache.usergrid.persistence.core.astyanax.ScopedRowKey;
import org.apache.usergrid.persistence.core.consistency.TimeService;
import org.apache.usergrid.persistence.core.scope.ApplicationScope;
import org.apache.usergrid.persistence.core.util.ValidationUtils;
import org.apache.usergrid.persistence.graph.Edge;
import org.apache.usergrid.persistence.graph.GraphFig;
import org.apache.usergrid.persistence.graph.MarkedEdge;
import org.apache.usergrid.persistence.graph.SearchByEdge;
import org.apache.usergrid.persistence.graph.SearchByEdgeType;
import org.apache.usergrid.persistence.graph.SearchByIdType;
import org.apache.usergrid.persistence.graph.impl.SimpleMarkedEdge;
import org.apache.usergrid.persistence.graph.serialization.impl.shard.*;
import org.apache.usergrid.persistence.graph.serialization.impl.shard.impl.comparators.DescendingTimestampComparator;
import org.apache.usergrid.persistence.graph.serialization.impl.shard.impl.comparators.OrderedComparator;
import org.apache.usergrid.persistence.graph.serialization.impl.shard.impl.comparators
.SourceDirectedEdgeDescendingComparator;
import org.apache.usergrid.persistence.graph.serialization.impl.shard.impl.comparators
.TargetDirectedEdgeDescendingComparator;
import org.apache.usergrid.persistence.graph.serialization.impl.shard.impl.serialize.EdgeSerializer;
import org.apache.usergrid.persistence.graph.serialization.util.GraphValidation;
import org.apache.usergrid.persistence.model.entity.Id;
import com.google.common.base.Function;
import com.google.inject.Singleton;
import com.netflix.astyanax.Keyspace;
import com.netflix.astyanax.MutationBatch;
import com.netflix.astyanax.Serializer;
import com.netflix.astyanax.util.RangeBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static com.google.common.base.Preconditions.checkNotNull;
/**
*
*/
@Singleton
public class ShardedEdgeSerializationImpl implements ShardedEdgeSerialization {
private static final Logger logger = LoggerFactory.getLogger( ShardedEdgeSerializationImpl.class );
protected final Keyspace keyspace;
protected final CassandraConfig cassandraConfig;
protected final GraphFig graphFig;
protected final EdgeShardStrategy writeEdgeShardStrategy;
protected final TimeService timeService;
protected final EdgeShardSerialization edgeShardSerialization;
@Inject
public ShardedEdgeSerializationImpl( final Keyspace keyspace, final CassandraConfig cassandraConfig,
final GraphFig graphFig, final EdgeShardStrategy writeEdgeShardStrategy,
final TimeService timeService,
final EdgeShardSerialization edgeShardSerialization ) {
checkNotNull( "keyspace required", keyspace );
checkNotNull( "cassandraConfig required", cassandraConfig );
checkNotNull( "consistencyFig required", graphFig );
checkNotNull( "writeEdgeShardStrategy required", writeEdgeShardStrategy );
checkNotNull( "timeService required", timeService );
checkNotNull( "edgeShardSerialization required", edgeShardSerialization );
this.keyspace = keyspace;
this.cassandraConfig = cassandraConfig;
this.graphFig = graphFig;
this.writeEdgeShardStrategy = writeEdgeShardStrategy;
this.timeService = timeService;
this.edgeShardSerialization = edgeShardSerialization;
}
@Override
public MutationBatch writeEdgeFromSource( final EdgeColumnFamilies columnFamilies, final ApplicationScope scope,
final MarkedEdge markedEdge, final Collection<Shard> shards,
final DirectedEdgeMeta directedEdgeMeta, final UUID timestamp ) {
ValidationUtils.validateApplicationScope( scope );
GraphValidation.validateEdge( markedEdge );
ValidationUtils.verifyTimeUuid( timestamp, "timestamp" );
return new SourceWriteOp( columnFamilies, markedEdge, directedEdgeMeta ) {
@Override
void writeEdge( final MutationBatch batch,
final MultiTenantColumnFamily<ScopedRowKey<RowKey>, DirectedEdge> columnFamily,
final ApplicationScope scope, final RowKey rowKey, final DirectedEdge edge,
final Shard shard, final boolean isDeleted ) {
batch.withRow( columnFamily, ScopedRowKey.fromKey( scope.getApplication(), rowKey ) ).putColumn( edge, isDeleted );
}
}.createBatch( scope, shards, timestamp );
}
@Override
public MutationBatch writeEdgeFromSourceWithTargetType( final EdgeColumnFamilies columnFamilies,
final ApplicationScope scope, final MarkedEdge markedEdge,
final Collection<Shard> shards,
final DirectedEdgeMeta directedEdgeMeta,
final UUID timestamp ) {
ValidationUtils.validateApplicationScope( scope );
GraphValidation.validateEdge( markedEdge );
ValidationUtils.verifyTimeUuid( timestamp, "timestamp" );
return new SourceTargetTypeWriteOp( columnFamilies, markedEdge, directedEdgeMeta ) {
@Override
void writeEdge( final MutationBatch batch,
final MultiTenantColumnFamily<ScopedRowKey<RowKeyType>, DirectedEdge> columnFamily,
final ApplicationScope scope, final RowKeyType rowKey, final DirectedEdge edge,
final Shard shard, final boolean isDeleted ) {
batch.withRow( columnFamily, ScopedRowKey.fromKey( scope.getApplication(), rowKey ) ).putColumn( edge, isDeleted );
}
}.createBatch( scope, shards, timestamp );
}
@Override
public MutationBatch writeEdgeToTarget( final EdgeColumnFamilies columnFamilies, final ApplicationScope scope,
final MarkedEdge markedEdge, final Collection<Shard> shards,
final DirectedEdgeMeta targetEdgeMeta, final UUID timestamp ) {
ValidationUtils.validateApplicationScope( scope );
GraphValidation.validateEdge( markedEdge );
ValidationUtils.verifyTimeUuid( timestamp, "timestamp" );
return new TargetWriteOp( columnFamilies, markedEdge, targetEdgeMeta ) {
@Override
void writeEdge( final MutationBatch batch,
final MultiTenantColumnFamily<ScopedRowKey<RowKey>, DirectedEdge> columnFamily,
final ApplicationScope scope, final RowKey rowKey, final DirectedEdge edge,
final Shard shard, final boolean isDeleted ) {
batch.withRow( columnFamily, ScopedRowKey.fromKey( scope.getApplication(), rowKey ) ).putColumn( edge, isDeleted );
}
}.createBatch( scope, shards, timestamp );
}
@Override
public MutationBatch writeEdgeToTargetWithSourceType( final EdgeColumnFamilies columnFamilies,
final ApplicationScope scope, final MarkedEdge markedEdge,
final Collection<Shard> shards,
final DirectedEdgeMeta directedEdgeMeta,
final UUID timestamp ) {
ValidationUtils.validateApplicationScope( scope );
GraphValidation.validateEdge( markedEdge );
ValidationUtils.verifyTimeUuid( timestamp, "timestamp" );
return new TargetSourceTypeWriteOp( columnFamilies, markedEdge, directedEdgeMeta ) {
@Override
void writeEdge( final MutationBatch batch,
final MultiTenantColumnFamily<ScopedRowKey<RowKeyType>, DirectedEdge> columnFamily,
final ApplicationScope scope, final RowKeyType rowKey, final DirectedEdge edge,
final Shard shard, final boolean isDeleted ) {
batch.withRow( columnFamilies.getTargetNodeSourceTypeCfName(), ScopedRowKey.fromKey( scope.getApplication(), rowKey ) )
.putColumn( edge, isDeleted );
}
}.createBatch( scope, shards, timestamp );
}
@Override
public MutationBatch writeEdgeVersions( final EdgeColumnFamilies columnFamilies, final ApplicationScope scope,
final MarkedEdge markedEdge, final Collection<Shard> shards,
final DirectedEdgeMeta directedEdgeMeta, final UUID timestamp ) {
ValidationUtils.validateApplicationScope( scope );
GraphValidation.validateEdge( markedEdge );
ValidationUtils.verifyTimeUuid( timestamp, "timestamp" );
return new EdgeVersions( columnFamilies, markedEdge, directedEdgeMeta ) {
@Override
void writeEdge( final MutationBatch batch,
final MultiTenantColumnFamily<ScopedRowKey<EdgeRowKey>, Long> columnFamily,
final ApplicationScope scope, final EdgeRowKey rowKey, final Long column, final Shard shard,
final boolean isDeleted ) {
batch.withRow( columnFamilies.getGraphEdgeVersions(), ScopedRowKey.fromKey( scope.getApplication(), rowKey ) )
.putColumn( column, isDeleted );
}
}.createBatch( scope, shards, timestamp );
}
@Override
public MutationBatch deleteEdgeFromSource( final EdgeColumnFamilies columnFamilies, final ApplicationScope scope,
final MarkedEdge markedEdge, final Collection<Shard> shards,
final DirectedEdgeMeta directedEdgeMeta, final UUID timestamp ) {
return new SourceWriteOp( columnFamilies, markedEdge, directedEdgeMeta ) {
@Override
void writeEdge( final MutationBatch batch,
final MultiTenantColumnFamily<ScopedRowKey<RowKey>, DirectedEdge> columnFamily,
final ApplicationScope scope, final RowKey rowKey, final DirectedEdge edge,
final Shard shard, final boolean isDeleted ) {
batch.withRow( columnFamily, ScopedRowKey.fromKey( scope.getApplication(), rowKey ) ).deleteColumn( edge );
}
}.createBatch( scope, shards, timestamp );
}
@Override
public MutationBatch deleteEdgeFromSourceWithTargetType( final EdgeColumnFamilies columnFamilies,
final ApplicationScope scope, final MarkedEdge markedEdge,
final Collection<Shard> shards,
final DirectedEdgeMeta directedEdgeMeta,
final UUID timestamp ) {
return new SourceTargetTypeWriteOp( columnFamilies, markedEdge, directedEdgeMeta ) {
@Override
void writeEdge( final MutationBatch batch,
final MultiTenantColumnFamily<ScopedRowKey<RowKeyType>, DirectedEdge> columnFamily,
final ApplicationScope scope, final RowKeyType rowKey, final DirectedEdge edge,
final Shard shard, final boolean isDeleted ) {
batch.withRow( columnFamilies.getSourceNodeTargetTypeCfName(), ScopedRowKey.fromKey( scope.getApplication(), rowKey ) )
.deleteColumn( edge );
}
}.createBatch( scope, shards, timestamp );
}
@Override
public MutationBatch deleteEdgeToTarget( final EdgeColumnFamilies columnFamilies, final ApplicationScope scope,
final MarkedEdge markedEdge, final Collection<Shard> shards,
final DirectedEdgeMeta directedEdgeMeta, final UUID timestamp ) {
return new TargetWriteOp( columnFamilies, markedEdge, directedEdgeMeta ) {
@Override
void writeEdge( final MutationBatch batch,
final MultiTenantColumnFamily<ScopedRowKey<RowKey>, DirectedEdge> columnFamily,
final ApplicationScope scope, final RowKey rowKey, final DirectedEdge edge,
final Shard shard, final boolean isDeleted ) {
batch.withRow( columnFamily, ScopedRowKey.fromKey( scope.getApplication(), rowKey ) ).deleteColumn( edge );
}
}.createBatch( scope, shards, timestamp );
}
@Override
public MutationBatch deleteEdgeToTargetWithSourceType( final EdgeColumnFamilies columnFamilies,
final ApplicationScope scope, final MarkedEdge markedEdge,
final Collection<Shard> shards,
final DirectedEdgeMeta directedEdgeMeta,
final UUID timestamp ) {
return new TargetSourceTypeWriteOp( columnFamilies, markedEdge, directedEdgeMeta ) {
@Override
void writeEdge( final MutationBatch batch,
final MultiTenantColumnFamily<ScopedRowKey<RowKeyType>, DirectedEdge> columnFamily,
final ApplicationScope scope, final RowKeyType rowKey, final DirectedEdge edge,
final Shard shard, final boolean isDeleted ) {
batch.withRow( columnFamilies.getTargetNodeSourceTypeCfName(), ScopedRowKey.fromKey( scope.getApplication(), rowKey ) )
.deleteColumn( edge );
}
}.createBatch( scope, shards, timestamp );
}
@Override
public MutationBatch deleteEdgeVersions( final EdgeColumnFamilies columnFamilies, final ApplicationScope scope,
final MarkedEdge markedEdge, final Collection<Shard> shards,
final DirectedEdgeMeta directedEdgeMeta, final UUID timestamp ) {
return new EdgeVersions( columnFamilies, markedEdge, directedEdgeMeta ) {
@Override
void writeEdge( final MutationBatch batch,
final MultiTenantColumnFamily<ScopedRowKey<EdgeRowKey>, Long> columnFamily,
final ApplicationScope scope, final EdgeRowKey rowKey, final Long column, final Shard shard,
final boolean isDeleted ) {
batch.withRow( columnFamilies.getGraphEdgeVersions(), ScopedRowKey.fromKey( scope.getApplication(), rowKey ) )
.deleteColumn( column );
}
}.createBatch( scope, shards, timestamp );
}
@Override
public Iterator<MarkedEdge> getEdgeVersions( final EdgeColumnFamilies columnFamilies, final ApplicationScope scope,
final SearchByEdge search, final Collection<Shard> shards ) {
ValidationUtils.validateApplicationScope( scope );
GraphValidation.validateSearchByEdge( search );
final Id targetId = search.targetNode();
final Id sourceId = search.sourceNode();
final String type = search.getType();
final long maxTimestamp = search.getMaxTimestamp();
final MultiTenantColumnFamily<ScopedRowKey<EdgeRowKey>, Long> columnFamily =
columnFamilies.getGraphEdgeVersions();
final Serializer<Long> serializer = columnFamily.getColumnSerializer();
final OrderedComparator<MarkedEdge> comparator = new OrderedComparator<>( DescendingTimestampComparator.INSTANCE, search.getOrder());
Optional<Long> lastTimestamp = Optional.absent();
if(search.last().isPresent()){
lastTimestamp = Optional.of(search.last().get().getTimestamp());
}
final EdgeSearcher<EdgeRowKey, Long, MarkedEdge> searcher =
new EdgeSearcher<EdgeRowKey, Long, MarkedEdge>( scope, shards, search.getOrder(), comparator, maxTimestamp,
search.last().transform( TRANSFORM ), lastTimestamp ) {
@Override
protected Serializer<Long> getSerializer() {
return serializer;
}
@Override
protected EdgeRowKey generateRowKey( long shard ) {
return new EdgeRowKey( sourceId, type, targetId, shard );
}
@Override
protected Long createColumn( final MarkedEdge last ) {
return last.getTimestamp();
}
@Override
protected void setTimeScan( final RangeBuilder rangeBuilder ) {
//start seeking at a value < our max version
rangeBuilder.setStart( maxTimestamp );
}
@Override
protected MarkedEdge createEdge( final Long column, final boolean marked ) {
return new SimpleMarkedEdge( sourceId, type, targetId, column.longValue(), marked );
}
};
return new ShardsColumnIterator<>( searcher, columnFamily, keyspace, cassandraConfig.getReadCL(),
graphFig.getScanPageSize(), graphFig.getSmartShardSeekEnabled() );
}
@Override
public Iterator<MarkedEdge> getEdgesFromSource( final EdgeColumnFamilies columnFamilies,
final ApplicationScope scope, final SearchByEdgeType search,
final Collection<Shard> shards ) {
ValidationUtils.validateApplicationScope( scope );
GraphValidation.validateSearchByEdgeType( search );
if(logger.isTraceEnabled()){
logger.trace("getEdgesFromSource shards: {}", shards);
}
final Id sourceId = search.getNode();
final String type = search.getType();
final long maxTimestamp = search.getMaxTimestamp();
final MultiTenantColumnFamily<ScopedRowKey<RowKey>, DirectedEdge> columnFamily =
columnFamilies.getSourceNodeCfName();
final Serializer<DirectedEdge> serializer = columnFamily.getColumnSerializer();
final OrderedComparator<MarkedEdge> comparator = new OrderedComparator<>( TargetDirectedEdgeDescendingComparator.INSTANCE, search.getOrder());
Optional<Long> lastTimestamp = Optional.absent();
if(search.last().isPresent()){
lastTimestamp = Optional.of(search.last().get().getTimestamp());
}
final EdgeSearcher<RowKey, DirectedEdge, MarkedEdge> searcher =
new EdgeSearcher<RowKey, DirectedEdge, MarkedEdge>( scope, shards, search.getOrder(), comparator, maxTimestamp,
search.last().transform( TRANSFORM ), lastTimestamp ) {
@Override
protected Serializer<DirectedEdge> getSerializer() {
return serializer;
}
@Override
protected RowKey generateRowKey( long shard ) {
return new RowKey( sourceId, type, shard );
}
@Override
protected DirectedEdge createColumn( final MarkedEdge last ) {
return new DirectedEdge( last.getTargetNode(), last.getTimestamp() );
}
@Override
protected void setTimeScan( final RangeBuilder rangeBuilder ) {
final ByteBuffer buffer = EdgeSerializer.INSTANCE.fromTimeRange( maxTimestamp );
rangeBuilder.setStart( buffer );
}
@Override
protected MarkedEdge createEdge( final DirectedEdge edge, final boolean marked ) {
return new SimpleMarkedEdge( sourceId, type, edge.id, edge.timestamp, marked );
}
};
return new ShardsColumnIterator<>( searcher, columnFamily, keyspace, cassandraConfig.getReadCL(),
graphFig.getScanPageSize(), graphFig.getSmartShardSeekEnabled() );
}
@Override
public Iterator<MarkedEdge> getEdgesFromSourceByTargetType( final EdgeColumnFamilies columnFamilies,
final ApplicationScope scope,
final SearchByIdType search,
final Collection<Shard> shards ) {
ValidationUtils.validateApplicationScope( scope );
GraphValidation.validateSearchByEdgeType( search );
final Id targetId = search.getNode();
final String type = search.getType();
final String targetType = search.getIdType();
final long maxTimestamp = search.getMaxTimestamp();
final MultiTenantColumnFamily<ScopedRowKey<RowKeyType>, DirectedEdge> columnFamily =
columnFamilies.getSourceNodeTargetTypeCfName();
final Serializer<DirectedEdge> serializer = columnFamily.getColumnSerializer();
final OrderedComparator<MarkedEdge> comparator = new OrderedComparator<>( TargetDirectedEdgeDescendingComparator.INSTANCE, search.getOrder());
Optional<Long> lastTimestamp = Optional.absent();
if(search.last().isPresent()){
lastTimestamp = Optional.of(search.last().get().getTimestamp());
}
final EdgeSearcher<RowKeyType, DirectedEdge, MarkedEdge> searcher =
new EdgeSearcher<RowKeyType, DirectedEdge, MarkedEdge>( scope, shards, search.getOrder(), comparator, maxTimestamp,
search.last().transform( TRANSFORM ), lastTimestamp ) {
@Override
protected Serializer<DirectedEdge> getSerializer() {
return serializer;
}
@Override
protected RowKeyType generateRowKey( long shard ) {
return new RowKeyType( targetId, type, targetType, shard );
}
@Override
protected DirectedEdge createColumn( final MarkedEdge last ) {
return new DirectedEdge( last.getTargetNode(), last.getTimestamp() );
}
@Override
protected void setTimeScan( final RangeBuilder rangeBuilder ) {
final ByteBuffer buffer = EdgeSerializer.INSTANCE.fromTimeRange( maxTimestamp );
rangeBuilder.setStart( buffer );
}
@Override
protected MarkedEdge createEdge( final DirectedEdge edge, final boolean marked ) {
return new SimpleMarkedEdge( targetId, type, edge.id, edge.timestamp, marked );
}
};
return new ShardsColumnIterator( searcher, columnFamily, keyspace, cassandraConfig.getReadCL(),
graphFig.getScanPageSize(), graphFig.getSmartShardSeekEnabled() );
}
@Override
public Iterator<MarkedEdge> getEdgesToTarget( final EdgeColumnFamilies columnFamilies, final ApplicationScope scope,
final SearchByEdgeType search, final Collection<Shard> shards ) {
ValidationUtils.validateApplicationScope( scope );
GraphValidation.validateSearchByEdgeType( search );
final Id targetId = search.getNode();
final String type = search.getType();
final long maxTimestamp = search.getMaxTimestamp();
final MultiTenantColumnFamily<ScopedRowKey<RowKey>, DirectedEdge> columnFamily =
columnFamilies.getTargetNodeCfName();
final Serializer<DirectedEdge> serializer = columnFamily.getColumnSerializer();
final OrderedComparator<MarkedEdge> comparator = new OrderedComparator<>( SourceDirectedEdgeDescendingComparator.INSTANCE, search.getOrder());
Optional<Long> lastTimestamp = Optional.absent();
if(search.last().isPresent()){
lastTimestamp = Optional.of(search.last().get().getTimestamp());
}
final EdgeSearcher<RowKey, DirectedEdge, MarkedEdge> searcher =
new EdgeSearcher<RowKey, DirectedEdge, MarkedEdge>( scope, shards, search.getOrder(),comparator, maxTimestamp,
search.last().transform( TRANSFORM ), lastTimestamp ) {
@Override
protected Serializer<DirectedEdge> getSerializer() {
return serializer;
}
@Override
protected RowKey generateRowKey( long shard ) {
return new RowKey( targetId, type, shard );
}
@Override
protected DirectedEdge createColumn( final MarkedEdge last ) {
return new DirectedEdge( last.getSourceNode(), last.getTimestamp() );
}
@Override
protected void setTimeScan( final RangeBuilder rangeBuilder ) {
final ByteBuffer buffer = EdgeSerializer.INSTANCE.fromTimeRange( maxTimestamp );
rangeBuilder.setStart( buffer );
}
@Override
protected MarkedEdge createEdge( final DirectedEdge edge, final boolean marked ) {
return new SimpleMarkedEdge( edge.id, type, targetId, edge.timestamp, marked );
}
};
return new ShardsColumnIterator<>( searcher, columnFamily, keyspace, cassandraConfig.getReadCL(),
graphFig.getScanPageSize(), graphFig.getSmartShardSeekEnabled() );
}
@Override
public Iterator<MarkedEdge> getEdgesToTargetBySourceType( final EdgeColumnFamilies columnFamilies,
final ApplicationScope scope,
final SearchByIdType search,
final Collection<Shard> shards ) {
ValidationUtils.validateApplicationScope( scope );
GraphValidation.validateSearchByEdgeType( search );
final Id targetId = search.getNode();
final String sourceType = search.getIdType();
final String type = search.getType();
final long maxTimestamp = search.getMaxTimestamp();
final MultiTenantColumnFamily<ScopedRowKey<RowKeyType>, DirectedEdge> columnFamily =
columnFamilies.getTargetNodeSourceTypeCfName();
final Serializer<DirectedEdge> serializer = columnFamily.getColumnSerializer();
final OrderedComparator<MarkedEdge> comparator = new OrderedComparator<>( SourceDirectedEdgeDescendingComparator.INSTANCE, search.getOrder());
Optional<Long> lastTimestamp = Optional.absent();
if(search.last().isPresent()){
lastTimestamp = Optional.of(search.last().get().getTimestamp());
}
final EdgeSearcher<RowKeyType, DirectedEdge, MarkedEdge> searcher =
new EdgeSearcher<RowKeyType, DirectedEdge, MarkedEdge>( scope, shards, search.getOrder(), comparator, maxTimestamp,
search.last().transform( TRANSFORM ), lastTimestamp ) {
@Override
protected Serializer<DirectedEdge> getSerializer() {
return serializer;
}
@Override
protected RowKeyType generateRowKey( final long shard ) {
return new RowKeyType( targetId, type, sourceType, shard );
}
@Override
protected DirectedEdge createColumn( final MarkedEdge last ) {
return new DirectedEdge( last.getTargetNode(), last.getTimestamp() );
}
@Override
protected void setTimeScan( final RangeBuilder rangeBuilder ) {
final ByteBuffer buffer = EdgeSerializer.INSTANCE.fromTimeRange( maxTimestamp );
rangeBuilder.setStart( buffer );
}
@Override
protected MarkedEdge createEdge( final DirectedEdge edge, final boolean marked ) {
return new SimpleMarkedEdge( edge.id, type, targetId, edge.timestamp, marked );
}
};
return new ShardsColumnIterator<>( searcher, columnFamily, keyspace, cassandraConfig.getReadCL(),
graphFig.getScanPageSize(), graphFig.getSmartShardSeekEnabled() );
}
/**
* Simple callback to perform puts and deletes with a common row setup code
*
* @param <R> The row key type
* @param <C> The column type
*/
private abstract class RowOp<R, C> {
/**
* Return the column family used for the write
*/
protected abstract MultiTenantColumnFamily<ScopedRowKey<R>, C> getColumnFamily();
/**
* Get the row key
*/
public abstract R getRowKey( final Shard shard );
/**
* Get the column family value
*/
protected abstract C getDirectedEdge();
/**
* Get the flag on if it's deleted
*/
protected abstract boolean isDeleted();
/**
* Get the directed edge meta for the op
*/
protected abstract DirectedEdgeMeta getDirectedEdgeMeta();
/**
* Write the edge with the given data
*/
abstract void writeEdge( final MutationBatch batch,
final MultiTenantColumnFamily<ScopedRowKey<R>, C> columnFamily,
final ApplicationScope scope, final R rowKey, final C column, final Shard shard,
final boolean isDeleted );
/**
* Create a mutation batch
*/
public MutationBatch createBatch( final ApplicationScope scope, final Collection<Shard> shards,
final UUID opTimestamp ) {
final MutationBatch batch =
keyspace.prepareMutationBatch().withConsistencyLevel( cassandraConfig.getWriteCL() )
.withTimestamp( opTimestamp.timestamp() );
final C column = getDirectedEdge();
final MultiTenantColumnFamily<ScopedRowKey<R>, C> columnFamily = getColumnFamily();
final boolean isDeleted = isDeleted();
for ( Shard shard : shards ) {
final R rowKey = getRowKey( shard );
writeEdge( batch, columnFamily, scope, rowKey, column, shard, isDeleted );
if(logger.isTraceEnabled() && getDirectedEdge() instanceof DirectedEdge){
DirectedEdge directedEdge = (DirectedEdge) getDirectedEdge();
if( shard != null && shard.getShardEnd().isPresent()
&& directedEdge.timestamp > shard.getShardEnd().get().timestamp){
logger.trace("Writing edge past shard end for edge: {}, shard: {}", directedEdge, shard );
}
}
// if an edge is being written to this shard, un-delete it in case it was previously marked
// don't un-delete if the edge write is to actually remove an edge
// Usergrid allows entities to be written with a UUID generated from the past (time)
if(shard.isDeleted() && !isDeleted) {
logger.info("Shard is deleted. Un-deleting as new data is being written to the shard - {}", shard);
shard.setDeleted(false);
batch.mergeShallow(edgeShardSerialization.writeShardMeta(scope, shard, getDirectedEdgeMeta()));
}
}
return batch;
}
}
/**
* Perform a write of the source->target
*/
private abstract class SourceWriteOp extends RowOp<RowKey, DirectedEdge> {
private final MultiTenantColumnFamily<ScopedRowKey<RowKey>, DirectedEdge> columnFamily;
private final Id sourceNodeId;
private final String type;
private final boolean isDeleted;
private final DirectedEdge directedEdge;
private final DirectedEdgeMeta directedEdgeMeta;
/**
* Write the source write operation
*/
private SourceWriteOp( final EdgeColumnFamilies edgeColumnFamilies, final MarkedEdge markedEdge,
final DirectedEdgeMeta directedEdgeMeta ) {
this.columnFamily = edgeColumnFamilies.getSourceNodeCfName();
this.sourceNodeId = markedEdge.getSourceNode();
this.type = markedEdge.getType();
this.isDeleted = markedEdge.isDeleted();
this.directedEdge = new DirectedEdge( markedEdge.getTargetNode(), markedEdge.getTimestamp() );
this.directedEdgeMeta = directedEdgeMeta;
}
@Override
protected MultiTenantColumnFamily<ScopedRowKey<RowKey>, DirectedEdge> getColumnFamily() {
return columnFamily;
}
@Override
public RowKey getRowKey( final Shard shard ) {
return new RowKey( sourceNodeId, type, shard.getShardIndex() );
}
@Override
protected DirectedEdge getDirectedEdge() {
return directedEdge;
}
@Override
protected boolean isDeleted() {
return isDeleted;
}
@Override
protected DirectedEdgeMeta getDirectedEdgeMeta() {
return directedEdgeMeta;
}
}
/**
* Perform a write of the source->target with target type
*/
private abstract class SourceTargetTypeWriteOp extends RowOp<RowKeyType, DirectedEdge> {
private final MultiTenantColumnFamily<ScopedRowKey<RowKeyType>, DirectedEdge> columnFamily;
private final Id sourceNodeId;
private final String type;
private Id targetId;
private final boolean isDeleted;
private final DirectedEdge directedEdge;
private final DirectedEdgeMeta directedEdgeMeta;
/**
* Write the source write operation
*/
private SourceTargetTypeWriteOp( final EdgeColumnFamilies edgeColumnFamilies, final MarkedEdge markedEdge,
final DirectedEdgeMeta directedEdgeMeta ) {
this.columnFamily = edgeColumnFamilies.getSourceNodeTargetTypeCfName();
this.sourceNodeId = markedEdge.getSourceNode();
this.type = markedEdge.getType();
this.targetId = markedEdge.getTargetNode();
this.isDeleted = markedEdge.isDeleted();
this.directedEdge = new DirectedEdge( targetId, markedEdge.getTimestamp() );
this.directedEdgeMeta = directedEdgeMeta;
}
@Override
protected MultiTenantColumnFamily<ScopedRowKey<RowKeyType>, DirectedEdge> getColumnFamily() {
return columnFamily;
}
@Override
public RowKeyType getRowKey( final Shard shard ) {
return new RowKeyType( sourceNodeId, type, targetId, shard.getShardIndex() );
}
@Override
protected DirectedEdge getDirectedEdge() {
return directedEdge;
}
@Override
protected boolean isDeleted() {
return isDeleted;
}
@Override
protected DirectedEdgeMeta getDirectedEdgeMeta() {
return directedEdgeMeta;
}
}
/**
* Perform a write of the target <-- source
*/
private abstract class TargetWriteOp extends RowOp<RowKey, DirectedEdge> {
private final MultiTenantColumnFamily<ScopedRowKey<RowKey>, DirectedEdge> columnFamily;
private final Id targetNode;
private final String type;
private final boolean isDeleted;
private final DirectedEdge directedEdge;
private final DirectedEdgeMeta directedEdgeMeta;
/**
* Write the source write operation
*/
private TargetWriteOp( final EdgeColumnFamilies edgeColumnFamilies, final MarkedEdge markedEdge,
final DirectedEdgeMeta directedEdgeMeta ) {
this.columnFamily = edgeColumnFamilies.getTargetNodeCfName();
this.targetNode = markedEdge.getTargetNode();
this.type = markedEdge.getType();
this.isDeleted = markedEdge.isDeleted();
this.directedEdge = new DirectedEdge( markedEdge.getSourceNode(), markedEdge.getTimestamp() );
this.directedEdgeMeta = directedEdgeMeta;
}
@Override
protected MultiTenantColumnFamily<ScopedRowKey<RowKey>, DirectedEdge> getColumnFamily() {
return columnFamily;
}
@Override
public RowKey getRowKey( final Shard shard ) {
return new RowKey( targetNode, type, shard.getShardIndex() );
}
@Override
protected DirectedEdge getDirectedEdge() {
return directedEdge;
}
@Override
protected boolean isDeleted() {
return isDeleted;
}
@Override
protected DirectedEdgeMeta getDirectedEdgeMeta() {
return directedEdgeMeta;
}
}
/**
* Perform a write of the target<--source with source type
*/
private abstract class TargetSourceTypeWriteOp extends RowOp<RowKeyType, DirectedEdge> {
private final MultiTenantColumnFamily<ScopedRowKey<RowKeyType>, DirectedEdge> columnFamily;
private final Id targetNode;
private final Id sourceNode;
final String type;
final boolean isDeleted;
final DirectedEdge directedEdge;
final DirectedEdgeMeta directedEdgeMeta;
/**
* Write the source write operation
*/
private TargetSourceTypeWriteOp( final EdgeColumnFamilies edgeColumnFamilies, final MarkedEdge markedEdge,
final DirectedEdgeMeta directedEdgeMeta ) {
this.columnFamily = edgeColumnFamilies.getSourceNodeTargetTypeCfName();
this.targetNode = markedEdge.getTargetNode();
this.sourceNode = markedEdge.getSourceNode();
this.type = markedEdge.getType();
this.isDeleted = markedEdge.isDeleted();
this.directedEdge = new DirectedEdge( sourceNode, markedEdge.getTimestamp() );
this.directedEdgeMeta = directedEdgeMeta;
}
@Override
protected MultiTenantColumnFamily<ScopedRowKey<RowKeyType>, DirectedEdge> getColumnFamily() {
return columnFamily;
}
@Override
public RowKeyType getRowKey( final Shard shard ) {
return new RowKeyType( targetNode, type, sourceNode, shard.getShardIndex() );
}
@Override
protected DirectedEdge getDirectedEdge() {
return directedEdge;
}
@Override
protected boolean isDeleted() {
return isDeleted;
}
@Override
protected DirectedEdgeMeta getDirectedEdgeMeta() {
return directedEdgeMeta;
}
}
/**
* Perform a write of the edge versions
*/
private abstract class EdgeVersions extends RowOp<EdgeRowKey, Long> {
private final MultiTenantColumnFamily<ScopedRowKey<EdgeRowKey>, Long> columnFamily;
private final Id targetNode;
private final Id sourceNode;
final String type;
final boolean isDeleted;
final Long edgeVersion;
final DirectedEdgeMeta directedEdgeMeta;
/**
* Write the source write operation
*/
private EdgeVersions( final EdgeColumnFamilies edgeColumnFamilies, final MarkedEdge markedEdge,
final DirectedEdgeMeta directedEdgeMeta ) {
this.columnFamily = edgeColumnFamilies.getGraphEdgeVersions();
this.targetNode = markedEdge.getTargetNode();
this.sourceNode = markedEdge.getSourceNode();
this.type = markedEdge.getType();
this.isDeleted = markedEdge.isDeleted();
this.edgeVersion = markedEdge.getTimestamp();
this.directedEdgeMeta = directedEdgeMeta;
}
@Override
protected MultiTenantColumnFamily<ScopedRowKey<EdgeRowKey>, Long> getColumnFamily() {
return columnFamily;
}
@Override
public EdgeRowKey getRowKey( final Shard shard ) {
return new EdgeRowKey( sourceNode, type, targetNode, shard.getShardIndex() );
}
@Override
protected Long getDirectedEdge() {
return edgeVersion;
}
@Override
protected boolean isDeleted() {
return isDeleted;
}
@Override
protected DirectedEdgeMeta getDirectedEdgeMeta() {
return directedEdgeMeta;
}
}
private static final Function<Edge, MarkedEdge> TRANSFORM = new Function<Edge, MarkedEdge>() {
@Nullable
@Override
public MarkedEdge apply( @Nullable final Edge input ) {
if ( input == null ) {
return null;
}
if ( input instanceof MarkedEdge ) {
return ( MarkedEdge ) input;
}
return new SimpleMarkedEdge( input.getSourceNode(), input.getType(), input.getTargetNode(),
input.getTimestamp(), false );
}
};
}