diff options
author | michaere <michaere@amdocs.com> | 2018-03-05 16:33:32 +0000 |
---|---|---|
committer | michaere <michaere@amdocs.com> | 2018-03-07 11:17:22 +0000 |
commit | c74f7b13b573386e70c10721fc391624ee792ed6 (patch) | |
tree | b44995474ff938b4b03c9b234f95b71bc75d6b79 /champ-lib/champ-titan/src | |
parent | 9fc28cff11a4b570618c0f533ce9de6209a5dd0c (diff) |
Port champ-microservice project restructure
Includes project restructure and introduction of a parent pom. The
original source folder and core functionality is now held within champ-lib, with champ-service
forming the ajsc microservice from which it injects champ-lib core
functionality.
Issue-ID: AAI-813
Change-Id: I2ce0c4a70e485665276e7955572de23969deb706
Signed-off-by: michaere <michaere@amdocs.com>
Diffstat (limited to 'champ-lib/champ-titan/src')
10 files changed, 1603 insertions, 0 deletions
diff --git a/champ-lib/champ-titan/src/main/java/org/onap/aai/champtitan/graph/impl/GraphSON.java b/champ-lib/champ-titan/src/main/java/org/onap/aai/champtitan/graph/impl/GraphSON.java new file mode 100644 index 0000000..e36bd53 --- /dev/null +++ b/champ-lib/champ-titan/src/main/java/org/onap/aai/champtitan/graph/impl/GraphSON.java @@ -0,0 +1,66 @@ +/** + * ============LICENSE_START========================================== + * org.onap.aai + * =================================================================== + * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017 Amdocs + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END============================================ + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.champtitan.graph.impl; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.thinkaurelius.titan.graphdb.tinkerpop.TitanIoRegistry; +import org.apache.tinkerpop.gremlin.structure.Direction; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONMapper; +import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONWriter; +import org.onap.aai.champcore.FormatMapper; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +public class GraphSON implements FormatMapper { + private final GraphSONMapper mapper; + private final GraphSONWriter writer; + protected JsonParser parser; + + public GraphSON() { + this.mapper = GraphSONMapper.build().addRegistry(TitanIoRegistry.INSTANCE).create(); + this.writer = GraphSONWriter.build().mapper(this.mapper).create(); + this.parser = new JsonParser(); + } + + public JsonObject formatObject(Object v) { + OutputStream os = new ByteArrayOutputStream(); + String result = ""; + + try { + this.writer.writeVertex(os, (Vertex)v, Direction.BOTH); + result = os.toString(); + } catch (IOException var5) { + var5.printStackTrace(); + } + + return this.parser.parse(result).getAsJsonObject(); + } + + public int parallelThreshold() { + return 50; + } + +} diff --git a/champ-lib/champ-titan/src/main/java/org/onap/aai/champtitan/graph/impl/TitanChampGraphImpl.java b/champ-lib/champ-titan/src/main/java/org/onap/aai/champtitan/graph/impl/TitanChampGraphImpl.java new file mode 100644 index 0000000..56434be --- /dev/null +++ b/champ-lib/champ-titan/src/main/java/org/onap/aai/champtitan/graph/impl/TitanChampGraphImpl.java @@ -0,0 +1,474 @@ +/** + * ============LICENSE_START========================================== + * org.onap.aai + * =================================================================== + * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017 Amdocs + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END============================================ + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.champtitan.graph.impl; + +import java.time.temporal.ChronoUnit; +import java.util.*; +import java.util.Map.Entry; +import java.util.concurrent.ExecutionException; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.onap.aai.champcore.ChampCapabilities; +import org.onap.aai.champcore.FormatMapper; +import org.onap.aai.champcore.Formatter; +import org.onap.aai.champcore.exceptions.ChampIndexNotExistsException; +import org.onap.aai.champcore.exceptions.ChampSchemaViolationException; +import org.onap.aai.champcore.graph.impl.AbstractTinkerpopChampGraph; +import org.onap.aai.champcore.model.ChampCardinality; +import org.onap.aai.champcore.model.ChampObject; +import org.onap.aai.champcore.model.ChampObjectConstraint; +import org.onap.aai.champcore.model.ChampObjectIndex; +import org.onap.aai.champcore.model.ChampPropertyConstraint; +import org.onap.aai.champcore.model.ChampRelationship; +import org.onap.aai.champcore.model.ChampRelationshipConstraint; +import org.onap.aai.champcore.model.ChampRelationshipIndex; +import org.onap.aai.champcore.model.ChampSchema; +import org.onap.aai.champcore.schema.ChampSchemaEnforcer; +import org.onap.aai.champcore.schema.DefaultChampSchemaEnforcer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.thinkaurelius.titan.core.Cardinality; +import com.thinkaurelius.titan.core.EdgeLabel; +import com.thinkaurelius.titan.core.PropertyKey; +import com.thinkaurelius.titan.core.SchemaViolationException; +import com.thinkaurelius.titan.core.TitanEdge; +import com.thinkaurelius.titan.core.TitanFactory; +import com.thinkaurelius.titan.core.TitanGraph; +import com.thinkaurelius.titan.core.TitanVertex; +import com.thinkaurelius.titan.core.schema.SchemaAction; +import com.thinkaurelius.titan.core.schema.SchemaStatus; +import com.thinkaurelius.titan.core.schema.TitanGraphIndex; +import com.thinkaurelius.titan.core.schema.TitanManagement; +import com.thinkaurelius.titan.graphdb.database.management.ManagementSystem; + +public final class TitanChampGraphImpl extends AbstractTinkerpopChampGraph { + + private static final Logger LOGGER = LoggerFactory.getLogger(TitanChampGraphImpl.class); + private static final String TITAN_UNIQUE_SUFFIX = "graph.unique-instance-id-suffix"; + private static final String TITAN_CASSANDRA_KEYSPACE = "storage.cassandra.keyspace"; + private static final String TITAN_HBASE_TABLE = "storage.hbase.table"; + private static final ChampSchemaEnforcer SCHEMA_ENFORCER = new DefaultChampSchemaEnforcer(); + private static final int REGISTER_OBJECT_INDEX_TIMEOUT_SECS = 30; + + private static final ChampCapabilities CAPABILITIES = new ChampCapabilities() { + + @Override + public boolean canDeleteObjectIndices() { + return false; + } + + @Override + public boolean canDeleteRelationshipIndices() { + return false; + } + }; + + private final TitanGraph graph; + + public TitanChampGraphImpl(Builder builder) { + super(builder.graphConfiguration); + final TitanFactory.Builder titanGraphBuilder = TitanFactory.build(); + + for (Entry<String, Object> titanGraphProperty : builder.graphConfiguration.entrySet()) { + titanGraphBuilder.set(titanGraphProperty.getKey(), titanGraphProperty.getValue()); + } + + titanGraphBuilder.set(TITAN_UNIQUE_SUFFIX, ((short) new Random().nextInt(Short.MAX_VALUE)+"")); + + final Object storageBackend = builder.graphConfiguration.get("storage.backend"); + + if ("cassandra".equals(storageBackend) || + "cassandrathrift".equals(storageBackend) || + "astyanax".equals(storageBackend) || + "embeddedcassandra".equals(storageBackend)) { + titanGraphBuilder.set(TITAN_CASSANDRA_KEYSPACE, builder.graphName); + } else if ("hbase".equals(storageBackend)) { + titanGraphBuilder.set(TITAN_HBASE_TABLE, builder.graphName); + } else if ("berkleyje".equals(storageBackend)) { + throw new RuntimeException("storage.backend=berkleyje cannot handle multiple graphs on a single DB, not usable"); + } else if ("inmemory".equals(storageBackend)) { + } else { + throw new RuntimeException("Unknown storage.backend=" + storageBackend); + } + + LOGGER.info("Instantiated data access layer for Titan graph data store with backend: " + storageBackend); + + this.graph = titanGraphBuilder.open(); + } + + public static class Builder { + private final String graphName; + + private final Map<String, Object> graphConfiguration = new HashMap<String, Object> (); + + public Builder(String graphName) { + this.graphName = graphName; + } + + public Builder(String graphName, Map<String, Object> properties) { + this.graphName = graphName; + properties(properties); + } + + public Builder properties(Map<String, Object> properties) { + if (properties.containsKey(TITAN_CASSANDRA_KEYSPACE)) + throw new IllegalArgumentException("Cannot use path " + TITAN_CASSANDRA_KEYSPACE + + " in initial configuration - this path is used" + + " to specify graph names"); + + this.graphConfiguration.putAll(properties); + return this; + } + + public Builder property(String path, Object value) { + if (path.equals(TITAN_CASSANDRA_KEYSPACE)) + throw new IllegalArgumentException("Cannot use path " + TITAN_CASSANDRA_KEYSPACE + + " in initial configuration - this path is used" + + " to specify graph names"); + graphConfiguration.put(path, value); + return this; + } + + public TitanChampGraphImpl build() { + return new TitanChampGraphImpl(this); + } + } + + @Override + protected TitanGraph getGraph() { + return graph; + } + + @Override + protected ChampSchemaEnforcer getSchemaEnforcer() { + return SCHEMA_ENFORCER; + } + + public void executeStoreObjectIndex(ChampObjectIndex index) { + if (isShutdown()) throw new IllegalStateException("Cannot call storeObjectIndex() after shutdown has been initiated"); + + final TitanGraph graph = getGraph(); + final TitanManagement createIndexMgmt = graph.openManagement(); + final PropertyKey pk = createIndexMgmt.getOrCreatePropertyKey(index.getField().getName()); + + if (createIndexMgmt.getGraphIndex(index.getName()) != null) { + createIndexMgmt.rollback(); + return; //Ignore, index already exists + } + + createIndexMgmt.buildIndex(index.getName(), Vertex.class).addKey(pk).buildCompositeIndex(); + + createIndexMgmt.commit(); + graph.tx().commit(); + + awaitIndexCreation(index.getName()); + } + + @Override + public Optional<ChampObjectIndex> retrieveObjectIndex(String indexName) { + if (isShutdown()) throw new IllegalStateException("Cannot call retrieveObjectIndex() after shutdown has been initiated"); + + final TitanManagement retrieveIndexMgmt = getGraph().openManagement(); + final TitanGraphIndex index = retrieveIndexMgmt.getGraphIndex(indexName); + + if (index == null) return Optional.empty(); + if (index.getIndexedElement() != TitanVertex.class) return Optional.empty(); + + return Optional.of(ChampObjectIndex.create() + .ofName(indexName) + .onType(ChampObject.ReservedTypes.ANY.toString()) + .forField(index.getFieldKeys()[0].name()) + .build()); + } + + @Override + public Stream<ChampObjectIndex> retrieveObjectIndices() { + if (isShutdown()) throw new IllegalStateException("Cannot call retrieveObjectIndices() after shutdown has been initiated"); + + final TitanManagement createIndexMgmt = getGraph().openManagement(); + final Iterator<TitanGraphIndex> indices = createIndexMgmt.getGraphIndexes(Vertex.class).iterator(); + + final Iterator<ChampObjectIndex> objIter = new Iterator<ChampObjectIndex> () { + + private ChampObjectIndex next; + + @Override + public boolean hasNext() { + if (indices.hasNext()) { + final TitanGraphIndex index = indices.next(); + + next = ChampObjectIndex.create() + .ofName(index.name()) + .onType(ChampObject.ReservedTypes.ANY.toString()) + .forField(index.getFieldKeys()[0].name()) + .build(); + return true; + } + + next = null; + return false; + } + + @Override + public ChampObjectIndex next() { + if (next == null) throw new NoSuchElementException(); + + return next; + } + }; + + return StreamSupport.stream(Spliterators.spliteratorUnknownSize( + objIter, Spliterator.ORDERED | Spliterator.NONNULL), false); + } + + public void executeDeleteObjectIndex(String indexName) throws ChampIndexNotExistsException { + if (isShutdown()) throw new IllegalStateException("Cannot call deleteObjectIndex() after shutdown has been initiated"); + + throw new UnsupportedOperationException("Cannot delete indices using the TitanChampImpl"); + } + + public void executeStoreRelationshipIndex(ChampRelationshipIndex index) { + if (isShutdown()) throw new IllegalStateException("Cannot call storeRelationshipIndex() after shutdown has been initiated"); + + final TitanGraph graph = getGraph(); + final TitanManagement createIndexMgmt = graph.openManagement(); + final PropertyKey pk = createIndexMgmt.getOrCreatePropertyKey(index.getField().getName()); + + if (createIndexMgmt.getGraphIndex(index.getName()) != null) return; //Ignore, index already exists + createIndexMgmt.buildIndex(index.getName(), Edge.class).addKey(pk).buildCompositeIndex(); + + createIndexMgmt.commit(); + graph.tx().commit(); + + awaitIndexCreation(index.getName()); + } + + @Override + public Optional<ChampRelationshipIndex> retrieveRelationshipIndex(String indexName) { + if (isShutdown()) throw new IllegalStateException("Cannot call retrieveRelationshipIndex() after shutdown has been initiated"); + + final TitanManagement retrieveIndexMgmt = getGraph().openManagement(); + final TitanGraphIndex index = retrieveIndexMgmt.getGraphIndex(indexName); + + if (index == null) return Optional.empty(); + if (index.getIndexedElement() != TitanEdge.class) return Optional.empty(); + + return Optional.of(ChampRelationshipIndex.create() + .ofName(indexName) + .onType(ChampObject.ReservedTypes.ANY.toString()) + .forField(index.getFieldKeys()[0].name()) + .build()); + } + + @Override + public Stream<ChampRelationshipIndex> retrieveRelationshipIndices() { + if (isShutdown()) throw new IllegalStateException("Cannot call retrieveRelationshipIndices() after shutdown has been initiated"); + + final TitanManagement createIndexMgmt = getGraph().openManagement(); + final Iterator<TitanGraphIndex> indices = createIndexMgmt.getGraphIndexes(Edge.class).iterator(); + + final Iterator<ChampRelationshipIndex> objIter = new Iterator<ChampRelationshipIndex> () { + + private ChampRelationshipIndex next; + + @Override + public boolean hasNext() { + if (indices.hasNext()) { + final TitanGraphIndex index = indices.next(); + + next = ChampRelationshipIndex.create() + .ofName(index.name()) + .onType(ChampRelationship.ReservedTypes.ANY.toString()) + .forField(index.getFieldKeys()[0].name()) + .build(); + return true; + } + + next = null; + return false; + } + + @Override + public ChampRelationshipIndex next() { + if (next == null) throw new NoSuchElementException(); + + return next; + } + }; + + return StreamSupport.stream(Spliterators.spliteratorUnknownSize( + objIter, Spliterator.ORDERED | Spliterator.NONNULL), false); + } + + public void executeDeleteRelationshipIndex(String indexName) throws ChampIndexNotExistsException { + if (isShutdown()) throw new IllegalStateException("Cannot call deleteRelationshipIndex() after shutdown has been initiated"); + + throw new UnsupportedOperationException("Cannot delete indices using the TitanChampImpl"); + } + + private Cardinality getTitanCardinality(ChampCardinality cardinality) { + switch (cardinality) { + case LIST: + return Cardinality.LIST; + case SET: + return Cardinality.SET; + case SINGLE: + return Cardinality.SINGLE; + default: + throw new RuntimeException("Unknown ChampCardinality " + cardinality); + } + } + + private void awaitIndexCreation(String indexName) { + //Wait for the index to become available + try { + if (ManagementSystem.awaitGraphIndexStatus(graph, indexName) + .status(SchemaStatus.ENABLED) + .timeout(1, ChronoUnit.SECONDS) + .call() + .getSucceeded()) { + return; //Empty graphs immediately ENABLE indices + } + + if (!ManagementSystem.awaitGraphIndexStatus(graph, indexName) + .status(SchemaStatus.REGISTERED) + .timeout(REGISTER_OBJECT_INDEX_TIMEOUT_SECS, ChronoUnit.SECONDS) + .call() + .getSucceeded()) { + LOGGER.warn("Object index was created, but timed out while waiting for it to be registered"); + return; + } + } catch (InterruptedException e) { + LOGGER.warn("Interrupted while waiting for object index creation status"); + return; + } + + //Reindex the existing data + + try { + final TitanManagement updateIndexMgmt = graph.openManagement(); + updateIndexMgmt.updateIndex(updateIndexMgmt.getGraphIndex(indexName),SchemaAction.REINDEX).get(); + updateIndexMgmt.commit(); + } catch (InterruptedException e) { + LOGGER.warn("Interrupted while reindexing for object index"); + return; + } catch (ExecutionException e) { + LOGGER.warn("Exception occurred during reindexing procedure for creating object index " + indexName, e); + } + + try { + ManagementSystem.awaitGraphIndexStatus(graph, indexName) + .status(SchemaStatus.ENABLED) + .timeout(10, ChronoUnit.MINUTES) + .call(); + } catch (InterruptedException e) { + LOGGER.warn("Interrupted while waiting for index to transition to ENABLED state"); + return; + } + } + + @Override + public ChampCapabilities capabilities() { + return CAPABILITIES; + } + + @Override + public void storeSchema(ChampSchema schema) throws ChampSchemaViolationException { + if (isShutdown()) throw new IllegalStateException("Cannot call storeSchema() after shutdown has been initiated"); + + final ChampSchema currentSchema = retrieveSchema(); + final TitanManagement mgmt = getGraph().openManagement(); + + try { + for (ChampObjectConstraint objConstraint : schema.getObjectConstraints().values()) { + for (ChampPropertyConstraint propConstraint : objConstraint.getPropertyConstraints()) { + final Optional<ChampObjectConstraint> currentObjConstraint = currentSchema.getObjectConstraint(objConstraint.getType()); + + if (currentObjConstraint.isPresent()) { + final Optional<ChampPropertyConstraint> currentPropConstraint = currentObjConstraint.get().getPropertyConstraint(propConstraint.getField().getName()); + + if (currentPropConstraint.isPresent() && currentPropConstraint.get().compareTo(propConstraint) != 0) { + throw new ChampSchemaViolationException("Cannot update already existing property on object type " + objConstraint.getType() + ": " + propConstraint); + } + } + + final String newPropertyKeyName = propConstraint.getField().getName(); + + if (mgmt.getPropertyKey(newPropertyKeyName) != null) continue; //Check Titan to see if another node created this property key + + mgmt.makePropertyKey(newPropertyKeyName) + .dataType(propConstraint.getField().getJavaType()) + .cardinality(getTitanCardinality(propConstraint.getCardinality())) + .make(); + } + } + + for (ChampRelationshipConstraint relConstraint : schema.getRelationshipConstraints().values()) { + + final Optional<ChampRelationshipConstraint> currentRelConstraint = currentSchema.getRelationshipConstraint(relConstraint.getType()); + + for (ChampPropertyConstraint propConstraint : relConstraint.getPropertyConstraints()) { + + if (currentRelConstraint.isPresent()) { + final Optional<ChampPropertyConstraint> currentPropConstraint = currentRelConstraint.get().getPropertyConstraint(propConstraint.getField().getName()); + + if (currentPropConstraint.isPresent() && currentPropConstraint.get().compareTo(propConstraint) != 0) { + throw new ChampSchemaViolationException("Cannot update already existing property on relationship type " + relConstraint.getType()); + } + } + + final String newPropertyKeyName = propConstraint.getField().getName(); + + if (mgmt.getPropertyKey(newPropertyKeyName) != null) continue; //Check Titan to see if another node created this property key + + mgmt.makePropertyKey(newPropertyKeyName) + .dataType(propConstraint.getField().getJavaType()) + .cardinality(getTitanCardinality(propConstraint.getCardinality())) + .make(); + } + + final EdgeLabel edgeLabel = mgmt.getEdgeLabel(relConstraint.getType()); + + if (edgeLabel != null) mgmt.makeEdgeLabel(relConstraint.getType()) + .directed() + .make(); + } + + mgmt.commit(); + + super.storeSchema(schema); + } catch (SchemaViolationException | ChampSchemaViolationException e) { + mgmt.rollback(); + throw new ChampSchemaViolationException(e); + } + } + + @Override + public GraphTraversal<?, ?> hasLabel(GraphTraversal<?, ?> query, Object type) { + return query.hasLabel((String) type); + } +} diff --git a/champ-lib/champ-titan/src/main/java/org/onap/aai/champtitan/perf/ChampAPIPerformanceTest.java b/champ-lib/champ-titan/src/main/java/org/onap/aai/champtitan/perf/ChampAPIPerformanceTest.java new file mode 100644 index 0000000..37ea4ff --- /dev/null +++ b/champ-lib/champ-titan/src/main/java/org/onap/aai/champtitan/perf/ChampAPIPerformanceTest.java @@ -0,0 +1,458 @@ +/** + * ============LICENSE_START========================================== + * org.onap.aai + * =================================================================== + * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017 Amdocs + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END============================================ + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.champtitan.perf; + +import com.thinkaurelius.titan.core.TitanFactory; +import com.thinkaurelius.titan.core.TitanFactory.Builder; +import com.thinkaurelius.titan.core.TitanGraph; +import com.thinkaurelius.titan.core.util.TitanCleanup; + +import org.onap.aai.champcore.ChampGraph; +import org.onap.aai.champcore.exceptions.*; +import org.onap.aai.champcore.graph.impl.InMemoryChampGraphImpl; +import org.onap.aai.champcore.model.*; +import org.onap.aai.champcore.schema.ChampSchemaEnforcer; +import org.onap.aai.champtitan.graph.impl.TitanChampGraphImpl; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.*; +import java.util.Map.Entry; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class ChampAPIPerformanceTest { + + private static final Logger LOGGER = LoggerFactory.getLogger(ChampAPIPerformanceTest.class); + + private static final int NUM_OBJECTS = 1000; + private static final int NUM_RELATIONSHIPS = 1000; + private static final String GRAPH_NAME = ChampAPIPerformanceTest.class.getSimpleName(); + + private static final String getGraphName() { + return GRAPH_NAME; + } + + private static void cleanUp(String graphName, Map<String, String> settings) { + LOGGER.debug("Cleaning up graph {}", graphName); + + try { + final Builder graphBuilder = TitanFactory.build(); + + for (Entry<String, String> setting : settings.entrySet()) { + graphBuilder.set(setting.getKey(), setting.getValue()); + } + + final String storageBackend = settings.getOrDefault("storage.backend", "inmemory"); + + if (storageBackend.equals("cassandra") || + storageBackend.equals("cassandrathrift") || + storageBackend.equals("astyanax") || + storageBackend.equals("embeddedcassandra")) { + graphBuilder.set("storage.cassandra.keyspace", graphName); + } else if (storageBackend.equals("hbase")) { + graphBuilder.set("storage.hbase.table", graphName); + } + + final TitanGraph graph = graphBuilder.open(); + + graph.close(); + TitanCleanup.clear(graph); + } catch (IllegalArgumentException e) { + LOGGER.warn("Could not clean up graph - unable to instantiate"); + } + } + + public static void main(String[] args) { + + if (args.length < 1 || !args[0].startsWith("--champcore.graph.type=")) { + throw new RuntimeException("Must provide --champcore.graph.type=" + " as first parameter"); + } + + final String graphType = args[0].split("=")[1]; + + final Map<String, String> settings = new HashMap<String, String> (); + + for (int i = 1; i < args.length; i++) { + if (!args[i].startsWith("--")) { + throw new RuntimeException("Bad command line argument: " + args[i]); + } + + final String[] keyValue = args[i].replaceFirst("--", "").split("="); + + if (keyValue.length != 2) { + throw new RuntimeException("Bad command line argument: " + args[i]); + } + + settings.put(keyValue[0], keyValue[1]); + } + + LOGGER.info("Provided graph settings: " + settings); + + if (graphType.equals("TITAN")) { + cleanUp(getGraphName(), settings); + } + + LOGGER.info("Graph cleaned, instantiating ChampGraph"); + + final ChampGraph graph; + + switch (graphType) { + case "IN_MEMORY": + final InMemoryChampGraphImpl.Builder inMemGraphBuilder = new InMemoryChampGraphImpl.Builder(); + + if (settings.containsKey("champcore.schema.enforcer")) { + final String schemaEnforcerClassStr = settings.get("champcore.schema.enforcer"); + + try { + final Class<?> schemaEnforcer = Class.forName(schemaEnforcerClassStr); + + if (!schemaEnforcer.isAssignableFrom(ChampSchemaEnforcer.class)) { + throw new RuntimeException("Unknown ChampSchemaEnforcer " + schemaEnforcer); + } + + inMemGraphBuilder.schemaEnforcer((ChampSchemaEnforcer) schemaEnforcer.newInstance()); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } catch (InstantiationException e) { + throw new RuntimeException(e); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + graph = inMemGraphBuilder.build(); + break; + case "TITAN": + final TitanChampGraphImpl.Builder graphBuilder = new TitanChampGraphImpl.Builder(getGraphName()); + + for (Entry<String, String> setting : settings.entrySet()) { + graphBuilder.property(setting.getKey(), setting.getValue()); + } + + graph = graphBuilder.build(); + break; + default: + throw new RuntimeException("Unknown ChampGraph.Type " + graphType); + } + + try { + if (graph.queryObjects(Collections.emptyMap(), Optional.empty()).limit(1).count() > 0) { + graph.shutdown(); + throw new RuntimeException("Expected empty graph"); + } + } catch (ChampTransactionException e) { + throw new RuntimeException("Transaction failure"); + } + + LOGGER.info("Graph instantiated, warming up JVM"); + warmUp(graph); + + LOGGER.info("Warm up complete, starting to record performance measurements"); + + LOGGER.info("Performance without indexing/schema"); + + storeObjects(graph, false); + storeRelationships(graph, false); + retrieveIndividualObjects(graph, false); + retrieveBulkRelationships(graph, false); + retrieveIndividualRelationships(graph, false); + + LOGGER.info("Storing indices + schema"); + + storeIndices(graph, false); + storeSchema(graph, false); + + LOGGER.info("Stored indices + schema"); + + LOGGER.info("Performance with indexing + schema"); + + storeObjects(graph, false); + storeRelationships(graph, false); + retrieveIndividualObjects(graph, false); + retrieveBulkRelationships(graph, false); + retrieveIndividualRelationships(graph, false); + + LOGGER.info("Performance test complete, shutting down graph"); + + graph.shutdown(); + + LOGGER.info("Graph shutdown, JVM exiting"); + } + + private static void storeSchema(ChampGraph graph, boolean warmUp) { + try { + graph.storeSchema( + ChampSchema.create() + .withObjectConstraint() + .onType("foo") + .withPropertyConstraint() + .onField("fooObjectNumber") + .optional() + .build() + .build() + .withRelationshipConstraint() + .onType("bar") + .withPropertyConstraint() + .onField("barObjectNumber") + .ofType(ChampField.Type.INTEGER) + .optional() + .build() + .build() + .build() + ); + } catch (ChampSchemaViolationException e) { + throw new AssertionError(e); + } + } + + private static void storeIndices(ChampGraph graph, boolean warmUp) { + graph.storeObjectIndex( + ChampObjectIndex.create() + .ofName("objectNumberIndex") + .onType("foo") + .forField("objectNumber") + .build() + ); + + graph.storeRelationshipIndex(ChampRelationshipIndex.create() + .ofName("relationshipNumberIndex") + .onType("bazz") + .forField("relationshipNumber") + .build() + ); + } + + private static void warmUp(ChampGraph graph) { + storeObjects(graph, false); + storeRelationships(graph, false); + retrieveIndividualObjects(graph, false); + retrieveBulkRelationships(graph, false); + retrieveIndividualRelationships(graph, false); + } + + private static void retrieveIndividualRelationships(ChampGraph graph, boolean warmUp) { + final double[] latencies = new double[NUM_RELATIONSHIPS]; + final long totalStartTime = System.nanoTime(); + + for (int i = 0; i < NUM_RELATIONSHIPS; i++) { + final long startTime = System.nanoTime(); + + Stream<ChampRelationship> objects; + try { + objects = graph.queryRelationships(Collections.singletonMap("relationshipNumber", i), Optional.empty()); + } catch (ChampTransactionException e) { + throw new RuntimeException(e); + } + + objects.findFirst().get(); + final double elapsedMs = (System.nanoTime() - startTime) / 1000.0 / 1000.0; + latencies[i] = elapsedMs; + } + + final double totalElapsedTimeSecs = (System.nanoTime() - totalStartTime) / 1000.0 / 1000.0 / 1000.0; + LOGGER.info("Individually read " + NUM_RELATIONSHIPS + " relationships in " + totalElapsedTimeSecs + "s (" + NUM_RELATIONSHIPS / totalElapsedTimeSecs + " relationships/s)"); + + Arrays.sort(latencies); + + if (!warmUp) { + LOGGER.info("Retrieve individual relationship latencies"); + LOGGER.info("\t50th percentile: " + latencies[(int) (NUM_RELATIONSHIPS * 0.50)]); + LOGGER.info("\t75th percentile: " + latencies[(int) (NUM_RELATIONSHIPS * 0.75)]); + LOGGER.info("\t90th percentile: " + latencies[(int) (NUM_RELATIONSHIPS * 0.90)]); + LOGGER.info("\t99th percentile: " + latencies[(int) (NUM_RELATIONSHIPS * 0.99)]); + } + } + + private static void retrieveIndividualObjects(ChampGraph graph, boolean warmUp) { + + final double[] latencies = new double[NUM_OBJECTS]; + final long totalStartTime = System.nanoTime(); + + for (int i = 0; i < NUM_OBJECTS; i++) { + final long startTime = System.nanoTime(); + Stream<ChampObject> objects; + try { + objects = graph.queryObjects(Collections.singletonMap("objectNumber", i), Optional.empty()); + } catch (ChampTransactionException e) { + throw new RuntimeException(e); + } + + objects.findFirst().get(); + + final double elapsedMs = (System.nanoTime() - startTime) / 1000.0 / 1000.0; + + latencies[i] = elapsedMs; + } + + final double totalElapsedTimeSecs = (System.nanoTime() - totalStartTime) / 1000.0 / 1000.0 / 1000.0; + + LOGGER.info("Individually read " + NUM_OBJECTS + " objects in " + totalElapsedTimeSecs + "s (" + NUM_OBJECTS / totalElapsedTimeSecs + " objects/s)"); + Arrays.sort(latencies); + + if (!warmUp) { + LOGGER.info("Retrieve individual object latencies"); + LOGGER.info("\t50th percentile: " + latencies[(int) (NUM_OBJECTS * 0.50)]); + LOGGER.info("\t75th percentile: " + latencies[(int) (NUM_OBJECTS * 0.75)]); + LOGGER.info("\t90th percentile: " + latencies[(int) (NUM_OBJECTS * 0.90)]); + LOGGER.info("\t99th percentile: " + latencies[(int) (NUM_OBJECTS * 0.99)]); + } + } + + private static List<ChampObject> retrieveBulkObjects(ChampGraph graph, boolean warmUp) { + + final long startTime = System.nanoTime(); + Stream<ChampObject> objects; + try { + objects = graph.queryObjects( + Collections.singletonMap( + ChampObject.ReservedPropertyKeys.CHAMP_OBJECT_TYPE.toString(), "foo" + ), Optional.empty() + ); + } catch (ChampTransactionException e) { + throw new RuntimeException(e); + } + + final List<ChampObject> objectsAsList = objects.collect(Collectors.toList()); + final double elapsedSecs = (System.nanoTime() - startTime) / 1000.0 / 1000.0 / 1000.0; + + if (!warmUp) { + LOGGER.info("Bulk read " + objectsAsList.size() + " objects in " + elapsedSecs + "s (" + objectsAsList.size() / elapsedSecs + " objects/s)"); + } + + return objectsAsList; + } + + private static List<ChampRelationship> retrieveBulkRelationships(ChampGraph graph, boolean warmUp) { + final long startTime = System.nanoTime(); + Stream<ChampRelationship> relationships; + try { + relationships = graph.queryRelationships( + Collections.singletonMap( + ChampRelationship.ReservedPropertyKeys.CHAMP_RELATIONSHIP_TYPE.toString(), "bazz" + ), Optional.empty() + ); + } catch (ChampTransactionException e) { + throw new RuntimeException(e); + } + + final List<ChampRelationship> relationshipsAsList = relationships.collect(Collectors.toList()); + final double elapsedSecs = (System.nanoTime() - startTime) / 1000.0 / 1000.0 / 1000.0; + + if (!warmUp) { + LOGGER.info("Bulk read " + relationshipsAsList.size() + " relationships in " + elapsedSecs + "s (" + relationshipsAsList.size() / elapsedSecs + " relationships/s)"); + } + + return relationshipsAsList; + } + + private static void storeObjects(ChampGraph graph, boolean warmUp) { + final double[] latencies = new double[NUM_OBJECTS]; + final long totalStartTime = System.nanoTime(); + + for (int i = 0; i < NUM_OBJECTS; i++) { + try { + final long startTime = System.nanoTime(); + + graph.storeObject( + ChampObject.create() + .ofType("foo") + .withoutKey() + .withProperty("objectNumber", i) + .build(), Optional.empty() + ); + + final double elapsedMs = (System.nanoTime() - startTime) / 1000.0 / 1000.0; + latencies[i] = elapsedMs; + } catch (ChampMarshallingException e) { + throw new RuntimeException(e); + } catch (ChampSchemaViolationException e) { + //Ignore, no schema set + } catch (ChampObjectNotExistsException e) { + //Ignore, not an update + } catch (ChampTransactionException e) { + throw new RuntimeException(e); + } + } + + final double totalElapsedTimeSecs = (System.nanoTime() - totalStartTime) / 1000.0 / 1000.0 / 1000.0; + LOGGER.info("Wrote " + NUM_OBJECTS + " objects in " + totalElapsedTimeSecs + "s (" + NUM_OBJECTS / totalElapsedTimeSecs + " objects/s)"); + + Arrays.sort(latencies); + + if (!warmUp) { + LOGGER.info("Store object latencies"); + LOGGER.info("\t50th percentile: " + latencies[(int) (NUM_OBJECTS * 0.50)]); + LOGGER.info("\t75th percentile: " + latencies[(int) (NUM_OBJECTS * 0.75)]); + LOGGER.info("\t90th percentile: " + latencies[(int) (NUM_OBJECTS * 0.90)]); + LOGGER.info("\t99th percentile: " + latencies[(int) (NUM_OBJECTS * 0.99)]); + } + } + + private static void storeRelationships(ChampGraph graph, boolean warmUp) { + final List<ChampObject> objects = retrieveBulkObjects(graph, warmUp); + final double[] latencies = new double[NUM_RELATIONSHIPS]; + final long totalStartTime = System.nanoTime(); + + for (int i = 0; i < NUM_RELATIONSHIPS; i++) { + try { + final long startTime = System.nanoTime(); + + graph.storeRelationship( + new ChampRelationship.Builder( + objects.get(i % objects.size()), objects.get((i + 1) % objects.size()), "bazz" + ).property("relationshipNumber", i) + .build() + , Optional.empty()); + + final double elapsedMs = (System.nanoTime() - startTime) / 1000.0 / 1000.0; + + latencies[i] = elapsedMs; + } catch (ChampMarshallingException e) { + throw new RuntimeException(e); + } catch (ChampObjectNotExistsException e) { + throw new RuntimeException(e); + } catch (ChampSchemaViolationException e) { + throw new RuntimeException(e); + } catch (ChampRelationshipNotExistsException e) { + throw new RuntimeException(e); + } catch (ChampUnmarshallingException e) { + throw new RuntimeException(e); + } catch (ChampTransactionException e) { + throw new RuntimeException(e); + } + } + + final double totalElapsedTimeSecs = (System.nanoTime() - totalStartTime) / 1000.0 / 1000.0 / 1000.0; + LOGGER.info("Wrote " + NUM_RELATIONSHIPS + " relationships in " + totalElapsedTimeSecs + "s (" + NUM_RELATIONSHIPS / totalElapsedTimeSecs + " relationships/s)"); + + Arrays.sort(latencies); + + if (!warmUp) { + LOGGER.info("Store relationship latencies"); + LOGGER.info("\t50th percentile: " + latencies[(int) (NUM_RELATIONSHIPS * 0.50)]); + LOGGER.info("\t75th percentile: " + latencies[(int) (NUM_RELATIONSHIPS * 0.75)]); + LOGGER.info("\t90th percentile: " + latencies[(int) (NUM_RELATIONSHIPS * 0.90)]); + LOGGER.info("\t99th percentile: " + latencies[(int) (NUM_RELATIONSHIPS * 0.99)]); + } + } +} diff --git a/champ-lib/champ-titan/src/test/java/org/onap/aai/champtitan/concurrency/ConcurrencyTest.java b/champ-lib/champ-titan/src/test/java/org/onap/aai/champtitan/concurrency/ConcurrencyTest.java new file mode 100644 index 0000000..9e08f55 --- /dev/null +++ b/champ-lib/champ-titan/src/test/java/org/onap/aai/champtitan/concurrency/ConcurrencyTest.java @@ -0,0 +1,33 @@ +/** + * ============LICENSE_START========================================== + * org.onap.aai + * =================================================================== + * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017 Amdocs + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END============================================ + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.champtitan.concurrency; + +import org.junit.Test; + +public class ConcurrencyTest { + + @Test + public void runInMemoryConcurrentTest() { + org.onap.aai.champcore.concurrency.ConcurrencyTest baseTest = new org.onap.aai.champcore.concurrency.ConcurrencyTest(); + baseTest.runConcurrentTest("TITAN"); + } +} diff --git a/champ-lib/champ-titan/src/test/java/org/onap/aai/champtitan/core/ChampAPITest.java b/champ-lib/champ-titan/src/test/java/org/onap/aai/champtitan/core/ChampAPITest.java new file mode 100644 index 0000000..8d4bc57 --- /dev/null +++ b/champ-lib/champ-titan/src/test/java/org/onap/aai/champtitan/core/ChampAPITest.java @@ -0,0 +1,40 @@ +/** + * ============LICENSE_START========================================== + * org.onap.aai + * =================================================================== + * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017 Amdocs + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END============================================ + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.champtitan.core; + +import org.junit.Test; +import org.onap.aai.champtitan.graph.impl.TitanChampGraphImpl; + +public class ChampAPITest { + @Test + public void testChampGraphInstantiation() throws Exception { + TitanChampGraphImpl graph = new TitanChampGraphImpl.Builder("testGraph") + .property("storage.backend", "inmemory") + .build(); + + org.onap.aai.champcore.core.ChampAPITest baseTest = new org.onap.aai.champcore.core.ChampAPITest(); + + baseTest.testChampGraphInstantiation(graph); + } + + +} diff --git a/champ-lib/champ-titan/src/test/java/org/onap/aai/champtitan/core/ChampObjectIndexTest.java b/champ-lib/champ-titan/src/test/java/org/onap/aai/champtitan/core/ChampObjectIndexTest.java new file mode 100644 index 0000000..bd9295f --- /dev/null +++ b/champ-lib/champ-titan/src/test/java/org/onap/aai/champtitan/core/ChampObjectIndexTest.java @@ -0,0 +1,48 @@ +/** + * ============LICENSE_START========================================== + * org.onap.aai + * =================================================================== + * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017 Amdocs + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END============================================ + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.champtitan.core; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.onap.aai.champcore.exceptions.ChampIndexNotExistsException; +import org.onap.aai.champcore.model.ChampCardinality; +import org.onap.aai.champtitan.graph.impl.TitanChampGraphImpl; + +public class ChampObjectIndexTest { + @Rule + public final ExpectedException exception = ExpectedException.none(); + + @Test + public void testChampObjectIndexCrud() throws Exception { + TitanChampGraphImpl graph = new TitanChampGraphImpl.Builder("testGraph") + .property("storage.backend", "inmemory") + .build(); + + org.onap.aai.champcore.core.ChampObjectIndexTest.testChampObjectIndexCrud(graph); + + graph.shutdown(); + + exception.expect(IllegalStateException.class); + graph.executeDeleteObjectIndex("any"); + } +} diff --git a/champ-lib/champ-titan/src/test/java/org/onap/aai/champtitan/core/ChampRelationshipIndexTest.java b/champ-lib/champ-titan/src/test/java/org/onap/aai/champtitan/core/ChampRelationshipIndexTest.java new file mode 100644 index 0000000..e515101 --- /dev/null +++ b/champ-lib/champ-titan/src/test/java/org/onap/aai/champtitan/core/ChampRelationshipIndexTest.java @@ -0,0 +1,38 @@ +/** + * ============LICENSE_START========================================== + * org.onap.aai + * =================================================================== + * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017 Amdocs + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END============================================ + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.champtitan.core; + +import org.junit.Test; +import org.onap.aai.champtitan.graph.impl.TitanChampGraphImpl; + +public class ChampRelationshipIndexTest { + @Test + public void testChampRelationshipIndexCrud() { + TitanChampGraphImpl graph = new TitanChampGraphImpl.Builder("testGraph") + .property("storage.backend", "inmemory") + .build(); + org.onap.aai.champcore.core.ChampRelationshipIndexTest baseTest = new org.onap.aai.champcore.core.ChampRelationshipIndexTest(); + baseTest.testChampRelationshipIndexCrud(graph); + + graph.shutdown(); + } +} diff --git a/champ-lib/champ-titan/src/test/java/org/onap/aai/champtitan/core/ChampSchemaTest.java b/champ-lib/champ-titan/src/test/java/org/onap/aai/champtitan/core/ChampSchemaTest.java new file mode 100644 index 0000000..ac29fc8 --- /dev/null +++ b/champ-lib/champ-titan/src/test/java/org/onap/aai/champtitan/core/ChampSchemaTest.java @@ -0,0 +1,37 @@ +/** + * ============LICENSE_START========================================== + * org.onap.aai + * =================================================================== + * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017 Amdocs + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END============================================ + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.champtitan.core; + +import org.junit.Test; +import org.onap.aai.champtitan.graph.impl.TitanChampGraphImpl; + +public class ChampSchemaTest { + @Test + public void testChampSchemaCrud() { + TitanChampGraphImpl graph = new TitanChampGraphImpl.Builder("testGraph") + .property("storage.backend", "inmemory") + .build(); + org.onap.aai.champcore.core.ChampSchemaTest.testChampSchemaCrud(graph); + + graph.shutdown(); + } +} diff --git a/champ-lib/champ-titan/src/test/java/org/onap/aai/champtitan/core/ChampTransactionTest.java b/champ-lib/champ-titan/src/test/java/org/onap/aai/champtitan/core/ChampTransactionTest.java new file mode 100644 index 0000000..aafc053 --- /dev/null +++ b/champ-lib/champ-titan/src/test/java/org/onap/aai/champtitan/core/ChampTransactionTest.java @@ -0,0 +1,335 @@ +/** + * ============LICENSE_START========================================== + * org.onap.aai + * =================================================================== + * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017 Amdocs + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END============================================ + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.champtitan.core; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.util.Optional; +import java.util.concurrent.CountDownLatch; + +import org.junit.Before; +import org.junit.Test; +import org.onap.aai.champcore.ChampTransaction; +import org.onap.aai.champcore.exceptions.ChampMarshallingException; +import org.onap.aai.champcore.exceptions.ChampObjectNotExistsException; +import org.onap.aai.champcore.exceptions.ChampRelationshipNotExistsException; +import org.onap.aai.champcore.exceptions.ChampSchemaViolationException; +import org.onap.aai.champcore.exceptions.ChampTransactionException; +import org.onap.aai.champcore.exceptions.ChampUnmarshallingException; +import org.onap.aai.champcore.model.ChampObject; +import org.onap.aai.champcore.model.ChampRelationship; +import org.onap.aai.champtitan.graph.impl.TitanChampGraphImpl; + +public class ChampTransactionTest { + + public TitanChampGraphImpl graph = null; + public CountDownLatch latch = new CountDownLatch(2); + public ChampObject[] storedVertices = new ChampObject[2]; + + + @Before + public void setup() { + graph = new TitanChampGraphImpl.Builder("TransactionTestGraph") + .property("storage.backend", "inmemory") + .build(); + } + + + /** + * This test validates that multiple CRUD operations can be performed against vertices within + * the same transactional context and that, once the transaction is committed, the final + * state of the vertices reflects the sum of all of the operations performed within the + * transaction. + * + * @throws ChampMarshallingException + * @throws ChampSchemaViolationException + * @throws ChampObjectNotExistsException + * @throws ChampUnmarshallingException + * @throws ChampTransactionException + */ + @Test + public void champObjectSingleTxTest() throws ChampMarshallingException, ChampSchemaViolationException, ChampObjectNotExistsException, ChampUnmarshallingException, ChampTransactionException { + + ChampObject v1 = ChampObject.create() + .ofType("foo") + .withoutKey() + .withProperty("property1", "value1") + .withProperty("property2", "value2") + .build(); + + ChampObject v2 = ChampObject.create() + .ofType("foo") + .withoutKey() + .withProperty("property3", "value3") + .withProperty("property4", "value4") + .build(); + + ChampObject v3 = ChampObject.create() + .ofType("foo") + .withoutKey() + .withProperty("property5", "value5") + .withProperty("property6", "value6") + .build(); + + ChampObject v4 = ChampObject.create() + .ofType("foo") + .withoutKey() + .withProperty("property7", "value7") + .withProperty("property8", "value8") + .build(); + + + // Open a transaction with the graph data store. + ChampTransaction tx = graph.openTransaction(); + + // Create all of our vertices. + ChampObject storedV1 = graph.storeObject(v1, Optional.of(tx)); + ChampObject storedV2 = graph.storeObject(v2, Optional.of(tx)); + ChampObject storedV3 = graph.storeObject(v3, Optional.of(tx)); + ChampObject storedV4 = graph.storeObject(v4, Optional.of(tx)); + + // Now, within the same transactional context, do a replacement against one of the + // vertices that we just created. + ChampObject replacedV2 = graph.replaceObject(ChampObject.create() + .ofType("foo") + .withKey(storedV2.getKey().get()) + .withProperty("replacedProperty3", "replacedValue3") + .withProperty("replacedProperty4", "replacedValue4") + .build(), + Optional.of(tx)); + + // Within the same transactional context, do an update against one of the vertices + // that we just created. + final ChampObject updatedV3 = graph.storeObject(ChampObject.create() + .from(storedV3) + .withKey(storedV3.getKey().get()) + .withProperty("updatedProperty", "updatedValue") + .build(), Optional.of(tx)); + + // Within the same transactional context, delete one of the vertices that we just + // created. + graph.deleteObject(storedV4.getKey().get(), Optional.of(tx)); + + // Finally, commit our transaction. + tx.commit(); + + tx = graph.openTransaction(); + + Optional<ChampObject> retrievedV1 = graph.retrieveObject(storedV1.getKey().get(), Optional.of(tx)); + assertTrue(retrievedV1.isPresent()); + assertTrue(retrievedV1.get().getProperty("property1").get().equals("value1")); + assertTrue(retrievedV1.get().getProperty("property2").get().equals("value2")); + + + Optional<ChampObject> retrievedV2 = graph.retrieveObject(storedV2.getKey().get(), Optional.of(tx)); + assertTrue(retrievedV2.isPresent()); + assertTrue(retrievedV2.get().getProperty("replacedProperty3").get().equals("replacedValue3")); + assertTrue(retrievedV2.get().getProperty("replacedProperty4").get().equals("replacedValue4")); + assertFalse(retrievedV2.get().getProperty("value3").isPresent()); + assertFalse(retrievedV2.get().getProperty("value4").isPresent()); + + Optional<ChampObject> retrievedV3 = graph.retrieveObject(storedV3.getKey().get(), Optional.of(tx)); + assertTrue(retrievedV3.isPresent()); + assertTrue(retrievedV3.get().getProperty("property5").get().equals("value5")); + assertTrue(retrievedV3.get().getProperty("property6").get().equals("value6")); + assertTrue(retrievedV3.get().getProperty("updatedProperty").get().equals("updatedValue")); + + + Optional<ChampObject> retrievedV4 = graph.retrieveObject(storedV4.getKey().get(), Optional.of(tx)); + assertFalse("Deleted vertex should not be present in graph", retrievedV4.isPresent()); + + tx.commit(); + } + + + /** + * This test validates that multiple threads can each open their own transactions with the + * graph data store, and that there is no leakage between each thread's transactions. + * + * @throws ChampMarshallingException + * @throws ChampSchemaViolationException + * @throws ChampObjectNotExistsException + * @throws ChampUnmarshallingException + * @throws ChampTransactionException + */ + @Test + public void multipleTransactionTest() throws ChampMarshallingException, ChampSchemaViolationException, ChampObjectNotExistsException, ChampUnmarshallingException, ChampTransactionException { + + ChampObject v1 = ChampObject.create() + .ofType("foo") + .withoutKey() + .withProperty("property1", "value1") + .withProperty("property2", "value2") + .build(); + + ChampObject v2 = ChampObject.create() + .ofType("bar") + .withoutKey() + .withProperty("property3", "value3") + .withProperty("property4", "value4") + .build(); + + // Instantiate and start our two transactional worker threads... + Thread thread1 = new Thread(new VertexWriter(v1, 0)); + Thread thread2 = new Thread(new VertexWriter(v2, 1)); + thread1.start(); + thread2.start(); + + // and wait for the threads to complete. + try { + thread1.join(); + thread2.join(); + + } catch (InterruptedException e) { } + + // Now that all of our data has been committed, let's open a new transaction + // and verify that all of our vertices can be retrieved. + ChampTransaction tx3 = graph.openTransaction(); + + Optional<ChampObject> retrievedV1 = graph.retrieveObject(storedVertices[0].getKey().get(), Optional.of(tx3)); + assertTrue(retrievedV1.isPresent()); + Optional<ChampObject> retrievedV2 = graph.retrieveObject(storedVertices[1].getKey().get(), Optional.of(tx3)); + assertTrue(retrievedV2.isPresent()); + + tx3.commit(); + } + + + /** + * This method validates that edges can be successfully created within a single transaction. + * + * @throws ChampMarshallingException + * @throws ChampSchemaViolationException + * @throws ChampObjectNotExistsException + * @throws ChampUnmarshallingException + * @throws ChampRelationshipNotExistsException + * @throws ChampTransactionException + */ + @Test + public void edgeTest() throws ChampMarshallingException, ChampSchemaViolationException, ChampObjectNotExistsException, ChampUnmarshallingException, ChampRelationshipNotExistsException, ChampTransactionException { + + // Create the source and target vertices for our edge. + final ChampObject source = ChampObject.create() + .ofType("foo") + .withoutKey() + .withProperty("property1", "value1") + .build(); + + final ChampObject target = ChampObject.create() + .ofType("foo") + .withoutKey() + .build(); + + // Open a transaction with the graph data store. + ChampTransaction tx = graph.openTransaction(); + + // Now, create our vertices. + ChampObject storedSource = graph.storeObject(source, Optional.of(tx)); + ChampObject storedTarget = graph.storeObject(target, Optional.of(tx)); + + // Create the edge between the vertices. + ChampRelationship relationship = new ChampRelationship.Builder(storedSource, storedTarget, "relationship") + .property("property-1", "value-1") + .property("property-2", 3) + .build(); + ChampRelationship storedRelationship = graph.storeRelationship(relationship, Optional.of(tx)); + + // Validate that we can read back the edge within the transactional context. + Optional<ChampRelationship> retrievedRelationship = graph.retrieveRelationship(storedRelationship.getKey().get(), Optional.of(tx)); + assertTrue("Failed to retrieve stored relationship", retrievedRelationship.isPresent()); + + // Commit our transaction. + graph.commitTransaction(tx); + + // Now, open a new transaction. + tx = graph.openTransaction(); + + // Now, read back the edge that we just created again, validating that it was + // successfully committed to the graph. + retrievedRelationship = graph.retrieveRelationship(storedRelationship.getKey().get(), Optional.of(tx)); + assertTrue("Failed to retrieve stored relationship", retrievedRelationship.isPresent()); + + graph.commitTransaction(tx); + } + + private class VertexWriter implements Runnable { + + ChampObject vertex; + int index; + + public VertexWriter(ChampObject vertex, int index) { + this.vertex = vertex; + this.index = index; + } + + public void run() { + + ChampTransaction tx=null; + try { + + // Open a threaded transaction to do some work in. + tx = graph.openTransaction(); + + // Now store one of our two vertices within the context of this transaction. + storedVertices[index] = graph.storeObject(vertex, Optional.of(tx)); + + // Use our latch to indicate that we are done creating vertices, and wait for + // the other thread to do the same. + latch.countDown(); + latch.await(); + + // Validate that the vertex we created is visible to us in the graph, but the + // one that the other thread created is not. + Optional<ChampObject> retrievedV2 = graph.retrieveObject(storedVertices[index].getKey().get(), Optional.of(tx)); + assertTrue(retrievedV2.isPresent()); + Optional<ChampObject> retrievedV1 = graph.retrieveObject(storedVertices[(index+1)%2].getKey().get(), Optional.of(tx)); + assertFalse(retrievedV1.isPresent()); + + } catch (InterruptedException | + ChampUnmarshallingException | + ChampMarshallingException | + ChampSchemaViolationException | + ChampObjectNotExistsException | ChampTransactionException e) { + + fail("Thread failed to interact with graph due to " + e.getMessage()); + + } finally { + + try { + Thread.sleep(index * 500); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + // Now, commit our transaction and bail... + try { + graph.commitTransaction(tx); + } catch (ChampTransactionException e) { + + } + } + } + }; +} + diff --git a/champ-lib/champ-titan/src/test/java/org/onap/aai/champtitan/core/TitanChampSetupTest.java b/champ-lib/champ-titan/src/test/java/org/onap/aai/champtitan/core/TitanChampSetupTest.java new file mode 100644 index 0000000..bca61ec --- /dev/null +++ b/champ-lib/champ-titan/src/test/java/org/onap/aai/champtitan/core/TitanChampSetupTest.java @@ -0,0 +1,74 @@ +/** + * ============LICENSE_START========================================== + * org.onap.aai + * =================================================================== + * Copyright © 2017 AT&T Intellectual Property. All rights reserved. + * Copyright © 2017 Amdocs + * =================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END============================================ + * ECOMP is a trademark and service mark of AT&T Intellectual Property. + */ +package org.onap.aai.champtitan.core; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.onap.aai.champtitan.graph.impl.TitanChampGraphImpl; + +import java.util.HashMap; +import java.util.Map; + +public class TitanChampSetupTest { + @Rule + public final ExpectedException exception = ExpectedException.none(); + + @Test + public void TitanSetupBadBackendTest() { + exception.expect(RuntimeException.class); + TitanChampGraphImpl graph = new TitanChampGraphImpl.Builder("testGraph") + .property("storage.backend", "bad-backend") + .build(); + } + + @Test + public void TitanSetupBerkleyBackendTest() { + exception.expect(RuntimeException.class); + Map<String, Object> propertiesMap = new HashMap<String, Object>(); + propertiesMap.put("storage.backend", "berkleyje"); + TitanChampGraphImpl graph = new TitanChampGraphImpl.Builder("testGraph") + .properties(propertiesMap) + .build(); + } + + @Test + public void TitanSetupBadPropertyTest() { + exception.expect(RuntimeException.class); + TitanChampGraphImpl graph = new TitanChampGraphImpl.Builder("testGraph") + .property("storage.backend", "in-memory") + .property("storage.cassandra.keyspace", "anything") + .build(); + } + + @Test + public void TitanSetupBadPropertiesTest() { + exception.expect(RuntimeException.class); + Map<String, Object> propertiesMap = new HashMap<String, Object>(); + propertiesMap.put("storage.cassandra.keyspace", "anything"); + + TitanChampGraphImpl graph = new TitanChampGraphImpl.Builder("testGraph") + .property("storage.backend", "in-memory") + .properties(propertiesMap) + .build(); + } +} |