You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@usergrid.apache.org by to...@apache.org on 2014/08/14 17:20:20 UTC
[1/6] git commit: Merge pull request #239 from usergrid/USERGRID-154
Repository: incubator-usergrid
Updated Branches:
refs/heads/USERGRID-188 395162b01 -> e9d652dd3 (forced update)
Merge pull request #239 from usergrid/USERGRID-154
Usergrid 154
Project: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/commit/71cb9c26
Tree: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/tree/71cb9c26
Diff: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/diff/71cb9c26
Branch: refs/heads/USERGRID-188
Commit: 71cb9c266a0038570bb40aec0908fc17d809a89e
Parents: 998dd71 2a98cae
Author: Scott Ganyo <sc...@ganyo.com>
Authored: Tue Jul 1 17:31:08 2014 -0700
Committer: Scott Ganyo <sc...@ganyo.com>
Committed: Tue Jul 1 17:31:08 2014 -0700
----------------------------------------------------------------------
.../impl/EntityCollectionManagerImpl.java | 2 +-
.../core/hystrix/HystrixCassandra.java | 132 +++++
.../core/hystrix/HystrixObservable.java | 92 ---
.../persistence/core/rx/ObservableIterator.java | 15 +-
stack/corepersistence/graph/pom.xml | 8 +-
.../usergrid/persistence/graph/GraphFig.java | 38 +-
.../graph/exception/GraphRuntimeException.java | 52 ++
.../graph/guice/CommitLogEdgeSerialization.java | 34 --
.../persistence/graph/guice/GraphModule.java | 164 +-----
.../graph/impl/GraphManagerImpl.java | 230 ++++----
.../graph/impl/stage/EdgeDeleteRepairImpl.java | 27 +-
.../graph/impl/stage/EdgeMetaRepairImpl.java | 55 +-
.../graph/impl/stage/EdgeWriteCompact.java | 48 --
.../graph/impl/stage/EdgeWriteCompactImpl.java | 140 -----
.../impl/stage/NodeDeleteListenerImpl.java | 76 +--
.../impl/EdgeSerializationImpl.java | 34 +-
.../serialization/impl/MergedEdgeReader.java | 90 ---
.../impl/MergedEdgeReaderImpl.java | 501 ----------------
.../impl/NodeSerializationImpl.java | 5 +-
.../shard/EdgeShardCounterSerialization.java | 63 ---
.../impl/shard/NodeShardAllocation.java | 4 +-
.../impl/shard/NodeShardApproximation.java | 6 +
.../impl/shard/NodeShardCache.java | 10 -
.../serialization/impl/shard/count/Counter.java | 125 ++++
.../shard/count/NodeShardApproximationImpl.java | 207 +++++++
.../count/NodeShardCounterSerialization.java | 48 ++
.../NodeShardCounterSerializationImpl.java | 139 +++++
.../impl/shard/count/ShardKey.java | 102 ++++
.../impl/EdgeShardCounterSerializationImpl.java | 141 -----
.../shard/impl/NodeShardAllocationImpl.java | 137 +++--
.../shard/impl/NodeShardApproximationImpl.java | 158 ------
.../impl/shard/impl/NodeShardCacheImpl.java | 32 +-
.../shard/impl/SizebasedEdgeShardStrategy.java | 16 +-
.../shard/impl/TimebasedEdgeShardStrategy.java | 96 ----
.../persistence/graph/GraphManagerIT.java | 10 +-
.../graph/GraphManagerShardingIT.java | 189 +++++++
.../graph/StorageGraphManagerIT.java | 8 +-
.../graph/impl/EdgeDeleteListenerTest.java | 185 +-----
.../graph/impl/EdgeWriteListenerTest.java | 451 ---------------
.../graph/impl/NodeDeleteListenerTest.java | 6 +-
.../graph/impl/stage/EdgeDeleteRepairTest.java | 29 +-
.../graph/impl/stage/EdgeMetaRepairTest.java | 8 +-
.../CommitlogSerializationTest.java | 47 --
.../MergedEdgeReaderImplComparatorTest.java | 254 ---------
.../impl/MergedEdgeReaderTest.java | 507 -----------------
.../impl/shard/CountMinSketchTest.java | 249 --------
.../EdgeShardCounterSerializationTest.java | 148 -----
.../impl/shard/NodeShardAllocationTest.java | 59 +-
.../impl/shard/NodeShardApproximationTest.java | 142 -----
.../shard/count/NodeShardApproximationTest.java | 565 +++++++++++++++++++
.../NodeShardCounterSerializationTest.java | 126 +++++
stack/corepersistence/pom.xml | 2 +-
52 files changed, 2087 insertions(+), 3925 deletions(-)
----------------------------------------------------------------------
[5/6] Implemented New rolling shard algorithm. This should allow
eventual shard consistency between all clients without the need for an
external locking allocation system
Posted by to...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/DirectedEdgeMeta.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/DirectedEdgeMeta.java b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/DirectedEdgeMeta.java
new file mode 100644
index 0000000..89e46d9
--- /dev/null
+++ b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/DirectedEdgeMeta.java
@@ -0,0 +1,405 @@
+/*
+ *
+ * * 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;
+
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.usergrid.persistence.core.scope.ApplicationScope;
+import org.apache.usergrid.persistence.graph.MarkedEdge;
+import org.apache.usergrid.persistence.graph.SearchByEdgeType;
+import org.apache.usergrid.persistence.graph.SearchByIdType;
+import org.apache.usergrid.persistence.graph.impl.SimpleSearchByEdge;
+import org.apache.usergrid.persistence.graph.impl.SimpleSearchByEdgeType;
+import org.apache.usergrid.persistence.graph.impl.SimpleSearchByIdType;
+import org.apache.usergrid.persistence.model.entity.Id;
+
+
+/**
+ * A bean to define directed edge meta data. This is used to encapsulate the meta data around a source or target node,
+ * and the types used for grouping them.
+ */
+public abstract class DirectedEdgeMeta {
+
+
+ protected final NodeMeta[] nodes;
+ protected final String[] types;
+
+
+ private DirectedEdgeMeta( NodeMeta[] nodes, String[] types ) {
+ this.nodes = nodes;
+ this.types = types;
+ }
+
+
+ public NodeMeta[] getNodes() {
+ return nodes;
+ }
+
+
+ public String[] getTypes() {
+ return types;
+ }
+
+
+ /**
+ * Inner class to represent node meta dat
+ */
+ public static class NodeMeta {
+ private final Id id;
+ private final NodeType nodeType;
+
+
+ public NodeMeta( final Id id, final NodeType nodeType ) {
+ this.id = id;
+ this.nodeType = nodeType;
+ }
+
+
+ public Id getId() {
+ return id;
+ }
+
+
+ public NodeType getNodeType() {
+ return nodeType;
+ }
+ }
+
+
+ @Override
+ public boolean equals( final Object o ) {
+ if ( this == o ) {
+ return true;
+ }
+ if ( o == null || getClass() != o.getClass() ) {
+ return false;
+ }
+
+ final DirectedEdgeMeta that = ( DirectedEdgeMeta ) o;
+
+ if ( !Arrays.equals( nodes, that.nodes ) ) {
+ return false;
+ }
+ if ( !Arrays.equals( types, that.types ) ) {
+ return false;
+ }
+
+ return true;
+ }
+
+
+ @Override
+ public int hashCode() {
+ int result = Arrays.hashCode( nodes );
+ result = 31 * result + Arrays.hashCode( types );
+ return result;
+ }
+
+
+ /**
+ * Given the edge serialization, load all shard in the shard group
+ */
+ public abstract Iterator<MarkedEdge> loadEdges( final ShardedEdgeSerialization serialization,
+ final EdgeColumnFamilies edgeColumnFamilies,
+ final ApplicationScope scope, final ShardEntryGroup group,
+ final long maxValue );
+
+ /**
+ * Get the type of this directed edge
+ */
+ public abstract MetaType getType();
+
+
+ public enum MetaType {
+ SOURCE( 0 ),
+ SOURCETARGET( 1 ),
+ TARGET( 2 ),
+ TARGETSOURCE( 3 ),
+ VERSIONS( 4 );
+
+ private final int storageValue;
+
+
+ MetaType( final int storageValue ) {this.storageValue = storageValue;}
+
+
+ public int getStorageValue() {
+ return storageValue;
+ }
+
+
+ /**
+ * Get value from storageValue
+ */
+ public static MetaType fromStorage( final int ordinal ) {
+ return mappings.get( ordinal );
+ }
+
+
+ private static Map<Integer, MetaType> mappings = new HashMap<Integer, MetaType>();
+
+
+ static {
+
+ for ( MetaType meta : MetaType.values() ) {
+ mappings.put( meta.storageValue, meta );
+ }
+ }
+ }
+
+
+ /**
+ * Created directed edge meta data from source node
+ */
+ public static DirectedEdgeMeta fromSourceNode( final Id sourceId, final String edgeType ) {
+ return fromSourceNode(
+ new DirectedEdgeMeta.NodeMeta[] { new DirectedEdgeMeta.NodeMeta( sourceId, NodeType.SOURCE ) },
+ new String[] { edgeType } );
+ }
+
+
+ /**
+ * Return meta data from the source node by edge type
+ */
+ private static DirectedEdgeMeta fromSourceNode( final NodeMeta[] nodes, final String[] types ) {
+
+ return new DirectedEdgeMeta( nodes, types ) {
+
+ @Override
+ public Iterator<MarkedEdge> loadEdges( final ShardedEdgeSerialization serialization,
+ final EdgeColumnFamilies edgeColumnFamilies,
+ final ApplicationScope scope, final ShardEntryGroup group,
+ final long maxValue ) {
+
+ final Id sourceId = nodes[0].id;
+ final String edgeType = types[0];
+
+ final SearchByEdgeType search = new SimpleSearchByEdgeType( sourceId, edgeType, maxValue, null );
+
+ return serialization.getEdgesFromSource( edgeColumnFamilies, scope, search,
+ Collections.singleton( group ).iterator() );
+ }
+
+
+ @Override
+ public MetaType getType() {
+ return MetaType.SOURCE;
+ }
+ };
+ }
+
+
+ /**
+ * Return meta data that represents a source node with edge type and target type
+ */
+ public static DirectedEdgeMeta fromSourceNodeTargetType( final Id sourceId, final String edgeType,
+ final String targetType ) {
+ return fromSourceNodeTargetType(
+ new DirectedEdgeMeta.NodeMeta[] { new DirectedEdgeMeta.NodeMeta( sourceId, NodeType.SOURCE ) },
+ new String[] { edgeType, targetType } );
+ }
+
+
+ /**
+ * Return meta data that represents a source node with edge type and target type
+ */
+ private static DirectedEdgeMeta fromSourceNodeTargetType( NodeMeta[] nodes, String[] types ) {
+ return new DirectedEdgeMeta( nodes, types ) {
+
+ @Override
+ public Iterator<MarkedEdge> loadEdges( final ShardedEdgeSerialization serialization,
+ final EdgeColumnFamilies edgeColumnFamilies,
+ final ApplicationScope scope, final ShardEntryGroup group,
+ final long maxValue ) {
+
+ final Id sourceId = nodes[0].id;
+ final String edgeType = types[0];
+ final String targetType = types[1];
+
+ final SearchByIdType search =
+ new SimpleSearchByIdType( sourceId, edgeType, maxValue, targetType, null );
+
+ return serialization.getEdgesFromSourceByTargetType( edgeColumnFamilies, scope, search,
+ Collections.singleton( group ).iterator() );
+ }
+
+
+ @Override
+ public MetaType getType() {
+ return MetaType.SOURCETARGET;
+ }
+ };
+ }
+
+
+ public static DirectedEdgeMeta fromTargetNode( final Id targetId, final String edgeType ) {
+ return fromTargetNode(
+ new DirectedEdgeMeta.NodeMeta[] { new DirectedEdgeMeta.NodeMeta( targetId, NodeType.TARGET ) },
+ new String[] { edgeType } );
+ }
+
+
+ /**
+ * Return meta data that represents from a target node by edge type
+ */
+ private static DirectedEdgeMeta fromTargetNode( final NodeMeta[] nodes, final String[] types ) {
+ return new DirectedEdgeMeta( nodes, types ) {
+
+ @Override
+ public Iterator<MarkedEdge> loadEdges( final ShardedEdgeSerialization serialization,
+ final EdgeColumnFamilies edgeColumnFamilies,
+ final ApplicationScope scope, final ShardEntryGroup group,
+ final long maxValue ) {
+
+
+ final Id targetId = nodes[0].id;
+ final String edgeType = types[0];
+
+ final SearchByEdgeType search = new SimpleSearchByEdgeType( targetId, edgeType, maxValue, null );
+
+ return serialization.getEdgesToTarget( edgeColumnFamilies, scope, search,
+ Collections.singleton( group ).iterator() );
+ }
+
+
+ @Override
+ public MetaType getType() {
+ return MetaType.TARGET;
+ }
+ };
+ }
+
+
+ public static DirectedEdgeMeta fromTargetNodeSourceType( final Id targetId, final String edgeType,
+ final String sourceType ) {
+ return fromTargetNodeSourceType(
+ new DirectedEdgeMeta.NodeMeta[] { new DirectedEdgeMeta.NodeMeta( targetId, NodeType.TARGET ) },
+ new String[] { edgeType, sourceType } );
+ }
+
+
+ /**
+ * Return meta data that represents a target node and a source node type
+ */
+ private static DirectedEdgeMeta fromTargetNodeSourceType( final NodeMeta[] nodes, final String[] types ) {
+ return new DirectedEdgeMeta( nodes, types ) {
+
+ @Override
+ public Iterator<MarkedEdge> loadEdges( final ShardedEdgeSerialization serialization,
+ final EdgeColumnFamilies edgeColumnFamilies,
+ final ApplicationScope scope, final ShardEntryGroup group,
+ final long maxValue ) {
+
+ final Id targetId = nodes[0].id;
+ final String edgeType = types[0];
+ final String sourceType = types[1];
+
+
+ final SearchByIdType search =
+ new SimpleSearchByIdType( targetId, edgeType, maxValue, sourceType, null );
+
+ return serialization.getEdgesToTargetBySourceType( edgeColumnFamilies, scope, search,
+ Collections.singleton( group ).iterator() );
+ }
+
+
+ @Override
+ public MetaType getType() {
+ return MetaType.TARGETSOURCE;
+ }
+ };
+ }
+
+
+ /**
+ * Return meta data that represents an entire edge
+ */
+ public static DirectedEdgeMeta fromEdge( final Id sourceId, final Id targetId, final String edgeType ) {
+ return fromEdge( new DirectedEdgeMeta.NodeMeta[] {
+ new DirectedEdgeMeta.NodeMeta( sourceId, NodeType.SOURCE ),
+ new DirectedEdgeMeta.NodeMeta( targetId, NodeType.TARGET )
+ }, new String[] { edgeType } );
+ }
+
+
+ /**
+ * Return meta data that represents an entire edge
+ */
+ private static DirectedEdgeMeta fromEdge( final NodeMeta[] nodes, final String[] types ) {
+ return new DirectedEdgeMeta( nodes, types ) {
+
+ @Override
+ public Iterator<MarkedEdge> loadEdges( final ShardedEdgeSerialization serialization,
+ final EdgeColumnFamilies edgeColumnFamilies,
+ final ApplicationScope scope, final ShardEntryGroup group,
+ final long maxValue ) {
+
+ final Id sourceId = nodes[0].id;
+ final Id targetId = nodes[1].id;
+ final String edgeType = types[0];
+
+ final SimpleSearchByEdge search =
+ new SimpleSearchByEdge( sourceId, edgeType, targetId, maxValue, null );
+
+ return serialization.getEdgeVersions( edgeColumnFamilies, scope, search,
+ Collections.singleton( group ).iterator() );
+ }
+
+
+ @Override
+ public MetaType getType() {
+ return MetaType.VERSIONS;
+ }
+ };
+ }
+
+
+ /**
+ * Create a directed edge from the stored meta data
+ * @param metaType The meta type stored
+ * @param nodes The metadata of the nodes
+ * @param types The types in the meta data
+ *
+ *
+ */
+ public static DirectedEdgeMeta fromStorage( final MetaType metaType, final NodeMeta[] nodes, final String[] types ) {
+ switch ( metaType ) {
+ case SOURCE:
+ return fromSourceNode( nodes, types );
+ case SOURCETARGET:
+ return fromSourceNodeTargetType( nodes, types );
+ case TARGET:
+ return fromTargetNode( nodes, types );
+ case TARGETSOURCE:
+ return fromTargetNodeSourceType( nodes, types );
+ case VERSIONS:
+ return fromEdge(nodes, types);
+ default:
+ throw new UnsupportedOperationException( "No supported meta type found" );
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/EdgeColumnFamilies.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/EdgeColumnFamilies.java b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/EdgeColumnFamilies.java
new file mode 100644
index 0000000..6f6c72d
--- /dev/null
+++ b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/EdgeColumnFamilies.java
@@ -0,0 +1,60 @@
+/*
+ *
+ * * 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;
+
+
+import org.apache.usergrid.persistence.core.astyanax.MultiTennantColumnFamily;
+import org.apache.usergrid.persistence.core.migration.Migration;
+import org.apache.usergrid.persistence.core.scope.ApplicationScope;
+
+
+/**
+ * Implementation for using multiple column families
+ */
+public interface EdgeColumnFamilies extends Migration{
+
+ /**
+ * Get the name of the column family for getting source nodes
+ */
+ public MultiTennantColumnFamily<ApplicationScope, RowKey, DirectedEdge> getSourceNodeCfName();
+
+ /**
+ * Get the name of the column family for getting target nodes
+ */
+ public MultiTennantColumnFamily<ApplicationScope, RowKey, DirectedEdge> getTargetNodeCfName();
+
+
+ /**
+ * Get the name of the column family for getting source nodes with a target type
+ */
+ public MultiTennantColumnFamily<ApplicationScope, RowKeyType, DirectedEdge> getSourceNodeTargetTypeCfName();
+
+ /**
+ * Get the name of the column family for getting target nodes with a source type
+ */
+ public MultiTennantColumnFamily<ApplicationScope, RowKeyType, DirectedEdge> getTargetNodeSourceTypeCfName();
+
+ /**
+ * Get the Graph edge versions cf
+ * @return
+ */
+ public MultiTennantColumnFamily<ApplicationScope, EdgeRowKey, Long> getGraphEdgeVersions();
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/EdgeRowKey.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/EdgeRowKey.java b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/EdgeRowKey.java
new file mode 100644
index 0000000..d7982d6
--- /dev/null
+++ b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/EdgeRowKey.java
@@ -0,0 +1,44 @@
+/*
+ *
+ * * 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;
+
+
+import org.apache.usergrid.persistence.model.entity.Id;
+
+
+/**
+ * Used to store row keys by sourceId, targetId and edgeType
+ */
+public class EdgeRowKey {
+ public final Id sourceId;
+ public final Id targetId;
+ public final String edgeType;
+ public final long shardId;
+
+
+ public EdgeRowKey( final Id sourceId, final String edgeType, final Id targetId, final long shardId ) {
+ this.sourceId = sourceId;
+ this.targetId = targetId;
+ this.edgeType = edgeType;
+ this.shardId = shardId;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/EdgeShardSerialization.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/EdgeShardSerialization.java b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/EdgeShardSerialization.java
index d49cfdf..81dcb39 100644
--- a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/EdgeShardSerialization.java
+++ b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/EdgeShardSerialization.java
@@ -38,31 +38,28 @@ public interface EdgeShardSerialization extends Migration{
/**
* Write a new time shard for the meta data
* @param scope The scope to write
- * @param nodeId The id in the edge
- * @param shard The next time to write
- * @param types The types to write to. Can be edge type, or edgeType+id type
+ * @param shard The shard to write
+ * @param directedEdgeMeta The edge meta data to use
*/
- public MutationBatch writeEdgeMeta(ApplicationScope scope, Id nodeId, long shard, String... types);
+ public MutationBatch writeShardMeta( ApplicationScope scope, Shard shard, DirectedEdgeMeta directedEdgeMeta );
/**
* Get an iterator of all meta data and types. Returns a range from High to low
* @param scope The organization scope
- * @param nodeId The id of the node
* @param start The shard time to start seeking from. Values <= this value will be returned.
- * @param types The types to use
+ * @param directedEdgeMeta The edge meta data to use
* @return
*/
- public Iterator<Long> getEdgeMetaData(ApplicationScope scope, Id nodeId, Optional<Long> start, String... types);
+ public Iterator<Shard> getShardMetaData( ApplicationScope scope, Optional<Shard> start, DirectedEdgeMeta directedEdgeMeta);
/**
* Remove the shard from the edge meta data from the types.
- * @param scope
- * @param nodeId
- * @param shard
- * @param types
+ * @param scope The scope of the application
+ * @param shard The shard to remove
+ * @param directedEdgeMeta The edge meta data to use
* @return
*/
- public MutationBatch removeEdgeMeta(ApplicationScope scope, Id nodeId, long shard, String... types);
+ public MutationBatch removeShardMeta( ApplicationScope scope, Shard shard, DirectedEdgeMeta directedEdgeMeta );
}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/EdgeShardStrategy.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/EdgeShardStrategy.java b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/EdgeShardStrategy.java
index 09436ac..10f3794 100644
--- a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/EdgeShardStrategy.java
+++ b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/EdgeShardStrategy.java
@@ -21,7 +21,6 @@ package org.apache.usergrid.persistence.graph.serialization.impl.shard;
import java.util.Iterator;
-import java.util.UUID;
import org.apache.usergrid.persistence.core.scope.ApplicationScope;
import org.apache.usergrid.persistence.model.entity.Id;
@@ -32,62 +31,31 @@ public interface EdgeShardStrategy {
/**
* Get the shard key used for writing this shard. CUD operations should use this
*
- * @param scope The application's scope
- * @param rowKeyId The id being used in the row key
+ * @param scope The application's scope]
* @param timestamp The timestamp on the edge
- * @param types The types in the edge
*/
- public long getWriteShard(final ApplicationScope scope, final Id rowKeyId, final long timestamp, final String... types );
+ public ShardEntryGroup getWriteShards( final ApplicationScope scope, final long timestamp, final DirectedEdgeMeta directedEdgeMeta );
/**
* Get the iterator of all shards for this entity
*
* @param scope The application scope
- * @param rowKeyId The id used in the row key
* @param maxTimestamp The max timestamp to use
- * @param types the types in the edge
*/
- public Iterator<Long> getReadShards(final ApplicationScope scope,final Id rowKeyId, final long maxTimestamp,final String... types );
+ public Iterator<ShardEntryGroup> getReadShards(final ApplicationScope scope, final long maxTimestamp, final DirectedEdgeMeta directedEdgeMeta );
/**
* Increment our count meta data by the passed value. Can be a positive or a negative number.
* @param scope The scope in the application
- * @param rowKeyId The row key id
- * @param shardId The shard id to use
+ * @param shard The shard to use
* @param count The amount to increment or decrement
- * @param types The types
+ * @param directedEdgeMeta The edge meta data to use
* @return
*/
- public void increment(final ApplicationScope scope,final Id rowKeyId, long shardId, long count ,final String... types );
+ public void increment(final ApplicationScope scope, Shard shard, long count, final DirectedEdgeMeta directedEdgeMeta );
- /**
- * Get the name of the column family for getting source nodes
- */
- public String getSourceNodeCfName();
-
- /**
- * Get the name of the column family for getting target nodes
- */
- public String getTargetNodeCfName();
-
-
- /**
- * Get the name of the column family for getting source nodes with a target type
- */
- public String getSourceNodeTargetTypeCfName();
-
- /**
- * Get the name of the column family for getting target nodes with a source type
- */
- public String getTargetNodeSourceTypeCfName();
-
- /**
- * Get the Graph edge versions cf
- * @return
- */
- public String getGraphEdgeVersions();
}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/NodeShardAllocation.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/NodeShardAllocation.java b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/NodeShardAllocation.java
index 1097ced..75bdc74 100644
--- a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/NodeShardAllocation.java
+++ b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/NodeShardAllocation.java
@@ -36,27 +36,31 @@ public interface NodeShardAllocation {
/**
- * Get all shards for the given info. If none exist, a default shard should be allocated
+ * Get all shards for the given info. If none exist, a default shard should be allocated. The nodeId is the source node
*
* @param scope The application scope
- * @param nodeId
* @param maxShardId The max value to start seeking from. Values <= this will be returned if specified
- * @param edgeTypes
+ * @param directedEdgeMeta The directed edge metadata to use
* @return A list of all shards <= the current shard. This will always return 0l if no shards are allocated
*/
- public Iterator<Long> getShards( final ApplicationScope scope, final Id nodeId, Optional<Long> maxShardId,
- final String... edgeTypes );
+ public Iterator<ShardEntryGroup> getShards( final ApplicationScope scope, Optional<Shard> maxShardId, final DirectedEdgeMeta directedEdgeMeta );
/**
* Audit our highest shard for it's maximum capacity. If it has reached the max capacity <=, it will allocate a new shard
*
* @param scope The app scope
- * @param nodeId The node id
- * @param edgeType The edge types
+ * @param shardEntryGroup The shard operator to use
+ * @param directedEdgeMeta The directed edge metadata to use
* @return True if a new shard was allocated
*/
- public boolean auditMaxShard(final ApplicationScope scope, final Id nodeId, final String... edgeType);
+ public boolean auditShard(final ApplicationScope scope, final ShardEntryGroup shardEntryGroup, final DirectedEdgeMeta directedEdgeMeta);
+
+ /**
+ * Get the minimum time that a created shard should be considered "new", and be used for both new writes and reads
+ * @return
+ */
+ public long getMinTime();
}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/NodeShardApproximation.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/NodeShardApproximation.java b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/NodeShardApproximation.java
index 90503d4..dbe645f 100644
--- a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/NodeShardApproximation.java
+++ b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/NodeShardApproximation.java
@@ -34,24 +34,34 @@ public interface NodeShardApproximation {
* Increment the shard Id the specified amount
*
* @param scope The scope
- * @param nodeId The node id
- * @param shardId The shard id
- * @param count
- * @param edgeType The edge type
+ * @param shard The shard to use
+ * @param count The count to increment
+ * @param directedEdgeMeta The directed edge meta data to use
*/
- public void increment( final ApplicationScope scope, final Id nodeId, final long shardId, final long count,
- final String... edgeType );
+ public void increment( final ApplicationScope scope, final Shard shard,
+ final long count, final DirectedEdgeMeta directedEdgeMeta );
/**
* Get the approximation of the number of unique items
+ *
+ * @param scope The scope
+ * @param directedEdgeMeta The directed edge meta data to use
*/
- public long getCount( final ApplicationScope scope, final Id nodeId, final long shardId,
- final String... edgeType );
+ public long getCount( final ApplicationScope scope, final Shard shard, final DirectedEdgeMeta directedEdgeMeta );
/**
- * Flush the current counters in the Approximation
+ * Flush the current counters in the Approximation. Will return immediately after the flush. You can then use flushPending
+ * to check the state.
*/
- public void flush();
+ public void beginFlush();
+
+ /**
+ * Return true if there is data to be flushed
+ * @return
+ */
+ public boolean flushPending();
+
+
}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/NodeShardCache.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/NodeShardCache.java b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/NodeShardCache.java
index 667fdbf..58924af 100644
--- a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/NodeShardCache.java
+++ b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/NodeShardCache.java
@@ -20,7 +20,6 @@ package org.apache.usergrid.persistence.graph.serialization.impl.shard;
import java.util.Iterator;
-import java.util.UUID;
import org.apache.usergrid.persistence.core.scope.ApplicationScope;
import org.apache.usergrid.persistence.model.entity.Id;
@@ -35,21 +34,22 @@ public interface NodeShardCache {
/**
- * Get the time meta data for the given node
- * @param nodeId
+ * Get the shard for the given timestamp
+ * @param scope The scope for the application
* @param timestamp The time to select the slice for.
- * @param edgeType
+ * @param directedEdgeMeta The directed edge meta data
*/
- public long getSlice(final ApplicationScope scope, final Id nodeId, final long timestamp, final String... edgeType);
+ public ShardEntryGroup getWriteShardGroup( final ApplicationScope scope,
+ final long timestamp, final DirectedEdgeMeta directedEdgeMeta );
/**
- * Get an iterator of all versions <= the version
- * @param scope
- * @param nodeId
+ * Get an iterator of all versions <= the version for iterating shard entry sets. The iterator of groups will be ordered
+ * highest to lowest. I.E range scanning from Long.MAX_VALUE to 0
+ * @param scope The scope for the application
* @param maxTimestamp The highest timestamp
- * @param edgeType
+ * @param directedEdgeMeta The directed edge meta data
* @return
*/
- public Iterator<Long> getVersions(final ApplicationScope scope, final Id nodeId, final long maxTimestamp, final String... edgeType);
+ public Iterator<ShardEntryGroup> getReadShardGroup( final ApplicationScope scope, final long maxTimestamp, final DirectedEdgeMeta directedEdgeMeta );
}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/NodeType.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/NodeType.java b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/NodeType.java
new file mode 100644
index 0000000..62c2f11
--- /dev/null
+++ b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/NodeType.java
@@ -0,0 +1,66 @@
+/*
+ * 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;
+
+
+import java.util.HashMap;
+
+
+/**
+ * The node type of the source or target
+ */
+public enum NodeType {
+ SOURCE( 0 ),
+ TARGET( 1 );
+
+ private final int ordinal;
+
+
+ private NodeType( final int ordinal ) {this.ordinal = ordinal;}
+
+
+ public int getStorageValue() {
+ return ordinal;
+ }
+
+
+ /**
+ * Get the type from the storageValue value
+ * @param storageValue
+ * @return
+ */
+ public static NodeType get(final int storageValue){
+ return types.get( storageValue );
+ }
+
+
+ /**
+ * Internal map and initialization for fast access
+ */
+ private static final HashMap<Integer, NodeType> types;
+
+
+ static{
+ types = new HashMap<>();
+
+ for(NodeType type: NodeType.values()){
+ types.put( type.getStorageValue(), type );
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/RowKey.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/RowKey.java b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/RowKey.java
new file mode 100644
index 0000000..9895978
--- /dev/null
+++ b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/RowKey.java
@@ -0,0 +1,54 @@
+/*
+ *
+ * * 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;
+
+
+import org.apache.usergrid.persistence.graph.serialization.util.EdgeHasher;
+import org.apache.usergrid.persistence.model.entity.Id;
+
+
+/**
+ * Class that represents an edge row key
+ */
+public class RowKey {
+ public final Id nodeId;
+ public final long[] hash;
+ public final long shardId;
+
+
+ /**
+ * Create a row key with the node and the edgeType
+ */
+ public RowKey( Id nodeId, String edgeType, final long shardId ) {
+ this( nodeId, EdgeHasher.createEdgeHash( edgeType ), shardId );
+ }
+
+
+ /**
+ * Create a new row key with the hash, should only be used in deserialization or internal callers.
+ */
+ public RowKey( Id nodeId, long[] hash, final long shardId ) {
+ this.nodeId = nodeId;
+ this.hash = hash;
+ this.shardId = shardId;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/RowKeyType.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/RowKeyType.java b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/RowKeyType.java
new file mode 100644
index 0000000..5705eb3
--- /dev/null
+++ b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/RowKeyType.java
@@ -0,0 +1,60 @@
+/*
+ *
+ * * 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;
+
+
+import org.apache.usergrid.persistence.graph.serialization.util.EdgeHasher;
+import org.apache.usergrid.persistence.model.entity.Id;
+
+
+/**
+ * The row key with the additional type
+ */
+public class RowKeyType extends RowKey {
+
+ /**
+ * Create a row key with the node id in the row key, the edge type, and the type from the typeid
+ *
+ * @param nodeId The node id in the row key
+ * @param edgeType The type of the edge
+ * @param typeId The type of hte id
+ */
+ public RowKeyType( final Id nodeId, final String edgeType, final Id typeId, final long shardId ) {
+ this( nodeId, edgeType, typeId.getType(), shardId );
+ }
+
+
+ /**
+ * Create a row key with the node id in the row key, the edge type, adn the target type from the id
+ */
+ public RowKeyType( final Id nodeId, final String edgeType, final String targetType, final long shardId ) {
+ super( nodeId, EdgeHasher.createEdgeHash( edgeType, targetType ), shardId );
+ }
+
+
+ /**
+ * Internal use in de-serializing. Should only be used in this case or by internal callers
+ */
+ public RowKeyType( final Id nodeId, final long[] hash, final long shardId ) {
+ super( nodeId, hash, shardId );
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/Shard.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/Shard.java b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/Shard.java
new file mode 100644
index 0000000..38fe51c
--- /dev/null
+++ b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/Shard.java
@@ -0,0 +1,142 @@
+/*
+ * 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;
+
+
+public class Shard implements Comparable<Shard> {
+
+ private final long shardIndex;
+ private final long createdTime;
+ private final boolean compacted;
+
+
+ public Shard( final long shardIndex, final long createdTime, final boolean compacted ) {
+ this.shardIndex = shardIndex;
+ this.createdTime = createdTime;
+ this.compacted = compacted;
+ }
+
+
+ /**
+ * Get the long shard index
+ */
+ public long getShardIndex() {
+ return shardIndex;
+ }
+
+
+ /**
+ * Get the timestamp in epoch millis this shard was created
+ */
+ public long getCreatedTime() {
+ return createdTime;
+ }
+
+
+ /**
+ * Return true if this shard has been compacted
+ */
+ public boolean isCompacted() {
+ return compacted;
+ }
+
+
+ /**
+ * Compare the shards based on the timestamp first, then the created time second
+ */
+ @Override
+ public int compareTo( final Shard o ) {
+ if ( o == null ) {
+ return 1;
+ }
+
+ if ( shardIndex > o.shardIndex ) {
+ return 1;
+ }
+
+ else if ( shardIndex == o.shardIndex ) {
+ if ( createdTime > o.createdTime ) {
+ return 1;
+ }
+ else if ( createdTime < o.createdTime ) {
+ return -1;
+ }
+
+ else {
+
+ //kind of arbitrary compacted takes precedence
+ if ( compacted && !o.compacted ) {
+ return 1;
+ }
+
+ else if ( !compacted && o.compacted ){
+ return -1;
+ }
+
+
+ }
+ return 0;
+ }
+
+ return -1;
+ }
+
+
+ @Override
+ public boolean equals( final Object o ) {
+ if ( this == o ) {
+ return true;
+ }
+ if ( o == null || getClass() != o.getClass() ) {
+ return false;
+ }
+
+ final Shard shard = ( Shard ) o;
+
+ if ( compacted != shard.compacted ) {
+ return false;
+ }
+ if ( createdTime != shard.createdTime ) {
+ return false;
+ }
+ if ( shardIndex != shard.shardIndex ) {
+ return false;
+ }
+
+ return true;
+ }
+
+
+ @Override
+ public int hashCode() {
+ int result = ( int ) ( shardIndex ^ ( shardIndex >>> 32 ) );
+ result = 31 * result + ( int ) ( createdTime ^ ( createdTime >>> 32 ) );
+ result = 31 * result + ( compacted ? 1 : 0 );
+ return result;
+ }
+
+
+ @Override
+ public String toString() {
+ return "Shard{" +
+ "shardIndex=" + shardIndex +
+ ", createdTime=" + createdTime +
+ "} ";
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/ShardEntryGroup.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/ShardEntryGroup.java b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/ShardEntryGroup.java
new file mode 100644
index 0000000..6e82cbc
--- /dev/null
+++ b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/ShardEntryGroup.java
@@ -0,0 +1,270 @@
+/*
+ * 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;
+
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.TreeSet;
+
+import com.google.common.base.Preconditions;
+
+
+/**
+ * There are cases where we need to read or write to more than 1 shard. This object encapsulates a set of shards that
+ * should be written to and read from. All reads should combine the data sets from all shards in the group, and writes
+ * should be written to each shard. Once the shard can safely be compacted a background process should be triggered to
+ * remove additional shards and make seeks faster. This multiread/write should only occur during the time period of the
+ * delta (in milliseconds), after which the next read will asynchronously compact the shards into a single shard.
+ */
+public class ShardEntryGroup {
+
+
+ private List<Shard> shards;
+
+ private final long delta;
+
+ private long maxCreatedTime;
+
+ private Shard compactionTarget;
+
+
+ /**
+ * The max delta we accept in milliseconds for create time to be considered a member of this group
+ */
+ public ShardEntryGroup( final long delta ) {
+ Preconditions.checkArgument( delta > 0, "delta must be greater than 0" );
+ this.delta = delta;
+ this.shards = new ArrayList<>();
+ this.maxCreatedTime = 0;
+ }
+
+
+ /**
+ * Only add a shard if it is within the rules require to meet a group. The rules are outlined below.
+ *
+ * Case 1) First shard in the group, always added
+ *
+ * Case 2) Shard is unmerged, it should be included with it's peers since other nodes may not have it yet
+ *
+ * Case 3) The list contains only non compacted shards, and this is last and and merged. It is considered a lower
+ * bound
+ */
+ public boolean addShard( final Shard shard ) {
+
+ Preconditions.checkNotNull( "shard cannot be null", shard );
+
+ final int size = shards.size();
+
+ if ( size == 0 ) {
+ addShardInternal( shard );
+ return true;
+ }
+
+ final Shard minShard = shards.get( size - 1 );
+
+ Preconditions.checkArgument( minShard.compareTo( shard ) > 0, "shard must be less than the current max" );
+
+ //shard is not compacted, or it's predecessor isn't, we should include it in this group
+ if ( !minShard.isCompacted() ) {
+ addShardInternal( shard );
+ return true;
+ }
+
+
+ return false;
+ }
+
+
+ /**
+ * Add the shard and set the min created time
+ */
+ private void addShardInternal( final Shard shard ) {
+ shards.add( shard );
+
+ maxCreatedTime = Math.max( maxCreatedTime, shard.getCreatedTime() );
+
+ //we're changing our structure, unset the compaction target
+ compactionTarget = null;
+ }
+
+
+ /**
+ * Return the minum shard based on time indexes
+ */
+ public Shard getMinShard() {
+ final int size = shards.size();
+
+ if ( size < 1 ) {
+ return null;
+ }
+
+ return shards.get( size - 1 );
+ }
+
+
+ /**
+ * Get the entries that we should read from.
+ */
+ public Collection<Shard> getReadShards() {
+ return shards;
+ }
+
+
+ /**
+ * Get the entries, with the max shard time being first. We write to all shards until they're migrated
+ */
+ public Collection<Shard> getWriteShards( long currentTime ) {
+
+ /**
+ * The shards in this set can be combined, we should only write to the compaction target to avoid
+ * adding data to other shards
+ */
+ if ( !isTooSmallToCompact() && shouldCompact( currentTime ) ) {
+ return Collections.singleton( getCompactionTarget() );
+ }
+
+
+ return shards;
+ }
+
+
+ /**
+ * Return true if we have a pending compaction
+ */
+ public boolean isCompactionPending() {
+ return !isTooSmallToCompact();
+ }
+
+
+ /**
+ * Get the shard all compactions should write to. Null indicates we cannot find a shard that could be used as a
+ * compaction target. Note that this shard may not have surpassed the delta yet You should invoke "shouldCompact"
+ * first to ensure all criteria are met before initiating compaction
+ */
+ public Shard getCompactionTarget() {
+
+ if ( compactionTarget != null ) {
+ return compactionTarget;
+ }
+
+
+ //we have < 2 shards, we can't compact
+ if ( isTooSmallToCompact() ) {
+ return null;
+ }
+
+
+ final int lastIndex = shards.size() - 1;
+
+ final Shard last = shards.get( lastIndex );
+
+ //Our oldest isn't compacted. As a result we have no "bookend" to delimit this entry group. Therefore we
+ // can't compact
+ if ( !last.isCompacted() ) {
+ return null;
+ }
+
+ //Start seeking from the end of our group. The first shard we encounter that is not compacted is our
+ // compaction target
+ //NOTE: This does not mean we can compact, rather it's just an indication that we have a target set.
+ for ( int i = lastIndex - 1; i > -1; i-- ) {
+ final Shard compactionCandidate = shards.get( i );
+
+
+ if ( !compactionCandidate.isCompacted() ) {
+ compactionTarget = compactionCandidate;
+ break;
+ }
+ }
+
+ return compactionTarget;
+ }
+
+
+ /**
+ * Return the number of entries in this shard group
+ */
+ public int entrySize() {
+ return shards.size();
+ }
+
+
+ /**
+ * Return true if there are not enough elements in this entry group to consider compaction
+ */
+ private boolean isTooSmallToCompact() {
+ return shards.size() < 2;
+ }
+
+
+ /**
+ * Returns true if the newest created shard is path the currentTime - delta
+ *
+ * @param currentTime The current system time in milliseconds
+ *
+ * @return True if these shards can safely be combined into a single shard, false otherwise
+ */
+ public boolean shouldCompact( final long currentTime ) {
+
+ /**
+ * We don't have enough shards to compact, ignore
+ */
+ return getCompactionTarget() != null
+
+
+ /**
+ * If something was created within the delta time frame, not everyone may have seen it due to
+ * cache refresh, we can't compact yet.
+ */
+
+ && currentTime - delta > maxCreatedTime;
+ }
+
+
+ /**
+ * Return true if this shard can be deleted AFTER all of the data in it has been moved
+ */
+ public boolean canBeDeleted( final Shard shard ) {
+ //if we're a neighbor shard (n-1) or the target compaction shard, we can't be deleted
+ //we purposefully use shard index comparison over .equals here, since 2 shards might have the same index with
+ // different timestamps
+ // (unlikely but could happen)
+
+ final Shard compactionTarget = getCompactionTarget();
+
+
+ return !shard.isCompacted() && ( compactionTarget != null && compactionTarget.getShardIndex() != shard
+ .getShardIndex() );
+ }
+
+
+ /**
+ * Helper method to create a shard entry group with a single shard
+ */
+ public static ShardEntryGroup singletonGroup( final Shard shard, final long delta ) {
+ ShardEntryGroup group = new ShardEntryGroup( delta );
+ group.addShard( shard );
+
+ return group;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/ShardGroupCompaction.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/ShardGroupCompaction.java b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/ShardGroupCompaction.java
new file mode 100644
index 0000000..13c5596
--- /dev/null
+++ b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/ShardGroupCompaction.java
@@ -0,0 +1,43 @@
+/*
+ *
+ * * 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;
+
+
+import rx.Observable;
+
+
+/**
+ * Defines tasks for running compaction
+ *
+ *
+ */
+public interface ShardGroupCompaction {
+
+
+ /**
+ * Execute the compaction task. Will return the number of edges that have
+ * @param group The shard entry group to compact
+ * @return The number of edges that are now compacted into the target shard
+ */
+ public Observable<Integer> compact(ShardEntryGroup group);
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/ShardOperator.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/ShardOperator.java b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/ShardOperator.java
new file mode 100644
index 0000000..895c6d6
--- /dev/null
+++ b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/ShardOperator.java
@@ -0,0 +1,43 @@
+/*
+ *
+ * * 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;
+
+
+import java.util.Iterator;
+
+import org.apache.usergrid.persistence.core.scope.ApplicationScope;
+import org.apache.usergrid.persistence.graph.MarkedEdge;
+
+
+/**
+ *
+ * Strategy for performing shard operations based on their shard types
+ *
+ */
+public interface ShardOperator {
+
+ /**
+ * Get the edges for this operator. Search
+ * @return
+ */
+ public Iterator<MarkedEdge> getEdges(final ApplicationScope scope, final Shard shard, final long maxValue);
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/ShardedEdgeSerialization.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/ShardedEdgeSerialization.java b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/ShardedEdgeSerialization.java
new file mode 100644
index 0000000..d1ad18d
--- /dev/null
+++ b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/ShardedEdgeSerialization.java
@@ -0,0 +1,119 @@
+/*
+ * 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;
+
+
+import java.util.Iterator;
+import java.util.UUID;
+
+import org.apache.usergrid.persistence.core.scope.ApplicationScope;
+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 com.netflix.astyanax.MutationBatch;
+
+
+/**
+ * Performs serialization on the shards
+ */
+public interface ShardedEdgeSerialization {
+
+ /**
+ * EdgeWrite both the source--->Target edge and the target <----- source edge into the mutation
+ *
+ * @param columnFamilies The column families to use
+ * @param scope The org scope of the graph
+ * @param markedEdge The edge to write
+ * @param timestamp The timestamp to use
+ */
+ MutationBatch writeEdge( EdgeColumnFamilies columnFamilies, ApplicationScope scope, MarkedEdge markedEdge,
+ UUID timestamp );
+
+ /**
+ * EdgeWrite both the source -->target edge and the target<--- source edge into the mutation
+ *
+ * @param columnFamilies The column families to use
+ * @param scope The org scope of the graph
+ * @param markedEdge The edge to write
+ * @param timestamp The timestamp of the uuid
+ */
+ MutationBatch deleteEdge( EdgeColumnFamilies columnFamilies, ApplicationScope scope, MarkedEdge markedEdge,
+ UUID timestamp );
+
+
+ /**
+ * Search for all versions of this edge < the search version. Returns all versions
+ *
+ * @param columnFamilies The column families to use
+ * @param scope The application scope
+ * @param search The search criteria
+ * @param shards The shards to iterate when searching
+ */
+ Iterator<MarkedEdge> getEdgeVersions( EdgeColumnFamilies columnFamilies, ApplicationScope scope,
+ SearchByEdge search, Iterator<ShardEntryGroup> shards );
+
+ /**
+ * Get an iterator of all edges by edge type originating from source node
+ *
+ * @param columnFamilies The column families to use
+ * @param scope The application scope
+ * @param search The search criteria
+ * @param shards The shards to iterate when searching
+ */
+ Iterator<MarkedEdge> getEdgesFromSource( EdgeColumnFamilies columnFamilies, ApplicationScope scope,
+ SearchByEdgeType search, Iterator<ShardEntryGroup> shards );
+
+
+ /**
+ * Get an iterator of all edges by edge type originating from source node. Also filters by target node id type
+ *
+ * @param columnFamilies The column families to use
+ * @param scope The application scope
+ * @param search The search criteria
+ * @param shards The shards to iterate when searching
+ */
+ Iterator<MarkedEdge> getEdgesFromSourceByTargetType( EdgeColumnFamilies columnFamilies, ApplicationScope scope,
+ SearchByIdType search, Iterator<ShardEntryGroup> shards );
+
+ /**
+ * Get an iterator of all edges by edge type pointing to the target node. Returns all versions
+ *
+ * @param columnFamilies The column families to use
+ * @param scope The application scope
+ * @param search The search criteria
+ * @param shards The shards to iterate when searching
+ */
+ Iterator<MarkedEdge> getEdgesToTarget( EdgeColumnFamilies columnFamilies, ApplicationScope scope,
+ SearchByEdgeType search, Iterator<ShardEntryGroup> shards );
+
+
+ /**
+ * Get an iterator of all edges by edge type pointing to the target node. Also uses the source id type to limit the
+ * results
+ *
+ * @param columnFamilies The column families to use
+ * @param scope The application scope
+ * @param search The search criteria
+ * @param shards The shards to iterate when searching
+ */
+ Iterator<MarkedEdge> getEdgesToTargetBySourceType( EdgeColumnFamilies columnFamilies, ApplicationScope scope,
+ SearchByIdType search, Iterator<ShardEntryGroup> shards );
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/count/Counter.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/count/Counter.java b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/count/Counter.java
index 4318200..f5666a2 100644
--- a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/count/Counter.java
+++ b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/count/Counter.java
@@ -24,6 +24,8 @@ import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
+import com.google.common.base.Preconditions;
+
/**
* This class is synchronized for addition. It is meant to be used across multiple threads
@@ -35,7 +37,7 @@ public class Counter {
private final AtomicLong invokeCounter;
/**
- * Pointer to our "current" counter map. We flush this when time expires or we hit our count
+ * Pointer to our "current" counter map. We beginFlush this when time expires or we hit our count
*/
private final ConcurrentHashMap<ShardKey, AtomicLong> counts;
@@ -94,6 +96,10 @@ public class Counter {
* @param other
*/
public void merge(final Counter other){
+
+ Preconditions.checkNotNull(other, "other cannot be null");
+ Preconditions.checkNotNull( other.counts, "other.counts cannot be null" );
+
for(Map.Entry<ShardKey, AtomicLong> entry: other.counts.entrySet()){
add(entry.getKey(), entry.getValue().get());
}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/count/NodeShardApproximationImpl.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/count/NodeShardApproximationImpl.java b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/count/NodeShardApproximationImpl.java
index 7dc763f..b4d88d5 100644
--- a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/count/NodeShardApproximationImpl.java
+++ b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/count/NodeShardApproximationImpl.java
@@ -20,15 +20,23 @@
package org.apache.usergrid.persistence.graph.serialization.impl.shard.count;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import javax.inject.Inject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
import org.apache.usergrid.persistence.core.consistency.TimeService;
import org.apache.usergrid.persistence.core.hystrix.HystrixCassandra;
import org.apache.usergrid.persistence.core.scope.ApplicationScope;
import org.apache.usergrid.persistence.graph.GraphFig;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.DirectedEdgeMeta;
import org.apache.usergrid.persistence.graph.serialization.impl.shard.NodeShardApproximation;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.NodeType;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.Shard;
import org.apache.usergrid.persistence.model.entity.Id;
import com.netflix.astyanax.MutationBatch;
@@ -40,10 +48,12 @@ import rx.schedulers.Schedulers;
/**
* Implementation for doing edge approximation based on counters. Uses a guava loading cache to load values from
- * cassandra, and flush them on cache eviction.
+ * cassandra, and beginFlush them on cache eviction.
*/
public class NodeShardApproximationImpl implements NodeShardApproximation {
+ private static final Logger LOG = LoggerFactory.getLogger(NodeShardApproximationImpl.class);
+
/**
* Read write locks to ensure we atomically swap correctly
*/
@@ -63,7 +73,9 @@ public class NodeShardApproximationImpl implements NodeShardApproximation {
/**
* The counter that is currently in process of flushing to Cassandra. Can be null
*/
- private volatile Counter flushPending;
+ private final BlockingQueue<Counter> flushQueue;
+
+ private final FlushWorker worker;
/**
@@ -77,15 +89,22 @@ public class NodeShardApproximationImpl implements NodeShardApproximation {
this.nodeShardCounterSerialization = nodeShardCounterSerialization;
this.timeService = timeService;
this.currentCounter = new Counter();
+ this.flushQueue = new LinkedBlockingQueue<>( graphFig.getCounterFlushQueueSize() );
+
+ this.worker = new FlushWorker( this.flushQueue, nodeShardCounterSerialization );
+
+ Schedulers.newThread().createWorker().schedule( worker );
+
}
@Override
- public void increment( final ApplicationScope scope, final Id nodeId, final long shardId, final long count,
- final String... edgeType ) {
+ public void increment(
+ final ApplicationScope scope, final Shard shard,
+ final long count, final DirectedEdgeMeta directedEdgeMeta ) {
- final ShardKey key = new ShardKey( scope, nodeId, shardId, edgeType );
+ final ShardKey key = new ShardKey( scope, shard, directedEdgeMeta );
readLock.lock();
@@ -102,10 +121,9 @@ public class NodeShardApproximationImpl implements NodeShardApproximation {
@Override
- public long getCount( final ApplicationScope scope, final Id nodeId, final long shardId,
- final String... edgeType ) {
+ public long getCount( final ApplicationScope scope, final Shard shard, final DirectedEdgeMeta directedEdgeMeta ) {
- final ShardKey key = new ShardKey( scope, nodeId, shardId, edgeType );
+ final ShardKey key = new ShardKey( scope, shard, directedEdgeMeta );
readLock.lock();
@@ -115,9 +133,6 @@ public class NodeShardApproximationImpl implements NodeShardApproximation {
try {
count = currentCounter.get( key );
- if ( flushPending != null ) {
- count += flushPending.get( key );
- }
}
finally {
readLock.unlock();
@@ -130,78 +145,121 @@ public class NodeShardApproximationImpl implements NodeShardApproximation {
@Override
- public void flush() {
+ public void beginFlush() {
writeLockLock.lock();
try {
- flushPending = currentCounter;
- currentCounter = new Counter();
+
+ final boolean queued = flushQueue.offer( currentCounter );
+
+ /**
+ * We were able to q the beginFlush, swap it
+ */
+ if ( queued ) {
+ currentCounter = new Counter();
+ }
}
finally {
writeLockLock.unlock();
}
+ }
- //copy to the batch outside of the command for performance
- final MutationBatch batch = nodeShardCounterSerialization.flush( flushPending );
+ @Override
+ public boolean flushPending() {
+ return flushQueue.size() > 0 || worker.isFlushing();
+ }
- /**
- * Execute the command in hystrix to avoid slamming cassandra
- */
- new HystrixCommand( HystrixCassandra.ASYNC_GROUP ) {
- @Override
- protected Void run() throws Exception {
- /**
- * Execute the batch asynchronously
- */
- batch.execute();
+ /**
+ * Check if we need to beginFlush. If we do, perform the beginFlush
+ */
+ private void checkFlush() {
- return null;
- }
+ //there's no beginFlush pending and we're past the timeout or count
+ if ( currentCounter.getCreateTimestamp() + graphFig.getCounterFlushInterval() > timeService.getCurrentTime()
+ || currentCounter.getInvokeCount() >= graphFig.getCounterFlushCount() ) {
+ beginFlush();
+ }
+ }
- @Override
- protected Object getFallback() {
- //we've failed to mutate. Merge this count back into the current one
- currentCounter.merge( flushPending );
+ /**
+ * Worker that will take from the queue
+ */
+ private static class FlushWorker implements Action0 {
- return null;
- }
- }.execute();
+ private final BlockingQueue<Counter> counterQueue;
+ private final NodeShardCounterSerialization nodeShardCounterSerialization;
- writeLockLock.lock();
+ private volatile Counter rollUp;
- try {
- flushPending = null;
- }
- finally {
- writeLockLock.unlock();
+
+ private FlushWorker( final BlockingQueue<Counter> counterQueue,
+ final NodeShardCounterSerialization nodeShardCounterSerialization ) {
+ this.counterQueue = counterQueue;
+ this.nodeShardCounterSerialization = nodeShardCounterSerialization;
}
- }
- /**
- * Check if we need to flush. If we do, perform the flush
- */
- private void checkFlush() {
+ @Override
+ public void call() {
- //there's no flush pending and we're past the timeout or count
- if ( flushPending == null && (
- currentCounter.getCreateTimestamp() + graphFig.getCounterFlushInterval() > timeService.getCurrentTime()
- || currentCounter.getInvokeCount() >= graphFig.getCounterFlushCount() ) ) {
+ while ( true ) {
+ /**
+ * Block taking the first element. Once we take this, batch drain and roll up the rest
+ */
+
+ try {
+ rollUp = null;
+ rollUp = counterQueue.take();
+ }
+ catch ( InterruptedException e ) {
+ LOG.error( "Unable to read from counter queue", e );
+ throw new RuntimeException( "Unable to read from counter queue", e );
- /**
- * Fire the flush action asynchronously
- */
- Schedulers.immediate().createWorker().schedule( new Action0() {
- @Override
- public void call() {
- flush();
}
- } );
+
+
+
+
+ //copy to the batch outside of the command for performance
+ final MutationBatch batch = nodeShardCounterSerialization.flush( rollUp );
+
+ /**
+ * Execute the command in hystrix to avoid slamming cassandra
+ */
+ new HystrixCommand( HystrixCassandra.ASYNC_GROUP ) {
+
+ @Override
+ protected Void run() throws Exception {
+ batch.execute();
+
+ return null;
+ }
+
+
+ @Override
+ protected Object getFallback() {
+ //we've failed to mutate. Merge this count back into the current one
+ counterQueue.offer( rollUp );
+
+ return null;
+ }
+ }.execute();
+ }
+
+ }
+
+
+ /**
+ * Return true if we're in the process of flushing
+ * @return
+ */
+ public boolean isFlushing(){
+ return rollUp != null;
}
}
}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/count/NodeShardCounterSerialization.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/count/NodeShardCounterSerialization.java b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/count/NodeShardCounterSerialization.java
index 41eb525..4b05401 100644
--- a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/count/NodeShardCounterSerialization.java
+++ b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/count/NodeShardCounterSerialization.java
@@ -27,7 +27,7 @@ import com.netflix.astyanax.MutationBatch;
/**
* Serialization for flushing and reading counters
*/
-public interface NodeShardCounterSerialization extends Migration {
+public interface NodeShardCounterSerialization extends Migration {
/**
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/count/NodeShardCounterSerializationImpl.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/count/NodeShardCounterSerializationImpl.java b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/count/NodeShardCounterSerializationImpl.java
index da318bf..6b99e93 100644
--- a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/count/NodeShardCounterSerializationImpl.java
+++ b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/count/NodeShardCounterSerializationImpl.java
@@ -29,15 +29,20 @@ import org.apache.cassandra.db.marshal.CounterColumnType;
import org.apache.usergrid.persistence.core.astyanax.CassandraConfig;
import org.apache.usergrid.persistence.core.astyanax.ColumnTypes;
+import org.apache.usergrid.persistence.core.astyanax.CompositeFieldSerializer;
+import org.apache.usergrid.persistence.core.astyanax.IdRowCompositeSerializer;
import org.apache.usergrid.persistence.core.astyanax.MultiTennantColumnFamily;
import org.apache.usergrid.persistence.core.astyanax.MultiTennantColumnFamilyDefinition;
import org.apache.usergrid.persistence.core.astyanax.OrganizationScopedRowKeySerializer;
import org.apache.usergrid.persistence.core.astyanax.ScopedRowKey;
import org.apache.usergrid.persistence.core.scope.ApplicationScope;
+import org.apache.usergrid.persistence.core.scope.ApplicationScopeImpl;
import org.apache.usergrid.persistence.graph.GraphFig;
import org.apache.usergrid.persistence.graph.exception.GraphRuntimeException;
-import org.apache.usergrid.persistence.graph.serialization.impl.shard.impl.EdgeRowKey;
-import org.apache.usergrid.persistence.graph.serialization.impl.shard.impl.EdgeRowKeySerializer;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.DirectedEdgeMeta;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.Shard;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.impl.EdgeShardRowKeySerializer;
+import org.apache.usergrid.persistence.model.entity.Id;
import com.google.common.base.Preconditions;
import com.google.inject.Inject;
@@ -48,20 +53,23 @@ import com.netflix.astyanax.connectionpool.OperationResult;
import com.netflix.astyanax.connectionpool.exceptions.ConnectionException;
import com.netflix.astyanax.connectionpool.exceptions.NotFoundException;
import com.netflix.astyanax.model.Column;
-import com.netflix.astyanax.serializers.LongSerializer;
-
+import com.netflix.astyanax.model.CompositeBuilder;
+import com.netflix.astyanax.model.CompositeParser;
+import com.netflix.astyanax.serializers.BooleanSerializer;
@Singleton
public class NodeShardCounterSerializationImpl implements NodeShardCounterSerialization {
+ private static final ShardKeySerializer SHARD_KEY_SERIALIZER = new ShardKeySerializer();
+
/**
* Edge shards
*/
- private static final MultiTennantColumnFamily<ApplicationScope, EdgeRowKey, Long> EDGE_SHARD_COUNTS =
+ private static final MultiTennantColumnFamily<ApplicationScope, ShardKey, Boolean> EDGE_SHARD_COUNTS =
new MultiTennantColumnFamily<>( "Edge_Shard_Counts",
- new OrganizationScopedRowKeySerializer<>( new EdgeRowKeySerializer() ), LongSerializer.get() );
+ new OrganizationScopedRowKeySerializer<>( SHARD_KEY_SERIALIZER ), BooleanSerializer.get() );
protected final Keyspace keyspace;
@@ -92,12 +100,11 @@ public class NodeShardCounterSerializationImpl implements NodeShardCounterSerial
final ShardKey key = entry.getKey();
final long value = entry.getValue().get();
- final EdgeRowKey edgeRowKey = new EdgeRowKey( key.getNodeId(), key.getEdgeTypes() );
- final ScopedRowKey rowKey = ScopedRowKey.fromKey( key.getScope(), edgeRowKey );
+ final ScopedRowKey rowKey = ScopedRowKey.fromKey( key.scope, key );
- batch.withRow( EDGE_SHARD_COUNTS, rowKey ).incrementCounterColumn( key.getShardId(), value );
+ batch.withRow( EDGE_SHARD_COUNTS, rowKey ).incrementCounterColumn(true , value );
}
@@ -108,14 +115,12 @@ public class NodeShardCounterSerializationImpl implements NodeShardCounterSerial
@Override
public long getCount( final ShardKey key ) {
- final EdgeRowKey edgeRowKey = new EdgeRowKey( key.getNodeId(), key.getEdgeTypes() );
-
- final ScopedRowKey rowKey = ScopedRowKey.fromKey( key.getScope(), edgeRowKey );
+ final ScopedRowKey rowKey = ScopedRowKey.fromKey( key.scope, key );
try {
- OperationResult<Column<Long>> column =
- keyspace.prepareQuery( EDGE_SHARD_COUNTS ).getKey( rowKey ).getColumn( key.getShardId() ).execute();
+ OperationResult<Column<Boolean>> column =
+ keyspace.prepareQuery( EDGE_SHARD_COUNTS ).getKey( rowKey ).getColumn( true ).execute();
return column.getResult().getLongValue();
}
@@ -133,7 +138,50 @@ public class NodeShardCounterSerializationImpl implements NodeShardCounterSerial
public Collection<MultiTennantColumnFamilyDefinition> getColumnFamilies() {
return Collections.singleton(
new MultiTennantColumnFamilyDefinition( EDGE_SHARD_COUNTS, BytesType.class.getSimpleName(),
- ColumnTypes.LONG_TYPE_REVERSED, CounterColumnType.class.getSimpleName(),
- MultiTennantColumnFamilyDefinition.CacheOption.KEYS ) );
+ ColumnTypes.BOOLEAN, CounterColumnType.class.getSimpleName(),
+ MultiTennantColumnFamilyDefinition.CacheOption.ALL ) );
}
+
+
+
+ private static class ShardKeySerializer implements CompositeFieldSerializer<ShardKey> {
+
+
+ private static final IdRowCompositeSerializer ID_SER = IdRowCompositeSerializer.get();
+
+ private static final EdgeShardRowKeySerializer EDGE_SHARD_ROW_KEY_SERIALIZER = EdgeShardRowKeySerializer.INSTANCE;
+
+
+ @Override
+ public void toComposite( final CompositeBuilder builder, final ShardKey key ) {
+
+ ID_SER.toComposite( builder, key.scope.getApplication() );
+
+ EDGE_SHARD_ROW_KEY_SERIALIZER.toComposite( builder, key.directedEdgeMeta );
+
+ builder.addLong( key.shard.getShardIndex() );
+
+ builder.addLong( key.shard.getCreatedTime() );
+ }
+
+
+ @Override
+ public ShardKey fromComposite( final CompositeParser composite ) {
+
+ final Id applicationId = ID_SER.fromComposite( composite );
+
+ final ApplicationScope scope = new ApplicationScopeImpl( applicationId );
+
+ final DirectedEdgeMeta directedEdgeMeta = EDGE_SHARD_ROW_KEY_SERIALIZER.fromComposite( composite );
+
+ final long shardIndex = composite.readLong();
+
+ final long shardCreatedTime = composite.readLong();
+
+ return new ShardKey( scope, new Shard( shardIndex, shardCreatedTime, false ), directedEdgeMeta );
+ }
+
+
+ }
+
}
[6/6] git commit: Implemented New rolling shard algorithm. This
should allow eventual shard consistency between all clients without the need
for an external locking allocation system
Posted by to...@apache.org.
Implemented New rolling shard algorithm. This should allow eventual shard consistency between all clients without the need for an external locking allocation system
Project: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/commit/e9d652dd
Tree: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/tree/e9d652dd
Diff: http://git-wip-us.apache.org/repos/asf/incubator-usergrid/diff/e9d652dd
Branch: refs/heads/USERGRID-188
Commit: e9d652dd3ec0862e032145b97522d1a73f6f4f53
Parents: 71cb9c2
Author: Todd Nine <tn...@apigee.com>
Authored: Wed Jul 2 09:35:36 2014 -0600
Committer: Todd Nine <to...@apache.org>
Committed: Thu Aug 14 09:19:15 2014 -0600
----------------------------------------------------------------------
.../UniqueValueSerializationStrategyImpl.java | 2 +-
.../persistence/core/astyanax/ColumnTypes.java | 3 +
.../astyanax/MultiKeyColumnNameIterator.java | 217 ++++
.../core/astyanax/MultiTennantColumnFamily.java | 13 +-
.../persistence/core/rx/OrderedMerge.java | 3 +
.../core/scope/ApplicationScopeImpl.java | 3 +-
.../usergrid/persistence/graph/GraphFig.java | 30 +-
.../persistence/graph/guice/GraphModule.java | 37 +-
.../impl/stage/EdgeDeleteListenerImpl.java | 1 -
.../graph/serialization/EdgeSerialization.java | 2 +-
.../impl/EdgeSerializationImpl.java | 1019 +++---------------
.../impl/NodeSerializationImpl.java | 10 +-
.../serialization/impl/shard/DirectedEdge.java | 41 +
.../impl/shard/DirectedEdgeMeta.java | 405 +++++++
.../impl/shard/EdgeColumnFamilies.java | 60 ++
.../serialization/impl/shard/EdgeRowKey.java | 44 +
.../impl/shard/EdgeShardSerialization.java | 21 +-
.../impl/shard/EdgeShardStrategy.java | 44 +-
.../impl/shard/NodeShardAllocation.java | 20 +-
.../impl/shard/NodeShardApproximation.java | 30 +-
.../impl/shard/NodeShardCache.java | 20 +-
.../serialization/impl/shard/NodeType.java | 66 ++
.../graph/serialization/impl/shard/RowKey.java | 54 +
.../serialization/impl/shard/RowKeyType.java | 60 ++
.../graph/serialization/impl/shard/Shard.java | 142 +++
.../impl/shard/ShardEntryGroup.java | 270 +++++
.../impl/shard/ShardGroupCompaction.java | 43 +
.../serialization/impl/shard/ShardOperator.java | 43 +
.../impl/shard/ShardedEdgeSerialization.java | 119 ++
.../serialization/impl/shard/count/Counter.java | 8 +-
.../shard/count/NodeShardApproximationImpl.java | 174 ++-
.../count/NodeShardCounterSerialization.java | 2 +-
.../NodeShardCounterSerializationImpl.java | 80 +-
.../impl/shard/count/ShardKey.java | 51 +-
.../impl/shard/impl/EdgeRowKey.java | 46 -
.../impl/shard/impl/EdgeRowKeySerializer.java | 17 +-
.../impl/shard/impl/EdgeSearcher.java | 143 +++
.../impl/shard/impl/EdgeSerializer.java | 77 ++
.../shard/impl/EdgeShardRowKeySerializer.java | 103 ++
.../shard/impl/EdgeShardSerializationImpl.java | 93 +-
.../shard/impl/NodeShardAllocationImpl.java | 193 ++--
.../impl/shard/impl/NodeShardCacheImpl.java | 256 +++--
.../impl/shard/impl/RowSerializer.java | 64 ++
.../impl/shard/impl/RowTypeSerializer.java | 60 ++
.../shard/impl/ShardEntryGroupIterator.java | 100 ++
.../shard/impl/ShardGroupCompactionImpl.java | 75 ++
.../impl/shard/impl/ShardRowIterator.java | 134 +++
.../impl/ShardedEdgeSerializationImpl.java | 656 +++++++++++
.../shard/impl/SizebasedEdgeColumnFamilies.java | 150 +++
.../shard/impl/SizebasedEdgeShardStrategy.java | 52 +-
.../impl/shard/impl/SourceEdgeSearcher.java | 22 +
.../graph/ComittedGraphManagerIT.java | 138 ---
.../graph/CommittedGraphManagerIT.java | 138 +++
.../graph/GraphManagerShardingIT.java | 43 +-
.../impl/shard/EdgeShardSerializationTest.java | 108 +-
.../impl/shard/NodeShardAllocationTest.java | 461 ++++++--
.../impl/shard/NodeShardCacheTest.java | 228 ++--
.../impl/shard/ShardEntryGroupTest.java | 422 ++++++++
.../shard/count/NodeShardApproximationTest.java | 107 +-
.../NodeShardCounterSerializationTest.java | 9 +-
.../shard/impl/ShardEntryGroupIteratorTest.java | 232 ++++
stack/corepersistence/pom.xml | 2 +-
62 files changed, 5429 insertions(+), 1837 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/write/UniqueValueSerializationStrategyImpl.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/write/UniqueValueSerializationStrategyImpl.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/write/UniqueValueSerializationStrategyImpl.java
index 73c038e..b7e113e 100644
--- a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/write/UniqueValueSerializationStrategyImpl.java
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/write/UniqueValueSerializationStrategyImpl.java
@@ -56,7 +56,7 @@ public class UniqueValueSerializationStrategyImpl
private static final EntityVersionSerializer ENTITY_VERSION_SER = new EntityVersionSerializer();
- private static final MultiTennantColumnFamily<CollectionScope, Field, EntityVersion>
+ private static final MultiTennantColumnFamily<CollectionScope, Field, EntityVersion>
CF_UNIQUE_VALUES =
new MultiTennantColumnFamily<CollectionScope, Field, EntityVersion>( "Unique_Values",
ROW_KEY_SER,
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/common/src/main/java/org/apache/usergrid/persistence/core/astyanax/ColumnTypes.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/common/src/main/java/org/apache/usergrid/persistence/core/astyanax/ColumnTypes.java b/stack/corepersistence/common/src/main/java/org/apache/usergrid/persistence/core/astyanax/ColumnTypes.java
index b5e41ff..a055ca7 100644
--- a/stack/corepersistence/common/src/main/java/org/apache/usergrid/persistence/core/astyanax/ColumnTypes.java
+++ b/stack/corepersistence/common/src/main/java/org/apache/usergrid/persistence/core/astyanax/ColumnTypes.java
@@ -1,6 +1,7 @@
package org.apache.usergrid.persistence.core.astyanax;
+import org.apache.cassandra.db.marshal.BooleanType;
import org.apache.cassandra.db.marshal.DynamicCompositeType;
@@ -19,6 +20,8 @@ public class ColumnTypes {
public static final String UUID_TYPE_REVERSED = "UUIDType(reversed=true)";
+ public static final String BOOLEAN = "BooleanType";
+
/**
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/common/src/main/java/org/apache/usergrid/persistence/core/astyanax/MultiKeyColumnNameIterator.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/common/src/main/java/org/apache/usergrid/persistence/core/astyanax/MultiKeyColumnNameIterator.java b/stack/corepersistence/common/src/main/java/org/apache/usergrid/persistence/core/astyanax/MultiKeyColumnNameIterator.java
new file mode 100644
index 0000000..c5a8c95
--- /dev/null
+++ b/stack/corepersistence/common/src/main/java/org/apache/usergrid/persistence/core/astyanax/MultiKeyColumnNameIterator.java
@@ -0,0 +1,217 @@
+/*
+ *
+ * * 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.core.astyanax;
+
+
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.NoSuchElementException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Exchanger;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+import org.apache.usergrid.persistence.core.rx.OrderedMerge;
+
+import com.amazonaws.services.redshift.model.UnsupportedOptionException;
+import com.google.common.base.Preconditions;
+
+import rx.Observable;
+import rx.Subscriber;
+import rx.schedulers.Schedulers;
+
+
+/**
+ * Simple iterator that wraps a collection of ColumnNameIterators. We do this because we can't page with a
+ * multiRangeScan correctly for multiple round trips. As a result, we do this since only 1 iterator with minimum values
+ * could potentially feed the entire result set.
+ *
+ * Compares the parsed values and puts them in order. If more than one row key emits the same value the first value is
+ * selected, and ignored from subsequent iterators.
+ */
+public class MultiKeyColumnNameIterator<C, T> implements Iterable<T>, Iterator<T> {
+
+
+ private InnerIterator<T> iterator;
+
+
+ public MultiKeyColumnNameIterator( final Collection<ColumnNameIterator<C, T>> columnNameIterators,
+ final Comparator<T> comparator, final int bufferSize ) {
+
+
+ Observable<T>[] observables = new Observable[columnNameIterators.size()];
+
+ int i = 0;
+
+ for ( ColumnNameIterator<C, T> columnNameIterator : columnNameIterators ) {
+
+ observables[i] = Observable.from( columnNameIterator, Schedulers.io() );
+
+ i++;
+ }
+
+
+ //merge them into 1 observable, and remove duplicates from the stream
+ Observable<T> merged = OrderedMerge.orderedMerge( comparator, bufferSize, observables ).distinctUntilChanged();
+
+
+ iterator = new InnerIterator(bufferSize);
+
+ merged.subscribe( iterator );
+ }
+
+
+ @Override
+ public Iterator<T> iterator() {
+ return this;
+ }
+
+
+ @Override
+ public boolean hasNext() {
+ return iterator.hasNext();
+ }
+
+
+ @Override
+ public T next() {
+ return iterator.next();
+ }
+
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException( "You cannot remove elements from a merged iterator, it is read only" );
+ }
+
+
+ /**
+ * Internal iterator that will put next elements into a blocking queue until it reaches capacity. At this point it
+ * will block then emitting thread until more elements are taken. Assumed the Observable is run on a I/O thread,
+ * NOT the current thread.
+ */
+ private final class InnerIterator<T> extends Subscriber<T> implements Iterator<T> {
+
+ private CountDownLatch startLatch = new CountDownLatch( 1 );
+
+ private final LinkedBlockingQueue<T> queue;
+
+
+ private Throwable error;
+ private boolean done = false;
+
+ private T next;
+
+
+ private InnerIterator( int maxSize ) {
+ queue = new LinkedBlockingQueue<>( maxSize );
+ }
+
+
+ @Override
+ public boolean hasNext() {
+
+
+ //we're done
+ if ( next != null ) {
+ return true;
+ }
+
+
+ try {
+ startLatch.await();
+ }
+ catch ( InterruptedException e ) {
+ throw new RuntimeException( "Unable to wait for start of submission" );
+ }
+
+
+
+ //this is almost a busy wait, and is intentional, if we have nothing to poll, we want to get it as soon
+ //as it's available. We generally only hit this once
+ do {
+ next = queue.poll();
+ }
+ while ( next == null && !done );
+
+
+ return next != null;
+ }
+
+
+ @Override
+ public T next() {
+
+ if ( error != null ) {
+ throw new RuntimeException( "An error occurred when populating the iterator", error );
+ }
+
+ if ( !hasNext() ) {
+ throw new NoSuchElementException( "No more elements are present" );
+ }
+
+
+ T toReturn = next;
+ next = null;
+ return toReturn;
+ }
+
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOptionException( "Remove is unsupported" );
+ }
+
+
+ @Override
+ public void onCompleted() {
+ done = true;
+ startLatch.countDown();
+ }
+
+
+ @Override
+ public void onError( final Throwable e ) {
+ error = e;
+ done = true;
+ startLatch.countDown();
+ }
+
+
+ @Override
+ public void onNext( final T t ) {
+
+ //may block if we get full, that's expected behavior
+ try {
+ queue.put( t );
+ }
+ catch ( InterruptedException e ) {
+ throw new RuntimeException( "Unable to take from queue" );
+ }
+
+ startLatch.countDown();
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/common/src/main/java/org/apache/usergrid/persistence/core/astyanax/MultiTennantColumnFamily.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/common/src/main/java/org/apache/usergrid/persistence/core/astyanax/MultiTennantColumnFamily.java b/stack/corepersistence/common/src/main/java/org/apache/usergrid/persistence/core/astyanax/MultiTennantColumnFamily.java
index 19e5aae..6234184 100644
--- a/stack/corepersistence/common/src/main/java/org/apache/usergrid/persistence/core/astyanax/MultiTennantColumnFamily.java
+++ b/stack/corepersistence/common/src/main/java/org/apache/usergrid/persistence/core/astyanax/MultiTennantColumnFamily.java
@@ -33,20 +33,15 @@ import com.netflix.astyanax.model.ColumnFamily;
public class MultiTennantColumnFamily<S extends ApplicationScope, K, V>
extends ColumnFamily<ScopedRowKey<S, K>, V> {
- public MultiTennantColumnFamily(
- final String columnFamilyName,
- final Serializer<ScopedRowKey<S, K>> keySerializer,
- final Serializer<V> columnSerializer ) {
+ public MultiTennantColumnFamily( final String columnFamilyName, final Serializer<ScopedRowKey<S, K>> keySerializer,
+ final Serializer<V> columnSerializer ) {
super( columnFamilyName, keySerializer, columnSerializer );
}
- public MultiTennantColumnFamily(
- final String columnFamilyName,
- final Serializer<ScopedRowKey<S, K>> keySerializer,
- final Serializer<V> columnSerializer,
- final Serializer<?> defaultValueSerializer ) {
+ public MultiTennantColumnFamily( final String columnFamilyName, final Serializer<ScopedRowKey<S, K>> keySerializer,
+ final Serializer<V> columnSerializer, final Serializer<?> defaultValueSerializer ) {
super( columnFamilyName, keySerializer, columnSerializer, defaultValueSerializer );
}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/common/src/main/java/org/apache/usergrid/persistence/core/rx/OrderedMerge.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/common/src/main/java/org/apache/usergrid/persistence/core/rx/OrderedMerge.java b/stack/corepersistence/common/src/main/java/org/apache/usergrid/persistence/core/rx/OrderedMerge.java
index 5ba3fbb..4032176 100644
--- a/stack/corepersistence/common/src/main/java/org/apache/usergrid/persistence/core/rx/OrderedMerge.java
+++ b/stack/corepersistence/common/src/main/java/org/apache/usergrid/persistence/core/rx/OrderedMerge.java
@@ -181,6 +181,9 @@ public final class OrderedMerge<T> implements Observable.OnSubscribe<T> {
InnerObserver<T> maxObserver = null;
T max = null;
+ /**
+ * TODO T.N. change this to be an 0(1) for min and O(log n) to update after pop rather than O(n*inner)
+ */
for ( InnerObserver<T> inner : innerSubscribers ) {
//nothing to do, this inner
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/common/src/main/java/org/apache/usergrid/persistence/core/scope/ApplicationScopeImpl.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/common/src/main/java/org/apache/usergrid/persistence/core/scope/ApplicationScopeImpl.java b/stack/corepersistence/common/src/main/java/org/apache/usergrid/persistence/core/scope/ApplicationScopeImpl.java
index 692ba49..4e067c2 100644
--- a/stack/corepersistence/common/src/main/java/org/apache/usergrid/persistence/core/scope/ApplicationScopeImpl.java
+++ b/stack/corepersistence/common/src/main/java/org/apache/usergrid/persistence/core/scope/ApplicationScopeImpl.java
@@ -33,7 +33,8 @@ public class ApplicationScopeImpl implements ApplicationScope {
public ApplicationScopeImpl( final Id application ) {
- this.application = application;}
+ this.application = application;
+ }
@Override
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/GraphFig.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/GraphFig.java b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/GraphFig.java
index e0ce45c..45064c9 100644
--- a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/GraphFig.java
+++ b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/GraphFig.java
@@ -39,11 +39,26 @@ public interface GraphFig extends GuicyFig {
public static final String SHARD_CACHE_SIZE = "usergrid.graph.shard.cache.size";
+
+ /**
+ * Get the cache timeout. The local cache will exist for this amount of time max (in millis).
+ */
public static final String SHARD_CACHE_TIMEOUT = "usergrid.graph.shard.cache.timeout";
- public static final String COUNTER_WRITE_FLUSH_COUNT = "usergrid.graph.shard.counter.flush.count";
+ /**
+ * The minimum amount of time than can occur (in millis) between shard allocation. Must be at least 2x the cache timeout.
+ *
+ * Note that you should also pad this for node clock drift. A good value for this would be 2x the shard cache timeout + 30 seconds,
+ * assuming you have NTP and allow a max drift of 30 seconds
+ */
+ public static final String SHARD_MIN_DELTA = "usergrid.graph.shard.min.delta";
+
+
+ public static final String COUNTER_WRITE_FLUSH_COUNT = "usergrid.graph.shard.counter.beginFlush.count";
- public static final String COUNTER_WRITE_FLUSH_INTERVAL = "usergrid.graph.shard.counter.flush.interval";
+ public static final String COUNTER_WRITE_FLUSH_INTERVAL = "usergrid.graph.shard.counter.beginFlush.interval";
+
+ public static final String COUNTER_WRITE_FLUSH_QUEUE_SIZE = "usergrid.graph.shard.counter.queue.size";
@@ -69,11 +84,18 @@ public interface GraphFig extends GuicyFig {
@Key(SHARD_CACHE_TIMEOUT)
long getShardCacheTimeout();
+ @Default("60000")
+ @Key( SHARD_MIN_DELTA )
+ long getShardMinDelta();
+
+
@Default( "250000" )
@Key( SHARD_CACHE_SIZE )
long getShardCacheSize();
+
+
@Default( "10000" )
@Key( COUNTER_WRITE_FLUSH_COUNT )
long getCounterFlushCount();
@@ -82,5 +104,9 @@ public interface GraphFig extends GuicyFig {
@Default( "30000" )
@Key( COUNTER_WRITE_FLUSH_INTERVAL )
long getCounterFlushInterval();
+
+ @Default( "1000" )
+ @Key(COUNTER_WRITE_FLUSH_QUEUE_SIZE )
+ int getCounterFlushQueueSize();
}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/guice/GraphModule.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/guice/GraphModule.java b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/guice/GraphModule.java
index aca8d00..ca7c270 100644
--- a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/guice/GraphModule.java
+++ b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/guice/GraphModule.java
@@ -43,17 +43,21 @@ import org.apache.usergrid.persistence.graph.serialization.NodeSerialization;
import org.apache.usergrid.persistence.graph.serialization.impl.EdgeMetadataSerializationImpl;
import org.apache.usergrid.persistence.graph.serialization.impl.EdgeSerializationImpl;
import org.apache.usergrid.persistence.graph.serialization.impl.NodeSerializationImpl;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.EdgeColumnFamilies;
import org.apache.usergrid.persistence.graph.serialization.impl.shard.EdgeShardSerialization;
import org.apache.usergrid.persistence.graph.serialization.impl.shard.EdgeShardStrategy;
import org.apache.usergrid.persistence.graph.serialization.impl.shard.NodeShardAllocation;
import org.apache.usergrid.persistence.graph.serialization.impl.shard.NodeShardApproximation;
import org.apache.usergrid.persistence.graph.serialization.impl.shard.NodeShardCache;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.ShardedEdgeSerialization;
import org.apache.usergrid.persistence.graph.serialization.impl.shard.count.NodeShardApproximationImpl;
import org.apache.usergrid.persistence.graph.serialization.impl.shard.count.NodeShardCounterSerialization;
import org.apache.usergrid.persistence.graph.serialization.impl.shard.count.NodeShardCounterSerializationImpl;
import org.apache.usergrid.persistence.graph.serialization.impl.shard.impl.EdgeShardSerializationImpl;
import org.apache.usergrid.persistence.graph.serialization.impl.shard.impl.NodeShardAllocationImpl;
import org.apache.usergrid.persistence.graph.serialization.impl.shard.impl.NodeShardCacheImpl;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.impl.ShardedEdgeSerializationImpl;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.impl.SizebasedEdgeColumnFamilies;
import org.apache.usergrid.persistence.graph.serialization.impl.shard.impl.SizebasedEdgeShardStrategy;
import com.google.inject.AbstractModule;
@@ -101,7 +105,6 @@ public class GraphModule extends AbstractModule {
bind( EdgeShardSerialization.class ).to( EdgeShardSerializationImpl.class );
-
//Repair/cleanup classes.
bind( EdgeMetaRepair.class ).to( EdgeMetaRepairImpl.class );
bind( EdgeDeleteRepair.class ).to( EdgeDeleteRepairImpl.class );
@@ -111,7 +114,9 @@ public class GraphModule extends AbstractModule {
* Add our listeners
*/
bind( NodeDeleteListener.class ).to( NodeDeleteListenerImpl.class );
- bind( EdgeDeleteListener.class).to( EdgeDeleteListenerImpl.class );
+ bind( EdgeDeleteListener.class ).to( EdgeDeleteListenerImpl.class );
+
+
/**
@@ -129,7 +134,7 @@ public class GraphModule extends AbstractModule {
migrationBinding.addBinding().to( Key.get( EdgeMetadataSerialization.class ) );
//bind each singleton to the multi set. Otherwise we won't migrate properly
- migrationBinding.addBinding().to( Key.get( EdgeSerialization.class, StorageEdgeSerialization.class ) );
+ migrationBinding.addBinding().to( Key.get( EdgeColumnFamilies.class, StorageEdgeSerialization.class ) );
migrationBinding.addBinding().to( Key.get( EdgeShardSerialization.class ) );
migrationBinding.addBinding().to( Key.get( NodeShardCounterSerialization.class ) );
@@ -143,22 +148,36 @@ public class GraphModule extends AbstractModule {
@Singleton
@Inject
@StorageEdgeSerialization
- public EdgeSerialization permanentStorageSerialization( final NodeShardCache cache, final Keyspace keyspace,
- final CassandraConfig cassandraConfig,
- final GraphFig graphFig,
- final NodeShardApproximation shardApproximation) {
+ public EdgeSerialization storageSerialization( final NodeShardCache cache, final Keyspace keyspace,
+ final CassandraConfig cassandraConfig, final GraphFig graphFig,
+ final NodeShardApproximation shardApproximation,
+ final TimeService timeService,
+ @StorageEdgeSerialization
+ final EdgeColumnFamilies edgeColumnFamilies ) {
final EdgeShardStrategy sizeBasedStrategy = new SizebasedEdgeShardStrategy( cache, shardApproximation );
+
+ final ShardedEdgeSerialization serialization = new ShardedEdgeSerializationImpl(keyspace, cassandraConfig, graphFig, sizeBasedStrategy,
+ timeService );
+
+
final EdgeSerializationImpl edgeSerialization =
- new EdgeSerializationImpl( keyspace, cassandraConfig, graphFig, sizeBasedStrategy );
+ new EdgeSerializationImpl( keyspace, cassandraConfig, graphFig, sizeBasedStrategy, edgeColumnFamilies,
+ serialization );
return edgeSerialization;
}
-
+ @Provides
+ @Singleton
+ @Inject
+ @StorageEdgeSerialization
+ public EdgeColumnFamilies storageSerializationColumnFamilies() {
+ return new SizebasedEdgeColumnFamilies();
+ }
}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/impl/stage/EdgeDeleteListenerImpl.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/impl/stage/EdgeDeleteListenerImpl.java b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/impl/stage/EdgeDeleteListenerImpl.java
index 55ab25b..6cfe6cc 100644
--- a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/impl/stage/EdgeDeleteListenerImpl.java
+++ b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/impl/stage/EdgeDeleteListenerImpl.java
@@ -48,7 +48,6 @@ public class EdgeDeleteListenerImpl implements EdgeDeleteListener {
@Inject
public EdgeDeleteListenerImpl(
final EdgeDeleteRepair edgeDeleteRepair, final EdgeMetaRepair edgeMetaRepair ) {
-
this.edgeDeleteRepair = edgeDeleteRepair;
this.edgeMetaRepair = edgeMetaRepair;
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/EdgeSerialization.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/EdgeSerialization.java b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/EdgeSerialization.java
index b80b7ab..0caffc3 100644
--- a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/EdgeSerialization.java
+++ b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/EdgeSerialization.java
@@ -36,7 +36,7 @@ import com.netflix.astyanax.MutationBatch;
/**
* Simple interface for serializing ONLY an edge
*/
-public interface EdgeSerialization extends Migration {
+public interface EdgeSerialization {
/**
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/EdgeSerializationImpl.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/EdgeSerializationImpl.java b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/EdgeSerializationImpl.java
index 10d1048..18facc0 100644
--- a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/EdgeSerializationImpl.java
+++ b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/EdgeSerializationImpl.java
@@ -20,333 +20,82 @@
package org.apache.usergrid.persistence.graph.serialization.impl;
-import java.nio.ByteBuffer;
-import java.util.Arrays;
-import java.util.Collection;
import java.util.Iterator;
-import java.util.NoSuchElementException;
import java.util.UUID;
import javax.inject.Inject;
-import org.apache.cassandra.db.marshal.BytesType;
-import org.apache.cassandra.db.marshal.DynamicCompositeType;
-
import org.apache.usergrid.persistence.core.astyanax.CassandraConfig;
-import org.apache.usergrid.persistence.core.astyanax.ColumnNameIterator;
-import org.apache.usergrid.persistence.core.astyanax.ColumnParser;
-import org.apache.usergrid.persistence.core.astyanax.ColumnTypes;
-import org.apache.usergrid.persistence.core.astyanax.CompositeFieldSerializer;
-import org.apache.usergrid.persistence.core.astyanax.IdColDynamicCompositeSerializer;
-import org.apache.usergrid.persistence.core.astyanax.IdRowCompositeSerializer;
-import org.apache.usergrid.persistence.core.astyanax.MultiTennantColumnFamily;
-import org.apache.usergrid.persistence.core.astyanax.MultiTennantColumnFamilyDefinition;
-import org.apache.usergrid.persistence.core.astyanax.OrganizationScopedRowKeySerializer;
-import org.apache.usergrid.persistence.core.astyanax.ScopedRowKey;
-import org.apache.usergrid.persistence.core.migration.Migration;
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.EdgeSerialization;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.DirectedEdgeMeta;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.EdgeColumnFamilies;
import org.apache.usergrid.persistence.graph.serialization.impl.shard.EdgeShardStrategy;
-import org.apache.usergrid.persistence.graph.serialization.util.EdgeHasher;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.Shard;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.ShardEntryGroup;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.ShardOperator;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.ShardedEdgeSerialization;
import org.apache.usergrid.persistence.graph.serialization.util.EdgeUtils;
import org.apache.usergrid.persistence.model.entity.Id;
-import com.google.common.base.Optional;
-import com.google.common.base.Preconditions;
import com.google.inject.Singleton;
import com.netflix.astyanax.Keyspace;
import com.netflix.astyanax.MutationBatch;
-import com.netflix.astyanax.Serializer;
-import com.netflix.astyanax.model.AbstractComposite;
-import com.netflix.astyanax.model.Column;
-import com.netflix.astyanax.model.CompositeBuilder;
-import com.netflix.astyanax.model.CompositeParser;
-import com.netflix.astyanax.model.DynamicComposite;
-import com.netflix.astyanax.query.RowQuery;
-import com.netflix.astyanax.serializers.AbstractSerializer;
-import com.netflix.astyanax.serializers.LongSerializer;
-import com.netflix.astyanax.serializers.StringSerializer;
-import com.netflix.astyanax.serializers.UUIDSerializer;
-import com.netflix.astyanax.util.RangeBuilder;
import static com.google.common.base.Preconditions.checkNotNull;
-import static org.apache.usergrid.persistence.core.astyanax.ColumnTypes.LONG_TYPE_REVERSED;
-import static org.apache.usergrid.persistence.core.astyanax.ColumnTypes.UUID_TYPE_REVERSED;
/**
* Serialization for edges. Delegates partitioning to the sharding strategy.
*/
@Singleton
-public class EdgeSerializationImpl implements EdgeSerialization, Migration {
-
-
- //Row key with no type
- private static final RowSerializer ROW_SERIALIZER = new RowSerializer();
-
- //row key with target id type
- private static final RowTypeSerializer ROW_TYPE_SERIALIZER = new RowTypeSerializer();
-
- private static final EdgeRowKeySerializer EDGE_ROW_KEY_SERIALIZER = new EdgeRowKeySerializer();
-
- //Edge serializers
- private static final EdgeSerializer EDGE_SERIALIZER = new EdgeSerializer();
-
- private static final LongSerializer LONG_SERIALIZER = LongSerializer.get();
-
-
- /**
- * Constant for the dynamic composite comparator type we'll need
- */
- public static final String EDGE_DYNAMIC_COMPOSITE_TYPE =
- //we purposefully associate lower case "l" and "u" with reversed types. This way we can use
- //the default serialization in Astayanax, but get reverse order in cassandra
- DynamicCompositeType.class.getSimpleName() + "(s=>UTF8Type,l=>" + LONG_TYPE_REVERSED + ",u=>"
- + UUID_TYPE_REVERSED + ")";
-
+public class EdgeSerializationImpl implements EdgeSerialization {
- /**
- * Get all graph edge versions
- */
- private final MultiTennantColumnFamily<ApplicationScope, EdgeRowKey, Long> graphEdgeVersionsCf;
-
-
- // column families
- /**
- * Edges that are from the source node. The row key is the source node
- */
- private final MultiTennantColumnFamily<ApplicationScope, RowKey, DirectedEdge> sourceNodeEdgesCf;
-
-
- /**
- * Edges that are incoming to the target node. The target node is the row key
- */
- private final MultiTennantColumnFamily<ApplicationScope, RowKey, DirectedEdge> targetNodeEdgesCf;
-
- /**
- * The edges that are from the source node with target type. The source node is the row key.
- */
- private final MultiTennantColumnFamily<ApplicationScope, RowKeyType, DirectedEdge> sourceNodeTargetTypeCf;
-
-
- /**
- * The edges that are to the target node with the source type. The target node is the row key
- */
- private final MultiTennantColumnFamily<ApplicationScope, RowKeyType, DirectedEdge> targetNodeSourceTypeCf;
protected final Keyspace keyspace;
protected final CassandraConfig cassandraConfig;
protected final GraphFig graphFig;
protected final EdgeShardStrategy edgeShardStrategy;
+ protected final EdgeColumnFamilies edgeColumnFamilies;
+ protected final ShardedEdgeSerialization shardedEdgeSerialization;
@Inject
public EdgeSerializationImpl( final Keyspace keyspace, final CassandraConfig cassandraConfig,
- final GraphFig graphFig, final EdgeShardStrategy edgeShardStrategy ) {
+ final GraphFig graphFig, final EdgeShardStrategy edgeShardStrategy,
+ final EdgeColumnFamilies edgeColumnFamilies,
+ final ShardedEdgeSerialization shardedEdgeSerialization ) {
+
checkNotNull( "keyspace required", keyspace );
checkNotNull( "cassandraConfig required", cassandraConfig );
checkNotNull( "consistencyFig required", graphFig );
- checkNotNull( "sourceNodeCfName required", edgeShardStrategy.getSourceNodeCfName() );
- checkNotNull( "targetNodeCfName required", edgeShardStrategy.getTargetNodeCfName() );
- checkNotNull( "sourceNodeTargetTypeCfName required", edgeShardStrategy.getSourceNodeTargetTypeCfName() );
- checkNotNull( "targetNodeSourceTypeCfName required", edgeShardStrategy.getTargetNodeSourceTypeCfName() );
this.keyspace = keyspace;
this.cassandraConfig = cassandraConfig;
this.graphFig = graphFig;
this.edgeShardStrategy = edgeShardStrategy;
-
- //initialize the CF's from our implementation
- sourceNodeEdgesCf = new MultiTennantColumnFamily<>( edgeShardStrategy.getSourceNodeCfName(),
- new OrganizationScopedRowKeySerializer<>( ROW_SERIALIZER ), EDGE_SERIALIZER );
-
-
- targetNodeEdgesCf = new MultiTennantColumnFamily<>( edgeShardStrategy.getTargetNodeCfName(),
- new OrganizationScopedRowKeySerializer<>( ROW_SERIALIZER ), EDGE_SERIALIZER );
-
-
- sourceNodeTargetTypeCf = new MultiTennantColumnFamily<>( edgeShardStrategy.getSourceNodeTargetTypeCfName(),
- new OrganizationScopedRowKeySerializer<>( ROW_TYPE_SERIALIZER ), EDGE_SERIALIZER );
-
-
- /**
- * The edges that are to the target node with the source type. The target node is the row key
- */
- targetNodeSourceTypeCf = new MultiTennantColumnFamily<>( edgeShardStrategy.getTargetNodeSourceTypeCfName(),
- new OrganizationScopedRowKeySerializer<>( ROW_TYPE_SERIALIZER ), EDGE_SERIALIZER );
-
- graphEdgeVersionsCf = new MultiTennantColumnFamily<>( edgeShardStrategy.getGraphEdgeVersions(),
- new OrganizationScopedRowKeySerializer<>( EDGE_ROW_KEY_SERIALIZER ), LONG_SERIALIZER );
+ this.edgeColumnFamilies = edgeColumnFamilies;
+ this.shardedEdgeSerialization = shardedEdgeSerialization;
}
@Override
public MutationBatch writeEdge( final ApplicationScope scope, final MarkedEdge markedEdge, final UUID timestamp ) {
- ValidationUtils.validateApplicationScope( scope );
- EdgeUtils.validateEdge( markedEdge );
- ValidationUtils.verifyTimeUuid( timestamp, "timestamp" );
-
-
- final MutationBatch batch = keyspace.prepareMutationBatch().withConsistencyLevel( cassandraConfig.getWriteCL() )
- .withTimestamp( timestamp.timestamp() );
-
- final boolean isDeleted = markedEdge.isDeleted();
-
-
- doWrite( scope, markedEdge, new RowOp<RowKey>() {
- @Override
- public void writeEdge( final MultiTennantColumnFamily<ApplicationScope, RowKey, DirectedEdge> columnFamily,
- final RowKey rowKey, final DirectedEdge edge ) {
- batch.withRow( columnFamily, ScopedRowKey.fromKey( scope, rowKey ) ).putColumn( edge, isDeleted );
- }
-
-
- @Override
- public void countEdge( final Id rowId, final long shardId, final String... types ) {
- if(!isDeleted){
- edgeShardStrategy.increment( scope, rowId, shardId, 1l, types );
- }
- }
-
-
- @Override
- public void writeVersion( final MultiTennantColumnFamily<ApplicationScope, EdgeRowKey, Long> columnFamily,
- final EdgeRowKey rowKey, final long timestamp ) {
- batch.withRow( columnFamily, ScopedRowKey.fromKey( scope, rowKey ) ).putColumn( timestamp, isDeleted );
- }
- } );
-
-
- return batch;
+ return shardedEdgeSerialization.writeEdge( edgeColumnFamilies, scope, markedEdge, timestamp );
}
@Override
public MutationBatch deleteEdge( final ApplicationScope scope, final MarkedEdge markedEdge, final UUID timestamp ) {
- ValidationUtils.validateApplicationScope( scope );
- EdgeUtils.validateEdge( markedEdge );
- ValidationUtils.verifyTimeUuid( timestamp, "timestamp" );
-
-
- final MutationBatch batch = keyspace.prepareMutationBatch().withConsistencyLevel( cassandraConfig.getWriteCL() )
- .withTimestamp( timestamp.timestamp() );
-
-
- doWrite( scope, markedEdge, new RowOp<RowKey>() {
- @Override
- public void writeEdge( final MultiTennantColumnFamily<ApplicationScope, RowKey, DirectedEdge> columnFamily,
- final RowKey rowKey, final DirectedEdge edge ) {
- batch.withRow( columnFamily, ScopedRowKey.fromKey( scope, rowKey ) ).deleteColumn( edge );
- }
-
-
- @Override
- public void countEdge( final Id rowId, final long shardId, final String... types ) {
- edgeShardStrategy.increment( scope, rowId, shardId, -1, types );
- }
-
-
- @Override
- public void writeVersion( final MultiTennantColumnFamily<ApplicationScope, EdgeRowKey, Long> columnFamily,
- final EdgeRowKey rowKey, final long timestamp ) {
- batch.withRow( columnFamily, ScopedRowKey.fromKey( scope, rowKey ) ).deleteColumn( timestamp );
- }
- } );
-
-
- return batch;
- }
-
-
- /**
- * EdgeWrite the edges internally
- *
- * @param scope The scope to encapsulate
- * @param edge The edge to write
- * @param op The row operation to invoke
- */
- private void doWrite( final ApplicationScope scope, final MarkedEdge edge, final RowOp op ) {
- ValidationUtils.validateApplicationScope( scope );
- EdgeUtils.validateEdge( edge );
-
- final Id sourceNodeId = edge.getSourceNode();
- final String souceNodeType = sourceNodeId.getType();
- final Id targetNodeId = edge.getTargetNode();
- final String targetNodeType = targetNodeId.getType();
- final long timestamp = edge.getTimestamp();
- final String type = edge.getType();
-
-
- /**
- * Key in the serializers based on the edge
- */
-
- final long sourceRowKeyShard = edgeShardStrategy.getWriteShard( scope, sourceNodeId, timestamp, type );
- final RowKey sourceRowKey = new RowKey( sourceNodeId, type, sourceRowKeyShard);
-
-
-
- final long sourceWithTypeRowKeyShard = edgeShardStrategy.getWriteShard( scope, sourceNodeId, timestamp, type, targetNodeType );
-
- final RowKeyType sourceRowKeyType = new RowKeyType( sourceNodeId, type, targetNodeId, sourceWithTypeRowKeyShard );
-
- final DirectedEdge sourceEdge = new DirectedEdge( targetNodeId, timestamp );
-
-
-
- final long targetRowKeyShard = edgeShardStrategy.getWriteShard( scope, targetNodeId, timestamp, type );
- final RowKey targetRowKey = new RowKey( targetNodeId, type, targetRowKeyShard);
-
- final long targetWithTypeRowKeyShard = edgeShardStrategy.getWriteShard( scope, targetNodeId, timestamp, type, souceNodeType );
- final RowKeyType targetRowKeyType = new RowKeyType( targetNodeId, type, sourceNodeId, targetWithTypeRowKeyShard );
-
- final DirectedEdge targetEdge = new DirectedEdge( sourceNodeId, timestamp );
-
-
- final EdgeRowKey edgeRowKey = new EdgeRowKey( sourceNodeId, type, targetNodeId, edgeShardStrategy
- .getWriteShard( scope, sourceNodeId, timestamp, type, targetNodeId.getUuid().toString(),
- targetNodeId.getType() ) );
-
-
- /**
- * write edges from source->target
- */
-
- op.writeEdge( sourceNodeEdgesCf, sourceRowKey, sourceEdge );
- op.countEdge( sourceNodeId, sourceRowKeyShard, type );
-
- op.writeEdge( sourceNodeTargetTypeCf, sourceRowKeyType, sourceEdge );
- op.countEdge( sourceNodeId, sourceWithTypeRowKeyShard, type, targetNodeType );
-
-
-
-
- /**
- * write edges from target<-source
- */
- op.writeEdge( targetNodeEdgesCf, targetRowKey, targetEdge );
- op.countEdge( targetNodeId, targetRowKeyShard, type );
-
- op.writeEdge( targetNodeSourceTypeCf, targetRowKeyType, targetEdge );
- op.countEdge( targetNodeId, targetWithTypeRowKeyShard, type, souceNodeType );
-
-
- /**
- * Write this in the timestamp log for this edge of source->target
- */
- op.writeVersion( graphEdgeVersionsCf, edgeRowKey, timestamp );
-
-
+ return shardedEdgeSerialization.deleteEdge( edgeColumnFamilies, scope, markedEdge, timestamp );
}
@@ -360,55 +109,42 @@ public class EdgeSerializationImpl implements EdgeSerialization, Migration {
final String type = search.getType();
final long maxTimestamp = search.getMaxTimestamp();
- final EdgeSearcher<EdgeRowKey, Long, MarkedEdge> searcher =
- new EdgeSearcher<EdgeRowKey, Long, MarkedEdge>( scope, maxTimestamp, search.last(),
- edgeShardStrategy.getReadShards( scope, sourceId, maxTimestamp, type ) ) {
-
- @Override
- protected Serializer<Long> getSerializer() {
- return LONG_SERIALIZER;
- }
-
-
- @Override
- public void setRange( final RangeBuilder builder ) {
-
-
- if ( last.isPresent() ) {
- super.setRange( builder );
- return;
- }
-
- //start seeking at a value < our max version
- builder.setStart( maxTimestamp );
- }
-
-
- @Override
- protected EdgeRowKey generateRowKey( long shard ) {
- return new EdgeRowKey( sourceId, type, targetId, shard );
- }
+ //Create our operator to perform seeks on the shard
+// final ShardOperator operator = new Edge(sourceId, NodeType.SOURCE, type) {
+//
+// @Override
+// public Iterator<MarkedEdge> getEdges( final ApplicationScope scope, final Shard shard, final long maxValue ) {
+//
+// final SearchByEdgeType search = new SimpleSearchByEdgeType( sourceId, type, maxTimestamp, null);
+//
+// return shardedEdgeSerialization.getEdgesFromSource( edgeColumnFamilies, scope, search,
+// createGroup( shard ) );
+// }
+// };
+//
+ final ShardOperator shardOperator = new ShardOperator() {
+ @Override
+ public Iterator<MarkedEdge> getEdges( final ApplicationScope scope, final Shard shard,
+ final long maxValue ) {
+ return null;
+ }
+ };
- @Override
- protected Long getStartColumn( final Edge last ) {
- return last.getTimestamp();
- }
+ final DirectedEdgeMeta versionMetaData = DirectedEdgeMeta.fromEdge( sourceId, targetId, type );
- @Override
- protected MarkedEdge createEdge( final Long column, final boolean marked ) {
- return new SimpleMarkedEdge( sourceId, type, targetId, column.longValue(), marked );
- }
- };
+ final Iterator<ShardEntryGroup> readShards =
+ edgeShardStrategy.getReadShards(scope, maxTimestamp, versionMetaData );
- return new ShardRowIterator<>( searcher, graphEdgeVersionsCf );
+ return shardedEdgeSerialization.getEdgeVersions( edgeColumnFamilies, scope, search, readShards );
}
@Override
public Iterator<MarkedEdge> getEdgesFromSource( final ApplicationScope scope, final SearchByEdgeType edgeType ) {
+
ValidationUtils.validateApplicationScope( scope );
EdgeUtils.validateSearchByEdgeType( edgeType );
@@ -416,36 +152,21 @@ public class EdgeSerializationImpl implements EdgeSerialization, Migration {
final String type = edgeType.getType();
final long maxTimestamp = edgeType.getMaxTimestamp();
- final EdgeSearcher<RowKey, DirectedEdge, MarkedEdge> searcher =
- new EdgeSearcher<RowKey, DirectedEdge, MarkedEdge>( scope, maxTimestamp, edgeType.last(),
- edgeShardStrategy.getReadShards( scope, sourceId, maxTimestamp, type ) ) {
-
-
- @Override
- protected Serializer<DirectedEdge> getSerializer() {
- return EDGE_SERIALIZER;
- }
-
-
- @Override
- protected RowKey generateRowKey( long shard ) {
- return new RowKey( sourceId, type, shard );
- }
-
+ final ShardOperator shardOperator = new ShardOperator() {
+ @Override
+ public Iterator<MarkedEdge> getEdges( final ApplicationScope scope, final Shard shard,
+ final long maxValue ) {
+ return null;
+ }
+ };
- @Override
- protected DirectedEdge getStartColumn( final Edge last ) {
- return new DirectedEdge( last.getTargetNode(), last.getTimestamp() );
- }
+ final DirectedEdgeMeta sourceMeta = DirectedEdgeMeta.fromSourceNode( sourceId, type );
- @Override
- protected MarkedEdge createEdge( final DirectedEdge edge, final boolean marked ) {
- return new SimpleMarkedEdge( sourceId, type, edge.id, edge.timestamp, marked );
- }
- };
+ final Iterator<ShardEntryGroup> readShards =
+ edgeShardStrategy.getReadShards(scope, maxTimestamp, sourceMeta );
- return new ShardRowIterator<>( searcher, sourceNodeEdgesCf );
+ return shardedEdgeSerialization.getEdgesFromSource( edgeColumnFamilies, scope, edgeType, readShards );
}
@@ -456,40 +177,42 @@ public class EdgeSerializationImpl implements EdgeSerialization, Migration {
ValidationUtils.validateApplicationScope( scope );
EdgeUtils.validateSearchByEdgeType( edgeType );
- final Id targetId = edgeType.getNode();
+ final Id sourceId = edgeType.getNode();
final String type = edgeType.getType();
final String targetType = edgeType.getIdType();
final long maxTimestamp = edgeType.getMaxTimestamp();
- final EdgeSearcher<RowKeyType, DirectedEdge, MarkedEdge> searcher =
- new EdgeSearcher<RowKeyType, DirectedEdge, MarkedEdge>( scope, maxTimestamp, edgeType.last(),
- edgeShardStrategy.getReadShards( scope, targetId, maxTimestamp, type, targetType ) ) {
-
- @Override
- protected Serializer<DirectedEdge> getSerializer() {
- return EDGE_SERIALIZER;
- }
+// //Create our operator to perform seeks on the shard
+// final ShardOperator operator = new EdgeByNodeTypeShardOperator(sourceId, NodeType.SOURCE, type, targetType) {
+//
+// @Override
+// public Iterator<MarkedEdge> getEdges( final ApplicationScope scope, final Shard shard, final long maxValue ) {
+//
+// final SearchByIdType search = new SimpleSearchByIdType( sourceId, type, maxTimestamp, targetType, null);
+//
+// return shardedEdgeSerialization.getEdgesFromSourceByTargetType( edgeColumnFamilies, scope, search, createGroup(shard));
+// }
+// };
+//
+//
+// final Iterator<ShardEntryGroup> readShards = edgeShardStrategy.getReadShards( scope, maxTimestamp, operator );
+ final ShardOperator shardOperator = new ShardOperator() {
+ @Override
+ public Iterator<MarkedEdge> getEdges( final ApplicationScope scope, final Shard shard,
+ final long maxValue ) {
+ return null;
+ }
+ };
- @Override
- protected RowKeyType generateRowKey( long shard ) {
- return new RowKeyType( targetId, type, targetType, shard );
- }
-
+ final DirectedEdgeMeta sourceMeta = DirectedEdgeMeta.fromSourceNodeTargetType( sourceId, type, targetType );
- @Override
- protected DirectedEdge getStartColumn( final Edge last ) {
- return new DirectedEdge( last.getTargetNode(), last.getTimestamp() );
- }
+ final Iterator<ShardEntryGroup> readShards =
+ edgeShardStrategy.getReadShards(scope, maxTimestamp, sourceMeta );
- @Override
- protected MarkedEdge createEdge( final DirectedEdge edge, final boolean marked ) {
- return new SimpleMarkedEdge( targetId, type, edge.id, edge.timestamp, marked );
- }
- };
- return new ShardRowIterator( searcher, sourceNodeTargetTypeCf );
+ return shardedEdgeSerialization.getEdgesFromSourceByTargetType( edgeColumnFamilies, scope, edgeType, readShards );
}
@@ -503,36 +226,39 @@ public class EdgeSerializationImpl implements EdgeSerialization, Migration {
final String type = edgeType.getType();
final long maxTimestamp = edgeType.getMaxTimestamp();
- final EdgeSearcher<RowKey, DirectedEdge, MarkedEdge> searcher =
- new EdgeSearcher<RowKey, DirectedEdge, MarkedEdge>( scope, maxTimestamp, edgeType.last(),
- edgeShardStrategy.getReadShards( scope, targetId, maxTimestamp, type ) ) {
-
- @Override
- protected Serializer<DirectedEdge> getSerializer() {
- return EDGE_SERIALIZER;
- }
+// //Create our operator to perform seeks on the shard
+// final ShardOperator operator = new EdgeByTypeShardOperator(targetId, NodeType.TARGET, type) {
+//
+// @Override
+// public Iterator<MarkedEdge> getEdges( final ApplicationScope scope, final Shard shard, final long maxValue ) {
+//
+// final SearchByEdgeType search = new SimpleSearchByEdgeType( targetId, type, maxTimestamp, null);
+//
+// return shardedEdgeSerialization.getEdgesToTarget( edgeColumnFamilies, scope, search,
+// createGroup( shard ) );
+// }
+// };
+//
+//
+// final Iterator<ShardEntryGroup> readShards = edgeShardStrategy.getReadShards( scope, maxTimestamp, operator);
- @Override
- protected RowKey generateRowKey( long shard ) {
- return new RowKey( targetId, type, shard );
- }
-
+ final ShardOperator shardOperator = new ShardOperator() {
+ @Override
+ public Iterator<MarkedEdge> getEdges( final ApplicationScope scope, final Shard shard,
+ final long maxValue ) {
+ return null;
+ }
+ };
- @Override
- protected DirectedEdge getStartColumn( final Edge last ) {
- return new DirectedEdge( last.getSourceNode(), last.getTimestamp() );
- }
+ final DirectedEdgeMeta sourceMeta = DirectedEdgeMeta.fromTargetNode( targetId, type );
- @Override
- protected MarkedEdge createEdge( final DirectedEdge edge, final boolean marked ) {
- return new SimpleMarkedEdge( edge.id, type, targetId, edge.timestamp, marked );
- }
- };
+ final Iterator<ShardEntryGroup> readShards =
+ edgeShardStrategy.getReadShards(scope, maxTimestamp, sourceMeta );
- return new ShardRowIterator<>( searcher, targetNodeEdgesCf );
+ return shardedEdgeSerialization.getEdgesToTarget( edgeColumnFamilies, scope, edgeType, readShards );
}
@@ -548,523 +274,38 @@ public class EdgeSerializationImpl implements EdgeSerialization, Migration {
final String type = edgeType.getType();
final long maxTimestamp = edgeType.getMaxTimestamp();
-
- final EdgeSearcher<RowKeyType, DirectedEdge, MarkedEdge> searcher =
- new EdgeSearcher<RowKeyType, DirectedEdge, MarkedEdge>( scope, maxTimestamp, edgeType.last(),
- edgeShardStrategy.getReadShards( scope, targetId, maxTimestamp, type, sourceType ) ) {
- @Override
- protected Serializer<DirectedEdge> getSerializer() {
- return EDGE_SERIALIZER;
- }
-
-
- @Override
- protected RowKeyType generateRowKey( final long shard ) {
- return new RowKeyType( targetId, type, sourceType, shard );
- }
-
-
- @Override
- protected DirectedEdge getStartColumn( final Edge last ) {
- return new DirectedEdge( last.getTargetNode(), last.getTimestamp() );
- }
-
-
- @Override
- protected MarkedEdge createEdge( final DirectedEdge edge, final boolean marked ) {
- return new SimpleMarkedEdge( edge.id, type, targetId, edge.timestamp, marked );
- }
- };
-
- return new ShardRowIterator<>( searcher, targetNodeSourceTypeCf );
- }
-
-
- @Override
- public Collection<MultiTennantColumnFamilyDefinition> getColumnFamilies() {
- return Arrays
- .asList( graphCf( sourceNodeEdgesCf ), graphCf( targetNodeEdgesCf ), graphCf( sourceNodeTargetTypeCf ),
- graphCf( targetNodeSourceTypeCf ),
- new MultiTennantColumnFamilyDefinition( graphEdgeVersionsCf, BytesType.class.getSimpleName(),
- ColumnTypes.LONG_TYPE_REVERSED, BytesType.class.getSimpleName(),
- MultiTennantColumnFamilyDefinition.CacheOption.KEYS ) );
- }
-
-
- /**
- * Helper to generate an edge definition by the type
- */
- private MultiTennantColumnFamilyDefinition graphCf( MultiTennantColumnFamily cf ) {
- return new MultiTennantColumnFamilyDefinition( cf, BytesType.class.getSimpleName(), EDGE_DYNAMIC_COMPOSITE_TYPE,
- BytesType.class.getSimpleName(), MultiTennantColumnFamilyDefinition.CacheOption.KEYS );
- }
-
-
- /**
- * Internal class to represent edge data for serialization
- */
- private static class DirectedEdge {
-
- public final long timestamp;
- public final Id id;
-
-
- private DirectedEdge( final Id id, final long timestamp ) {
- this.timestamp = timestamp;
- this.id = id;
- }
- }
-
-
- /**
- * Serializes to a source->target edge Note that we cannot set the edge type on de-serialization. Only the target
- * Id and version.
- */
- private static class EdgeSerializer extends AbstractSerializer<DirectedEdge> {
-
- private static final IdColDynamicCompositeSerializer ID_COL_SERIALIZER = IdColDynamicCompositeSerializer.get();
-// private static final UUIDSerializer UUID_SERIALIZER = UUIDSerializer.get();
-// private static final StringSerializer STRING_SERIALIZER = StringSerializer.get().getString(;
-// )
-
-
- @Override
- public ByteBuffer toByteBuffer( final DirectedEdge edge ) {
-
- DynamicComposite composite = new DynamicComposite();
-
-// //add our edge
-// composite.addComponent( edge.timestamp, LONG_SERIALIZER, LONG_TYPE_REVERSED,
-// AbstractComposite.ComponentEquality.EQUAL );
-
-// //we do this explicity instead of re-using the id composite serializer b/c we want high order
-// //time uuids first, not second. In this column family, there is no sort
-// composite.addComponent( edge.id.getUuid(), UUID_SERIALIZER, UUID_TYPE_REVERSED,
-// AbstractComposite.ComponentEquality.EQUAL );
+// //Create our operator to perform seeks on the shard
+// final ShardOperator operator = new EdgeByNodeTypeShardOperator(targetId, NodeType.TARGET, type, sourceType) {
//
-// composite.addComponent( edge.id.getType(), STRING_SERIALIZER );
-
- composite.addComponent( edge.timestamp, LONG_SERIALIZER );
-
- ID_COL_SERIALIZER.toComposite( composite, edge.id);
-
- return composite.serialize();
- }
-
-
- @Override
- public DirectedEdge fromByteBuffer( final ByteBuffer byteBuffer ) {
- DynamicComposite composite = DynamicComposite.fromByteBuffer( byteBuffer );
-
- Preconditions.checkArgument( composite.size() == 3, "Composite should have 3 elements" );
-
-
- //return the version
- final long timestamp = composite.get( 0, LONG_SERIALIZER );
-
-
- //parse our id
- final Id id = ID_COL_SERIALIZER.fromComposite( composite, 1 );
-
-
- return new DirectedEdge( id, timestamp );
- }
- }
-
-
- /**
- * Class that represents an edge row key
- */
- private static class RowKey {
- public final Id nodeId;
- public final long[] hash;
- public final long shard;
-
-
- /**
- * Create a row key with the node and the edgeType
- */
- public RowKey( Id nodeId, String edgeType, final long shard ) {
- this( nodeId, EdgeHasher.createEdgeHash( edgeType ), shard );
- }
-
-
- /**
- * Create a new row key with the hash, should only be used in deserialization or internal callers.
- */
- protected RowKey( Id nodeId, long[] hash, final long shard ) {
- this.nodeId = nodeId;
- this.hash = hash;
- this.shard = shard;
- }
- }
-
-
- /**
- * The row key with the additional type
- */
- private static class RowKeyType extends RowKey {
-
- /**
- * Create a row key with the node id in the row key, the edge type, and the type from the typeid
- *
- * @param nodeId The node id in the row key
- * @param edgeType The type of the edge
- * @param typeId The type of hte id
- */
- public RowKeyType( final Id nodeId, final String edgeType, final Id typeId, final long shard ) {
- this( nodeId, edgeType, typeId.getType(), shard );
- }
-
-
- /**
- * Create a row key with the node id in the row key, the edge type, adn the target type from the id
- */
- public RowKeyType( final Id nodeId, final String edgeType, final String targetType, final long shard ) {
- super( nodeId, EdgeHasher.createEdgeHash( edgeType, targetType ), shard );
- }
-
-
- /**
- * Internal use in de-serializing. Should only be used in this case or by internal callers
- */
- private RowKeyType( final Id nodeId, final long[] hash, final long shard ) {
- super( nodeId, hash, shard );
- }
- }
-
-
- /**
- * Used to store row keys by sourceId, targetId and edgeType
- */
- private static class EdgeRowKey {
- public final Id sourceId;
- public final Id targetId;
- public final String edgeType;
- public final long shardId;
-
-
- private EdgeRowKey( final Id sourceId, final String edgeType, final Id targetId, final long shardId ) {
- this.sourceId = sourceId;
- this.targetId = targetId;
- this.edgeType = edgeType;
- this.shardId = shardId;
- }
- }
-
-
- /**
- * Searcher to be used when performing the search. Performs I/O transformation as well as parsing for the iterator.
- * If there are more row keys available to seek, the iterator will return true
- *
- * @param <R> The row type
- * @param <C> The column type
- * @param <T> The parsed return type
- */
- private static abstract class EdgeSearcher<R, C, T>
- implements ColumnParser<C, T>, Iterator<ScopedRowKey<ApplicationScope, R>> {
-
- protected final Optional<Edge> last;
- protected final long maxTimestamp;
- protected final ApplicationScope scope;
- protected final Iterator<Long> shards;
-
-
- protected EdgeSearcher( final ApplicationScope scope, final long maxTimestamp, final Optional<Edge> last,
- final Iterator<Long> shards ) {
- this.scope = scope;
- this.maxTimestamp = maxTimestamp;
- this.last = last;
- this.shards = shards;
- }
-
-
- @Override
- public boolean hasNext() {
- return shards.hasNext();
- }
-
-
- @Override
- public ScopedRowKey<ApplicationScope, R> next() {
- return ScopedRowKey.fromKey( scope, generateRowKey( shards.next() ) );
- }
-
-
- @Override
- public void remove() {
- throw new UnsupportedOperationException( "Remove is unsupported" );
- }
-
-
- /**
- * Set the range on a search
- */
- public void setRange( final RangeBuilder builder ) {
-
- //set our start range since it was supplied to us
- if ( last.isPresent() ) {
- C sourceEdge = getStartColumn( last.get() );
-
-
- builder.setStart( sourceEdge, getSerializer() );
- }
- else {
-
-
+// @Override
+// public Iterator<MarkedEdge> getEdges( final ApplicationScope scope, final Shard shard, final long maxValue ) {
+//
+// final SearchByIdType search = new SimpleSearchByIdType( targetId, type, maxTimestamp, sourceType, null);
+//
+// return shardedEdgeSerialization.getEdgesFromSourceByTargetType( edgeColumnFamilies, scope, search, createGroup(shard));
+// }
+// };
+//
+//
+//
+// Iterator<ShardEntryGroup> readShards = edgeShardStrategy
+// .getReadShards( scope, maxTimestamp, operator );
+ final ShardOperator shardOperator = new ShardOperator() {
+ @Override
+ public Iterator<MarkedEdge> getEdges( final ApplicationScope scope, final Shard shard,
+ final long maxValue ) {
+ return null;
}
- }
-
-
- public boolean hasPage() {
- return last.isPresent();
- }
-
+ };
- @Override
- public T parseColumn( final Column<C> column ) {
- final C edge = column.getName();
+ final DirectedEdgeMeta sourceMeta = DirectedEdgeMeta.fromSourceNodeTargetType( targetId, type, sourceType );
- return createEdge( edge, column.getBooleanValue() );
- }
+ final Iterator<ShardEntryGroup> readShards =
+ edgeShardStrategy.getReadShards(scope, maxTimestamp, sourceMeta );
- /**
- * Get the column's serializer
- */
- protected abstract Serializer<C> getSerializer();
-
- /**
- * Create a row key for this search to use
- *
- * @param shard The shard to use in the row key
- */
- protected abstract R generateRowKey( final long shard );
-
-
- /**
- * Set the start column to begin searching from. The last is provided
- */
- protected abstract C getStartColumn( final Edge last );
-
-
- /**
- * Create an edge to return to the user based on the directed edge provided
- *
- * @param column The column name
- * @param marked The marked flag in the column value
- */
- protected abstract T createEdge( final C column, final boolean marked );
- }
-
-
- /**
- * Class to perform serialization for row keys from edges
- */
- private static class RowSerializer implements CompositeFieldSerializer<RowKey> {
-
- private static final IdRowCompositeSerializer ID_SER = IdRowCompositeSerializer.get();
-
-
- @Override
- public void toComposite( final CompositeBuilder builder, final RowKey key ) {
-
- //add the row id to the composite
- ID_SER.toComposite( builder, key.nodeId );
-
- builder.addLong( key.hash[0] );
- builder.addLong( key.hash[1] );
- builder.addLong( key.shard );
- }
-
-
- @Override
- public RowKey fromComposite( final CompositeParser composite ) {
-
- final Id id = ID_SER.fromComposite( composite );
- final long[] hash = new long[] { composite.readLong(), composite.readLong() };
- final long shard = composite.readLong();
-
-
- return new RowKey( id, hash, shard );
- }
- }
-
-
- /**
- * Class to perform serialization for row keys from edges
- */
- private static class RowTypeSerializer implements CompositeFieldSerializer<RowKeyType> {
-
- private static final IdRowCompositeSerializer ID_SER = IdRowCompositeSerializer.get();
-
-
- @Override
- public void toComposite( final CompositeBuilder builder, final RowKeyType keyType ) {
-
- //add the row id to the composite
- ID_SER.toComposite( builder, keyType.nodeId );
-
- builder.addLong( keyType.hash[0] );
- builder.addLong( keyType.hash[1] );
- builder.addLong( keyType.shard );
- }
-
-
- @Override
- public RowKeyType fromComposite( final CompositeParser composite ) {
-
- final Id id = ID_SER.fromComposite( composite );
- final long[] hash = new long[] { composite.readLong(), composite.readLong() };
- final long shard = composite.readLong();
-
- return new RowKeyType( id, hash, shard );
- }
- }
-
-
- /**
- * Class to perform serialization for row keys from edges
- */
- private static class EdgeRowKeySerializer implements CompositeFieldSerializer<EdgeRowKey> {
-
- private static final IdRowCompositeSerializer ID_SER = IdRowCompositeSerializer.get();
-
-
- @Override
- public void toComposite( final CompositeBuilder builder, final EdgeRowKey key ) {
-
- //add the row id to the composite
- ID_SER.toComposite( builder, key.sourceId );
- builder.addString( key.edgeType );
- ID_SER.toComposite( builder, key.targetId );
- builder.addLong( key.shardId );
- }
-
-
- @Override
- public EdgeRowKey fromComposite( final CompositeParser composite ) {
-
- final Id sourceId = ID_SER.fromComposite( composite );
- final String edgeType = composite.readString();
- final Id targetId = ID_SER.fromComposite( composite );
- final long shard = composite.readLong();
-
- return new EdgeRowKey( sourceId, edgeType, targetId, shard );
- }
- }
-
-
- /**
- * Simple callback to perform puts and deletes with a common row setup code
- *
- * @param <R> The row key type
- */
- private static interface RowOp<R> {
-
- /**
- * Write the edge with the given data
- */
- void writeEdge( final MultiTennantColumnFamily<ApplicationScope, R, DirectedEdge> columnFamily, R rowKey,
- DirectedEdge edge );
-
- /**
- * Perform the count on the edge
- */
- void countEdge( final Id rowId, long shardId, String... types );
-
- /**
- * Write the edge into the version cf
- */
- void writeVersion( final MultiTennantColumnFamily<ApplicationScope, EdgeRowKey, Long> columnFamily,
- EdgeRowKey rowKey, long timestamp );
+ return shardedEdgeSerialization.getEdgesToTargetBySourceType( edgeColumnFamilies, scope, edgeType, readShards );
}
-
- /**
- * Internal iterator to iterate over multiple row keys
- *
- * @param <R> The row type
- * @param <C> The column type
- * @param <T> The parsed return type
- */
- private class ShardRowIterator<R, C, T> implements Iterator<T> {
-
- private final EdgeSearcher<R, C, T> searcher;
-
- private final MultiTennantColumnFamily<ApplicationScope, R, C> cf;
-
- private Iterator<T> currentColumnIterator;
-
-
- private ShardRowIterator( final EdgeSearcher<R, C, T> searcher,
- final MultiTennantColumnFamily<ApplicationScope, R, C> cf ) {
- this.searcher = searcher;
- this.cf = cf;
- }
-
-
- @Override
- public boolean hasNext() {
- //we have more columns to return
- if ( currentColumnIterator != null && currentColumnIterator.hasNext() ) {
- return true;
- }
-
- /**
- * We have another row key, advance to it and re-check
- */
- if ( searcher.hasNext() ) {
- advanceRow();
- return hasNext();
- }
-
- //we have no more columns, and no more row keys, we're done
- return false;
- }
-
-
- @Override
- public T next() {
- if ( !hasNext() ) {
- throw new NoSuchElementException( "There are no more rows or columns left to advance" );
- }
-
- return currentColumnIterator.next();
- }
-
-
- @Override
- public void remove() {
- throw new UnsupportedOperationException( "Remove is unsupported" );
- }
-
-
- /**
- * Advance our iterator to the next row (assumes the check for row keys is elsewhere)
- */
- private void advanceRow() {
-
- /**
- * If the edge is present, we need to being seeking from this
- */
-
- final RangeBuilder rangeBuilder = new RangeBuilder().setLimit( graphFig.getScanPageSize() );
-
-
- //set the range into the search
- searcher.setRange( rangeBuilder );
-
- final ScopedRowKey<ApplicationScope, R> rowKey = searcher.next();
-
-
- RowQuery<ScopedRowKey<ApplicationScope, R>, C> query =
- keyspace.prepareQuery( cf ).setConsistencyLevel( cassandraConfig.getReadCL() ).getKey( rowKey )
- .autoPaginate( true ).withColumnRange( rangeBuilder.build() );
-
-
- currentColumnIterator = new ColumnNameIterator<C, T>( query, searcher, searcher.hasPage() );
- }
- }
}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/NodeSerializationImpl.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/NodeSerializationImpl.java b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/NodeSerializationImpl.java
index 5be988b..a5ca964 100644
--- a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/NodeSerializationImpl.java
+++ b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/NodeSerializationImpl.java
@@ -168,20 +168,20 @@ public class NodeSerializationImpl implements NodeSerialization, Migration {
@Override
- public Map<Id, Long> getMaxVersions( final ApplicationScope scope, final Collection<? extends Edge> nodeIds ) {
+ public Map<Id, Long> getMaxVersions( final ApplicationScope scope, final Collection<? extends Edge> edges ) {
ValidationUtils.validateApplicationScope( scope );
- Preconditions.checkNotNull( nodeIds, "nodeIds cannot be null" );
+ Preconditions.checkNotNull( edges, "edges cannot be null" );
final ColumnFamilyQuery<ScopedRowKey<ApplicationScope, Id>, Boolean> query = keyspace.prepareQuery( GRAPH_DELETE ).setConsistencyLevel( fig.getReadCL() );
- final List<ScopedRowKey<ApplicationScope, Id>> keys = new ArrayList<ScopedRowKey<ApplicationScope, Id>>(nodeIds.size());
+ final List<ScopedRowKey<ApplicationScope, Id>> keys = new ArrayList<ScopedRowKey<ApplicationScope, Id>>(edges.size());
//worst case all are marked
- final Map<Id, Long> versions = new HashMap<>(nodeIds.size());
+ final Map<Id, Long> versions = new HashMap<>(edges.size());
- for(final Edge edge: nodeIds){
+ for(final Edge edge: edges){
keys.add( ScopedRowKey.fromKey( scope, edge.getSourceNode() ) );
keys.add( ScopedRowKey.fromKey( scope, edge.getTargetNode() ) );
}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/DirectedEdge.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/DirectedEdge.java b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/DirectedEdge.java
new file mode 100644
index 0000000..720c948
--- /dev/null
+++ b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/DirectedEdge.java
@@ -0,0 +1,41 @@
+/*
+ *
+ * * 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;
+
+
+import org.apache.usergrid.persistence.model.entity.Id;
+
+
+/**
+ * Internal class to represent edge data for serialization
+ */
+public class DirectedEdge {
+
+ public final long timestamp;
+ public final Id id;
+
+
+ public DirectedEdge( final Id id, final long timestamp ) {
+ this.timestamp = timestamp;
+ this.id = id;
+ }
+}
[2/6] Implemented New rolling shard algorithm. This should allow
eventual shard consistency between all clients without the need for an
external locking allocation system
Posted by to...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/test/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/NodeShardAllocationTest.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/test/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/NodeShardAllocationTest.java b/stack/corepersistence/graph/src/test/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/NodeShardAllocationTest.java
index 5c846f1..4130771 100644
--- a/stack/corepersistence/graph/src/test/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/NodeShardAllocationTest.java
+++ b/stack/corepersistence/graph/src/test/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/NodeShardAllocationTest.java
@@ -20,19 +20,23 @@ package org.apache.usergrid.persistence.graph.serialization.impl.shard;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
-import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
+import org.apache.cassandra.thrift.Mutation;
+
import org.apache.usergrid.persistence.core.consistency.TimeService;
import org.apache.usergrid.persistence.core.scope.ApplicationScope;
import org.apache.usergrid.persistence.graph.GraphFig;
-import org.apache.usergrid.persistence.graph.serialization.impl.shard.count.NodeShardCounterSerialization;
-import org.apache.usergrid.persistence.graph.serialization.impl.shard.count.ShardKey;
+import org.apache.usergrid.persistence.graph.MarkedEdge;
+import org.apache.usergrid.persistence.graph.SearchByIdType;
+import org.apache.usergrid.persistence.graph.exception.GraphRuntimeException;
+import org.apache.usergrid.persistence.graph.impl.SimpleMarkedEdge;
import org.apache.usergrid.persistence.graph.serialization.impl.shard.impl.NodeShardAllocationImpl;
import org.apache.usergrid.persistence.model.entity.Id;
import org.apache.usergrid.persistence.model.util.UUIDGenerator;
@@ -40,11 +44,13 @@ import org.apache.usergrid.persistence.model.util.UUIDGenerator;
import com.google.common.base.Optional;
import com.netflix.astyanax.Keyspace;
import com.netflix.astyanax.MutationBatch;
+import com.netflix.astyanax.connectionpool.exceptions.ConnectionException;
import static junit.framework.TestCase.assertTrue;
import static org.apache.usergrid.persistence.graph.test.util.EdgeTestUtils.createId;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.same;
@@ -76,53 +82,100 @@ public class NodeShardAllocationTest {
when( graphFig.getShardCacheSize() ).thenReturn( 10000l );
when( graphFig.getShardSize() ).thenReturn( 20000l );
- when( graphFig.getShardCacheTimeout()).thenReturn( 30000l );
+
+ final long timeout = 30000;
+ when( graphFig.getShardCacheTimeout() ).thenReturn( timeout );
+ when( graphFig.getShardMinDelta() ).thenReturn( timeout * 2 );
}
@Test
- public void noShards() {
+ public void minTime() {
final EdgeShardSerialization edgeShardSerialization = mock( EdgeShardSerialization.class );
- final NodeShardApproximation nodeShardCounterSerialization =
- mock( NodeShardApproximation.class );
+ final EdgeColumnFamilies edgeColumnFamilies = mock( EdgeColumnFamilies.class );
+
+ final ShardedEdgeSerialization shardedEdgeSerialization = mock( ShardedEdgeSerialization.class );
+
+ final NodeShardApproximation nodeShardCounterSerialization = mock( NodeShardApproximation.class );
final TimeService timeService = mock( TimeService.class );
final Keyspace keyspace = mock( Keyspace.class );
- final MutationBatch batch = mock( MutationBatch.class );
-
- when( keyspace.prepareMutationBatch() ).thenReturn( batch );
NodeShardAllocation approximation =
- new NodeShardAllocationImpl( edgeShardSerialization, nodeShardCounterSerialization, timeService,
- graphFig );
+ new NodeShardAllocationImpl( edgeShardSerialization, edgeColumnFamilies, shardedEdgeSerialization,
+ nodeShardCounterSerialization, timeService, graphFig, keyspace );
- final Id nodeId = createId( "test" );
- final String type = "type";
- final String subType = "subType";
- /**
- * Mock up returning an empty iterator, our audit shouldn't create a new shard
- */
- when( edgeShardSerialization
- .getEdgeMetaData( same( scope ), same( nodeId ), any( Optional.class ), same( type ),
- same( subType ) ) ).thenReturn( Collections.<Long>emptyList().iterator() );
+ final long timeservicetime = System.currentTimeMillis();
- final boolean result = approximation.auditMaxShard( scope, nodeId, type, subType );
+ when( timeService.getCurrentTime() ).thenReturn( timeservicetime );
- assertFalse( "No shard allocated", result );
+ final long expected = timeservicetime - 2 * graphFig.getShardCacheTimeout();
+
+ final long returned = approximation.getMinTime();
+
+ assertEquals( "Correct time was returned", expected, returned );
}
+ // @Test
+ // public void noShards() {
+ // final EdgeShardSerialization edgeShardSerialization = mock( EdgeShardSerialization.class );
+ //
+ // final EdgeColumnFamilies edgeColumnFamilies = mock( EdgeColumnFamilies.class );
+ //
+ // final ShardedEdgeSerialization shardedEdgeSerialization = mock( ShardedEdgeSerialization.class );
+ //
+ //
+ // final NodeShardApproximation nodeShardCounterSerialization = mock( NodeShardApproximation.class );
+ //
+ //
+ // final TimeService timeService = mock( TimeService.class );
+ //
+ // final Keyspace keyspace = mock( Keyspace.class );
+ //
+ // final MutationBatch batch = mock( MutationBatch.class );
+ //
+ // when( keyspace.prepareMutationBatch() ).thenReturn( batch );
+ //
+ // NodeShardAllocation approximation =
+ // new NodeShardAllocationImpl( edgeShardSerialization, edgeColumnFamilies, shardedEdgeSerialization,
+ // nodeShardCounterSerialization, timeService, graphFig, keyspace );
+ //
+ // final Id nodeId = createId( "test" );
+ // final String type = "type";
+ // final String subType = "subType";
+ //
+ // /**
+ // * Mock up returning an empty iterator, our audit shouldn't create a new shard
+ // */
+ //
+ // final DirectedEdgeMeta targetEdgeMeta = DirectedEdgeMeta.fromSourceNodeTargetType(nodeId, type, subType );
+ //
+ //
+ // when( edgeShardSerialization
+ // .getShardMetaData( same( scope ), any( Optional.class ), same(targetEdgeMeta)) ).thenReturn(
+ // Collections.<Shard>emptyList().iterator() );
+ //
+ // final boolean result = approximation.auditShard(scope, null, targetEdgeMeta);
+ //
+ // assertFalse( "No shard allocated", result );
+ // }
+
+
@Test
- public void existingFutureShard() {
+ public void existingFutureShardSameTime() {
final EdgeShardSerialization edgeShardSerialization = mock( EdgeShardSerialization.class );
- final NodeShardApproximation nodeShardCounterSerialization =
- mock( NodeShardApproximation.class );
+ final EdgeColumnFamilies edgeColumnFamilies = mock( EdgeColumnFamilies.class );
+
+ final ShardedEdgeSerialization shardedEdgeSerialization = mock( ShardedEdgeSerialization.class );
+
+ final NodeShardApproximation nodeShardCounterSerialization = mock( NodeShardApproximation.class );
final TimeService timeService = mock( TimeService.class );
@@ -136,8 +189,8 @@ public class NodeShardAllocationTest {
NodeShardAllocation approximation =
- new NodeShardAllocationImpl( edgeShardSerialization, nodeShardCounterSerialization, timeService,
- graphFig );
+ new NodeShardAllocationImpl( edgeShardSerialization, edgeColumnFamilies, shardedEdgeSerialization,
+ nodeShardCounterSerialization, timeService, graphFig, keyspace );
final Id nodeId = createId( "test" );
final String type = "type";
@@ -148,16 +201,21 @@ public class NodeShardAllocationTest {
when( timeService.getCurrentTime() ).thenReturn( timeservicetime );
- final long futureShard = timeservicetime + graphFig.getShardCacheTimeout() * 2 ;
+ final Shard futureShard = new Shard( 10000l, timeservicetime, true );
+
+ final ShardEntryGroup shardEntryGroup = new ShardEntryGroup( 1000l );
+ shardEntryGroup.addShard( futureShard );
+
+ final DirectedEdgeMeta targetEdgeMeta = DirectedEdgeMeta.fromSourceNodeTargetType( nodeId, type, subType );
/**
* Mock up returning a min shard, and a future shard
*/
- when( edgeShardSerialization
- .getEdgeMetaData( same( scope ), same( nodeId ), any( Optional.class ), same( type ),
- same( subType ) ) ).thenReturn( Arrays.asList( futureShard ).iterator() );
+ when( edgeShardSerialization.getShardMetaData( same( scope ), any( Optional.class ), same( targetEdgeMeta ) ) )
+ .thenReturn( Arrays.asList( futureShard ).iterator() );
- final boolean result = approximation.auditMaxShard( scope, nodeId, type, subType );
+
+ final boolean result = approximation.auditShard( scope, shardEntryGroup, targetEdgeMeta );
assertFalse( "No shard allocated", result );
}
@@ -167,8 +225,11 @@ public class NodeShardAllocationTest {
public void lowCountFutureShard() {
final EdgeShardSerialization edgeShardSerialization = mock( EdgeShardSerialization.class );
- final NodeShardApproximation nodeShardApproximation =
- mock( NodeShardApproximation.class );
+ final EdgeColumnFamilies edgeColumnFamilies = mock( EdgeColumnFamilies.class );
+
+ final ShardedEdgeSerialization shardedEdgeSerialization = mock( ShardedEdgeSerialization.class );
+
+ final NodeShardApproximation nodeShardApproximation = mock( NodeShardApproximation.class );
final TimeService timeService = mock( TimeService.class );
@@ -181,8 +242,8 @@ public class NodeShardAllocationTest {
NodeShardAllocation approximation =
- new NodeShardAllocationImpl( edgeShardSerialization, nodeShardApproximation, timeService,
- graphFig );
+ new NodeShardAllocationImpl( edgeShardSerialization, edgeColumnFamilies, shardedEdgeSerialization,
+ nodeShardApproximation, timeService, graphFig, keyspace );
final Id nodeId = createId( "test" );
final String type = "type";
@@ -193,23 +254,32 @@ public class NodeShardAllocationTest {
when( timeService.getCurrentTime() ).thenReturn( timeservicetime );
+ final Shard futureShard = new Shard( 10000l, timeservicetime, true );
+
+ final ShardEntryGroup shardEntryGroup = new ShardEntryGroup( 1000l );
+ shardEntryGroup.addShard( futureShard );
+
+ final DirectedEdgeMeta targetEdgeMeta = DirectedEdgeMeta.fromSourceNodeTargetType( nodeId, type, subType );
+
+
+
/**
* Mock up returning a min shard, and a future shard
*/
when( edgeShardSerialization
- .getEdgeMetaData( same( scope ), same( nodeId ), any( Optional.class ), same( type ),
- same( subType ) ) ).thenReturn( Arrays.asList( 0l ).iterator() );
+ .getShardMetaData( same( scope ), any( Optional.class ), same(targetEdgeMeta)) )
+ .thenReturn( Arrays.asList( new Shard( 0l, 0l, true ) ).iterator() );
//return a shard size < our max by 1
final long count = graphFig.getShardSize() - 1;
- when( nodeShardApproximation.getCount(scope, nodeId, 0l, type, subType ))
- .thenReturn( count );
+ when( nodeShardApproximation.getCount( scope, futureShard, targetEdgeMeta ) )
+ .thenReturn( count );
- final boolean result = approximation.auditMaxShard( scope, nodeId, type, subType );
+ final boolean result = approximation.auditShard( scope, shardEntryGroup, targetEdgeMeta);
assertFalse( "Shard allocated", result );
}
@@ -219,22 +289,25 @@ public class NodeShardAllocationTest {
public void equalCountFutureShard() {
final EdgeShardSerialization edgeShardSerialization = mock( EdgeShardSerialization.class );
- final NodeShardApproximation nodeShardApproximation =
- mock( NodeShardApproximation.class );
+ final EdgeColumnFamilies edgeColumnFamilies = mock( EdgeColumnFamilies.class );
+
+ final ShardedEdgeSerialization shardedEdgeSerialization = mock( ShardedEdgeSerialization.class );
+
+ final NodeShardApproximation nodeShardApproximation = mock( NodeShardApproximation.class );
final TimeService timeService = mock( TimeService.class );
final Keyspace keyspace = mock( Keyspace.class );
- final MutationBatch batch = mock(MutationBatch.class);
+ final MutationBatch batch = mock( MutationBatch.class );
- when(keyspace.prepareMutationBatch()).thenReturn( batch );
+ when( keyspace.prepareMutationBatch() ).thenReturn( batch );
NodeShardAllocation approximation =
- new NodeShardAllocationImpl( edgeShardSerialization, nodeShardApproximation, timeService,
- graphFig );
+ new NodeShardAllocationImpl( edgeShardSerialization, edgeColumnFamilies, shardedEdgeSerialization,
+ nodeShardApproximation, timeService, graphFig, keyspace );
final Id nodeId = createId( "test" );
final String type = "type";
@@ -245,69 +318,92 @@ public class NodeShardAllocationTest {
when( timeService.getCurrentTime() ).thenReturn( timeservicetime );
+ final Shard futureShard = new Shard( 0l, 0l, true );
+
+ final ShardEntryGroup shardEntryGroup = new ShardEntryGroup( 1000l );
+ shardEntryGroup.addShard( futureShard );
+
+ final DirectedEdgeMeta targetEdgeMeta = DirectedEdgeMeta.fromSourceNodeTargetType( nodeId, type, subType );
+
+
/**
* Mock up returning a min shard
*/
when( edgeShardSerialization
- .getEdgeMetaData( same( scope ), same( nodeId ), any( Optional.class ), same( type ),
- same( subType ) ) ).thenReturn( Arrays.asList( 0l ).iterator() );
+ .getShardMetaData( same( scope ), any( Optional.class ), same(targetEdgeMeta) ) )
+ .thenReturn( Arrays.asList( futureShard ).iterator() );
final long shardCount = graphFig.getShardSize();
//return a shard size equal to our max
- when( nodeShardApproximation
- .getCount( scope , nodeId, 0l,type , subType ))
+ when( nodeShardApproximation.getCount( scope, futureShard, targetEdgeMeta) )
.thenReturn( shardCount );
- ArgumentCaptor<Long> newUUIDValue = ArgumentCaptor.forClass( Long.class );
+ ArgumentCaptor<Shard> shardValue = ArgumentCaptor.forClass( Shard.class );
//mock up our mutation
when( edgeShardSerialization
- .writeEdgeMeta( same( scope ), same( nodeId ), newUUIDValue.capture(), same( type ), same( subType ) ) )
- .thenReturn( mock( MutationBatch.class ) );
+ .writeShardMeta( same( scope ), shardValue.capture(), same(targetEdgeMeta) ) ).thenReturn( mock( MutationBatch.class ) );
- final boolean result = approximation.auditMaxShard( scope, nodeId, type, subType );
+ final SimpleMarkedEdge returnedEdge =
+ new SimpleMarkedEdge( nodeId, type, createId( "subType" ), 10005l, false );
+
+ final Iterator<MarkedEdge> edgeIterator = Collections.singleton( ( MarkedEdge ) returnedEdge ).iterator();
+
+ //mock up returning the value
+ when( shardedEdgeSerialization
+ .getEdgesFromSourceByTargetType( same( edgeColumnFamilies ), same( scope ), any( SearchByIdType.class ),
+ any( Iterator.class ) ) ).thenReturn( edgeIterator );
+
+
+ final boolean result = approximation.auditShard( scope, shardEntryGroup, targetEdgeMeta);
assertTrue( "Shard allocated", result );
//check our new allocated UUID
- final long expectedTime = timeservicetime + 2 * graphFig.getShardCacheTimeout();
- final long savedTimestamp = newUUIDValue.getValue();
+ final long savedTimestamp = shardValue.getValue().getCreatedTime();
+ assertEquals( "Expected time service time", timeservicetime, savedTimestamp );
- assertEquals( "Expected at 2x timeout generated", expectedTime, savedTimestamp );
- }
+ //now check our max value was set
+ final long savedShardPivot = shardValue.getValue().getShardIndex();
+
+ assertEquals( "Expected max value to be the same", returnedEdge.getTimestamp(), savedShardPivot );
+ }
@Test
public void futureCountShardCleanup() {
final EdgeShardSerialization edgeShardSerialization = mock( EdgeShardSerialization.class );
- final NodeShardApproximation nodeShardApproximation =
- mock( NodeShardApproximation.class );
+ final EdgeColumnFamilies edgeColumnFamilies = mock( EdgeColumnFamilies.class );
+
+ final ShardedEdgeSerialization shardedEdgeSerialization = mock( ShardedEdgeSerialization.class );
+
+ final NodeShardApproximation nodeShardApproximation = mock( NodeShardApproximation.class );
final TimeService timeService = mock( TimeService.class );
final Keyspace keyspace = mock( Keyspace.class );
- final MutationBatch batch = mock(MutationBatch.class);
+ final MutationBatch batch = mock( MutationBatch.class );
- when(keyspace.prepareMutationBatch()).thenReturn( batch );
+ when( keyspace.prepareMutationBatch() ).thenReturn( batch );
NodeShardAllocation approximation =
- new NodeShardAllocationImpl( edgeShardSerialization, nodeShardApproximation, timeService,
- graphFig );
+ new NodeShardAllocationImpl( edgeShardSerialization, edgeColumnFamilies, shardedEdgeSerialization,
+ nodeShardApproximation, timeService, graphFig, keyspace );
final Id nodeId = createId( "test" );
final String type = "type";
@@ -315,94 +411,133 @@ public class NodeShardAllocationTest {
/**
- * Use the time service to generate UUIDS
+ * Use the time service to generate timestamps
*/
- final long timeservicetime = System.currentTimeMillis();
+ final long timeservicetime = 10000;
when( timeService.getCurrentTime() ).thenReturn( timeservicetime );
- assertTrue("Shard cache mocked", graphFig.getShardCacheTimeout() > 0);
+ assertTrue( "Shard cache mocked", graphFig.getShardCacheTimeout() > 0 );
/**
* Simulates clock drift when 2 nodes create future shards near one another
*/
- final long futureTime = timeService.getCurrentTime() + 2 * graphFig.getShardCacheTimeout();
+ final long minDelta = graphFig.getShardMinDelta();
+
+
+ final Shard minShard = new Shard( 0l, 0l, true );
+ //a shard that isn't our minimum, but exists after compaction
+ final Shard compactedShard = new Shard( 5000, 1000, true );
/**
- * Simulate slow node
+ * Simulate different node time allocation
*/
- final long futureShard1 = futureTime - 1;
- final long futureShard2 = futureTime + 10000;
+ final long minTime = 10000;
+ //our second shard is the "oldest", and hence should be returned in the iterator. Future shard 1 and 3
+ // should be removed
- final long futureShard3 = futureShard2 + 10000;
+ //this should get dropped, It's allocated after future shard2 even though the time is less
+ final Shard futureShard1 = new Shard( 10000, minTime + minDelta, false );
+ //should get kept.
+ final Shard futureShard2 = new Shard( 10005, minTime, false );
- final int pageSize = 100;
+ //should be removed
+ final Shard futureShard3 = new Shard( 10010, minTime + minDelta / 2, false );
+
+ final DirectedEdgeMeta directedEdgeMeta = DirectedEdgeMeta.fromTargetNodeSourceType( nodeId, type, subType );
/**
* Mock up returning a min shard
*/
when( edgeShardSerialization
- .getEdgeMetaData( same( scope ), same( nodeId ), any( Optional.class ), same( type ),
- same( subType ) ) ).thenReturn( Arrays.asList(futureShard3, futureShard2, futureShard1, 0l).iterator() );
+ .getShardMetaData( same( scope ), any( Optional.class ), same(directedEdgeMeta)) ).thenReturn(
+ Arrays.asList( futureShard3, futureShard2, futureShard1, compactedShard, minShard ).iterator() );
+ ArgumentCaptor<Shard> newLongValue = ArgumentCaptor.forClass( Shard.class );
- ArgumentCaptor<Long> newLongValue = ArgumentCaptor.forClass( Long.class );
+ //mock up our mutation
+ when( edgeShardSerialization
+ .removeShardMeta( same( scope ), newLongValue.capture(), same(directedEdgeMeta)) ).thenReturn( mock( MutationBatch.class ) );
+ final Iterator<ShardEntryGroup> result =
+ approximation.getShards( scope, Optional.<Shard>absent(), directedEdgeMeta);
- //mock up our mutation
- when( edgeShardSerialization
- .removeEdgeMeta( same( scope ), same( nodeId ), newLongValue.capture(), same( type ), same( subType ) ) )
- .thenReturn( mock( MutationBatch.class ) );
+ assertTrue( "Shards present", result.hasNext() );
+
+
+ ShardEntryGroup shardEntryGroup = result.next();
+
+ assertEquals( "Future shard returned", futureShard1, shardEntryGroup.getCompactionTarget() );
- final Iterator<Long>
- result = approximation.getShards( scope, nodeId, Optional.<Long>absent(), type, subType );
+
+ //now verify all 4 are in this group. This is because the first shard (0,0) (n-1_ may be the only shard other
+ //nodes see while we're rolling our state. This means it should be read and merged from as well
+
+ Collection<Shard> writeShards = shardEntryGroup.getWriteShards( minTime + minDelta );
+
+ assertEquals( "Shard size as expected", 4, writeShards.size() );
+
+ assertTrue( writeShards.contains( futureShard1 ) );
+ assertTrue( writeShards.contains( futureShard2 ) );
+ assertTrue( writeShards.contains( futureShard3 ) );
+ assertTrue( writeShards.contains( compactedShard ) );
+
+
+ Collection<Shard> readShards = shardEntryGroup.getReadShards();
+
+ assertEquals( "Shard size as expected", 4, readShards.size() );
+
+ assertTrue( readShards.contains( futureShard1 ) );
+ assertTrue( readShards.contains( futureShard2 ) );
+ assertTrue( readShards.contains( futureShard3 ) );
+ assertTrue( readShards.contains( compactedShard ) );
assertTrue( "Shards present", result.hasNext() );
- assertEquals("Only single next shard returned", futureShard1, result.next().longValue());
+ shardEntryGroup = result.next();
- assertTrue("Shards present", result.hasNext());
- assertEquals("Previous shard present", 0l, result.next().longValue());
+ writeShards = shardEntryGroup.getWriteShards( minTime + minDelta );
- assertFalse("No shards left", result.hasNext());
- /**
- * Now we need to verify that both our mutations have been added
- */
+ assertTrue( "Previous shard present", writeShards.contains( minShard ) );
- List<Long> values = newLongValue.getAllValues();
- assertEquals("2 values removed", 2, values.size());
+ writeShards = shardEntryGroup.getReadShards();
- assertEquals("Deleted Max Future", futureShard3, values.get( 0 ).longValue());
- assertEquals("Deleted Next Future", futureShard2, values.get( 1 ).longValue());
- }
+ assertTrue( "Previous shard present", writeShards.contains( minShard ) );
+ assertFalse( "No shards left", result.hasNext() );
+ }
@Test
- public void noShardsReturns() {
+ public void noShardsReturns() throws ConnectionException {
final EdgeShardSerialization edgeShardSerialization = mock( EdgeShardSerialization.class );
- final NodeShardApproximation nodeShardApproximation =
- mock( NodeShardApproximation.class );
+ final EdgeColumnFamilies edgeColumnFamilies = mock( EdgeColumnFamilies.class );
+
+ final ShardedEdgeSerialization shardedEdgeSerialization = mock( ShardedEdgeSerialization.class );
+
+ final NodeShardApproximation nodeShardApproximation = mock( NodeShardApproximation.class );
final TimeService timeService = mock( TimeService.class );
+ when( timeService.getCurrentTime() ).thenReturn( 10000l );
+
final Keyspace keyspace = mock( Keyspace.class );
final MutationBatch batch = mock( MutationBatch.class );
@@ -410,25 +545,139 @@ public class NodeShardAllocationTest {
when( keyspace.prepareMutationBatch() ).thenReturn( batch );
NodeShardAllocation approximation =
- new NodeShardAllocationImpl( edgeShardSerialization, nodeShardApproximation, timeService,
- graphFig );
+ new NodeShardAllocationImpl( edgeShardSerialization, edgeColumnFamilies, shardedEdgeSerialization,
+ nodeShardApproximation, timeService, graphFig, keyspace );
final Id nodeId = createId( "test" );
final String type = "type";
final String subType = "subType";
+ final DirectedEdgeMeta directedEdgeMeta = DirectedEdgeMeta.fromTargetNodeSourceType( nodeId, type, subType );
+
+
+
/**
* Mock up returning an empty iterator, our audit shouldn't create a new shard
*/
when( edgeShardSerialization
- .getEdgeMetaData( same( scope ), same( nodeId ), any( Optional.class ), same( type ),
- same( subType ) ) ).thenReturn( Collections.<Long>emptyList().iterator() );
+ .getShardMetaData( same( scope ), any( Optional.class ), same(directedEdgeMeta) ) ).thenReturn( Collections.<Shard>emptyList().iterator() );
+
+
+ ArgumentCaptor<Shard> shardArgumentCaptor = ArgumentCaptor.forClass( Shard.class );
+
+ when( edgeShardSerialization
+ .writeShardMeta( same( scope ), shardArgumentCaptor.capture(), same(directedEdgeMeta)) ).thenReturn( batch );
+
+
+ final Iterator<ShardEntryGroup> result =
+ approximation.getShards( scope, Optional.<Shard>absent(), directedEdgeMeta );
+
+
+ ShardEntryGroup shardEntryGroup = result.next();
+
+ final Shard rootShard = new Shard( 0, 0, true );
+
+ assertEquals( "Shard size expected", 1, shardEntryGroup.entrySize() );
- final Iterator<Long> result = approximation.getShards( scope, nodeId, Optional.<Long>absent(), type, subType );
- assertEquals("0 shard allocated", 0l, result.next().longValue());
+ //ensure we persisted the new shard.
+ assertEquals( "Root shard was persisted", rootShard, shardArgumentCaptor.getValue() );
- assertFalse( "No shard allocated", result.hasNext() );
+
+ //now verify all 4 are in this group. This is because the first shard (0,0) (n-1_ may be the only shard other
+ //nodes see while we're rolling our state. This means it should be read and merged from as well
+
+ Collection<Shard> writeShards = shardEntryGroup.getWriteShards( timeService.getCurrentTime() );
+
+ Collection<Shard> readShards = shardEntryGroup.getReadShards();
+
+
+ assertTrue( "root shard allocated", writeShards.contains( rootShard ) );
+
+ assertTrue( "root shard allocated", readShards.contains( rootShard ) );
+
+
+ assertFalse( "No other shard group allocated", result.hasNext() );
}
+
+ @Test
+ public void invalidConfiguration() {
+
+ final GraphFig graphFig = mock( GraphFig.class );
+
+ final EdgeShardSerialization edgeShardSerialization = mock( EdgeShardSerialization.class );
+
+ final EdgeColumnFamilies edgeColumnFamilies = mock( EdgeColumnFamilies.class );
+
+ final ShardedEdgeSerialization shardedEdgeSerialization = mock( ShardedEdgeSerialization.class );
+
+ final NodeShardApproximation nodeShardApproximation = mock( NodeShardApproximation.class );
+
+
+ /**
+ * Return 100000 milliseconds
+ */
+ final TimeService timeService = mock( TimeService.class );
+
+ final long time = 100000l;
+
+ when( timeService.getCurrentTime() ).thenReturn( time );
+
+
+ final long cacheTimeout = 30000l;
+
+ when( graphFig.getShardCacheTimeout() ).thenReturn( 30000l );
+
+
+ final long tooSmallDelta = ( long ) ( ( cacheTimeout * 2 ) * .99 );
+
+ when( graphFig.getShardMinDelta() ).thenReturn( tooSmallDelta );
+
+
+ final Keyspace keyspace = mock( Keyspace.class );
+
+ final MutationBatch batch = mock( MutationBatch.class );
+
+ when( keyspace.prepareMutationBatch() ).thenReturn( batch );
+
+
+ NodeShardAllocation approximation =
+ new NodeShardAllocationImpl( edgeShardSerialization, edgeColumnFamilies, shardedEdgeSerialization,
+ nodeShardApproximation, timeService, graphFig, keyspace );
+
+
+ /**
+ * Should throw an exception
+ */
+ try {
+ approximation.getMinTime();
+ fail( "Should have thrown a GraphRuntimeException" );
+ }
+ catch ( GraphRuntimeException gre ) {
+ //swallow
+ }
+
+ //now test something that passes.
+
+ final long minDelta = cacheTimeout * 2;
+
+ when( graphFig.getShardMinDelta() ).thenReturn( minDelta );
+
+ long returned = approximation.getMinTime();
+
+ long expectedReturned = time - minDelta;
+
+ assertEquals( expectedReturned, returned );
+
+ final long delta = cacheTimeout * 4;
+
+ when( graphFig.getShardMinDelta() ).thenReturn( delta );
+
+ returned = approximation.getMinTime();
+
+ expectedReturned = time - delta;
+
+ assertEquals( expectedReturned, returned );
+ }
}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/test/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/NodeShardCacheTest.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/test/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/NodeShardCacheTest.java b/stack/corepersistence/graph/src/test/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/NodeShardCacheTest.java
index 6c46c32..f00a380 100644
--- a/stack/corepersistence/graph/src/test/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/NodeShardCacheTest.java
+++ b/stack/corepersistence/graph/src/test/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/NodeShardCacheTest.java
@@ -22,15 +22,16 @@ package org.apache.usergrid.persistence.graph.serialization.impl.shard;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
-import java.util.UUID;
import org.junit.Before;
import org.junit.Test;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.apache.usergrid.persistence.core.consistency.TimeService;
import org.apache.usergrid.persistence.core.scope.ApplicationScope;
import org.apache.usergrid.persistence.graph.GraphFig;
import org.apache.usergrid.persistence.graph.serialization.impl.shard.impl.NodeShardCacheImpl;
-import org.apache.usergrid.persistence.graph.test.util.EdgeTestUtils;
import org.apache.usergrid.persistence.model.entity.Id;
import org.apache.usergrid.persistence.model.util.UUIDGenerator;
@@ -39,6 +40,9 @@ import com.netflix.astyanax.connectionpool.exceptions.ConnectionException;
import static org.apache.usergrid.persistence.graph.test.util.EdgeTestUtils.createId;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.same;
@@ -76,6 +80,8 @@ public class NodeShardCacheTest {
final NodeShardAllocation allocation = mock( NodeShardAllocation.class );
+ final TimeService time = mock(TimeService.class);
+
final Id id = createId( "test" );
final String edgeType = "edge";
@@ -86,78 +92,67 @@ public class NodeShardCacheTest {
final long newTime = 10000l;
- NodeShardCache cache = new NodeShardCacheImpl( allocation, graphFig );
+ NodeShardCache cache = new NodeShardCacheImpl( allocation, graphFig, time );
final Optional max = Optional.absent();
- /**
- * Simulate returning no shards at all.
- */
- when( allocation
- .getShards( same( scope ), same( id ), same( max), same( edgeType ),
- same( otherIdType ) ) )
- .thenReturn( Collections.singletonList( 0l ).iterator() );
-
- long slice = cache.getSlice( scope, id, newTime, edgeType, otherIdType );
+ final ShardEntryGroup group = new ShardEntryGroup( newTime );
+ group.addShard( new Shard(0, 0, true) );
- //we return the min UUID possible, all edges should start by writing to this edge
- assertEquals(0l, slice );
+ DirectedEdgeMeta directedEdgeMeta = DirectedEdgeMeta.fromSourceNodeTargetType( id, edgeType, otherIdType ) ;
/**
- * Verify that we fired the audit
+ * Simulate returning no shards at all.
*/
- verify( allocation ).auditMaxShard( scope, id, edgeType, otherIdType );
- }
-
-
- @Test
- public void testSingleExistingShard() {
-
- final GraphFig graphFig = getFigMock();
-
- final NodeShardAllocation allocation = mock( NodeShardAllocation.class );
+ when( allocation.getShards( same( scope ), same( max ), same( directedEdgeMeta ) ) )
+ //use "thenAnswer" so we always return the value, even if it's invoked more than 1 time.
+ .thenAnswer( new Answer<Iterator<ShardEntryGroup>>() {
- final Id id = createId( "test" );
+ @Override
+ public Iterator<ShardEntryGroup> answer( final InvocationOnMock invocationOnMock )
+ throws Throwable {
+ return Collections.singletonList( group ).iterator();
+ }
+ });
- final String edgeType = "edge";
- final String otherIdType = "type";
+ ShardEntryGroup returnedGroup = cache.getWriteShardGroup( scope, newTime, directedEdgeMeta );
+ //ensure it's the same group
+ assertSame(group, returnedGroup);
- final long newTime = 10000l;
- final long min = 0;
+ Iterator<ShardEntryGroup>
+ shards = cache.getReadShardGroup( scope, newTime, directedEdgeMeta );
+ assertTrue(shards.hasNext());
- NodeShardCache cache = new NodeShardCacheImpl( allocation, graphFig );
+ returnedGroup = shards.next();
- final Optional max = Optional.absent();
+ assertSame("Single shard group expected", group, returnedGroup);
- /**
- * Simulate returning single shard
- */
- when( allocation.getShards( same( scope ), same( id ), same(max),
- same( edgeType ), same( otherIdType ) ) ).thenReturn( Collections.singletonList( min ).iterator() );
+ assertFalse(shards.hasNext());
- long slice = cache.getSlice( scope, id, newTime, edgeType, otherIdType );
//we return the min UUID possible, all edges should start by writing to this edge
- assertEquals( min, slice );
/**
* Verify that we fired the audit
+ *
+ * TODO, us the GUAVA Tick to make this happen
*/
- verify( allocation ).auditMaxShard( scope, id, edgeType, otherIdType );
+// verify(and allocation ).auditMaxShard( scope, id, edgeType, otherIdType );
}
+
@Test
public void testRangeShard() {
@@ -165,6 +160,8 @@ public class NodeShardCacheTest {
final NodeShardAllocation allocation = mock( NodeShardAllocation.class );
+ final TimeService time = mock(TimeService.class);
+
final Id id = createId( "test" );
final String edgeType = "edge";
@@ -175,150 +172,157 @@ public class NodeShardCacheTest {
/**
* Set our min mid and max
*/
- final long min = 0;
+ NodeShardCache cache = new NodeShardCacheImpl( allocation, graphFig, time );
+
+
+ final Shard minShard = new Shard(0, 0, true);
+ final Shard midShard = new Shard(10000, 1000, true);
+ final Shard maxShard = new Shard(20000, 2000, true);
+
+
+ /**
+ * Simulate returning all shards
+ */
+ final ShardEntryGroup minShardGroup = new ShardEntryGroup( 10000 );
+ minShardGroup.addShard( minShard );
+
+ final ShardEntryGroup midShardGroup = new ShardEntryGroup( 10000 );
+ midShardGroup.addShard( midShard );
- final long mid = 10000;
+ final ShardEntryGroup maxShardGroup = new ShardEntryGroup( 10000 );
+ maxShardGroup.addShard( maxShard );
- final long max = 20000;
- NodeShardCache cache = new NodeShardCacheImpl( allocation, graphFig );
+
+ DirectedEdgeMeta directedEdgeMeta = DirectedEdgeMeta.fromTargetNodeSourceType( id, edgeType, otherIdType ) ;
/**
- * Simulate returning all shards
+ * Simulate returning no shards at all.
*/
- when( allocation.getShards( same( scope ), same( id ), any( Optional.class ),
- same( edgeType ), same( otherIdType ) ) ).thenReturn( Arrays.asList( min, mid, max ).iterator() );
+ when( allocation
+ .getShards( same( scope ), any( Optional.class ), same( directedEdgeMeta ) ) )
+ //use "thenAnswer" so we always return the value, even if it's invoked more than 1 time.
+ .thenAnswer( new Answer<Iterator<ShardEntryGroup>>(){
- //check getting equal to our min, mid and max
+ @Override
+ public Iterator<ShardEntryGroup> answer( final InvocationOnMock invocationOnMock )
+ throws Throwable {
+ return Arrays.asList( maxShardGroup, midShardGroup, minShardGroup ).iterator();
+ }
+ });
- long slice = cache.getSlice( scope, id, min, edgeType, otherIdType );
+ //check getting equal to our min, mid and max
- //we return the min UUID possible, all edges should start by writing to this edge
- assertEquals( min, slice );
+ ShardEntryGroup writeShard = cache.getWriteShardGroup( scope, minShard.getShardIndex(), directedEdgeMeta );
- slice = cache.getSlice( scope, id, mid,
- edgeType, otherIdType );
+ assertSame(minShardGroup, writeShard);
- //we return the mid UUID possible, all edges should start by writing to this edge
- assertEquals( mid, slice );
+ Iterator<ShardEntryGroup>
+ groups = cache.getReadShardGroup( scope, minShard.getShardIndex(), directedEdgeMeta);
- slice = cache.getSlice( scope, id, max ,
- edgeType, otherIdType );
+ assertTrue(groups.hasNext());
+ assertSame("min shard expected", minShardGroup, groups.next());
- //we return the mid UUID possible, all edges should start by writing to this edge
- assertEquals( max, slice );
+ assertFalse(groups.hasNext());
- //now test in between
- slice = cache.getSlice( scope, id, min+1, edgeType, otherIdType );
- //we return the min UUID possible, all edges should start by writing to this edge
- assertEquals( min, slice );
- slice = cache.getSlice( scope, id, mid-1, edgeType, otherIdType );
+ //mid
+ writeShard = cache.getWriteShardGroup( scope, midShard.getShardIndex(), directedEdgeMeta );
+ assertSame(midShardGroup, writeShard);
- //we return the min UUID possible, all edges should start by writing to this edge
- assertEquals( min, slice );
+ groups = cache.getReadShardGroup( scope, midShard.getShardIndex(), directedEdgeMeta );
- slice = cache.getSlice( scope, id, mid+1, edgeType, otherIdType );
+ assertTrue(groups.hasNext());
+ assertSame("mid shard expected", midShardGroup, groups.next());
- //we return the mid UUID possible, all edges should start by writing to this edge
- assertEquals( mid, slice );
+ assertTrue(groups.hasNext());
- slice = cache.getSlice( scope, id, max-1, edgeType, otherIdType );
+ assertSame("min shard expected", minShardGroup, groups.next());
+ assertFalse(groups.hasNext());
- //we return the mid UUID possible, all edges should start by writing to this edge
- assertEquals( mid, slice );
- slice = cache.getSlice( scope, id, max, edgeType, otherIdType );
+ //max
+ writeShard = cache.getWriteShardGroup( scope, maxShard.getShardIndex(), directedEdgeMeta );
- //we return the mid UUID possible, all edges should start by writing to this edge
- assertEquals( max, slice );
+ assertSame(maxShardGroup, writeShard);
- /**
- * Verify that we fired the audit
- */
- verify( allocation ).auditMaxShard( scope, id, edgeType, otherIdType );
- }
+ groups = cache.getReadShardGroup( scope, maxShard.getShardIndex(), directedEdgeMeta );
- @Test
- public void testRangeShardIterator() {
+ assertTrue(groups.hasNext());
- final GraphFig graphFig = getFigMock();
+ assertSame("max shard expected", maxShardGroup, groups.next());
- final NodeShardAllocation allocation = mock( NodeShardAllocation.class );
+ assertTrue(groups.hasNext());
- final Id id = createId( "test" );
+ assertSame("mid shard expected", midShardGroup, groups.next());
- final String edgeType = "edge";
- final String otherIdType = "type";
+ assertTrue(groups.hasNext());
+ assertSame("min shard expected", minShardGroup, groups.next());
- /**
- * Set our min mid and max
- */
- final long min = 1;
+ assertFalse(groups.hasNext());
- final long mid = 100;
+ //now test at mid +1 to ensure we get mid + min
+ writeShard = cache.getWriteShardGroup( scope, midShard.getShardIndex() + 1, directedEdgeMeta );
- final long max = 200;
+ assertSame(midShardGroup, writeShard);
- NodeShardCache cache = new NodeShardCacheImpl( allocation, graphFig );
+ groups = cache.getReadShardGroup( scope, midShard.getShardIndex() + 1, directedEdgeMeta );
+ assertTrue(groups.hasNext());
- /**
- * Simulate returning all shards
- */
- when( allocation.getShards( same( scope ), same( id ), any(Optional.class),
- same( edgeType ), same( otherIdType ) ) ).thenReturn( Arrays.asList( min, mid, max ).iterator() );
+ assertSame("mid shard expected", midShardGroup, groups.next());
+ assertTrue(groups.hasNext());
- //check getting equal to our min, mid and max
+ assertSame("min shard expected", minShardGroup, groups.next());
- Iterator<Long> slice =
- cache.getVersions( scope, id, max, edgeType, otherIdType );
+ assertFalse(groups.hasNext());
- assertEquals( max, slice.next().longValue() );
- assertEquals( mid, slice.next().longValue() );
- assertEquals( min, slice.next().longValue() );
+ //now test at mid -1 to ensure we get min
+ writeShard = cache.getWriteShardGroup( scope, midShard.getShardIndex() - 1, directedEdgeMeta );
- slice = cache.getVersions( scope, id, mid,
- edgeType, otherIdType );
+ assertSame(minShardGroup, writeShard);
- assertEquals( mid, slice.next().longValue() );
- assertEquals( min, slice.next().longValue() );
+ groups = cache.getReadShardGroup( scope, midShard.getShardIndex() - 1, directedEdgeMeta );
- slice = cache.getVersions( scope, id, min,
- edgeType, otherIdType );
- assertEquals( min, slice.next().longValue() );
+ assertTrue(groups.hasNext());
+ assertSame("min shard expected", minShardGroup, groups.next());
+
+ assertFalse(groups.hasNext());
}
+
+
+
private GraphFig getFigMock() {
final GraphFig graphFig = mock( GraphFig.class );
when( graphFig.getShardCacheSize() ).thenReturn( 1000l );
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/test/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/ShardEntryGroupTest.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/test/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/ShardEntryGroupTest.java b/stack/corepersistence/graph/src/test/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/ShardEntryGroupTest.java
new file mode 100644
index 0000000..b08da9a
--- /dev/null
+++ b/stack/corepersistence/graph/src/test/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/ShardEntryGroupTest.java
@@ -0,0 +1,422 @@
+/*
+ * 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;
+
+
+import java.util.Collection;
+import java.util.Set;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * Test for the group functionality
+ */
+public class ShardEntryGroupTest {
+
+ @Test
+ public void singleEntry() {
+
+ final long delta = 10000;
+
+ Shard rootShard = new Shard( 0, 0, false );
+
+ ShardEntryGroup shardEntryGroup = new ShardEntryGroup( delta );
+
+ final boolean result = shardEntryGroup.addShard( rootShard );
+
+ assertTrue( "Shard added", result );
+
+ assertFalse( "Single shard cannot be deleted", shardEntryGroup.canBeDeleted( rootShard ) );
+
+ assertNull( "No merge target found", shardEntryGroup.getCompactionTarget() );
+
+ assertFalse( "Merge cannot be run with a single shard", shardEntryGroup.shouldCompact( Long.MAX_VALUE ) );
+ }
+
+
+ @Test
+ public void allocatedWithinDelta() {
+
+ final long delta = 10000;
+
+ Shard firstShard = new Shard( 1000, 1000, false );
+
+ Shard secondShard = new Shard( 1000, 1001, false );
+
+
+ ShardEntryGroup shardEntryGroup = new ShardEntryGroup( delta );
+
+ boolean result = shardEntryGroup.addShard( secondShard );
+
+ assertTrue( "Shard added", result );
+
+ result = shardEntryGroup.addShard( firstShard );
+
+ assertTrue( " Shard added", result );
+
+
+ assertFalse( "First shard cannot be deleted", shardEntryGroup.canBeDeleted( firstShard ) );
+
+ assertFalse( "Second shard cannot be deleted", shardEntryGroup.canBeDeleted( secondShard ) );
+
+ assertFalse( "Duplicate shard id cannot be deleted", shardEntryGroup.canBeDeleted( secondShard ) );
+
+ assertNull( "Can't compact, no min compacted shard present", shardEntryGroup.getCompactionTarget() );
+
+
+ //TODO, should this blow up in general? We don't have a compacted shard at the lower bounds, which shouldn't be allowed
+
+ }
+
+
+ @Test
+ public void testShardTarget() {
+
+ final long delta = 10000;
+
+ Shard compactedShard = new Shard( 0, 0, true );
+
+ Shard firstShard = new Shard( 1000, 1000, false );
+
+ Shard secondShard = new Shard( 1000, 1001, false );
+
+
+ ShardEntryGroup shardEntryGroup = new ShardEntryGroup( delta );
+
+ boolean result = shardEntryGroup.addShard( secondShard );
+
+
+ assertTrue( "Shard added", result );
+
+ result = shardEntryGroup.addShard( firstShard );
+
+ assertTrue( "Shard added", result );
+
+ result = shardEntryGroup.addShard( compactedShard );
+
+ assertTrue( " Shard added", result );
+
+
+ assertFalse( "First shard cannot be deleted", shardEntryGroup.canBeDeleted( firstShard ) );
+
+ assertFalse( "Second shard cannot be deleted", shardEntryGroup.canBeDeleted( secondShard ) );
+
+ assertFalse( "Duplicate shard id cannot be deleted", shardEntryGroup.canBeDeleted( secondShard ) );
+
+ assertEquals( "Min compaction target found", firstShard, shardEntryGroup.getCompactionTarget() );
+
+ //shouldn't return true, since we haven't passed delta time in the second shard
+ assertFalse( "Merge cannot be run within min time",
+ shardEntryGroup.shouldCompact( firstShard.getCreatedTime() + delta ) );
+
+ //shouldn't return true, since we haven't passed delta time in the second shard
+ assertFalse( "Merge cannot be run within min time",
+ shardEntryGroup.shouldCompact( secondShard.getCreatedTime() + delta ) );
+
+ //we haven't passed the delta in the neighbor that would be our source, shard2, we shouldn't return true
+ //we read from shard2 and write to shard1
+ assertFalse( "Merge cannot be run with after min time",
+ shardEntryGroup.shouldCompact( firstShard.getCreatedTime() + delta + 1 ) );
+
+ assertTrue( "Merge should be run with after min time",
+ shardEntryGroup.shouldCompact( secondShard.getCreatedTime() + delta + 1 ) );
+ }
+
+
+ @Test
+ public void multipleShardGroups() {
+
+ final long delta = 10000;
+
+ Shard firstShard = new Shard( 1000, 10000, false );
+
+ Shard secondShard = new Shard( 999, 9000, false );
+
+ Shard compactedShard1 = new Shard( 900, 8000, true );
+
+ Shard compactedShard2 = new Shard( 800, 7000, true );
+
+
+ ShardEntryGroup shardEntryGroup = new ShardEntryGroup( delta );
+
+ boolean result = shardEntryGroup.addShard( firstShard );
+
+ assertTrue( "Shard added", result );
+
+ result = shardEntryGroup.addShard( secondShard );
+
+ assertTrue( " Shard added", result );
+
+ result = shardEntryGroup.addShard( compactedShard1 );
+
+ assertTrue( "Shard added", result );
+
+ result = shardEntryGroup.addShard( compactedShard2 );
+
+ assertFalse( "Shouldn't add since it's compacted", result );
+
+ ShardEntryGroup secondGroup = new ShardEntryGroup( delta );
+
+ result = secondGroup.addShard( compactedShard2 );
+
+ assertTrue( "Added successfully", result );
+ }
+
+
+ @Test
+ public void boundShardGroup() {
+
+ final long delta = 10000;
+
+ Shard firstShard = new Shard( 1000, 10000, false );
+
+ Shard secondShard = new Shard( 999, 9000, false );
+
+ Shard compactedShard1 = new Shard( 900, 8000, true );
+
+
+ ShardEntryGroup shardEntryGroup = new ShardEntryGroup( delta );
+
+ boolean result = shardEntryGroup.addShard( firstShard );
+
+ assertTrue( "Shard added", result );
+
+ result = shardEntryGroup.addShard( secondShard );
+
+ assertTrue( " Shard added", result );
+
+ result = shardEntryGroup.addShard( compactedShard1 );
+
+ assertTrue( "Shard added", result );
+
+
+ assertTrue( "Shard can be deleted", shardEntryGroup.canBeDeleted( firstShard ) );
+
+ assertFalse( "Compaction shard shard cannot be deleted", shardEntryGroup.canBeDeleted( secondShard ) );
+
+ assertEquals( "Same shard for merge target", secondShard, shardEntryGroup.getCompactionTarget() );
+
+ //shouldn't return true, since we haven't passed delta time in the second shard
+ assertFalse( "Merge cannot be run within min time",
+ shardEntryGroup.shouldCompact( firstShard.getCreatedTime() + delta ) );
+
+ //shouldn't return true, since we haven't passed delta time in the second shard
+ assertFalse( "Merge cannot be run within min time",
+ shardEntryGroup.shouldCompact( secondShard.getCreatedTime() + delta ) );
+
+
+ assertFalse( "Merge cannot be run within min time",
+ shardEntryGroup.shouldCompact( secondShard.getCreatedTime() + delta + 1 ) );
+
+ assertTrue( "Merge should be run with after min time", shardEntryGroup.shouldCompact( firstShard.getCreatedTime() + delta + 1 ) );
+ }
+
+
+ /**
+ * Ensures that we read from all shards (even the compacted one)
+ */
+ @Test
+ public void getAllReadShards() {
+
+ final long delta = 10000;
+
+ Shard firstShard = new Shard( 1000, 10000, false );
+
+ Shard secondShard = new Shard( 999, 9000, false );
+
+ Shard compactedShard1 = new Shard( 900, 8000, true );
+
+
+ ShardEntryGroup shardEntryGroup = new ShardEntryGroup( delta );
+
+ boolean result = shardEntryGroup.addShard( firstShard );
+
+ assertTrue( "Shard added", result );
+
+ result = shardEntryGroup.addShard( secondShard );
+
+ assertTrue( " Shard added", result );
+
+ result = shardEntryGroup.addShard( compactedShard1 );
+
+ assertTrue( "Shard added", result );
+
+ Collection<Shard> readShards = shardEntryGroup.getReadShards();
+
+ assertEquals("Shard size correct", 3, readShards.size());
+
+ assertTrue("First shard present", readShards.contains( firstShard ) );
+
+ assertTrue("Second shard present", readShards.contains( firstShard ) );
+
+ assertTrue("Third shard present", readShards.contains( firstShard ) );
+
+ }
+
+
+ /**
+ * Ensures that we read from all shards (even the compacted one)
+ */
+ @Test
+ public void getAllWriteShardsNotPastCompaction() {
+
+ final long delta = 10000;
+
+ Shard firstShard = new Shard( 1000, 10000, false );
+
+ Shard secondShard = new Shard( 999, 9000, false );
+
+ Shard compactedShard = new Shard( 900, 8000, true );
+
+
+ ShardEntryGroup shardEntryGroup = new ShardEntryGroup( delta );
+
+ boolean result = shardEntryGroup.addShard( firstShard );
+
+ assertTrue( "Shard added", result );
+
+ result = shardEntryGroup.addShard( secondShard );
+
+ assertTrue( " Shard added", result );
+
+ result = shardEntryGroup.addShard( compactedShard );
+
+ assertTrue( "Shard added", result );
+
+
+
+ Collection<Shard> writeShards = shardEntryGroup.getWriteShards( firstShard.getCreatedTime() + delta );
+
+ assertEquals("Shard size correct", 3, writeShards.size());
+
+ assertTrue("First shard present", writeShards.contains( firstShard ) );
+
+ assertTrue("Second shard present", writeShards.contains( secondShard ) );
+
+ assertTrue("Third shard present", writeShards.contains( compactedShard ) );
+
+
+
+ writeShards = shardEntryGroup.getWriteShards(secondShard.getCreatedTime()+delta);
+
+ assertEquals("Shard size correct", 3, writeShards.size());
+
+ assertTrue("First shard present", writeShards.contains( firstShard ) );
+
+ assertTrue("Second shard present", writeShards.contains( secondShard ) );
+
+ assertTrue("Third shard present", writeShards.contains( compactedShard ) );
+
+
+ /**
+ * Not the max created timestamp, shouldn't return less than all shards
+ */
+ writeShards = shardEntryGroup.getWriteShards(secondShard.getCreatedTime() +1 + delta);
+
+ assertEquals("Shard size correct", 3, writeShards.size());
+
+ assertTrue("First shard present", writeShards.contains( firstShard ) );
+
+ assertTrue("Second shard present", writeShards.contains( secondShard ) );
+
+ assertTrue("Third shard present", writeShards.contains( compactedShard ) );
+
+
+
+ assertEquals("Compaction target correct", secondShard, shardEntryGroup.getCompactionTarget());
+
+ writeShards = shardEntryGroup.getWriteShards(firstShard.getCreatedTime() +1 + delta);
+
+ assertEquals("Shard size correct", 1, writeShards.size());
+
+
+ assertTrue("Second shard present", writeShards.contains( secondShard ) );
+
+ }
+
+
+ @Test(expected=IllegalArgumentException.class)
+ public void failsInsertionOrder() {
+
+ final long delta = 10000;
+
+ Shard secondShard = new Shard(20000, 10000, false);
+
+ Shard firstShard = new Shard(10000 , 10000, false );
+
+ Shard rootShard = new Shard( 0, 0, false );
+
+ ShardEntryGroup shardEntryGroup = new ShardEntryGroup( delta );
+
+ boolean result = shardEntryGroup.addShard( secondShard );
+
+ assertTrue( "Shard added", result );
+
+ result = shardEntryGroup.addShard( rootShard );
+
+ assertTrue( "Shard added", result );
+
+ //this should blow up, we can't add a shard in the middle, it must always be greater than the current max
+
+ shardEntryGroup.addShard( firstShard );
+
+
+ }
+
+
+
+ @Test
+ public void shardEntryAddList() {
+
+ final long delta = 10000;
+
+ Shard highShard = new Shard( 30000, 1000, false );
+
+ Shard midShard = new Shard( 20000, 1000, true );
+
+ Shard lowShard = new Shard( 10000, 1000, false);
+
+ ShardEntryGroup shardEntryGroup = new ShardEntryGroup( delta );
+
+ boolean result = shardEntryGroup.addShard( highShard );
+
+ assertTrue( "Shard added", result );
+
+ result = shardEntryGroup.addShard( midShard );
+
+ assertTrue( "Shard added", result );
+
+ result = shardEntryGroup.addShard( lowShard );
+
+ assertFalse( "Shard added", result );
+ }
+
+
+
+
+}
+
+
+
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/test/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/count/NodeShardApproximationTest.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/test/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/count/NodeShardApproximationTest.java b/stack/corepersistence/graph/src/test/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/count/NodeShardApproximationTest.java
index da19ce5..fd8ff26 100644
--- a/stack/corepersistence/graph/src/test/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/count/NodeShardApproximationTest.java
+++ b/stack/corepersistence/graph/src/test/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/count/NodeShardApproximationTest.java
@@ -39,12 +39,17 @@ import org.junit.Test;
import org.safehaus.guicyfig.Bypass;
import org.safehaus.guicyfig.OptionState;
import org.safehaus.guicyfig.Overrides;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.apache.usergrid.persistence.core.astyanax.MultiTennantColumnFamilyDefinition;
import org.apache.usergrid.persistence.core.consistency.TimeService;
import org.apache.usergrid.persistence.core.scope.ApplicationScope;
import org.apache.usergrid.persistence.graph.GraphFig;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.DirectedEdgeMeta;
import org.apache.usergrid.persistence.graph.serialization.impl.shard.NodeShardApproximation;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.NodeType;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.Shard;
import org.apache.usergrid.persistence.model.entity.Id;
import org.apache.usergrid.persistence.model.util.UUIDGenerator;
@@ -68,6 +73,7 @@ import static org.mockito.Mockito.when;
public class NodeShardApproximationTest {
+ private static final Logger LOG = LoggerFactory.getLogger( NodeShardApproximation.class );
private GraphFig graphFig;
@@ -92,35 +98,38 @@ public class NodeShardApproximationTest {
when( graphFig.getShardCacheSize() ).thenReturn( 10000l );
when( graphFig.getShardSize() ).thenReturn( 250000l );
+ when( graphFig.getCounterFlushQueueSize() ).thenReturn( 10000 );
nodeShardCounterSerialization = mock( NodeShardCounterSerialization.class );
- when(nodeShardCounterSerialization.flush( any(Counter.class) )).thenReturn( mock( MutationBatch.class) );
-
+ when( nodeShardCounterSerialization.flush( any( Counter.class ) ) ).thenReturn( mock( MutationBatch.class ) );
timeService = mock( TimeService.class );
- when(timeService.getCurrentTime()).thenReturn( System.currentTimeMillis() );
+ when( timeService.getCurrentTime() ).thenReturn( System.currentTimeMillis() );
}
@Test
- public void testSingleShard() {
-
+ public void testSingleShard() throws InterruptedException {
- when(graphFig.getCounterFlushCount()).thenReturn( 100000l );
+ when( graphFig.getCounterFlushCount() ).thenReturn( 100000l );
NodeShardApproximation approximation =
new NodeShardApproximationImpl( graphFig, nodeShardCounterSerialization, timeService );
final Id id = createId( "test" );
- final long shardId = 0l;
+ final Shard shard = new Shard(0, 0, true);
final String type = "type";
final String type2 = "subType";
- long count = approximation.getCount( scope, id, shardId, type, type2 );
+ final DirectedEdgeMeta directedEdgeMeta = DirectedEdgeMeta.fromTargetNodeSourceType( id, type, type2 );
+
+ long count = approximation.getCount( scope, shard, directedEdgeMeta);
+
+ waitForFlush( approximation );
assertEquals( 0, count );
}
@@ -130,8 +139,6 @@ public class NodeShardApproximationTest {
public void testSingleShardMultipleThreads() throws ExecutionException, InterruptedException {
-
-
NodeShardCounterSerialization serialization = new TestNodeShardCounterSerialization();
final NodeShardApproximation approximation =
@@ -144,8 +151,10 @@ public class NodeShardApproximationTest {
final Id id = createId( "test" );
final String type = "type";
final String type2 = "subType";
- final long shardId = 10000;
+ final Shard shard = new Shard(10000, 0, true);
+
+ final DirectedEdgeMeta directedEdgeMeta = DirectedEdgeMeta.fromTargetNodeSourceType( id, type, type2 );
ExecutorService executor = Executors.newFixedThreadPool( workers );
@@ -158,7 +167,7 @@ public class NodeShardApproximationTest {
public Long call() throws Exception {
for ( int i = 0; i < increments; i++ ) {
- approximation.increment( scope, id, shardId, 1, type, type2 );
+ approximation.increment( scope, shard, 1, directedEdgeMeta );
}
return 0l;
@@ -169,24 +178,25 @@ public class NodeShardApproximationTest {
}
-
for ( Future<Long> future : futures ) {
- future.get();
+ future.get();
}
-
+ waitForFlush( approximation );
//get our count. It should be accurate b/c we only have 1 instance
- final long returnedCount = approximation.getCount( scope, id, shardId, type, type2);
+ final long returnedCount = approximation.getCount( scope, shard, directedEdgeMeta );
final long expected = workers * increments;
- assertEquals(expected, returnedCount);
-
+ assertEquals( expected, returnedCount );
+ //test we get nothing with the other type
+ final long emptyCount = approximation.getCount( scope, shard, DirectedEdgeMeta.fromSourceNodeTargetType( id, type, type2 ));
+ assertEquals( 0, emptyCount );
}
@@ -195,8 +205,6 @@ public class NodeShardApproximationTest {
public void testMultipleShardMultipleThreads() throws ExecutionException, InterruptedException {
-
-
NodeShardCounterSerialization serialization = new TestNodeShardCounterSerialization();
final NodeShardApproximation approximation =
@@ -210,27 +218,32 @@ public class NodeShardApproximationTest {
final String type = "type";
final String type2 = "subType";
- final AtomicLong shardIdCounter = new AtomicLong( );
+ final AtomicLong shardIdCounter = new AtomicLong();
+
+
+ final DirectedEdgeMeta directedEdgeMeta = DirectedEdgeMeta.fromTargetNodeSourceType( id, type, type2 );
ExecutorService executor = Executors.newFixedThreadPool( workers );
- List<Future<Long>> futures = new ArrayList<>( workers );
+ List<Future<Shard>> futures = new ArrayList<>( workers );
for ( int i = 0; i < workers; i++ ) {
- final Future<Long> future = executor.submit( new Callable<Long>() {
+ final Future<Shard> future = executor.submit( new Callable<Shard>() {
@Override
- public Long call() throws Exception {
+ public Shard call() throws Exception {
final long threadShardId = shardIdCounter.incrementAndGet();
+ final Shard shard = new Shard( threadShardId, 0, true );
+
for ( int i = 0; i < increments; i++ ) {
- approximation.increment( scope, id, threadShardId, 1, type, type2 );
+ approximation.increment( scope, shard, 1, directedEdgeMeta );
}
- return threadShardId;
+ return shard;
}
} );
@@ -238,29 +251,41 @@ public class NodeShardApproximationTest {
}
+ for ( Future<Shard> future : futures ) {
+ final Shard shardId = future.get();
- for ( Future<Long> future : futures ) {
- final long shardId = future.get();
+ waitForFlush( approximation );
- final long returnedCount = approximation.getCount( scope, id, shardId, type, type2);
+ final long returnedCount = approximation.getCount( scope, shardId, directedEdgeMeta);
- assertEquals(increments, returnedCount);
+ assertEquals( increments, returnedCount );
}
+ }
+
+
+ private void waitForFlush( NodeShardApproximation approximation ) throws InterruptedException {
+ approximation.beginFlush();
+ while ( approximation.flushPending() ) {
+ LOG.info("Waiting on beginFlush to complete");
+ Thread.sleep( 100 );
+ }
}
+
/**
* These are created b/c we can't use Mockito. It OOM's with keeping track of all the mock invocations
*/
- private static class TestNodeShardCounterSerialization implements NodeShardCounterSerialization{
+ private static class TestNodeShardCounterSerialization implements NodeShardCounterSerialization {
private Counter copy = new Counter();
+
@Override
public MutationBatch flush( final Counter counter ) {
copy.merge( counter );
@@ -281,7 +306,6 @@ public class NodeShardApproximationTest {
}
-
/**
* Simple test mutation to no-op during tests
*/
@@ -415,14 +439,14 @@ public class NodeShardApproximationTest {
}
-
- private static class TestGraphFig implements GraphFig{
+ private static class TestGraphFig implements GraphFig {
@Override
public int getScanPageSize() {
return 0; //To change body of implemented methods use File | Settings | File Templates.
}
+
@Override
public int getRepairConcurrentSize() {
return 0; //To change body of implemented methods use File | Settings | File Templates.
@@ -442,6 +466,12 @@ public class NodeShardApproximationTest {
@Override
+ public long getShardMinDelta() {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+
+ @Override
public long getShardCacheSize() {
return 0; //To change body of implemented methods use File | Settings | File Templates.
}
@@ -460,6 +490,12 @@ public class NodeShardApproximationTest {
@Override
+ public int getCounterFlushQueueSize() {
+ return 10000;
+ }
+
+
+ @Override
public void addPropertyChangeListener( final PropertyChangeListener propertyChangeListener ) {
//To change body of implemented methods use File | Settings | File Templates.
}
@@ -555,7 +591,8 @@ public class NodeShardApproximationTest {
}
}
- private static class TestTimeService implements TimeService{
+
+ private static class TestTimeService implements TimeService {
@Override
public long getCurrentTime() {
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/test/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/count/NodeShardCounterSerializationTest.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/test/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/count/NodeShardCounterSerializationTest.java b/stack/corepersistence/graph/src/test/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/count/NodeShardCounterSerializationTest.java
index 9968f67..9edc66a 100644
--- a/stack/corepersistence/graph/src/test/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/count/NodeShardCounterSerializationTest.java
+++ b/stack/corepersistence/graph/src/test/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/count/NodeShardCounterSerializationTest.java
@@ -35,6 +35,9 @@ import org.apache.usergrid.persistence.core.scope.ApplicationScope;
import org.apache.usergrid.persistence.graph.GraphFig;
import org.apache.usergrid.persistence.graph.guice.TestGraphModule;
import org.apache.usergrid.persistence.graph.serialization.EdgeSerialization;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.DirectedEdgeMeta;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.NodeType;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.Shard;
import org.apache.usergrid.persistence.model.entity.Id;
import org.apache.usergrid.persistence.model.util.UUIDGenerator;
@@ -96,11 +99,11 @@ public class NodeShardCounterSerializationTest {
final Id id = createId( "test" );
- ShardKey key1 = new ShardKey( scope, id, 0, "type1" );
+ ShardKey key1 = new ShardKey( scope, new Shard(0, 0, false), DirectedEdgeMeta.fromSourceNode( id, "type1" ) );
- ShardKey key2 = new ShardKey( scope, id, 0, "type2" );
+ ShardKey key2 = new ShardKey( scope, new Shard(0, 0, false), DirectedEdgeMeta.fromSourceNode( id, "type2" ) );
- ShardKey key3 = new ShardKey( scope, id, 1, "type1" );
+ ShardKey key3 = new ShardKey( scope, new Shard(1, 0, false), DirectedEdgeMeta.fromSourceNode( id, "type1" ) );
Counter counter = new Counter();
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/test/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/ShardEntryGroupIteratorTest.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/test/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/ShardEntryGroupIteratorTest.java b/stack/corepersistence/graph/src/test/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/ShardEntryGroupIteratorTest.java
new file mode 100644
index 0000000..5b20647
--- /dev/null
+++ b/stack/corepersistence/graph/src/test/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/ShardEntryGroupIteratorTest.java
@@ -0,0 +1,232 @@
+/*
+ *
+ * * 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.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+
+import org.junit.Test;
+
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.Shard;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.ShardEntryGroup;
+
+import static junit.framework.TestCase.assertTrue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+
+public class ShardEntryGroupIteratorTest {
+
+ @Test(expected = IllegalArgumentException.class)
+ public void noShards(){
+ final long delta = 10000;
+ final Iterator<Shard> noShards = Collections.<Shard>emptyList().iterator();
+
+ //should blow up, our iterator is empty
+ new ShardEntryGroupIterator(noShards, delta);
+
+ }
+
+ @Test
+ public void existingSingleShard(){
+
+ final Shard minShard = new Shard(0, 0, true);
+ final long delta = 10000;
+ final Iterator<Shard> noShards = Collections.singleton( minShard ).iterator();
+
+ ShardEntryGroupIterator entryGroupIterator = new ShardEntryGroupIterator(noShards, delta);
+
+ assertTrue("Root shard always present", entryGroupIterator.hasNext());
+
+ ShardEntryGroup group = entryGroupIterator.next();
+
+ assertNotNull("Group returned", group);
+
+ Collection<Shard> readShards = group.getReadShards();
+
+ assertEquals("Min shard present", 1, readShards.size());
+
+ assertTrue("Min shard present", readShards.contains( minShard ));
+
+
+ Collection<Shard> writeShards = group.getWriteShards( 0 );
+
+ assertEquals("Min shard present", 1, writeShards.size());
+
+ assertTrue("Min shard present", writeShards.contains( minShard ));
+
+
+ writeShards = group.getWriteShards( Long.MAX_VALUE );
+
+ assertEquals("Min shard present", 1, writeShards.size());
+
+ assertTrue("Min shard present", writeShards.contains( minShard ));
+
+
+ }
+
+
+ /**
+ * Tests the iterator constructs boundaries between groups correctly. In a "real" runtime environment, I expect
+ * that only the last 1 or 2 groups will actually have more than 1 entry.
+ */
+ @Test
+ public void boundedShardSets(){
+
+ /**
+ * Next shard group
+ */
+ final Shard shardGroup1Shard1 = new Shard(0, 0, true);
+
+ final Shard shardGroup1Shard2 = new Shard(10000, 100, false);
+
+ final Shard shardGroup1Shard3 = new Shard(20000, 200, false);
+
+
+ /**
+ * Middle shard group
+ */
+ final Shard shardGroup2Shard1 = new Shard(30000, 300, true);
+
+ final Shard shardGroup2Shard2 = new Shard(40000, 400, false);
+
+
+ /**
+ * Highest shard group
+ */
+
+ final Shard shardGroup3Shard1 = new Shard(50000, 500, true);
+
+ final Shard shardGroup3Shard2 = new Shard(60000, 600, false);
+
+ final Shard shardGroup3Shard3 = new Shard(70000, 700, false);
+
+
+
+
+ final long delta = 10000;
+ final Iterator<Shard> noShards = Arrays.asList(shardGroup3Shard3, shardGroup3Shard2, shardGroup3Shard1, shardGroup2Shard2, shardGroup2Shard1, shardGroup1Shard3, shardGroup1Shard2, shardGroup1Shard1 ).iterator();
+
+
+
+
+ ShardEntryGroupIterator entryGroupIterator = new ShardEntryGroupIterator(noShards, delta);
+
+ assertTrue("max group present", entryGroupIterator.hasNext());
+
+ ShardEntryGroup group = entryGroupIterator.next();
+
+ assertNotNull("Group returned", group);
+
+ Collection<Shard> readShards = group.getReadShards();
+
+ assertEquals("Min shard present", 3, readShards.size());
+
+ assertTrue("shardGroup3Shard3 shard present", readShards.contains( shardGroup3Shard3 ));
+
+ assertTrue("shardGroup3Shard2 shard present", readShards.contains( shardGroup3Shard2 ));
+
+ assertTrue("shardGroup3Shard1 shard present", readShards.contains( shardGroup3Shard1 ));
+
+
+ Collection<Shard> writeShards = group.getWriteShards( 0 );
+
+ assertEquals("Min shard present", 3, writeShards.size());
+
+ assertTrue("shardGroup3Shard3 shard present", writeShards.contains( shardGroup3Shard3 ));
+
+ assertTrue("shardGroup3Shard2 shard present", writeShards.contains( shardGroup3Shard2 ));
+
+ assertTrue("shardGroup3Shard1 shard present", writeShards.contains( shardGroup3Shard1 ));
+
+
+
+
+
+ assertTrue("middle group present", entryGroupIterator.hasNext());
+
+ group = entryGroupIterator.next();
+
+ assertNotNull("Group returned", group);
+
+ readShards = group.getReadShards();
+
+ assertEquals("Min shard present", 2, readShards.size());
+
+ assertTrue("shardGroup2Shard1 shard present", readShards.contains( shardGroup2Shard1 ));
+
+ assertTrue("shardGroup2Shard2 shard present", readShards.contains( shardGroup2Shard2 ));
+
+
+
+ writeShards = group.getWriteShards( 0 );
+
+ assertEquals("Min shard present", 2, writeShards.size());
+
+ assertTrue("shardGroup2Shard1 shard present", writeShards.contains( shardGroup2Shard1 ));
+
+ assertTrue("shardGroup2Shard2 shard present", writeShards.contains( shardGroup2Shard2 ));
+
+
+
+
+
+
+ assertTrue("min group present", entryGroupIterator.hasNext());
+
+ group = entryGroupIterator.next();
+
+ assertNotNull("Group returned", group);
+
+ readShards = group.getReadShards();
+
+ assertEquals("Min shard present", 3, readShards.size());
+
+ assertTrue("shardGroup1Shard3 shard present", readShards.contains( shardGroup1Shard3 ));
+
+ assertTrue("shardGroup1Shard2 shard present", readShards.contains( shardGroup1Shard2 ));
+
+ assertTrue("shardGroup1Shard1 shard present", readShards.contains( shardGroup1Shard1 ));
+
+
+
+ writeShards = group.getWriteShards( 0 );
+
+ assertEquals("Min shard present", 3, writeShards.size());
+
+ assertTrue("shardGroup1Shard3 shard present", writeShards.contains( shardGroup1Shard3 ));
+
+ assertTrue("shardGroup1Shard2 shard present", writeShards.contains( shardGroup1Shard2 ));
+
+ assertTrue("shardGroup1Shard1 shard present", writeShards.contains( shardGroup1Shard1 ));
+
+
+
+
+
+
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/pom.xml
----------------------------------------------------------------------
diff --git a/stack/corepersistence/pom.xml b/stack/corepersistence/pom.xml
index 4716533..9565543 100644
--- a/stack/corepersistence/pom.xml
+++ b/stack/corepersistence/pom.xml
@@ -55,7 +55,7 @@
<junit.version>4.11</junit.version>
<kryo-serializers.version>0.26</kryo-serializers.version>
<log4j.version>1.2.17</log4j.version>
- <rx.version>0.19.0</rx.version>
+ <rx.version>0.19.6</rx.version>
<slf4j.version>1.7.2</slf4j.version>
<surefire.version>2.16</surefire.version>
[4/6] Implemented New rolling shard algorithm. This should allow
eventual shard consistency between all clients without the need for an
external locking allocation system
Posted by to...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/count/ShardKey.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/count/ShardKey.java b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/count/ShardKey.java
index 63c87d3..a463b1b 100644
--- a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/count/ShardKey.java
+++ b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/count/ShardKey.java
@@ -22,6 +22,9 @@ package org.apache.usergrid.persistence.graph.serialization.impl.shard.count;
import java.util.Arrays;
import org.apache.usergrid.persistence.core.scope.ApplicationScope;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.DirectedEdgeMeta;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.NodeType;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.Shard;
import org.apache.usergrid.persistence.model.entity.Id;
@@ -29,17 +32,15 @@ import org.apache.usergrid.persistence.model.entity.Id;
* Key for shards and counts
*/
public class ShardKey {
- private final ApplicationScope scope;
- private final Id nodeId;
- private final long shardId;
- private final String[] edgeTypes;
+ public final ApplicationScope scope;
+ public final Shard shard;
+ public final DirectedEdgeMeta directedEdgeMeta;
- public ShardKey( final ApplicationScope scope, final Id nodeId, final long shardId, final String... edgeTypes ) {
+ public ShardKey( final ApplicationScope scope, final Shard shard, final DirectedEdgeMeta directedEdgeMeta ) {
this.scope = scope;
- this.nodeId = nodeId;
- this.shardId = shardId;
- this.edgeTypes = edgeTypes;
+ this.shard = shard;
+ this.directedEdgeMeta = directedEdgeMeta;
}
@@ -54,16 +55,13 @@ public class ShardKey {
final ShardKey shardKey = ( ShardKey ) o;
- if ( shardId != shardKey.shardId ) {
+ if ( !directedEdgeMeta.equals( shardKey.directedEdgeMeta ) ) {
return false;
}
- if ( !Arrays.equals( edgeTypes, shardKey.edgeTypes ) ) {
- return false;
- }
- if ( !nodeId.equals( shardKey.nodeId ) ) {
+ if ( !scope.equals( shardKey.scope ) ) {
return false;
}
- if ( !scope.equals( shardKey.scope ) ) {
+ if ( !shard.equals( shardKey.shard ) ) {
return false;
}
@@ -71,32 +69,11 @@ public class ShardKey {
}
- public ApplicationScope getScope() {
- return scope;
- }
-
-
- public Id getNodeId() {
- return nodeId;
- }
-
-
- public long getShardId() {
- return shardId;
- }
-
-
- public String[] getEdgeTypes() {
- return edgeTypes;
- }
-
-
@Override
public int hashCode() {
int result = scope.hashCode();
- result = 31 * result + nodeId.hashCode();
- result = 31 * result + ( int ) ( shardId ^ ( shardId >>> 32 ) );
- result = 31 * result + Arrays.hashCode( edgeTypes );
+ result = 31 * result + shard.hashCode();
+ result = 31 * result + directedEdgeMeta.hashCode();
return result;
}
}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/EdgeRowKey.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/EdgeRowKey.java b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/EdgeRowKey.java
deleted file mode 100644
index f4d8467..0000000
--- a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/EdgeRowKey.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * 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 org.apache.usergrid.persistence.graph.serialization.util.EdgeHasher;
-import org.apache.usergrid.persistence.model.entity.Id;
-
-
-/**
- * Used to store row keys by sourceId, targetId and edgeType
- */
-public class EdgeRowKey {
- public final Id targetId;
- public final long[] edgeTypesHash;
-
-
- public EdgeRowKey( final Id rowId, final String[] edgeTypes ) {
- this( rowId, EdgeHasher.createEdgeHash( edgeTypes ) );
- }
-
-
- public EdgeRowKey( final Id rowId, final long[] hash ) {
- this.targetId = rowId;
- this.edgeTypesHash = hash;
- }
-}
-
-
-
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/EdgeRowKeySerializer.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/EdgeRowKeySerializer.java b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/EdgeRowKeySerializer.java
index cc615c1..90b264c 100644
--- a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/EdgeRowKeySerializer.java
+++ b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/EdgeRowKeySerializer.java
@@ -21,6 +21,7 @@ package org.apache.usergrid.persistence.graph.serialization.impl.shard.impl;
import org.apache.usergrid.persistence.core.astyanax.CompositeFieldSerializer;
import org.apache.usergrid.persistence.core.astyanax.IdRowCompositeSerializer;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.EdgeRowKey;
import org.apache.usergrid.persistence.model.entity.Id;
import com.netflix.astyanax.model.CompositeBuilder;
@@ -30,6 +31,7 @@ import com.netflix.astyanax.model.CompositeParser;
/**
* Class to perform serialization for row keys from edges
*/
+
public class EdgeRowKeySerializer implements CompositeFieldSerializer<EdgeRowKey> {
private static final IdRowCompositeSerializer ID_SER = IdRowCompositeSerializer.get();
@@ -39,10 +41,10 @@ public class EdgeRowKeySerializer implements CompositeFieldSerializer<EdgeRowKey
public void toComposite( final CompositeBuilder builder, final EdgeRowKey key ) {
//add the row id to the composite
+ ID_SER.toComposite( builder, key.sourceId );
+ builder.addString( key.edgeType );
ID_SER.toComposite( builder, key.targetId );
-
- builder.addLong( key.edgeTypesHash[0] );
- builder.addLong( key.edgeTypesHash[1] );
+ builder.addLong( key.shardId );
}
@@ -50,9 +52,12 @@ public class EdgeRowKeySerializer implements CompositeFieldSerializer<EdgeRowKey
public EdgeRowKey fromComposite( final CompositeParser composite ) {
final Id sourceId = ID_SER.fromComposite( composite );
+ final String edgeType = composite.readString();
+ final Id targetId = ID_SER.fromComposite( composite );
+ final long shard = composite.readLong();
- final long[] hash = { composite.readLong(), composite.readLong() };
-
- return new EdgeRowKey( sourceId, hash );
+ return new EdgeRowKey( sourceId, edgeType, targetId, shard );
}
+
+
}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/EdgeSearcher.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/EdgeSearcher.java b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/EdgeSearcher.java
new file mode 100644
index 0000000..b7cc25d
--- /dev/null
+++ b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/EdgeSearcher.java
@@ -0,0 +1,143 @@
+package org.apache.usergrid.persistence.graph.serialization.impl.shard.impl;
+
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.usergrid.persistence.core.astyanax.ColumnParser;
+import org.apache.usergrid.persistence.core.astyanax.ScopedRowKey;
+import org.apache.usergrid.persistence.core.scope.ApplicationScope;
+import org.apache.usergrid.persistence.graph.Edge;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.Shard;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.ShardEntryGroup;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.netflix.astyanax.Serializer;
+import com.netflix.astyanax.model.Column;
+import com.netflix.astyanax.util.RangeBuilder;
+
+
+/**
+ * Searcher to be used when performing the search. Performs I/O transformation as well as parsing for the iterator. If
+ * there are more row keys available to seek, the iterator will return true
+ *
+ * @param <R> The row type
+ * @param <C> The column type
+ * @param <T> The parsed return type
+ */
+public abstract class EdgeSearcher<R, C, T> implements ColumnParser<C, T>, Comparator<T>,
+ Iterator<List<ScopedRowKey<ApplicationScope, R>>> {
+
+ protected final Optional<Edge> last;
+ protected final long maxTimestamp;
+ protected final ApplicationScope scope;
+ protected final Iterator<ShardEntryGroup> shards;
+
+
+ protected EdgeSearcher( final ApplicationScope scope, final long maxTimestamp, final Optional<Edge> last,
+ final Iterator<ShardEntryGroup> shards ) {
+
+ Preconditions.checkArgument(shards.hasNext(), "Cannot search with no possible shards");
+
+ this.scope = scope;
+ this.maxTimestamp = maxTimestamp;
+ this.last = last;
+ this.shards = shards;
+ }
+
+
+ @Override
+ public boolean hasNext() {
+ return shards.hasNext();
+ }
+
+
+ @Override
+ public List<ScopedRowKey<ApplicationScope, R>> next() {
+ Collection<Shard> readShards = shards.next().getReadShards();
+
+ List<ScopedRowKey<ApplicationScope, R>> rowKeys = new ArrayList<>(readShards.size());
+
+ for(Shard shard : readShards){
+
+ final ScopedRowKey<ApplicationScope, R> rowKey = ScopedRowKey
+ .fromKey( scope, generateRowKey(shard.getShardIndex() ) );
+
+ rowKeys.add( rowKey );
+ }
+
+
+ return rowKeys;
+ }
+
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException( "Remove is unsupported" );
+ }
+
+
+ /**
+ * Set the range on a search
+ */
+ public void setRange( final RangeBuilder builder ) {
+
+ //set our start range since it was supplied to us
+ if ( last.isPresent() ) {
+ C sourceEdge = getStartColumn( last.get() );
+
+
+ builder.setStart( sourceEdge, getSerializer() );
+ }
+ else {
+
+
+ }
+ }
+
+
+ public boolean hasPage() {
+ return last.isPresent();
+ }
+
+
+ @Override
+ public T parseColumn( final Column<C> column ) {
+ final C edge = column.getName();
+
+ return createEdge( edge, column.getBooleanValue() );
+ }
+
+
+ /**
+ * Get the column's serializer
+ */
+ protected abstract Serializer<C> getSerializer();
+
+
+ /**
+ * Create a row key for this search to use
+ *
+ * @param shard The shard to use in the row key
+ */
+ protected abstract R generateRowKey( final long shard );
+
+
+ /**
+ * Set the start column to begin searching from. The last is provided
+ */
+ protected abstract C getStartColumn( final Edge last );
+
+
+ /**
+ * Create an edge to return to the user based on the directed edge provided
+ *
+ * @param column The column name
+ * @param marked The marked flag in the column value
+ */
+ protected abstract T createEdge( final C column, final boolean marked );
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/EdgeSerializer.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/EdgeSerializer.java b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/EdgeSerializer.java
new file mode 100644
index 0000000..d93f679
--- /dev/null
+++ b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/EdgeSerializer.java
@@ -0,0 +1,77 @@
+/*
+ *
+ * * 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 org.apache.usergrid.persistence.core.astyanax.IdColDynamicCompositeSerializer;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.DirectedEdge;
+import org.apache.usergrid.persistence.model.entity.Id;
+
+import com.google.common.base.Preconditions;
+import com.netflix.astyanax.model.DynamicComposite;
+import com.netflix.astyanax.serializers.AbstractSerializer;
+import com.netflix.astyanax.serializers.LongSerializer;
+
+
+/**
+ * Serializes to a source->target edge Note that we cannot set the edge type on de-serialization. Only the target
+ * Id and version.
+ */
+public class EdgeSerializer extends AbstractSerializer<DirectedEdge> {
+
+ private static final IdColDynamicCompositeSerializer ID_COL_SERIALIZER = IdColDynamicCompositeSerializer.get();
+ private static final LongSerializer LONG_SERIALIZER = LongSerializer.get();
+
+
+ @Override
+ public ByteBuffer toByteBuffer( final DirectedEdge edge ) {
+
+ DynamicComposite composite = new DynamicComposite();
+
+ composite.addComponent( edge.timestamp, LONG_SERIALIZER );
+
+ ID_COL_SERIALIZER.toComposite( composite, edge.id );
+
+ return composite.serialize();
+ }
+
+
+ @Override
+ public DirectedEdge fromByteBuffer( final ByteBuffer byteBuffer ) {
+ DynamicComposite composite = DynamicComposite.fromByteBuffer( byteBuffer );
+
+ Preconditions.checkArgument( composite.size() == 3, "Composite should have 3 elements" );
+
+
+ //return the version
+ final long timestamp = composite.get( 0, LONG_SERIALIZER );
+
+
+ //parse our id
+ final Id id = ID_COL_SERIALIZER.fromComposite( composite, 1 );
+
+
+ return new DirectedEdge( id, timestamp );
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/EdgeShardRowKeySerializer.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/EdgeShardRowKeySerializer.java b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/EdgeShardRowKeySerializer.java
new file mode 100644
index 0000000..0451d68
--- /dev/null
+++ b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/EdgeShardRowKeySerializer.java
@@ -0,0 +1,103 @@
+/*
+ *
+ * * 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 org.apache.usergrid.persistence.core.astyanax.CompositeFieldSerializer;
+import org.apache.usergrid.persistence.core.astyanax.IdRowCompositeSerializer;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.DirectedEdgeMeta;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.NodeType;
+import org.apache.usergrid.persistence.model.entity.Id;
+
+import com.netflix.astyanax.model.CompositeBuilder;
+import com.netflix.astyanax.model.CompositeParser;
+
+
+public class EdgeShardRowKeySerializer implements CompositeFieldSerializer<DirectedEdgeMeta> {
+
+ private static final IdRowCompositeSerializer ID_SER = IdRowCompositeSerializer.get();
+
+ public static final EdgeShardRowKeySerializer INSTANCE = new EdgeShardRowKeySerializer();
+
+
+ @Override
+ public void toComposite( final CompositeBuilder builder, final DirectedEdgeMeta meta ) {
+
+
+ final DirectedEdgeMeta.NodeMeta[] nodeMeta = meta.getNodes();
+
+ //add the stored value
+ builder.addInteger( meta.getType().getStorageValue() );
+
+ final int length = nodeMeta.length;
+
+ builder.addInteger( length );
+
+
+ for ( DirectedEdgeMeta.NodeMeta node : nodeMeta ) {
+ ID_SER.toComposite( builder, node.getId() );
+ builder.addInteger( node.getNodeType().getStorageValue() );
+ }
+
+ final String[] edgeTypes = meta.getTypes();
+
+ builder.addInteger( edgeTypes.length );
+
+ for ( String type : edgeTypes ) {
+ builder.addString( type );
+ }
+ }
+
+
+ @Override
+ public DirectedEdgeMeta fromComposite( final CompositeParser composite ) {
+
+
+ final int storageType = composite.readInteger();
+
+ final DirectedEdgeMeta.MetaType metaType = DirectedEdgeMeta.MetaType.fromStorage( storageType );
+
+ final int idLength = composite.readInteger();
+
+ final DirectedEdgeMeta.NodeMeta[] nodePairs = new DirectedEdgeMeta.NodeMeta[idLength];
+
+
+ for ( int i = 0; i < idLength; i++ ) {
+ final Id sourceId = ID_SER.fromComposite( composite );
+
+ final NodeType type = NodeType.get( composite.readInteger() );
+
+ nodePairs[i] = new DirectedEdgeMeta.NodeMeta( sourceId, type );
+ }
+
+
+ final int length = composite.readInteger();
+
+ String[] types = new String[length];
+
+ for ( int i = 0; i < length; i++ ) {
+ types[i] = composite.readString();
+ }
+
+ return DirectedEdgeMeta.fromStorage( metaType, nodePairs, types );
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/EdgeShardSerializationImpl.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/EdgeShardSerializationImpl.java b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/EdgeShardSerializationImpl.java
index a08ec3b..1be3fb3 100644
--- a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/EdgeShardSerializationImpl.java
+++ b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/EdgeShardSerializationImpl.java
@@ -26,18 +26,24 @@ import java.util.Iterator;
import org.apache.cassandra.db.marshal.BytesType;
-import org.apache.usergrid.persistence.core.scope.ApplicationScope;
+import org.apache.usergrid.persistence.core.astyanax.CassandraConfig;
+import org.apache.usergrid.persistence.core.astyanax.ColumnNameIterator;
+import org.apache.usergrid.persistence.core.astyanax.ColumnParser;
+import org.apache.usergrid.persistence.core.astyanax.ColumnTypes;
+import org.apache.usergrid.persistence.core.astyanax.CompositeFieldSerializer;
+import org.apache.usergrid.persistence.core.astyanax.IdRowCompositeSerializer;
import org.apache.usergrid.persistence.core.astyanax.MultiTennantColumnFamily;
import org.apache.usergrid.persistence.core.astyanax.MultiTennantColumnFamilyDefinition;
+import org.apache.usergrid.persistence.core.astyanax.OrganizationScopedRowKeySerializer;
import org.apache.usergrid.persistence.core.astyanax.ScopedRowKey;
-import org.apache.usergrid.persistence.core.astyanax.ColumnTypes;
+import org.apache.usergrid.persistence.core.scope.ApplicationScope;
import org.apache.usergrid.persistence.core.util.ValidationUtils;
import org.apache.usergrid.persistence.graph.GraphFig;
-import org.apache.usergrid.persistence.core.astyanax.CassandraConfig;
-import org.apache.usergrid.persistence.core.astyanax.OrganizationScopedRowKeySerializer;
-import org.apache.usergrid.persistence.core.astyanax.ColumnNameIterator;
-import org.apache.usergrid.persistence.core.astyanax.ColumnParser;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.DirectedEdgeMeta;
import org.apache.usergrid.persistence.graph.serialization.impl.shard.EdgeShardSerialization;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.NodeType;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.Shard;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.ShardOperator;
import org.apache.usergrid.persistence.model.entity.Id;
import com.google.common.base.Optional;
@@ -47,6 +53,8 @@ import com.google.inject.Singleton;
import com.netflix.astyanax.Keyspace;
import com.netflix.astyanax.MutationBatch;
import com.netflix.astyanax.model.Column;
+import com.netflix.astyanax.model.CompositeBuilder;
+import com.netflix.astyanax.model.CompositeParser;
import com.netflix.astyanax.query.RowQuery;
import com.netflix.astyanax.serializers.LongSerializer;
import com.netflix.astyanax.util.RangeBuilder;
@@ -58,14 +66,12 @@ public class EdgeShardSerializationImpl implements EdgeShardSerialization {
/**
* Edge shards
*/
- private static final MultiTennantColumnFamily<ApplicationScope, EdgeRowKey, Long> EDGE_SHARDS =
+ private static final MultiTennantColumnFamily<ApplicationScope, DirectedEdgeMeta, Long> EDGE_SHARDS =
new MultiTennantColumnFamily<>( "Edge_Shards",
- new OrganizationScopedRowKeySerializer<>( new EdgeRowKeySerializer() ), LongSerializer.get() );
-
+ new OrganizationScopedRowKeySerializer<>( EdgeShardRowKeySerializer.INSTANCE ), LongSerializer.get() );
- private static final byte HOLDER = 0x00;
- private static final LongColumnParser COLUMN_PARSER = new LongColumnParser();
+ private static final ShardColumnParser COLUMN_PARSER = new ShardColumnParser();
protected final Keyspace keyspace;
@@ -83,30 +89,37 @@ public class EdgeShardSerializationImpl implements EdgeShardSerialization {
@Override
- public MutationBatch writeEdgeMeta( final ApplicationScope scope, final Id nodeId, final long shard,
- final String... types ) {
-
+ public MutationBatch writeShardMeta( final ApplicationScope scope,
+ final Shard shard, final DirectedEdgeMeta metaData) {
ValidationUtils.validateApplicationScope( scope );
- ValidationUtils.verifyIdentity(nodeId);
- Preconditions.checkArgument( shard > -1, "shardId must be greater than -1" );
- Preconditions.checkNotNull( types );
- final EdgeRowKey key = new EdgeRowKey( nodeId, types );
+ Preconditions.checkNotNull( metaData, "metadata must be present" );
- final ScopedRowKey rowKey = ScopedRowKey.fromKey( scope, key );
+ Preconditions.checkNotNull( shard );
+ Preconditions.checkArgument( shard.getShardIndex() > -1, "shardid must be greater than -1" );
+ Preconditions.checkArgument( shard.getCreatedTime() > -1, "createdTime must be greater than -1" );
+
+ final ScopedRowKey rowKey = ScopedRowKey.fromKey( scope, metaData );
final MutationBatch batch = keyspace.prepareMutationBatch();
- batch.withRow( EDGE_SHARDS, rowKey ).putColumn( shard, HOLDER );
+ batch.withTimestamp( shard.getCreatedTime() ).withRow( EDGE_SHARDS, rowKey )
+ .putColumn( shard.getShardIndex(), shard.isCompacted() );
return batch;
}
@Override
- public Iterator<Long> getEdgeMetaData( final ApplicationScope scope, final Id nodeId, final Optional<Long> start,
- final String... types ) {
+ public Iterator<Shard> getShardMetaData( final ApplicationScope scope,
+ final Optional<Shard> start, final DirectedEdgeMeta metaData ) {
+
+ ValidationUtils.validateApplicationScope( scope );
+
+
+ Preconditions.checkNotNull( metaData, "metadata must be present" );
+
/**
* If the edge is present, we need to being seeking from this
*/
@@ -114,15 +127,14 @@ public class EdgeShardSerializationImpl implements EdgeShardSerialization {
final RangeBuilder rangeBuilder = new RangeBuilder().setLimit( graphFig.getScanPageSize() );
if ( start.isPresent() ) {
- rangeBuilder.setStart( start.get() );
+ rangeBuilder.setStart( start.get().getShardIndex() );
}
- final EdgeRowKey key = new EdgeRowKey( nodeId, types );
- final ScopedRowKey rowKey = ScopedRowKey.fromKey( scope, key );
+ final ScopedRowKey rowKey = ScopedRowKey.fromKey( scope, metaData );
- final RowQuery<ScopedRowKey<ApplicationScope, EdgeRowKey>, Long> query =
+ final RowQuery<ScopedRowKey<ApplicationScope, DirectedEdgeMeta>, Long> query =
keyspace.prepareQuery( EDGE_SHARDS ).setConsistencyLevel( cassandraConfig.getReadCL() ).getKey( rowKey )
.autoPaginate( true ).withColumnRange( rangeBuilder.build() );
@@ -132,21 +144,24 @@ public class EdgeShardSerializationImpl implements EdgeShardSerialization {
@Override
- public MutationBatch removeEdgeMeta( final ApplicationScope scope, final Id nodeId, final long shard,
- final String... types ) {
+ public MutationBatch removeShardMeta( final ApplicationScope scope,
+ final Shard shard, final DirectedEdgeMeta metaData) {
ValidationUtils.validateApplicationScope( scope );
- ValidationUtils.verifyIdentity(nodeId);
- Preconditions.checkArgument( shard > -1, "shard must be greater than -1" );
- Preconditions.checkNotNull( types );
- final EdgeRowKey key = new EdgeRowKey( nodeId, types );
+ Preconditions.checkNotNull( metaData, "metadata must be present" );
+
+ Preconditions.checkNotNull( shard );
+ Preconditions.checkArgument( shard.getShardIndex() > -1, "shardid must be greater than -1" );
+ Preconditions.checkArgument( shard.getCreatedTime() > -1, "createdTime must be greater than -1" );
- final ScopedRowKey rowKey = ScopedRowKey.fromKey( scope, key );
+
+
+ final ScopedRowKey rowKey = ScopedRowKey.fromKey( scope, metaData );
final MutationBatch batch = keyspace.prepareMutationBatch();
- batch.withRow( EDGE_SHARDS, rowKey ).deleteColumn( shard );
+ batch.withRow( EDGE_SHARDS, rowKey ).deleteColumn( shard.getShardIndex() );
return batch;
}
@@ -163,11 +178,15 @@ public class EdgeShardSerializationImpl implements EdgeShardSerialization {
}
- private static class LongColumnParser implements ColumnParser<Long, Long> {
+
+
+
+
+ private static class ShardColumnParser implements ColumnParser<Long, Shard> {
@Override
- public Long parseColumn( final Column<Long> column ) {
- return column.getName();
+ public Shard parseColumn( final Column<Long> column ) {
+ return new Shard( column.getName(), column.getTimestamp(), column.getBooleanValue() );
}
}
}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/NodeShardAllocationImpl.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/NodeShardAllocationImpl.java b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/NodeShardAllocationImpl.java
index cf70669..13c34ee 100644
--- a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/NodeShardAllocationImpl.java
+++ b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/NodeShardAllocationImpl.java
@@ -20,27 +20,34 @@
package org.apache.usergrid.persistence.graph.serialization.impl.shard.impl;
-import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
import java.util.Iterator;
-import java.util.List;
-import org.apache.commons.collections4.iterators.PushbackIterator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.apache.usergrid.persistence.core.consistency.TimeService;
import org.apache.usergrid.persistence.core.scope.ApplicationScope;
import org.apache.usergrid.persistence.graph.GraphFig;
+import org.apache.usergrid.persistence.graph.MarkedEdge;
import org.apache.usergrid.persistence.graph.exception.GraphRuntimeException;
+import org.apache.usergrid.persistence.graph.impl.SimpleSearchByEdge;
+import org.apache.usergrid.persistence.graph.impl.SimpleSearchByEdgeType;
+import org.apache.usergrid.persistence.graph.impl.SimpleSearchByIdType;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.DirectedEdgeMeta;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.EdgeColumnFamilies;
import org.apache.usergrid.persistence.graph.serialization.impl.shard.EdgeShardSerialization;
import org.apache.usergrid.persistence.graph.serialization.impl.shard.NodeShardAllocation;
import org.apache.usergrid.persistence.graph.serialization.impl.shard.NodeShardApproximation;
-import org.apache.usergrid.persistence.graph.serialization.impl.shard.count.NodeShardCounterSerialization;
-import org.apache.usergrid.persistence.graph.serialization.impl.shard.count.ShardKey;
-import org.apache.usergrid.persistence.model.entity.Id;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.NodeType;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.Shard;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.ShardEntryGroup;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.ShardedEdgeSerialization;
import com.google.common.base.Optional;
import com.google.inject.Inject;
import com.netflix.astyanax.Keyspace;
-import com.netflix.astyanax.MutationBatch;
import com.netflix.astyanax.connectionpool.exceptions.ConnectionException;
@@ -50,128 +57,127 @@ import com.netflix.astyanax.connectionpool.exceptions.ConnectionException;
public class NodeShardAllocationImpl implements NodeShardAllocation {
+ private static final Logger LOG = LoggerFactory.getLogger( NodeShardAllocationImpl.class );
+
+ private static final Shard MIN_SHARD = new Shard(0, 0, true);
+
private final EdgeShardSerialization edgeShardSerialization;
-// private final NodeShardCounterSerialization edgeShardCounterSerialization;
+ private final EdgeColumnFamilies edgeColumnFamilies;
+ private final ShardedEdgeSerialization shardedEdgeSerialization;
private final NodeShardApproximation nodeShardApproximation;
private final TimeService timeService;
private final GraphFig graphFig;
+ private final Keyspace keyspace;
@Inject
public NodeShardAllocationImpl( final EdgeShardSerialization edgeShardSerialization,
- final NodeShardApproximation nodeShardApproximation,
- final TimeService timeService, final GraphFig graphFig ) {
+ final EdgeColumnFamilies edgeColumnFamilies,
+ final ShardedEdgeSerialization shardedEdgeSerialization,
+ final NodeShardApproximation nodeShardApproximation, final TimeService timeService,
+ final GraphFig graphFig, final Keyspace keyspace ) {
this.edgeShardSerialization = edgeShardSerialization;
+ this.edgeColumnFamilies = edgeColumnFamilies;
+ this.shardedEdgeSerialization = shardedEdgeSerialization;
this.nodeShardApproximation = nodeShardApproximation;
this.timeService = timeService;
this.graphFig = graphFig;
+ this.keyspace = keyspace;
}
@Override
- public Iterator<Long> getShards( final ApplicationScope scope, final Id nodeId, Optional<Long> maxShardId, final String... edgeTypes ) {
-
- final Iterator<Long> existingShards =
- edgeShardSerialization.getEdgeMetaData( scope, nodeId, maxShardId, edgeTypes );
-
- final PushbackIterator<Long> pushbackIterator = new PushbackIterator( existingShards );
-//
-//
-// final long now = timeService.getCurrentTime();
-//
-//
-// final List<Long> futures = new ArrayList<Long>();
-//
-//
-// //loop through all shards, any shard > now+1 should be deleted
-// while ( pushbackIterator.hasNext() ) {
-//
-// final Long value = pushbackIterator.next();
-//
-// //we're done, our current time uuid is greater than the value stored
-// if ( now >= value ) {
-// //push it back into the iterator
-// pushbackIterator.pushback( value );
-// break;
-// }
-//
-// futures.add( value );
-// }
-//
-//
-// //we have more than 1 future value, we need to remove it
-//
-// MutationBatch cleanup = keyspace.prepareMutationBatch();
-//
-// //remove all futures except the last one, it is the only value we shouldn't lazy remove
-// for ( int i = 0; i < futures.size() -1; i++ ) {
-// final long toRemove = futures.get( i );
-//
-// final MutationBatch batch = edgeShardSerialization.removeEdgeMeta( scope, nodeId, toRemove, edgeTypes );
-//
-// cleanup.mergeShallow( batch );
-// }
-//
-//
-// try {
-// cleanup.execute();
-// }
-// catch ( ConnectionException e ) {
-// throw new GraphRuntimeException( "Unable to remove future shards, mutation error", e );
-// }
-//
-//
-// final int futuresSize = futures.size();
-//
-// if ( futuresSize > 0 ) {
-// pushbackIterator.pushback( futures.get( futuresSize - 1 ) );
-// }
-//
-//
- /**
- * Nothing to iterate, return an iterator with 0.
- */
- if(!pushbackIterator.hasNext()){
- pushbackIterator.pushback( 0l );
+ public Iterator<ShardEntryGroup> getShards( final ApplicationScope scope,
+ final Optional<Shard> maxShardId, final DirectedEdgeMeta directedEdgeMeta ) {
+
+ Iterator<Shard> existingShards =
+ edgeShardSerialization.getShardMetaData( scope, maxShardId, directedEdgeMeta );
+
+ if(!existingShards.hasNext()){
+
+ try {
+ edgeShardSerialization.writeShardMeta( scope, MIN_SHARD, directedEdgeMeta ).execute();
+ }
+ catch ( ConnectionException e ) {
+ throw new GraphRuntimeException( "Unable to allocate minimum shard" );
+ }
+
+ existingShards = Collections.singleton( MIN_SHARD ).iterator();
}
- return pushbackIterator;
+ return new ShardEntryGroupIterator( existingShards, graphFig.getShardMinDelta() );
}
@Override
- public boolean auditMaxShard( final ApplicationScope scope, final Id nodeId, final String... edgeType ) {
+ public boolean auditShard( final ApplicationScope scope, final ShardEntryGroup shardEntryGroup, final DirectedEdgeMeta directedEdgeMeta) {
- final Iterator<Long> maxShards = getShards( scope, nodeId, Optional.<Long>absent(), edgeType );
+
+ /**
+ * Nothing to do, it's been created very recently, we don't create a new one
+ */
+ if (shardEntryGroup.isCompactionPending()) {
+ return false;
+ }
+
+ //we can't allocate, we have more than 1 write shard
+ if(shardEntryGroup.entrySize() != 1){
+ return false;
+ }
- //if the first shard has already been allocated, do nothing.
+ /**
+ * Check the min shard in our system
+ */
+ final Shard shard = shardEntryGroup.getMinShard();
+
- //now is already > than the max, don't do anything
- if ( !maxShards.hasNext() ) {
+ if (shard.getCreatedTime() >= getMinTime()){
return false;
}
- final long maxShard = maxShards.next();
/**
* Check out if we have a count for our shard allocation
*/
+ final long count =
+ nodeShardApproximation.getCount( scope, shard, directedEdgeMeta);
- final long count = nodeShardApproximation.getCount( scope, nodeId, maxShard, edgeType );
if ( count < graphFig.getShardSize() ) {
return false;
}
- //try to get a lock here, and fail if one isn't present
- final long newShardTime = timeService.getCurrentTime() + graphFig.getShardCacheTimeout() * 2;
+
+
+ /**
+ * Allocate the shard
+ */
+
+ final Iterator<MarkedEdge> edges = directedEdgeMeta.loadEdges( shardedEdgeSerialization, edgeColumnFamilies, scope, shardEntryGroup, Long.MAX_VALUE );
+
+
+ if ( !edges.hasNext() ) {
+ LOG.warn( "Tried to allocate a new shard for edge meta data {}, "
+ + "but no max value could be found in that row", directedEdgeMeta );
+ return false;
+ }
+
+ //we have a next, allocate it based on the max
+
+ MarkedEdge marked = edges.next();
+
+ final long createTimestamp = timeService.getCurrentTime();
+
+ final Shard newShard = new Shard(marked.getTimestamp(), createTimestamp, false);
try {
- this.edgeShardSerialization.writeEdgeMeta( scope, nodeId, newShardTime, edgeType ).execute();
+ this.edgeShardSerialization
+ .writeShardMeta( scope, newShard, directedEdgeMeta )
+ .execute();
}
catch ( ConnectionException e ) {
throw new GraphRuntimeException( "Unable to write the new edge metadata" );
@@ -181,4 +187,21 @@ public class NodeShardAllocationImpl implements NodeShardAllocation {
return true;
}
+
+ @Override
+ public long getMinTime() {
+
+ final long minimumAllowed = 2 * graphFig.getShardCacheTimeout();
+
+ final long minDelta = graphFig.getShardMinDelta();
+
+
+ if ( minDelta < minimumAllowed ) {
+ throw new GraphRuntimeException( String.format(
+ "You must configure the property %s to be >= 2 x %s. Otherwise you risk losing data",
+ GraphFig.SHARD_MIN_DELTA, GraphFig.SHARD_CACHE_TIMEOUT ) );
+ }
+
+ return timeService.getCurrentTime() - minDelta;
+ }
}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/NodeShardCacheImpl.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/NodeShardCacheImpl.java b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/NodeShardCacheImpl.java
index 3b78898..58b4464 100644
--- a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/NodeShardCacheImpl.java
+++ b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/NodeShardCacheImpl.java
@@ -24,16 +24,25 @@ import java.beans.PropertyChangeListener;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
-import java.util.TreeSet;
-import java.util.UUID;
+import java.util.Map;
+import java.util.TreeMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.usergrid.persistence.core.consistency.TimeService;
import org.apache.usergrid.persistence.core.scope.ApplicationScope;
import org.apache.usergrid.persistence.graph.GraphFig;
import org.apache.usergrid.persistence.graph.exception.GraphRuntimeException;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.DirectedEdgeMeta;
import org.apache.usergrid.persistence.graph.serialization.impl.shard.NodeShardAllocation;
import org.apache.usergrid.persistence.graph.serialization.impl.shard.NodeShardCache;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.NodeType;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.Shard;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.ShardEntryGroup;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.ShardOperator;
import org.apache.usergrid.persistence.graph.serialization.util.IterableUtil;
import org.apache.usergrid.persistence.model.entity.Id;
@@ -42,34 +51,49 @@ import com.google.common.base.Preconditions;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
+import com.google.common.cache.RemovalListener;
+import com.google.common.cache.RemovalNotification;
+import com.google.common.cache.Weigher;
import com.google.inject.Inject;
-
/**
* Simple implementation of the shard. Uses a local Guava shard with a timeout. If a value is not present in the
* shard, it will need to be searched via cassandra.
*/
public class NodeShardCacheImpl implements NodeShardCache {
+ private static final Logger LOG = LoggerFactory.getLogger( NodeShardCacheImpl.class );
+
+ /**
+ * Only cache shards that have < 10k groups. This is an arbitrary amount, and may change with profiling and
+ * testing
+ */
+ private static final int MAX_WEIGHT_PER_ELEMENT = 10000;
+
private final NodeShardAllocation nodeShardAllocation;
private final GraphFig graphFig;
+ private final TimeService timeservice;
private LoadingCache<CacheKey, CacheEntry> graphs;
/**
- *
- * @param nodeShardAllocation
+ * @param nodeShardAllocation
* @param graphFig
+ * @param timeservice
*/
@Inject
- public NodeShardCacheImpl( final NodeShardAllocation nodeShardAllocation, final GraphFig graphFig ) {
- Preconditions.checkNotNull(nodeShardAllocation, "nodeShardAllocation is required");
- Preconditions.checkNotNull(graphFig, "consistencyFig is required");
+ public NodeShardCacheImpl( final NodeShardAllocation nodeShardAllocation, final GraphFig graphFig,
+ final TimeService timeservice ) {
+
+ Preconditions.checkNotNull( nodeShardAllocation, "nodeShardAllocation is required" );
+ Preconditions.checkNotNull( graphFig, "consistencyFig is required" );
+ Preconditions.checkNotNull( timeservice, "timeservice is required" );
this.nodeShardAllocation = nodeShardAllocation;
this.graphFig = graphFig;
+ this.timeservice = timeservice;
/**
* Add our listener to reconstruct the shard
@@ -79,24 +103,27 @@ public class NodeShardCacheImpl implements NodeShardCache {
public void propertyChange( final PropertyChangeEvent evt ) {
final String propertyName = evt.getPropertyName();
- if ( propertyName.equals( GraphFig.SHARD_CACHE_SIZE ) || propertyName.equals( GraphFig.SHARD_CACHE_TIMEOUT ) ) {
+ if ( propertyName.equals( GraphFig.SHARD_CACHE_SIZE ) || propertyName
+ .equals( GraphFig.SHARD_CACHE_TIMEOUT ) ) {
+
updateCache();
}
}
} );
/**
- * Initialize the shard
+ * Initialize the shard cache
*/
updateCache();
}
@Override
- public long getSlice( final ApplicationScope scope, final Id nodeId, final long timestamp, final String... edgeType ) {
+ public ShardEntryGroup getWriteShardGroup( final ApplicationScope scope, final long timestamp,
+ final DirectedEdgeMeta directedEdgeMeta ) {
- final CacheKey key = new CacheKey( scope, nodeId, edgeType );
+ final CacheKey key = new CacheKey( scope, directedEdgeMeta );
CacheEntry entry;
try {
@@ -106,7 +133,7 @@ public class NodeShardCacheImpl implements NodeShardCache {
throw new GraphRuntimeException( "Unable to load shard key for graph", e );
}
- final Long shardId = entry.getShardId( timestamp );
+ final ShardEntryGroup shardId = entry.getShardId( timestamp );
if ( shardId != null ) {
return shardId;
@@ -118,55 +145,38 @@ public class NodeShardCacheImpl implements NodeShardCache {
@Override
- public Iterator<Long> getVersions( final ApplicationScope scope, final Id nodeId, final long maxTimestamp,
- final String... edgeType ) {
- final CacheKey key = new CacheKey( scope, nodeId, edgeType );
- CacheEntry entry;
-
- try {
- entry = this.graphs.get( key );
- }
- catch ( ExecutionException e ) {
- throw new GraphRuntimeException( "Unable to load shard key for graph", e );
- }
-
- Iterator<Long> iterator = entry.getShards( maxTimestamp );
-
- if(iterator == null){
- return Collections.<Long>emptyList().iterator();
+ public Iterator<ShardEntryGroup> getReadShardGroup( final ApplicationScope scope, final long maxTimestamp,
+ final DirectedEdgeMeta directedEdgeMeta ) {
+ final CacheKey key = new CacheKey( scope, directedEdgeMeta );
+ CacheEntry entry;
+
+ try {
+ entry = this.graphs.get( key );
+ }
+ catch ( ExecutionException e ) {
+ throw new GraphRuntimeException( "Unable to load shard key for graph", e );
+ }
+
+ Iterator<ShardEntryGroup> iterator = entry.getShards( maxTimestamp );
+
+ if ( iterator == null ) {
+ return Collections.<ShardEntryGroup>emptyList().iterator();
}
return iterator;
}
+
/**
* This is a race condition. We could re-init the shard while another thread is reading it. This is fine, the read
* doesn't have to be precise. The algorithm accounts for stale data.
*/
private void updateCache() {
- this.graphs = CacheBuilder.newBuilder().maximumSize( graphFig.getShardCacheSize() )
- .expireAfterWrite( graphFig.getShardCacheSize(), TimeUnit.MILLISECONDS )
- .build( new CacheLoader<CacheKey, CacheEntry>() {
-
-
- @Override
- public CacheEntry load( final CacheKey key ) throws Exception {
-
-//
-// /**
-// * Perform an audit in case we need to allocate a new shard
-// */
-// nodeShardAllocation.auditMaxShard( key.scope, key.id, key.types );
-// //TODO, we need to put some sort of upper bounds on this, it could possibly get too large
-
-
- final Iterator<Long> edges = nodeShardAllocation
- .getShards( key.scope, key.id, Optional.<Long>absent(), key.types );
-
- return new CacheEntry( edges );
- }
- } );
+ this.graphs = CacheBuilder.newBuilder().expireAfterWrite( graphFig.getShardCacheSize(), TimeUnit.MILLISECONDS )
+ .removalListener( new ShardRemovalListener() )
+ .maximumWeight( MAX_WEIGHT_PER_ELEMENT * graphFig.getShardCacheSize() )
+ .weigher( new ShardWeigher() ).build( new ShardCacheLoader() );
}
@@ -175,14 +185,12 @@ public class NodeShardCacheImpl implements NodeShardCache {
*/
private static class CacheKey {
private final ApplicationScope scope;
- private final Id id;
- private final String[] types;
+ private final DirectedEdgeMeta directedEdgeMeta;
- private CacheKey( final ApplicationScope scope, final Id id, final String[] types ) {
+ private CacheKey( final ApplicationScope scope, final DirectedEdgeMeta directedEdgeMeta ) {
this.scope = scope;
- this.id = id;
- this.types = types;
+ this.directedEdgeMeta = directedEdgeMeta;
}
@@ -197,21 +205,23 @@ public class NodeShardCacheImpl implements NodeShardCache {
final CacheKey cacheKey = ( CacheKey ) o;
- if ( !id.equals( cacheKey.id ) ) {
+ if ( !scope.equals( cacheKey.scope ) ) {
return false;
}
- if ( !Arrays.equals( types, cacheKey.types ) ) {
+
+ if ( !directedEdgeMeta.equals( cacheKey.directedEdgeMeta ) ) {
return false;
}
+
return true;
}
@Override
public int hashCode() {
- int result = id.hashCode();
- result = 31 * result + Arrays.hashCode( types );
+ int result = scope.hashCode();
+ result = 31 * result + directedEdgeMeta.hashCode();
return result;
}
}
@@ -220,38 +230,140 @@ public class NodeShardCacheImpl implements NodeShardCache {
/**
* An entry for the shard.
*/
- private static class CacheEntry {
+ public static final class CacheEntry {
/**
* Get the list of all segments
*/
- private TreeSet<Long> shards;
+ private TreeMap<Long, ShardEntryGroup> shards;
- private CacheEntry( final Iterator<Long> shards ) {
- this.shards = new TreeSet<>( );
+ private CacheEntry( final Iterator<ShardEntryGroup> shards ) {
+ Preconditions.checkArgument( shards.hasNext(),
+ "More than 1 entry must be present in the shard to load into cache" );
- for ( Long shard : IterableUtil.wrap( shards ) ) {
- this.shards.add( shard );
+ this.shards = new TreeMap<>();
+ /**
+ * TODO, we need to bound this. While I don't envision more than a thousand groups max,
+ * we don't want 1 entry to use all our ram
+ */
+ for ( ShardEntryGroup shard : IterableUtil.wrap( shards ) ) {
+ this.shards.put( shard.getMinShard().getShardIndex(), shard );
}
}
/**
- * Get the shard's UUID for the uuid we're attempting to seek from
+ * Return the size of the elements in the cache
*/
- public Long getShardId( final Long seek ) {
- return this.shards.floor( seek );
+ public int getCacheSize() {
+ return this.shards.size();
}
/**
* Get all shards <= this one in decending order
- * @return
*/
- public Iterator<Long> getShards( final Long maxShard ){
- return this.shards.headSet(maxShard, true ).descendingIterator();
+ public Iterator<ShardEntryGroup> getShards( final Long maxShard ) {
+
+ final Long firstKey = shards.floorKey( maxShard );
+
+ return shards.headMap( firstKey, true ).descendingMap().values().iterator();
+ }
+
+
+ /**
+ * Get the shard entry that should hold this value
+ */
+ public ShardEntryGroup getShardId( final Long seek ) {
+ Map.Entry<Long, ShardEntryGroup> entry = shards.floorEntry( seek );
+
+ if ( entry == null ) {
+ throw new NullPointerException( "Entry should never be null, this is a bug" );
+ }
+
+ return entry.getValue();
+ }
+ }
+
+
+ /**
+ * Load the cache entries from the shards we have stored
+ */
+ final class ShardCacheLoader extends CacheLoader<CacheKey, CacheEntry> {
+
+
+ @Override
+ public CacheEntry load( final CacheKey key ) throws Exception {
+
+
+ final Iterator<ShardEntryGroup> edges =
+ nodeShardAllocation.getShards( key.scope, Optional.<Shard>absent(), key.directedEdgeMeta );
+
+ return new CacheEntry( edges );
}
}
+ /**
+ * Calculates the weight of the entry by geting the size of the cache
+ */
+ final class ShardWeigher implements Weigher<CacheKey, CacheEntry> {
+
+ @Override
+ public int weigh( final CacheKey key, final CacheEntry value ) {
+ return value.getCacheSize();
+ }
+ }
+
+
+ /**
+ * On removal from the cache, we want to audit the maximum shard. If it needs to allocate a new shard, we want to
+ * do so. IF there's a compaction pending, we want to run the compaction task
+ */
+ final class ShardRemovalListener implements RemovalListener<CacheKey, CacheEntry> {
+
+ @Override
+ public void onRemoval( final RemovalNotification<CacheKey, CacheEntry> notification ) {
+
+
+ final CacheKey key = notification.getKey();
+ final CacheEntry entry = notification.getValue();
+
+
+ Iterator<ShardEntryGroup> groups = entry.getShards( Long.MAX_VALUE );
+
+
+ /**
+ * Start at our max, then
+ */
+
+ //audit all our groups
+ while ( groups.hasNext() ) {
+ ShardEntryGroup group = groups.next();
+
+ /**
+ * We don't have a compaction pending. Run an audit on the shards
+ */
+ if ( !group.isCompactionPending() ) {
+ LOG.debug( "No compaction pending, checking max shard on expiration" );
+ /**
+ * Check if we should allocate, we may want to
+ */
+
+
+ nodeShardAllocation.auditShard( key.scope, group, key.directedEdgeMeta );
+ continue;
+ }
+ /**
+ * Do the compaction
+ */
+ if ( group.shouldCompact( timeservice.getCurrentTime() ) ) {
+ //launch the compaction
+ }
+
+ //no op, there's nothing we need to do to this shard
+
+ }
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/RowSerializer.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/RowSerializer.java b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/RowSerializer.java
new file mode 100644
index 0000000..f901699
--- /dev/null
+++ b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/RowSerializer.java
@@ -0,0 +1,64 @@
+/*
+ *
+ * * 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 org.apache.usergrid.persistence.core.astyanax.CompositeFieldSerializer;
+import org.apache.usergrid.persistence.core.astyanax.IdRowCompositeSerializer;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.RowKey;
+import org.apache.usergrid.persistence.model.entity.Id;
+
+import com.netflix.astyanax.model.CompositeBuilder;
+import com.netflix.astyanax.model.CompositeParser;
+
+
+/**
+ * Class to perform serialization for row keys from edges
+ */
+public class RowSerializer implements CompositeFieldSerializer<RowKey> {
+
+ private static final IdRowCompositeSerializer ID_SER = IdRowCompositeSerializer.get();
+
+
+ @Override
+ public void toComposite( final CompositeBuilder builder, final RowKey key ) {
+
+ //add the row id to the composite
+ ID_SER.toComposite( builder, key.nodeId );
+
+ builder.addLong( key.hash[0] );
+ builder.addLong( key.hash[1] );
+ builder.addLong( key.shardId );
+ }
+
+
+ @Override
+ public RowKey fromComposite( final CompositeParser composite ) {
+
+ final Id id = ID_SER.fromComposite( composite );
+ final long[] hash = new long[] { composite.readLong(), composite.readLong() };
+ final long shard = composite.readLong();
+
+
+ return new RowKey( id, hash, shard );
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/RowTypeSerializer.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/RowTypeSerializer.java b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/RowTypeSerializer.java
new file mode 100644
index 0000000..6591d72
--- /dev/null
+++ b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/RowTypeSerializer.java
@@ -0,0 +1,60 @@
+/*
+ *
+ * * 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 org.apache.usergrid.persistence.core.astyanax.CompositeFieldSerializer;
+import org.apache.usergrid.persistence.core.astyanax.IdRowCompositeSerializer;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.RowKeyType;
+import org.apache.usergrid.persistence.model.entity.Id;
+
+import com.netflix.astyanax.model.CompositeBuilder;
+import com.netflix.astyanax.model.CompositeParser;
+
+
+public class RowTypeSerializer implements CompositeFieldSerializer<RowKeyType> {
+
+ private static final IdRowCompositeSerializer ID_SER = IdRowCompositeSerializer.get();
+
+
+ @Override
+ public void toComposite( final CompositeBuilder builder, final RowKeyType keyType ) {
+
+ //add the row id to the composite
+ ID_SER.toComposite( builder, keyType.nodeId );
+
+ builder.addLong( keyType.hash[0] );
+ builder.addLong( keyType.hash[1] );
+ builder.addLong( keyType.shardId );
+ }
+
+
+ @Override
+ public RowKeyType fromComposite( final CompositeParser composite ) {
+
+ final Id id = ID_SER.fromComposite( composite );
+ final long[] hash = new long[] { composite.readLong(), composite.readLong() };
+ final long shard = composite.readLong();
+
+ return new RowKeyType( id, hash, shard );
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/ShardEntryGroupIterator.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/ShardEntryGroupIterator.java b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/ShardEntryGroupIterator.java
new file mode 100644
index 0000000..c8a884b
--- /dev/null
+++ b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/ShardEntryGroupIterator.java
@@ -0,0 +1,100 @@
+package org.apache.usergrid.persistence.graph.serialization.impl.shard.impl;
+
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+import org.apache.commons.collections4.iterators.PushbackIterator;
+
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.Shard;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.ShardEntryGroup;
+
+import com.google.common.base.Preconditions;
+
+
+/**
+ * Utility class that will take an iterator of all shards, and combine them into an iterator
+ * of ShardEntryGroups. These groups can then be used in a distributed system to handle concurrent reads and writes
+ */
+public class ShardEntryGroupIterator implements Iterator<ShardEntryGroup> {
+
+
+ private ShardEntryGroup next;
+ private final PushbackIterator<Shard> sourceIterator;
+ private final long minDelta;
+
+
+ /**
+ * Create a shard iterator
+ * @param shardIterator The iterator of all shards. Order is expected to be by the shard index from Long.MAX to Long.MIN
+ * @param minDelta The minimum delta we allow to consider shards the same group
+ */
+ public ShardEntryGroupIterator( final Iterator<Shard> shardIterator, final long minDelta ) {
+ Preconditions.checkArgument(shardIterator.hasNext(), "Shard iterator must have shards present");
+ this.sourceIterator = new PushbackIterator( shardIterator );
+ this.minDelta = minDelta;
+ }
+
+
+ @Override
+ public boolean hasNext() {
+ if ( next == null ) {
+ advance();
+ }
+
+ return next != null;
+ }
+
+
+ @Override
+ public ShardEntryGroup next() {
+ if ( !hasNext() ) {
+ throw new NoSuchElementException( "No more elements exist in iterator" );
+ }
+
+
+ final ShardEntryGroup toReturn = next;
+
+ next = null;
+
+ return toReturn;
+ }
+
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException( "Remove is not supported" );
+ }
+
+
+ /**
+ * Advance to the next element
+ */
+ private void advance() {
+
+ /**
+ * We loop through until we've exhausted our source, or we have 2 elements, which means
+ * they're > min time allocation from one another
+ */
+ while ( sourceIterator.hasNext() ) {
+
+ if(next == null){
+ next = new ShardEntryGroup( minDelta );
+ }
+
+ final Shard shard = sourceIterator.next();
+
+
+ //we can't add this one to the entries, it doesn't fit within the delta, allocate a new one and break
+ if ( next.addShard( shard ) ) {
+ continue;
+ }
+
+
+ sourceIterator.pushback( shard );
+ break;
+ }
+
+
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/ShardGroupCompactionImpl.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/ShardGroupCompactionImpl.java b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/ShardGroupCompactionImpl.java
new file mode 100644
index 0000000..48336cd
--- /dev/null
+++ b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/ShardGroupCompactionImpl.java
@@ -0,0 +1,75 @@
+/*
+ *
+ * * 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.util.Collection;
+
+import org.apache.usergrid.persistence.core.consistency.TimeService;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.Shard;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.ShardEntryGroup;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.ShardGroupCompaction;
+
+import com.google.common.base.Preconditions;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+import rx.Observable;
+
+
+/**
+ * Implementation of the shard group compaction
+ */
+@Singleton
+public class ShardGroupCompactionImpl implements ShardGroupCompaction {
+
+
+ private final TimeService timeService;
+
+
+ @Inject
+ public ShardGroupCompactionImpl( final TimeService timeService ) {this.timeService = timeService;}
+
+
+ @Override
+ public Observable<Integer> compact( final ShardEntryGroup group ) {
+ final long startTime = timeService.getCurrentTime();
+
+ Preconditions.checkNotNull(group, "group cannot be null");
+ Preconditions.checkArgument( group.isCompactionPending(), "Compaction is pending" );
+ Preconditions.checkArgument( group.shouldCompact(startTime ), "Compaction can now be run" );
+
+
+ final Shard targetShard = group.getCompactionTarget();
+
+ final Collection<Shard> sourceShards = group.getReadShards();
+
+
+ //now get iterators for each of the source shards, and then copy them to the compaction target shard
+
+
+
+
+ return null;
+
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/ShardRowIterator.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/ShardRowIterator.java b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/ShardRowIterator.java
new file mode 100644
index 0000000..0bbb011
--- /dev/null
+++ b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/ShardRowIterator.java
@@ -0,0 +1,134 @@
+package org.apache.usergrid.persistence.graph.serialization.impl.shard.impl;
+
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+import org.apache.usergrid.persistence.core.astyanax.ColumnNameIterator;
+import org.apache.usergrid.persistence.core.astyanax.MultiKeyColumnNameIterator;
+import org.apache.usergrid.persistence.core.astyanax.MultiTennantColumnFamily;
+import org.apache.usergrid.persistence.core.astyanax.ScopedRowKey;
+import org.apache.usergrid.persistence.core.scope.ApplicationScope;
+
+import com.netflix.astyanax.Keyspace;
+import com.netflix.astyanax.model.ConsistencyLevel;
+import com.netflix.astyanax.query.RowQuery;
+import com.netflix.astyanax.util.RangeBuilder;
+
+
+/**
+ * Internal iterator to iterate over multiple row keys
+ *
+ * @param <R> The row type
+ * @param <C> The column type
+ * @param <T> The parsed return type
+ */
+public class ShardRowIterator<R, C, T> implements Iterator<T> {
+
+ private final EdgeSearcher<R, C, T> searcher;
+
+ private final MultiTennantColumnFamily<ApplicationScope, R, C> cf;
+
+ private Iterator<T> currentColumnIterator;
+
+ private final Keyspace keyspace;
+
+ private final int pageSize;
+
+ private final ConsistencyLevel consistencyLevel;
+
+
+ public ShardRowIterator( final EdgeSearcher<R, C, T> searcher,
+ final MultiTennantColumnFamily<ApplicationScope, R, C> cf, final Keyspace keyspace,
+ final ConsistencyLevel consistencyLevel, final int pageSize ) {
+ this.searcher = searcher;
+ this.cf = cf;
+ this.keyspace = keyspace;
+ this.pageSize = pageSize;
+ this.consistencyLevel = consistencyLevel;
+ }
+
+
+ @Override
+ public boolean hasNext() {
+ //we have more columns to return
+ if ( currentColumnIterator != null && currentColumnIterator.hasNext() ) {
+ return true;
+ }
+
+ /**
+ * We have another row key, advance to it and re-check
+ */
+ if ( searcher.hasNext() ) {
+ advanceRow();
+ return hasNext();
+ }
+
+ //we have no more columns, and no more row keys, we're done
+ return false;
+ }
+
+
+ @Override
+ public T next() {
+ if ( !hasNext() ) {
+ throw new NoSuchElementException( "There are no more rows or columns left to advance" );
+ }
+
+ return currentColumnIterator.next();
+ }
+
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException( "Remove is unsupported" );
+ }
+
+
+ /**
+ * Advance our iterator to the next row (assumes the check for row keys is elsewhere)
+ */
+ private void advanceRow() {
+
+ /**
+ * If the edge is present, we need to being seeking from this
+ */
+
+ final RangeBuilder rangeBuilder = new RangeBuilder().setLimit( pageSize );
+
+
+ //set the range into the search
+ searcher.setRange( rangeBuilder );
+
+ /**
+ * Get our list of slices
+ */
+ final List<ScopedRowKey<ApplicationScope, R>> rowKeys = searcher.next();
+
+
+ final List<ColumnNameIterator<C, T>> columnNameIterators = new ArrayList<>( rowKeys.size() );
+
+ for(ScopedRowKey<ApplicationScope, R> rowKey: rowKeys){
+
+
+
+ final RowQuery<ScopedRowKey<ApplicationScope, R>, C> query =
+ keyspace.prepareQuery( cf ).setConsistencyLevel( consistencyLevel ).getKey( rowKey )
+ .autoPaginate( true ).withColumnRange( rangeBuilder.build() );
+
+
+ final ColumnNameIterator<C, T> columnNameIterator = new ColumnNameIterator<>( query, searcher, searcher.hasPage() );
+
+ columnNameIterators.add( columnNameIterator );
+
+ }
+
+
+
+ currentColumnIterator = new MultiKeyColumnNameIterator<>(columnNameIterators, searcher, pageSize);
+
+
+ }
+}
[3/6] Implemented New rolling shard algorithm. This should allow
eventual shard consistency between all clients without the need for an
external locking allocation system
Posted by to...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/ShardedEdgeSerializationImpl.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/ShardedEdgeSerializationImpl.java b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/ShardedEdgeSerializationImpl.java
new file mode 100644
index 0000000..c33b835
--- /dev/null
+++ b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/ShardedEdgeSerializationImpl.java
@@ -0,0 +1,656 @@
+/*
+ *
+ * * 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.util.Iterator;
+import java.util.UUID;
+
+import javax.inject.Inject;
+
+import org.apache.usergrid.persistence.core.astyanax.CassandraConfig;
+import org.apache.usergrid.persistence.core.astyanax.MultiTennantColumnFamily;
+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.DirectedEdge;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.DirectedEdgeMeta;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.EdgeColumnFamilies;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.EdgeRowKey;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.EdgeShardStrategy;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.NodeType;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.RowKey;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.RowKeyType;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.Shard;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.ShardEntryGroup;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.ShardedEdgeSerialization;
+import org.apache.usergrid.persistence.graph.serialization.util.EdgeUtils;
+import org.apache.usergrid.persistence.model.entity.Id;
+
+import com.fasterxml.uuid.UUIDComparator;
+import com.fasterxml.uuid.impl.UUIDUtil;
+import com.google.common.base.Optional;
+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 static com.google.common.base.Preconditions.checkNotNull;
+
+
+@Singleton
+public class ShardedEdgeSerializationImpl implements ShardedEdgeSerialization {
+
+ protected final Keyspace keyspace;
+ protected final CassandraConfig cassandraConfig;
+ protected final GraphFig graphFig;
+ protected final EdgeShardStrategy writeEdgeShardStrategy;
+ protected final TimeService timeService;
+
+
+ @Inject
+ public ShardedEdgeSerializationImpl( final Keyspace keyspace, final CassandraConfig cassandraConfig,
+ final GraphFig graphFig, final EdgeShardStrategy writeEdgeShardStrategy,
+ final TimeService timeService ) {
+
+
+ checkNotNull( "keyspace required", keyspace );
+ checkNotNull( "cassandraConfig required", cassandraConfig );
+ checkNotNull( "consistencyFig required", graphFig );
+ checkNotNull( "writeEdgeShardStrategy required", writeEdgeShardStrategy );
+ checkNotNull( "timeService required", timeService );
+
+
+ this.keyspace = keyspace;
+ this.cassandraConfig = cassandraConfig;
+ this.graphFig = graphFig;
+ this.writeEdgeShardStrategy = writeEdgeShardStrategy;
+ this.timeService = timeService;
+ }
+
+
+ @Override
+ public MutationBatch writeEdge( final EdgeColumnFamilies columnFamilies, final ApplicationScope scope,
+ final MarkedEdge markedEdge, final UUID timestamp ) {
+ ValidationUtils.validateApplicationScope( scope );
+ EdgeUtils.validateEdge( markedEdge );
+ ValidationUtils.verifyTimeUuid( timestamp, "timestamp" );
+
+
+ final MutationBatch batch = keyspace.prepareMutationBatch().withConsistencyLevel( cassandraConfig.getWriteCL() )
+ .withTimestamp( timestamp.timestamp() );
+
+ final boolean isDeleted = markedEdge.isDeleted();
+
+
+ doWrite( columnFamilies, scope, markedEdge, new RowOp<RowKey>() {
+ @Override
+ public void writeEdge( final MultiTennantColumnFamily<ApplicationScope, RowKey, DirectedEdge> columnFamily,
+ final RowKey rowKey, final DirectedEdge edge ) {
+ batch.withRow( columnFamily, ScopedRowKey.fromKey( scope, rowKey ) ).putColumn( edge, isDeleted );
+ }
+
+
+ @Override
+ public void countEdge( final Shard shard, final DirectedEdgeMeta directedEdgeMeta ) {
+ if(!isDeleted) {
+ writeEdgeShardStrategy.increment( scope, shard, 1, directedEdgeMeta );
+ }
+ }
+
+
+
+ @Override
+ public void writeVersion( final MultiTennantColumnFamily<ApplicationScope, EdgeRowKey, Long> columnFamily,
+ final EdgeRowKey rowKey, final long timestamp ) {
+ batch.withRow( columnFamily, ScopedRowKey.fromKey( scope, rowKey ) ).putColumn( timestamp, isDeleted );
+ }
+ } );
+
+
+ return batch;
+ }
+
+
+ @Override
+ public MutationBatch deleteEdge( final EdgeColumnFamilies columnFamilies, final ApplicationScope scope,
+ final MarkedEdge markedEdge, final UUID timestamp ) {
+ ValidationUtils.validateApplicationScope( scope );
+ EdgeUtils.validateEdge( markedEdge );
+ ValidationUtils.verifyTimeUuid( timestamp, "timestamp" );
+
+
+ final MutationBatch batch = keyspace.prepareMutationBatch().withConsistencyLevel( cassandraConfig.getWriteCL() )
+ .withTimestamp( timestamp.timestamp() );
+
+
+ doWrite( columnFamilies, scope, markedEdge, new RowOp<RowKey>() {
+ @Override
+ public void writeEdge( final MultiTennantColumnFamily<ApplicationScope, RowKey, DirectedEdge> columnFamily,
+ final RowKey rowKey, final DirectedEdge edge ) {
+ batch.withRow( columnFamily, ScopedRowKey.fromKey( scope, rowKey ) ).deleteColumn( edge );
+ }
+
+
+ @Override
+ public void countEdge( final Shard shard, final DirectedEdgeMeta directedEdgeMeta ) {
+ writeEdgeShardStrategy.increment( scope, shard, -1, directedEdgeMeta );
+ }
+
+
+ @Override
+ public void writeVersion( final MultiTennantColumnFamily<ApplicationScope, EdgeRowKey, Long> columnFamily,
+ final EdgeRowKey rowKey, final long timestamp ) {
+ batch.withRow( columnFamily, ScopedRowKey.fromKey( scope, rowKey ) ).deleteColumn( timestamp );
+ }
+ } );
+
+
+ return batch;
+ }
+
+
+ /**
+ * EdgeWrite the edges internally
+ *
+ * @param scope The scope to encapsulate
+ * @param edge The edge to write
+ * @param op The row operation to invoke
+ */
+ private void doWrite( final EdgeColumnFamilies columnFamilies, final ApplicationScope scope, final MarkedEdge edge,
+ final RowOp op ) {
+ ValidationUtils.validateApplicationScope( scope );
+ EdgeUtils.validateEdge( edge );
+
+ final Id sourceNodeId = edge.getSourceNode();
+ final String sourceNodeType = sourceNodeId.getType();
+ final Id targetNodeId = edge.getTargetNode();
+ final String targetNodeType = targetNodeId.getType();
+ final long timestamp = edge.getTimestamp();
+ final String type = edge.getType();
+
+
+ /**
+ * Key in the serializers based on the edge
+ */
+
+
+ /**
+ * write edges from source->target
+ */
+
+
+ final long time = timeService.getCurrentTime();
+
+ final DirectedEdge sourceEdge = new DirectedEdge( targetNodeId, timestamp );
+
+ final DirectedEdgeMeta sourceEdgeMeta = DirectedEdgeMeta.fromSourceNode( sourceNodeId, type );
+
+
+
+ final ShardEntryGroup sourceRowKeyShard =
+ writeEdgeShardStrategy.getWriteShards( scope, timestamp, sourceEdgeMeta );
+
+ final MultiTennantColumnFamily<ApplicationScope, RowKey, DirectedEdge> sourceCf =
+ columnFamilies.getSourceNodeCfName();
+
+
+ for ( Shard shard : sourceRowKeyShard.getWriteShards(time) ) {
+
+ final long shardId = shard.getShardIndex();
+ final RowKey sourceRowKey = new RowKey( sourceNodeId, type, shardId );
+ op.writeEdge( sourceCf, sourceRowKey, sourceEdge );
+ op.countEdge( shard, sourceEdgeMeta );
+ }
+
+
+
+ final DirectedEdgeMeta sourceEdgeTargetTypeMeta = DirectedEdgeMeta.fromSourceNodeTargetType( sourceNodeId, type, targetNodeType );
+
+
+ final ShardEntryGroup sourceWithTypeRowKeyShard = writeEdgeShardStrategy
+ .getWriteShards( scope, timestamp, sourceEdgeTargetTypeMeta );
+
+ final MultiTennantColumnFamily<ApplicationScope, RowKeyType, DirectedEdge> targetCf =
+ columnFamilies.getSourceNodeTargetTypeCfName();
+
+ for ( Shard shard : sourceWithTypeRowKeyShard.getWriteShards(time) ) {
+
+ final long shardId = shard.getShardIndex();
+ final RowKeyType sourceRowKeyType = new RowKeyType( sourceNodeId, type, targetNodeId, shardId );
+
+ op.writeEdge( targetCf, sourceRowKeyType, sourceEdge );
+ op.countEdge( shard, sourceEdgeTargetTypeMeta );
+ }
+
+
+ /**
+ * write edges from target<-source
+ */
+
+ final DirectedEdge targetEdge = new DirectedEdge( sourceNodeId, timestamp );
+
+ final DirectedEdgeMeta targetEdgeMeta = DirectedEdgeMeta.fromTargetNode( targetNodeId, type );
+
+
+
+ final ShardEntryGroup targetRowKeyShard =
+ writeEdgeShardStrategy.getWriteShards( scope, timestamp, targetEdgeMeta );
+
+ final MultiTennantColumnFamily<ApplicationScope, RowKey, DirectedEdge> sourceByTargetCf =
+ columnFamilies.getTargetNodeCfName();
+
+ for ( Shard shard : targetRowKeyShard.getWriteShards(time) ) {
+ final long shardId = shard.getShardIndex();
+ final RowKey targetRowKey = new RowKey( targetNodeId, type, shardId );
+
+ op.writeEdge( sourceByTargetCf, targetRowKey, targetEdge );
+ op.countEdge( shard, targetEdgeMeta );
+ }
+
+
+
+ final DirectedEdgeMeta targetEdgeSourceTypeMeta = DirectedEdgeMeta.fromTargetNodeSourceType( targetNodeId, type, sourceNodeType );
+
+
+ final ShardEntryGroup targetWithTypeRowKeyShard = writeEdgeShardStrategy
+ .getWriteShards( scope, timestamp, targetEdgeSourceTypeMeta );
+
+ final MultiTennantColumnFamily<ApplicationScope, RowKeyType, DirectedEdge> targetBySourceCf =
+ columnFamilies.getTargetNodeSourceTypeCfName();
+
+
+ for ( Shard shard : targetWithTypeRowKeyShard.getWriteShards(time) ) {
+
+ final long shardId = shard.getShardIndex();
+
+ final RowKeyType targetRowKeyType = new RowKeyType( targetNodeId, type, sourceNodeId, shardId );
+
+
+ op.writeEdge( targetBySourceCf, targetRowKeyType, targetEdge );
+ op.countEdge( shard, targetEdgeSourceTypeMeta );
+ }
+
+ /**
+ * Always a 0l shard, we're hard limiting 2b timestamps for the same edge
+ */
+ final EdgeRowKey edgeRowKey = new EdgeRowKey( sourceNodeId, type, targetNodeId, 0l );
+
+
+ /**
+ * Write this in the timestamp log for this edge of source->target
+ */
+ op.writeVersion( columnFamilies.getGraphEdgeVersions(), edgeRowKey, timestamp );
+ }
+
+
+ @Override
+ public Iterator<MarkedEdge> getEdgeVersions( final EdgeColumnFamilies columnFamilies, final ApplicationScope scope,
+ final SearchByEdge search, final Iterator<ShardEntryGroup> shards ) {
+ ValidationUtils.validateApplicationScope( scope );
+ EdgeUtils.validateSearchByEdge( search );
+
+ final Id targetId = search.targetNode();
+ final Id sourceId = search.sourceNode();
+ final String type = search.getType();
+ final long maxTimestamp = search.getMaxTimestamp();
+ final MultiTennantColumnFamily<ApplicationScope, EdgeRowKey, Long> columnFamily =
+ columnFamilies.getGraphEdgeVersions();
+ final Serializer<Long> serializer = columnFamily.getColumnSerializer();
+
+ final EdgeSearcher<EdgeRowKey, Long, MarkedEdge> searcher =
+ new EdgeSearcher<EdgeRowKey, Long, MarkedEdge>( scope, maxTimestamp, search.last(), shards ) {
+
+ @Override
+ protected Serializer<Long> getSerializer() {
+ return serializer;
+ }
+
+
+ @Override
+ public void setRange( final RangeBuilder builder ) {
+
+
+ if ( last.isPresent() ) {
+ super.setRange( builder );
+ return;
+ }
+
+ //start seeking at a value < our max version
+ builder.setStart( maxTimestamp );
+ }
+
+
+ @Override
+ protected EdgeRowKey generateRowKey( long shard ) {
+ return new EdgeRowKey( sourceId, type, targetId, shard );
+ }
+
+
+ @Override
+ protected Long getStartColumn( final Edge last ) {
+ return last.getTimestamp();
+ }
+
+
+ @Override
+ protected MarkedEdge createEdge( final Long column, final boolean marked ) {
+ return new SimpleMarkedEdge( sourceId, type, targetId, column.longValue(), marked );
+ }
+
+
+ @Override
+ public int compare( final MarkedEdge o1, final MarkedEdge o2 ) {
+ return Long.compare( o1.getTimestamp(), o2.getTimestamp() );
+ }
+ };
+
+ return new ShardRowIterator<>( searcher, columnFamily, keyspace, cassandraConfig.getReadCL(),
+ graphFig.getScanPageSize() );
+ }
+
+
+ @Override
+ public Iterator<MarkedEdge> getEdgesFromSource( final EdgeColumnFamilies columnFamilies,
+ final ApplicationScope scope, final SearchByEdgeType edgeType,
+ final Iterator<ShardEntryGroup> shards ) {
+
+ ValidationUtils.validateApplicationScope( scope );
+ EdgeUtils.validateSearchByEdgeType( edgeType );
+
+ final Id sourceId = edgeType.getNode();
+ final String type = edgeType.getType();
+ final long maxTimestamp = edgeType.getMaxTimestamp();
+ final MultiTennantColumnFamily<ApplicationScope, RowKey, DirectedEdge> columnFamily =
+ columnFamilies.getSourceNodeCfName();
+ final Serializer<DirectedEdge> serializer = columnFamily.getColumnSerializer();
+
+
+ final SourceEdgeSearcher<RowKey, DirectedEdge, MarkedEdge> searcher =
+ new SourceEdgeSearcher<RowKey, DirectedEdge, MarkedEdge>( scope, maxTimestamp, edgeType.last(), shards ) {
+
+
+ @Override
+ protected Serializer<DirectedEdge> getSerializer() {
+ return serializer;
+ }
+
+
+ @Override
+ protected RowKey generateRowKey( long shard ) {
+ return new RowKey( sourceId, type, shard );
+ }
+
+
+ @Override
+ protected DirectedEdge getStartColumn( final Edge last ) {
+ return new DirectedEdge( last.getTargetNode(), last.getTimestamp() );
+ }
+
+
+ @Override
+ protected MarkedEdge createEdge( final DirectedEdge edge, final boolean marked ) {
+ return new SimpleMarkedEdge( sourceId, type, edge.id, edge.timestamp, marked );
+ }
+
+
+ };
+
+
+ return new ShardRowIterator<>( searcher, columnFamily, keyspace, cassandraConfig.getReadCL(),
+ graphFig.getScanPageSize() );
+ }
+
+
+ @Override
+ public Iterator<MarkedEdge> getEdgesFromSourceByTargetType( final EdgeColumnFamilies columnFamilies,
+ final ApplicationScope scope,
+ final SearchByIdType edgeType,
+ final Iterator<ShardEntryGroup> shards ) {
+
+ ValidationUtils.validateApplicationScope( scope );
+ EdgeUtils.validateSearchByEdgeType( edgeType );
+
+ final Id targetId = edgeType.getNode();
+ final String type = edgeType.getType();
+ final String targetType = edgeType.getIdType();
+ final long maxTimestamp = edgeType.getMaxTimestamp();
+ final MultiTennantColumnFamily<ApplicationScope, RowKeyType, DirectedEdge> columnFamily =
+ columnFamilies.getSourceNodeTargetTypeCfName();
+ final Serializer<DirectedEdge> serializer = columnFamily.getColumnSerializer();
+
+
+ final SourceEdgeSearcher<RowKeyType, DirectedEdge, MarkedEdge> searcher =
+ new SourceEdgeSearcher<RowKeyType, DirectedEdge, MarkedEdge>( scope, maxTimestamp, edgeType.last(), shards ) {
+
+ @Override
+ protected Serializer<DirectedEdge> getSerializer() {
+ return serializer;
+ }
+
+
+ @Override
+ protected RowKeyType generateRowKey( long shard ) {
+ return new RowKeyType( targetId, type, targetType, shard );
+ }
+
+
+ @Override
+ protected DirectedEdge getStartColumn( final Edge last ) {
+ return new DirectedEdge( last.getTargetNode(), last.getTimestamp() );
+ }
+
+
+ @Override
+ protected MarkedEdge createEdge( final DirectedEdge edge, final boolean marked ) {
+ return new SimpleMarkedEdge( targetId, type, edge.id, edge.timestamp, marked );
+ }
+
+
+
+ };
+
+ return new ShardRowIterator( searcher, columnFamily, keyspace, cassandraConfig.getReadCL(),
+ graphFig.getScanPageSize() );
+ }
+
+
+
+
+ @Override
+ public Iterator<MarkedEdge> getEdgesToTarget( final EdgeColumnFamilies columnFamilies, final ApplicationScope scope,
+ final SearchByEdgeType edgeType,
+ final Iterator<ShardEntryGroup> shards ) {
+ ValidationUtils.validateApplicationScope( scope );
+ EdgeUtils.validateSearchByEdgeType( edgeType );
+
+ final Id targetId = edgeType.getNode();
+ final String type = edgeType.getType();
+ final long maxTimestamp = edgeType.getMaxTimestamp();
+ final MultiTennantColumnFamily<ApplicationScope, RowKey, DirectedEdge> columnFamily =
+ columnFamilies.getTargetNodeCfName();
+ final Serializer<DirectedEdge> serializer = columnFamily.getColumnSerializer();
+
+ final TargetEdgeSearcher<RowKey, DirectedEdge, MarkedEdge> searcher =
+ new TargetEdgeSearcher<RowKey, DirectedEdge, MarkedEdge>( scope, maxTimestamp, edgeType.last(), shards ) {
+
+ @Override
+ protected Serializer<DirectedEdge> getSerializer() {
+ return serializer;
+ }
+
+
+ @Override
+ protected RowKey generateRowKey( long shard ) {
+ return new RowKey( targetId, type, shard );
+ }
+
+
+ @Override
+ protected DirectedEdge getStartColumn( final Edge last ) {
+ return new DirectedEdge( last.getSourceNode(), last.getTimestamp() );
+ }
+
+
+ @Override
+ protected MarkedEdge createEdge( final DirectedEdge edge, final boolean marked ) {
+ return new SimpleMarkedEdge( edge.id, type, targetId, edge.timestamp, marked );
+ }
+ };
+
+
+ return new ShardRowIterator<>( searcher, columnFamily, keyspace, cassandraConfig.getReadCL(),
+ graphFig.getScanPageSize() );
+ }
+
+
+ @Override
+ public Iterator<MarkedEdge> getEdgesToTargetBySourceType( final EdgeColumnFamilies columnFamilies,
+ final ApplicationScope scope,
+ final SearchByIdType edgeType,
+ final Iterator<ShardEntryGroup> shards ) {
+
+ ValidationUtils.validateApplicationScope( scope );
+ EdgeUtils.validateSearchByEdgeType( edgeType );
+
+ final Id targetId = edgeType.getNode();
+ final String sourceType = edgeType.getIdType();
+ final String type = edgeType.getType();
+ final long maxTimestamp = edgeType.getMaxTimestamp();
+ final MultiTennantColumnFamily<ApplicationScope, RowKeyType, DirectedEdge> columnFamily =
+ columnFamilies.getTargetNodeSourceTypeCfName();
+ final Serializer<DirectedEdge> serializer = columnFamily.getColumnSerializer();
+
+
+ final TargetEdgeSearcher<RowKeyType, DirectedEdge, MarkedEdge> searcher =
+ new TargetEdgeSearcher<RowKeyType, DirectedEdge, MarkedEdge>( scope, maxTimestamp, edgeType.last(), shards ) {
+ @Override
+ protected Serializer<DirectedEdge> getSerializer() {
+ return serializer;
+ }
+
+
+ @Override
+ protected RowKeyType generateRowKey( final long shard ) {
+ return new RowKeyType( targetId, type, sourceType, shard );
+ }
+
+
+ @Override
+ protected DirectedEdge getStartColumn( final Edge last ) {
+ return new DirectedEdge( last.getTargetNode(), last.getTimestamp() );
+ }
+
+
+ @Override
+ protected MarkedEdge createEdge( final DirectedEdge edge, final boolean marked ) {
+ return new SimpleMarkedEdge( edge.id, type, targetId, edge.timestamp, marked );
+ }
+ };
+
+ return new ShardRowIterator<>( searcher, columnFamily, keyspace, cassandraConfig.getReadCL(),
+ graphFig.getScanPageSize() );
+ }
+
+
+ /**
+ * Class for performing searched on rows based on source id
+ */
+ private static abstract class SourceEdgeSearcher<R, C, T extends Edge> extends EdgeSearcher<R, C, T>{
+
+ protected SourceEdgeSearcher( final ApplicationScope scope, final long maxTimestamp, final Optional<Edge> last,
+ final Iterator<ShardEntryGroup> shards ) {
+ super( scope, maxTimestamp, last, shards );
+ }
+
+
+ public int compare( final T o1, final T o2 ) {
+ int compare = Long.compare(o1.getTimestamp(), o2.getTimestamp());
+
+ if(compare == 0){
+ compare = o1.getTargetNode().compareTo( o2.getTargetNode());
+ }
+
+ return compare;
+ }
+
+
+ }
+
+
+ /**
+ * Class for performing searched on rows based on target id
+ */
+ private static abstract class TargetEdgeSearcher<R, C, T extends Edge> extends EdgeSearcher<R, C, T>{
+
+ protected TargetEdgeSearcher( final ApplicationScope scope, final long maxTimestamp, final Optional<Edge> last,
+ final Iterator<ShardEntryGroup> shards ) {
+ super( scope, maxTimestamp, last, shards );
+ }
+
+
+ public int compare( final T o1, final T o2 ) {
+ int compare = Long.compare(o1.getTimestamp(), o2.getTimestamp());
+
+ if(compare == 0){
+ compare = o1.getTargetNode().compareTo( o2.getTargetNode());
+ }
+
+ return compare;
+ }
+
+
+ }
+
+ /**
+ * Simple callback to perform puts and deletes with a common row setup code
+ *
+ * @param <R> The row key type
+ */
+ private static interface RowOp<R> {
+
+ /**
+ * Write the edge with the given data
+ */
+ void writeEdge( final MultiTennantColumnFamily<ApplicationScope, R, DirectedEdge> columnFamily, final R rowKey,
+ final DirectedEdge edge );
+
+ /**
+ * Perform the count on the edge
+ */
+ void countEdge( final Shard shard, final DirectedEdgeMeta directedEdgeMeta);
+
+ /**
+ * Write the edge into the version cf
+ */
+ void writeVersion( final MultiTennantColumnFamily<ApplicationScope, EdgeRowKey, Long> columnFamily,
+ final EdgeRowKey rowKey, long timestamp );
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/SizebasedEdgeColumnFamilies.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/SizebasedEdgeColumnFamilies.java b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/SizebasedEdgeColumnFamilies.java
new file mode 100644
index 0000000..9050b0a
--- /dev/null
+++ b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/SizebasedEdgeColumnFamilies.java
@@ -0,0 +1,150 @@
+/*
+ *
+ * * 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.util.Arrays;
+import java.util.Collection;
+
+import org.apache.cassandra.db.marshal.BytesType;
+import org.apache.cassandra.db.marshal.DynamicCompositeType;
+
+import org.apache.usergrid.persistence.core.astyanax.ColumnTypes;
+import org.apache.usergrid.persistence.core.astyanax.MultiTennantColumnFamily;
+import org.apache.usergrid.persistence.core.astyanax.MultiTennantColumnFamilyDefinition;
+import org.apache.usergrid.persistence.core.astyanax.OrganizationScopedRowKeySerializer;
+import org.apache.usergrid.persistence.core.scope.ApplicationScope;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.DirectedEdge;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.EdgeColumnFamilies;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.EdgeRowKey;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.RowKey;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.RowKeyType;
+
+import com.netflix.astyanax.serializers.LongSerializer;
+
+import static org.apache.usergrid.persistence.core.astyanax.ColumnTypes.LONG_TYPE_REVERSED;
+import static org.apache.usergrid.persistence.core.astyanax.ColumnTypes.UUID_TYPE_REVERSED;
+
+
+/**
+ * Implementation of size based column family
+ */
+public class SizebasedEdgeColumnFamilies implements EdgeColumnFamilies {
+
+
+ //Row key with no type
+ private static final RowSerializer ROW_SERIALIZER = new RowSerializer();
+
+ //row key with target id type
+ private static final RowTypeSerializer ROW_TYPE_SERIALIZER = new RowTypeSerializer();
+
+ private static final EdgeRowKeySerializer EDGE_ROW_KEY_SERIALIZER = new EdgeRowKeySerializer();
+
+ //Edge serializers
+ private static final EdgeSerializer EDGE_SERIALIZER = new EdgeSerializer();
+
+ private static final LongSerializer LONG_SERIALIZER = LongSerializer.get();
+
+ private static final String EDGE_DYNAMIC_COMPOSITE_TYPE =
+ //we purposefully associate lower case "l" and "u" with reversed types. This way we can use
+ //the default serialization in Astayanax, but get reverse order in cassandra
+ DynamicCompositeType.class.getSimpleName() + "(s=>UTF8Type,l=>" + LONG_TYPE_REVERSED + ",u=>"
+ + UUID_TYPE_REVERSED + ")";
+
+
+ //initialize the CF's from our implementation
+ private static final MultiTennantColumnFamily<ApplicationScope, RowKey, DirectedEdge> SOURCE_NODE_EDGES =
+ new MultiTennantColumnFamily<>( "Graph_Source_Node_Edges",
+ new OrganizationScopedRowKeySerializer<>( ROW_SERIALIZER ), EDGE_SERIALIZER );
+
+
+ private static final MultiTennantColumnFamily<ApplicationScope, RowKey, DirectedEdge> TARGET_NODE_EDGES =
+ new MultiTennantColumnFamily<>( "Graph_Target_Node_Edges",
+ new OrganizationScopedRowKeySerializer<>( ROW_SERIALIZER ), EDGE_SERIALIZER );
+
+
+ private static final MultiTennantColumnFamily<ApplicationScope, RowKeyType, DirectedEdge> SOURCE_NODE_TARGET_TYPE =
+ new MultiTennantColumnFamily<>( "Graph_Source_Node_Target_Type",
+ new OrganizationScopedRowKeySerializer<>( ROW_TYPE_SERIALIZER ), EDGE_SERIALIZER );
+
+
+ /**
+ * The edges that are to the target node with the source type. The target node is the row key
+ */
+ private static final MultiTennantColumnFamily<ApplicationScope, RowKeyType, DirectedEdge> TARGET_NODE_SOURCE_TYPE =
+ new MultiTennantColumnFamily<>( "Graph_Target_Node_Source_Type",
+ new OrganizationScopedRowKeySerializer<>( ROW_TYPE_SERIALIZER ), EDGE_SERIALIZER );
+
+
+ private static final MultiTennantColumnFamily<ApplicationScope, EdgeRowKey, Long> EDGE_VERSIONS =
+ new MultiTennantColumnFamily<>( "Graph_Edge_Versions",
+ new OrganizationScopedRowKeySerializer<>( EDGE_ROW_KEY_SERIALIZER ), LONG_SERIALIZER );
+
+
+ @Override
+ public MultiTennantColumnFamily<ApplicationScope, RowKey, DirectedEdge> getSourceNodeCfName() {
+ return SOURCE_NODE_EDGES;
+ }
+
+
+ @Override
+ public MultiTennantColumnFamily<ApplicationScope, RowKey, DirectedEdge> getTargetNodeCfName() {
+ return TARGET_NODE_EDGES;
+ }
+
+
+ @Override
+ public MultiTennantColumnFamily<ApplicationScope, RowKeyType, DirectedEdge> getSourceNodeTargetTypeCfName() {
+ return SOURCE_NODE_TARGET_TYPE;
+ }
+
+
+ @Override
+ public MultiTennantColumnFamily<ApplicationScope, RowKeyType, DirectedEdge> getTargetNodeSourceTypeCfName() {
+ return TARGET_NODE_SOURCE_TYPE;
+ }
+
+
+ @Override
+ public MultiTennantColumnFamily<ApplicationScope, EdgeRowKey, Long> getGraphEdgeVersions() {
+ return EDGE_VERSIONS;
+ }
+
+
+ @Override
+ public Collection<MultiTennantColumnFamilyDefinition> getColumnFamilies() {
+ return Arrays
+ .asList( graphCf( SOURCE_NODE_EDGES ), graphCf( TARGET_NODE_EDGES ), graphCf( SOURCE_NODE_TARGET_TYPE ),
+ graphCf( TARGET_NODE_SOURCE_TYPE ),
+ new MultiTennantColumnFamilyDefinition( EDGE_VERSIONS, BytesType.class.getSimpleName(),
+ ColumnTypes.LONG_TYPE_REVERSED, BytesType.class.getSimpleName(),
+ MultiTennantColumnFamilyDefinition.CacheOption.KEYS ) );
+ }
+
+
+ /**
+ * Helper to generate an edge definition by the type
+ */
+ private MultiTennantColumnFamilyDefinition graphCf( MultiTennantColumnFamily cf ) {
+ return new MultiTennantColumnFamilyDefinition( cf, BytesType.class.getSimpleName(), EDGE_DYNAMIC_COMPOSITE_TYPE,
+ BytesType.class.getSimpleName(), MultiTennantColumnFamilyDefinition.CacheOption.KEYS );
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/SizebasedEdgeShardStrategy.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/SizebasedEdgeShardStrategy.java b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/SizebasedEdgeShardStrategy.java
index f246f23..6558273 100644
--- a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/SizebasedEdgeShardStrategy.java
+++ b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/SizebasedEdgeShardStrategy.java
@@ -23,9 +23,14 @@ package org.apache.usergrid.persistence.graph.serialization.impl.shard.impl;
import java.util.Iterator;
import org.apache.usergrid.persistence.core.scope.ApplicationScope;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.DirectedEdgeMeta;
import org.apache.usergrid.persistence.graph.serialization.impl.shard.EdgeShardStrategy;
import org.apache.usergrid.persistence.graph.serialization.impl.shard.NodeShardApproximation;
import org.apache.usergrid.persistence.graph.serialization.impl.shard.NodeShardCache;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.NodeType;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.Shard;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.ShardEntryGroup;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.ShardOperator;
import org.apache.usergrid.persistence.model.entity.Id;
import com.google.inject.Inject;
@@ -52,52 +57,21 @@ public class SizebasedEdgeShardStrategy implements EdgeShardStrategy {
@Override
- public long getWriteShard( final ApplicationScope scope, final Id rowKeyId, final long timestamp,
- final String... types ) {
- return shardCache.getSlice( scope, rowKeyId, timestamp, types );
+ public ShardEntryGroup getWriteShards( final ApplicationScope scope,
+ final long timestamp, final DirectedEdgeMeta directedEdgeMeta ) {
+ return shardCache.getWriteShardGroup( scope, timestamp, directedEdgeMeta);
}
@Override
- public Iterator<Long> getReadShards( final ApplicationScope scope, final Id rowKeyId, final long maxTimestamp,
- final String... types ) {
- return shardCache.getVersions( scope, rowKeyId, maxTimestamp, types );
+ public Iterator<ShardEntryGroup> getReadShards( final ApplicationScope scope, final long maxTimestamp, final DirectedEdgeMeta directedEdgeMeta ) {
+ return shardCache.getReadShardGroup( scope, maxTimestamp, directedEdgeMeta );
}
@Override
- public void increment( final ApplicationScope scope, final Id rowKeyId, final long shardId, final long count,
- final String... types ) {
- shardApproximation.increment( scope, rowKeyId, shardId, count, types );
- }
-
-
- @Override
- public String getSourceNodeCfName() {
- return "Graph_Source_Node_Edges";
- }
-
-
- @Override
- public String getTargetNodeCfName() {
- return "Graph_Target_Node_Edges";
- }
-
-
- @Override
- public String getSourceNodeTargetTypeCfName() {
- return "Graph_Source_Node_Target_Type";
- }
-
-
- @Override
- public String getTargetNodeSourceTypeCfName() {
- return "Graph_Target_Node_Source_Type";
- }
-
-
- @Override
- public String getGraphEdgeVersions() {
- return "Graph_Edge_Versions";
+ public void increment( final ApplicationScope scope, final Shard shard,
+ final long count, final DirectedEdgeMeta directedEdgeMeta) {
+ shardApproximation.increment( scope, shard, count, directedEdgeMeta );
}
}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/SourceEdgeSearcher.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/SourceEdgeSearcher.java b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/SourceEdgeSearcher.java
new file mode 100644
index 0000000..b33fcaf
--- /dev/null
+++ b/stack/corepersistence/graph/src/main/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/impl/SourceEdgeSearcher.java
@@ -0,0 +1,22 @@
+/*
+ * 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;
+
+
+public class SourceEdgeSearcher {}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/test/java/org/apache/usergrid/persistence/graph/ComittedGraphManagerIT.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/test/java/org/apache/usergrid/persistence/graph/ComittedGraphManagerIT.java b/stack/corepersistence/graph/src/test/java/org/apache/usergrid/persistence/graph/ComittedGraphManagerIT.java
deleted file mode 100644
index 596480d..0000000
--- a/stack/corepersistence/graph/src/test/java/org/apache/usergrid/persistence/graph/ComittedGraphManagerIT.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- *
- * * 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;
-
-
-import java.util.concurrent.TimeUnit;
-
-import org.jukito.JukitoRunner;
-import org.jukito.UseModules;
-import org.junit.runner.RunWith;
-
-import org.apache.usergrid.persistence.core.cassandra.ITRunner;
-import org.apache.usergrid.persistence.graph.guice.TestGraphModule;
-import org.apache.usergrid.persistence.model.entity.Id;
-
-import rx.Observable;
-
-
-/**
- * Integration test that performs all calls immediately after writes without blocking. Tests that our
- * view is immediately consistent to our users, even if we have yet to perform background processing
- */
-@RunWith(ITRunner.class)
-@UseModules({ TestGraphModule.class })
-public class ComittedGraphManagerIT extends GraphManagerIT {
-
-
- @Override
- protected GraphManager getHelper(GraphManager gm) {
- return new ComittedGraphTestHelper( gm );
- }
-
-
- /**
- * Doesn't wait for the async process to happen before returning. Simply executes and immediately returns.
- */
- public static class ComittedGraphTestHelper implements GraphManager {
-
- private final GraphManager graphManager;
-
-
- public ComittedGraphTestHelper( final GraphManager graphManager ) {
- this.graphManager = graphManager;
- }
-
-
- @Override
- public Observable<Edge> writeEdge( final Edge edge ) {
- return graphManager.writeEdge( edge );
- }
-
-
- @Override
- public Observable<Edge> deleteEdge( final Edge edge ) {
- return graphManager.deleteEdge( edge );
- }
-
-
- @Override
- public Observable<Id> deleteNode( final Id node, final long timestamp) {
- return graphManager.deleteNode( node, timestamp );
- }
-
-
- @Override
- public Observable<Edge> loadEdgeVersions( final SearchByEdge edge ) {
- return graphManager.loadEdgeVersions( edge );
- }
-
-
- @Override
- public Observable<Edge> loadEdgesFromSource( final SearchByEdgeType search ) {
- return graphManager.loadEdgesFromSource( search );
- }
-
-
- @Override
- public Observable<Edge> loadEdgesToTarget( final SearchByEdgeType search ) {
- return graphManager.loadEdgesToTarget( search );
- }
-
-
- @Override
- public Observable<Edge> loadEdgesFromSourceByType( final SearchByIdType search ) {
- return graphManager.loadEdgesFromSourceByType(search);
- }
-
-
- @Override
- public Observable<Edge> loadEdgesToTargetByType( final SearchByIdType search ) {
- return graphManager.loadEdgesToTargetByType( search );
- }
-
-
- @Override
- public Observable<String> getEdgeTypesFromSource( final SearchEdgeType search ) {
- return graphManager.getEdgeTypesFromSource( search );
- }
-
-
- @Override
- public Observable<String> getIdTypesFromSource( final SearchIdType search ) {
- return graphManager.getIdTypesFromSource( search );
- }
-
-
- @Override
- public Observable<String> getEdgeTypesToTarget( final SearchEdgeType search ) {
- return graphManager.getEdgeTypesToTarget( search );
- }
-
-
- @Override
- public Observable<String> getIdTypesToTarget( final SearchIdType search ) {
- return graphManager.getIdTypesToTarget( search );
- }
-
-
- }
-}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/test/java/org/apache/usergrid/persistence/graph/CommittedGraphManagerIT.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/test/java/org/apache/usergrid/persistence/graph/CommittedGraphManagerIT.java b/stack/corepersistence/graph/src/test/java/org/apache/usergrid/persistence/graph/CommittedGraphManagerIT.java
new file mode 100644
index 0000000..3c04ab4
--- /dev/null
+++ b/stack/corepersistence/graph/src/test/java/org/apache/usergrid/persistence/graph/CommittedGraphManagerIT.java
@@ -0,0 +1,138 @@
+/*
+ *
+ * * 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;
+
+
+import java.util.concurrent.TimeUnit;
+
+import org.jukito.JukitoRunner;
+import org.jukito.UseModules;
+import org.junit.runner.RunWith;
+
+import org.apache.usergrid.persistence.core.cassandra.ITRunner;
+import org.apache.usergrid.persistence.graph.guice.TestGraphModule;
+import org.apache.usergrid.persistence.model.entity.Id;
+
+import rx.Observable;
+
+
+/**
+ * Integration test that performs all calls immediately after writes without blocking. Tests that our
+ * view is immediately consistent to our users, even if we have yet to perform background processing
+ */
+@RunWith(ITRunner.class)
+@UseModules({ TestGraphModule.class })
+public class CommittedGraphManagerIT extends GraphManagerIT {
+
+
+ @Override
+ protected GraphManager getHelper(GraphManager gm) {
+ return new ComittedGraphTestHelper( gm );
+ }
+
+
+ /**
+ * Doesn't wait for the async process to happen before returning. Simply executes and immediately returns.
+ */
+ public static class ComittedGraphTestHelper implements GraphManager {
+
+ private final GraphManager graphManager;
+
+
+ public ComittedGraphTestHelper( final GraphManager graphManager ) {
+ this.graphManager = graphManager;
+ }
+
+
+ @Override
+ public Observable<Edge> writeEdge( final Edge edge ) {
+ return graphManager.writeEdge( edge );
+ }
+
+
+ @Override
+ public Observable<Edge> deleteEdge( final Edge edge ) {
+ return graphManager.deleteEdge( edge );
+ }
+
+
+ @Override
+ public Observable<Id> deleteNode( final Id node, final long timestamp) {
+ return graphManager.deleteNode( node, timestamp );
+ }
+
+
+ @Override
+ public Observable<Edge> loadEdgeVersions( final SearchByEdge edge ) {
+ return graphManager.loadEdgeVersions( edge );
+ }
+
+
+ @Override
+ public Observable<Edge> loadEdgesFromSource( final SearchByEdgeType search ) {
+ return graphManager.loadEdgesFromSource( search );
+ }
+
+
+ @Override
+ public Observable<Edge> loadEdgesToTarget( final SearchByEdgeType search ) {
+ return graphManager.loadEdgesToTarget( search );
+ }
+
+
+ @Override
+ public Observable<Edge> loadEdgesFromSourceByType( final SearchByIdType search ) {
+ return graphManager.loadEdgesFromSourceByType(search);
+ }
+
+
+ @Override
+ public Observable<Edge> loadEdgesToTargetByType( final SearchByIdType search ) {
+ return graphManager.loadEdgesToTargetByType( search );
+ }
+
+
+ @Override
+ public Observable<String> getEdgeTypesFromSource( final SearchEdgeType search ) {
+ return graphManager.getEdgeTypesFromSource( search );
+ }
+
+
+ @Override
+ public Observable<String> getIdTypesFromSource( final SearchIdType search ) {
+ return graphManager.getIdTypesFromSource( search );
+ }
+
+
+ @Override
+ public Observable<String> getEdgeTypesToTarget( final SearchEdgeType search ) {
+ return graphManager.getEdgeTypesToTarget( search );
+ }
+
+
+ @Override
+ public Observable<String> getIdTypesToTarget( final SearchIdType search ) {
+ return graphManager.getIdTypesToTarget( search );
+ }
+
+
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/test/java/org/apache/usergrid/persistence/graph/GraphManagerShardingIT.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/test/java/org/apache/usergrid/persistence/graph/GraphManagerShardingIT.java b/stack/corepersistence/graph/src/test/java/org/apache/usergrid/persistence/graph/GraphManagerShardingIT.java
index 01e07b1..4fe3098 100644
--- a/stack/corepersistence/graph/src/test/java/org/apache/usergrid/persistence/graph/GraphManagerShardingIT.java
+++ b/stack/corepersistence/graph/src/test/java/org/apache/usergrid/persistence/graph/GraphManagerShardingIT.java
@@ -34,7 +34,10 @@ import org.apache.usergrid.persistence.core.cassandra.ITRunner;
import org.apache.usergrid.persistence.core.scope.ApplicationScope;
import org.apache.usergrid.persistence.core.scope.ApplicationScopeImpl;
import org.apache.usergrid.persistence.graph.guice.TestGraphModule;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.DirectedEdgeMeta;
import org.apache.usergrid.persistence.graph.serialization.impl.shard.NodeShardApproximation;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.NodeType;
+import org.apache.usergrid.persistence.graph.serialization.impl.shard.Shard;
import org.apache.usergrid.persistence.model.entity.Id;
import org.apache.usergrid.persistence.model.util.UUIDGenerator;
@@ -101,7 +104,7 @@ public class GraphManagerShardingIT {
//each edge causes 4 counts
final long writeCount = flushCount/4;
- assertTrue( "Shard size must be >= flush Count", maxShardSize >= flushCount );
+ assertTrue( "Shard size must be >= beginFlush Count", maxShardSize >= flushCount );
Id targetId = null;
@@ -115,14 +118,21 @@ public class GraphManagerShardingIT {
}
- long shardCount = nodeShardApproximation.getCount( scope, sourceId, 0l, edgeType );
+
+ final DirectedEdgeMeta sourceEdgeMeta = DirectedEdgeMeta.fromSourceNodeTargetType(sourceId, edgeType, targetId.getType() );
+ final Shard shard = new Shard(0, 0, true);
+
+
+ long shardCount = nodeShardApproximation.getCount( scope, shard, sourceEdgeMeta );
assertEquals("Shard count for source node should be the same as write count", writeCount, shardCount);
//now verify it's correct for the target
+ final DirectedEdgeMeta targetEdgeMeta = DirectedEdgeMeta.fromTargetNodeSourceType(targetId, edgeType, sourceId.getType() );
+
- shardCount = nodeShardApproximation.getCount( scope, targetId, 0l, edgeType );
+ shardCount = nodeShardApproximation.getCount( scope, shard, targetEdgeMeta );
assertEquals(1, shardCount);
@@ -144,14 +154,10 @@ public class GraphManagerShardingIT {
final long maxShardSize = graphFig.getShardSize();
-
-
- final long startTime = System.currentTimeMillis();
-
- //each edge causes 4 counts
+ //each edge causes 4 counts
final long writeCount = flushCount/4;
- assertTrue( "Shard size must be >= flush Count", maxShardSize >= flushCount );
+ assertTrue( "Shard size must be >= beginFlush Count", maxShardSize >= flushCount );
Id sourceId = null;
@@ -165,14 +171,27 @@ public class GraphManagerShardingIT {
}
- long shardCount = nodeShardApproximation.getCount( scope, targetId, 0l, edgeType );
+ //this is from target->source, since the target id doesn't change
+ final DirectedEdgeMeta targetMeta = DirectedEdgeMeta.fromTargetNode( targetId, edgeType );
+ final Shard shard = new Shard(0l, 0l, true);
+
+ long targetWithType = nodeShardApproximation.getCount( scope, shard, targetMeta );
+
+ assertEquals("Shard count for target node should be the same as write count", writeCount, targetWithType);
- assertEquals("Shard count for source node should be the same as write count", writeCount, shardCount);
+
+ final DirectedEdgeMeta targetNodeSource = DirectedEdgeMeta.fromTargetNodeSourceType( targetId, edgeType, "source" );
+
+ long shardCount = nodeShardApproximation.getCount( scope, shard, targetNodeSource );
+
+ assertEquals("Shard count for target node should be the same as write count", writeCount, shardCount);
//now verify it's correct for the target
- shardCount = nodeShardApproximation.getCount( scope, sourceId, 0l, edgeType );
+ final DirectedEdgeMeta sourceMeta = DirectedEdgeMeta.fromSourceNode( sourceId, edgeType );
+
+ shardCount = nodeShardApproximation.getCount( scope, shard, sourceMeta );
assertEquals(1, shardCount);
http://git-wip-us.apache.org/repos/asf/incubator-usergrid/blob/e9d652dd/stack/corepersistence/graph/src/test/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/EdgeShardSerializationTest.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/graph/src/test/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/EdgeShardSerializationTest.java b/stack/corepersistence/graph/src/test/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/EdgeShardSerializationTest.java
index 479e1bf..da57b0a 100644
--- a/stack/corepersistence/graph/src/test/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/EdgeShardSerializationTest.java
+++ b/stack/corepersistence/graph/src/test/java/org/apache/usergrid/persistence/graph/serialization/impl/shard/EdgeShardSerializationTest.java
@@ -29,10 +29,10 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.apache.usergrid.persistence.collection.guice.MigrationManagerRule;
+import org.apache.usergrid.persistence.core.cassandra.CassandraRule;
import org.apache.usergrid.persistence.core.cassandra.ITRunner;
import org.apache.usergrid.persistence.core.scope.ApplicationScope;
-import org.apache.usergrid.persistence.core.cassandra.CassandraRule;
-import org.apache.usergrid.persistence.collection.guice.MigrationManagerRule;
import org.apache.usergrid.persistence.graph.guice.TestGraphModule;
import org.apache.usergrid.persistence.model.entity.Id;
import org.apache.usergrid.persistence.model.util.UUIDGenerator;
@@ -49,8 +49,8 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
-@RunWith( ITRunner.class )
-@UseModules( { TestGraphModule.class } )
+@RunWith(ITRunner.class)
+@UseModules({ TestGraphModule.class })
public class EdgeShardSerializationTest {
@ClassRule
@@ -86,39 +86,54 @@ public class EdgeShardSerializationTest {
final Id now = createId( "test" );
- final long slice1 = 1000l;
+ final long timestamp = 10000l;
+
+ final Shard shard1 = new Shard( 1000l, timestamp, false );
- final long slice2 = slice1 * 2;
+ final Shard shard2 = new Shard( shard1.getShardIndex() * 2, timestamp, true );
- final long slice3 = slice2 * 2;
+ final Shard shard3 = new Shard( shard2.getShardIndex() * 2, timestamp, false );
- String[] types = { "edgeType", "subType" };
+ final DirectedEdgeMeta sourceEdgeMeta = DirectedEdgeMeta.fromSourceNodeTargetType(now, "edgeType", "subType" );
- MutationBatch batch = edgeShardSerialization.writeEdgeMeta( scope, now, slice1, types );
+ MutationBatch batch = edgeShardSerialization.writeShardMeta( scope, shard1, sourceEdgeMeta );
- batch.mergeShallow( edgeShardSerialization.writeEdgeMeta( scope, now, slice2, types ) );
+ batch.mergeShallow( edgeShardSerialization.writeShardMeta( scope, shard2, sourceEdgeMeta ) );
- batch.mergeShallow( edgeShardSerialization.writeEdgeMeta( scope, now, slice3, types ) );
+ batch.mergeShallow( edgeShardSerialization.writeShardMeta( scope, shard3, sourceEdgeMeta ) );
batch.execute();
- Iterator<Long> results = edgeShardSerialization.getEdgeMetaData( scope, now, Optional.<Long>absent(), types );
+ Iterator<Shard> results =
+ edgeShardSerialization.getShardMetaData( scope, Optional.<Shard>absent(), sourceEdgeMeta );
+
- assertEquals( slice3, results.next().longValue() );
+ assertEquals( shard3, results.next() );
- assertEquals( slice2, results.next().longValue() );
+ assertEquals( shard2, results.next() );
- assertEquals( slice1, results.next().longValue() );
+ assertEquals( shard1, results.next() );
+
+
+ assertFalse( results.hasNext() );
+
+ final DirectedEdgeMeta targetEdgeMeta = DirectedEdgeMeta.fromTargetNodeSourceType( now, "edgeType", "subType" );
+
+ //test we get nothing with the other node type
+ results =
+ edgeShardSerialization.getShardMetaData( scope, Optional.<Shard>absent(), targetEdgeMeta );
assertFalse( results.hasNext() );
+
//test paging and size
- results = edgeShardSerialization.getEdgeMetaData( scope, now, Optional.of( slice2 ), types );
+ results = edgeShardSerialization.getShardMetaData( scope, Optional.of( shard2 ), sourceEdgeMeta );
- assertEquals( slice2, results.next().longValue() );
+ assertEquals( shard2, results.next() );
- assertEquals( slice1, results.next().longValue() );
+
+ assertEquals( shard1, results.next() );
assertFalse( results.hasNext() );
@@ -130,50 +145,71 @@ public class EdgeShardSerializationTest {
final Id now = createId( "test" );
- final long slice1 = 1000l;
- final long slice2 = slice1 * 2;
+ final long timestamp = 10000l;
+
+ final Shard shard1 = new Shard( 1000l, timestamp, false );
+
+ final Shard shard2 = new Shard( shard1.getShardIndex() * 2, timestamp, true );
- final long slice3 = slice2 * 2;
+ final Shard shard3 = new Shard( shard2.getShardIndex() * 2, timestamp, false );
- String[] types = { "edgeType", "subType" };
- MutationBatch batch = edgeShardSerialization.writeEdgeMeta( scope, now, slice1, types );
+ final DirectedEdgeMeta sourceEdgeMeta = DirectedEdgeMeta.fromSourceNodeTargetType(now, "edgeType", "subType" );
- batch.mergeShallow( edgeShardSerialization.writeEdgeMeta( scope, now, slice2, types ) );
- batch.mergeShallow( edgeShardSerialization.writeEdgeMeta( scope, now, slice3, types ) );
+ MutationBatch batch =
+ edgeShardSerialization.writeShardMeta( scope, shard1, sourceEdgeMeta );
+
+ batch.mergeShallow(
+ edgeShardSerialization.writeShardMeta( scope, shard2, sourceEdgeMeta ) );
+
+ batch.mergeShallow(
+ edgeShardSerialization.writeShardMeta( scope, shard3, sourceEdgeMeta ) );
batch.execute();
- Iterator<Long> results = edgeShardSerialization.getEdgeMetaData( scope, now, Optional.<Long>absent(), types );
+ Iterator<Shard> results =
+ edgeShardSerialization.getShardMetaData( scope, Optional.<Shard>absent(), sourceEdgeMeta );
- assertEquals( slice3, results.next().longValue() );
+ assertEquals( shard3, results.next() );
- assertEquals( slice2, results.next().longValue() );
+ assertEquals( shard2, results.next() );
- assertEquals( slice1, results.next().longValue() );
+ assertEquals( shard1, results.next() );
assertFalse( results.hasNext() );
+ //test nothing with other type
+
+ final DirectedEdgeMeta targetEdgeMeta = DirectedEdgeMeta.fromTargetNodeSourceType( now, "edgeType", "subType" );
+
+ results =
+ edgeShardSerialization.getShardMetaData( scope, Optional.<Shard>absent(), targetEdgeMeta );
+
+ assertFalse( results.hasNext() );
+
+
//test paging and size
- edgeShardSerialization.removeEdgeMeta( scope, now, slice1, types ).execute();
+ edgeShardSerialization.removeShardMeta( scope, shard1, sourceEdgeMeta ).execute();
- results = edgeShardSerialization.getEdgeMetaData( scope, now,Optional.<Long>absent(), types );
+ results =
+ edgeShardSerialization.getShardMetaData( scope, Optional.<Shard>absent(), sourceEdgeMeta );
- assertEquals( slice3, results.next().longValue() );
+ assertEquals( shard3, results.next() );
- assertEquals( slice2, results.next().longValue() );
+ assertEquals( shard2, results.next() );
assertFalse( results.hasNext() );
- edgeShardSerialization.removeEdgeMeta( scope, now, slice2, types ).execute();
+ edgeShardSerialization.removeShardMeta( scope, shard2, sourceEdgeMeta ).execute();
- edgeShardSerialization.removeEdgeMeta( scope, now, slice3, types ).execute();
+ edgeShardSerialization.removeShardMeta( scope, shard3, sourceEdgeMeta ).execute();
- results = edgeShardSerialization.getEdgeMetaData( scope, now, Optional.<Long>absent(), types );
+ results =
+ edgeShardSerialization.getShardMetaData( scope, Optional.<Shard>absent(), sourceEdgeMeta );
assertFalse( results.hasNext() );