2 * ============LICENSE_START==========================================
4 * ===================================================================
5 * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
6 * Copyright © 2017 Amdocs
7 * ===================================================================
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 * ============LICENSE_END============================================
20 * ECOMP is a trademark and service mark of AT&T Intellectual Property.
22 package org.onap.aai.champtitan.perf;
24 import com.thinkaurelius.titan.core.TitanFactory;
25 import com.thinkaurelius.titan.core.TitanFactory.Builder;
26 import com.thinkaurelius.titan.core.TitanGraph;
27 import com.thinkaurelius.titan.core.util.TitanCleanup;
29 import org.onap.aai.champcore.ChampGraph;
30 import org.onap.aai.champcore.exceptions.*;
31 import org.onap.aai.champcore.graph.impl.InMemoryChampGraphImpl;
32 import org.onap.aai.champcore.model.*;
33 import org.onap.aai.champcore.schema.ChampSchemaEnforcer;
34 import org.onap.aai.champtitan.graph.impl.TitanChampGraphImpl;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
39 import java.util.Map.Entry;
40 import java.util.stream.Collectors;
41 import java.util.stream.Stream;
43 public class ChampAPIPerformanceTest {
45 private static final Logger LOGGER = LoggerFactory.getLogger(ChampAPIPerformanceTest.class);
47 private static final int NUM_OBJECTS = 1000;
48 private static final int NUM_RELATIONSHIPS = 1000;
49 private static final String GRAPH_NAME = ChampAPIPerformanceTest.class.getSimpleName();
51 private static final String getGraphName() {
55 private static void cleanUp(String graphName, Map<String, String> settings) {
56 LOGGER.debug("Cleaning up graph {}", graphName);
59 final Builder graphBuilder = TitanFactory.build();
61 for (Entry<String, String> setting : settings.entrySet()) {
62 graphBuilder.set(setting.getKey(), setting.getValue());
65 final String storageBackend = settings.getOrDefault("storage.backend", "inmemory");
67 if (storageBackend.equals("cassandra") ||
68 storageBackend.equals("cassandrathrift") ||
69 storageBackend.equals("astyanax") ||
70 storageBackend.equals("embeddedcassandra")) {
71 graphBuilder.set("storage.cassandra.keyspace", graphName);
72 } else if (storageBackend.equals("hbase")) {
73 graphBuilder.set("storage.hbase.table", graphName);
76 final TitanGraph graph = graphBuilder.open();
79 TitanCleanup.clear(graph);
80 } catch (IllegalArgumentException e) {
81 LOGGER.warn("Could not clean up graph - unable to instantiate");
85 public static void main(String[] args) {
87 if (args.length < 1 || !args[0].startsWith("--champcore.graph.type=")) {
88 throw new RuntimeException("Must provide --champcore.graph.type=" + " as first parameter");
91 final String graphType = args[0].split("=")[1];
93 final Map<String, String> settings = new HashMap<String, String> ();
95 for (int i = 1; i < args.length; i++) {
96 if (!args[i].startsWith("--")) {
97 throw new RuntimeException("Bad command line argument: " + args[i]);
100 final String[] keyValue = args[i].replaceFirst("--", "").split("=");
102 if (keyValue.length != 2) {
103 throw new RuntimeException("Bad command line argument: " + args[i]);
106 settings.put(keyValue[0], keyValue[1]);
109 LOGGER.info("Provided graph settings: " + settings);
111 if (graphType.equals("TITAN")) {
112 cleanUp(getGraphName(), settings);
115 LOGGER.info("Graph cleaned, instantiating ChampGraph");
117 final ChampGraph graph;
121 final InMemoryChampGraphImpl.Builder inMemGraphBuilder = new InMemoryChampGraphImpl.Builder();
123 if (settings.containsKey("champcore.schema.enforcer")) {
124 final String schemaEnforcerClassStr = settings.get("champcore.schema.enforcer");
127 final Class<?> schemaEnforcer = Class.forName(schemaEnforcerClassStr);
129 if (!schemaEnforcer.isAssignableFrom(ChampSchemaEnforcer.class)) {
130 throw new RuntimeException("Unknown ChampSchemaEnforcer " + schemaEnforcer);
133 inMemGraphBuilder.schemaEnforcer((ChampSchemaEnforcer) schemaEnforcer.newInstance());
134 } catch (ClassNotFoundException e) {
135 throw new RuntimeException(e);
136 } catch (InstantiationException e) {
137 throw new RuntimeException(e);
138 } catch (IllegalAccessException e) {
139 throw new RuntimeException(e);
143 graph = inMemGraphBuilder.build();
146 final TitanChampGraphImpl.Builder graphBuilder = new TitanChampGraphImpl.Builder(getGraphName());
148 for (Entry<String, String> setting : settings.entrySet()) {
149 graphBuilder.property(setting.getKey(), setting.getValue());
152 graph = graphBuilder.build();
155 throw new RuntimeException("Unknown ChampGraph.Type " + graphType);
159 if (graph.queryObjects(Collections.emptyMap(), Optional.empty()).limit(1).count() > 0) {
161 throw new RuntimeException("Expected empty graph");
163 } catch (ChampTransactionException e) {
164 throw new RuntimeException("Transaction failure");
167 LOGGER.info("Graph instantiated, warming up JVM");
170 LOGGER.info("Warm up complete, starting to record performance measurements");
172 LOGGER.info("Performance without indexing/schema");
174 storeObjects(graph, false);
175 storeRelationships(graph, false);
176 retrieveIndividualObjects(graph, false);
177 retrieveBulkRelationships(graph, false);
178 retrieveIndividualRelationships(graph, false);
180 LOGGER.info("Storing indices + schema");
182 storeIndices(graph, false);
183 storeSchema(graph, false);
185 LOGGER.info("Stored indices + schema");
187 LOGGER.info("Performance with indexing + schema");
189 storeObjects(graph, false);
190 storeRelationships(graph, false);
191 retrieveIndividualObjects(graph, false);
192 retrieveBulkRelationships(graph, false);
193 retrieveIndividualRelationships(graph, false);
195 LOGGER.info("Performance test complete, shutting down graph");
199 LOGGER.info("Graph shutdown, JVM exiting");
202 private static void storeSchema(ChampGraph graph, boolean warmUp) {
206 .withObjectConstraint()
208 .withPropertyConstraint()
209 .onField("fooObjectNumber")
213 .withRelationshipConstraint()
215 .withPropertyConstraint()
216 .onField("barObjectNumber")
217 .ofType(ChampField.Type.INTEGER)
223 } catch (ChampSchemaViolationException e) {
224 throw new AssertionError(e);
228 private static void storeIndices(ChampGraph graph, boolean warmUp) {
229 graph.storeObjectIndex(
230 ChampObjectIndex.create()
231 .ofName("objectNumberIndex")
233 .forField("objectNumber")
237 graph.storeRelationshipIndex(ChampRelationshipIndex.create()
238 .ofName("relationshipNumberIndex")
240 .forField("relationshipNumber")
245 private static void warmUp(ChampGraph graph) {
246 storeObjects(graph, false);
247 storeRelationships(graph, false);
248 retrieveIndividualObjects(graph, false);
249 retrieveBulkRelationships(graph, false);
250 retrieveIndividualRelationships(graph, false);
253 private static void retrieveIndividualRelationships(ChampGraph graph, boolean warmUp) {
254 final double[] latencies = new double[NUM_RELATIONSHIPS];
255 final long totalStartTime = System.nanoTime();
257 for (int i = 0; i < NUM_RELATIONSHIPS; i++) {
258 final long startTime = System.nanoTime();
260 Stream<ChampRelationship> objects;
262 objects = graph.queryRelationships(Collections.singletonMap("relationshipNumber", i), Optional.empty());
263 } catch (ChampTransactionException e) {
264 throw new RuntimeException(e);
267 objects.findFirst().get();
268 final double elapsedMs = (System.nanoTime() - startTime) / 1000.0 / 1000.0;
269 latencies[i] = elapsedMs;
272 final double totalElapsedTimeSecs = (System.nanoTime() - totalStartTime) / 1000.0 / 1000.0 / 1000.0;
273 LOGGER.info("Individually read " + NUM_RELATIONSHIPS + " relationships in " + totalElapsedTimeSecs + "s (" + NUM_RELATIONSHIPS / totalElapsedTimeSecs + " relationships/s)");
275 Arrays.sort(latencies);
278 LOGGER.info("Retrieve individual relationship latencies");
279 LOGGER.info("\t50th percentile: " + latencies[(int) (NUM_RELATIONSHIPS * 0.50)]);
280 LOGGER.info("\t75th percentile: " + latencies[(int) (NUM_RELATIONSHIPS * 0.75)]);
281 LOGGER.info("\t90th percentile: " + latencies[(int) (NUM_RELATIONSHIPS * 0.90)]);
282 LOGGER.info("\t99th percentile: " + latencies[(int) (NUM_RELATIONSHIPS * 0.99)]);
286 private static void retrieveIndividualObjects(ChampGraph graph, boolean warmUp) {
288 final double[] latencies = new double[NUM_OBJECTS];
289 final long totalStartTime = System.nanoTime();
291 for (int i = 0; i < NUM_OBJECTS; i++) {
292 final long startTime = System.nanoTime();
293 Stream<ChampObject> objects;
295 objects = graph.queryObjects(Collections.singletonMap("objectNumber", i), Optional.empty());
296 } catch (ChampTransactionException e) {
297 throw new RuntimeException(e);
300 objects.findFirst().get();
302 final double elapsedMs = (System.nanoTime() - startTime) / 1000.0 / 1000.0;
304 latencies[i] = elapsedMs;
307 final double totalElapsedTimeSecs = (System.nanoTime() - totalStartTime) / 1000.0 / 1000.0 / 1000.0;
309 LOGGER.info("Individually read " + NUM_OBJECTS + " objects in " + totalElapsedTimeSecs + "s (" + NUM_OBJECTS / totalElapsedTimeSecs + " objects/s)");
310 Arrays.sort(latencies);
313 LOGGER.info("Retrieve individual object latencies");
314 LOGGER.info("\t50th percentile: " + latencies[(int) (NUM_OBJECTS * 0.50)]);
315 LOGGER.info("\t75th percentile: " + latencies[(int) (NUM_OBJECTS * 0.75)]);
316 LOGGER.info("\t90th percentile: " + latencies[(int) (NUM_OBJECTS * 0.90)]);
317 LOGGER.info("\t99th percentile: " + latencies[(int) (NUM_OBJECTS * 0.99)]);
321 private static List<ChampObject> retrieveBulkObjects(ChampGraph graph, boolean warmUp) {
323 final long startTime = System.nanoTime();
324 Stream<ChampObject> objects;
326 objects = graph.queryObjects(
327 Collections.singletonMap(
328 ChampObject.ReservedPropertyKeys.CHAMP_OBJECT_TYPE.toString(), "foo"
331 } catch (ChampTransactionException e) {
332 throw new RuntimeException(e);
335 final List<ChampObject> objectsAsList = objects.collect(Collectors.toList());
336 final double elapsedSecs = (System.nanoTime() - startTime) / 1000.0 / 1000.0 / 1000.0;
339 LOGGER.info("Bulk read " + objectsAsList.size() + " objects in " + elapsedSecs + "s (" + objectsAsList.size() / elapsedSecs + " objects/s)");
342 return objectsAsList;
345 private static List<ChampRelationship> retrieveBulkRelationships(ChampGraph graph, boolean warmUp) {
346 final long startTime = System.nanoTime();
347 Stream<ChampRelationship> relationships;
349 relationships = graph.queryRelationships(
350 Collections.singletonMap(
351 ChampRelationship.ReservedPropertyKeys.CHAMP_RELATIONSHIP_TYPE.toString(), "bazz"
354 } catch (ChampTransactionException e) {
355 throw new RuntimeException(e);
358 final List<ChampRelationship> relationshipsAsList = relationships.collect(Collectors.toList());
359 final double elapsedSecs = (System.nanoTime() - startTime) / 1000.0 / 1000.0 / 1000.0;
362 LOGGER.info("Bulk read " + relationshipsAsList.size() + " relationships in " + elapsedSecs + "s (" + relationshipsAsList.size() / elapsedSecs + " relationships/s)");
365 return relationshipsAsList;
368 private static void storeObjects(ChampGraph graph, boolean warmUp) {
369 final double[] latencies = new double[NUM_OBJECTS];
370 final long totalStartTime = System.nanoTime();
372 for (int i = 0; i < NUM_OBJECTS; i++) {
374 final long startTime = System.nanoTime();
380 .withProperty("objectNumber", i)
381 .build(), Optional.empty()
384 final double elapsedMs = (System.nanoTime() - startTime) / 1000.0 / 1000.0;
385 latencies[i] = elapsedMs;
386 } catch (ChampMarshallingException e) {
387 throw new RuntimeException(e);
388 } catch (ChampSchemaViolationException e) {
389 //Ignore, no schema set
390 } catch (ChampObjectNotExistsException e) {
391 //Ignore, not an update
392 } catch (ChampTransactionException e) {
393 throw new RuntimeException(e);
397 final double totalElapsedTimeSecs = (System.nanoTime() - totalStartTime) / 1000.0 / 1000.0 / 1000.0;
398 LOGGER.info("Wrote " + NUM_OBJECTS + " objects in " + totalElapsedTimeSecs + "s (" + NUM_OBJECTS / totalElapsedTimeSecs + " objects/s)");
400 Arrays.sort(latencies);
403 LOGGER.info("Store object latencies");
404 LOGGER.info("\t50th percentile: " + latencies[(int) (NUM_OBJECTS * 0.50)]);
405 LOGGER.info("\t75th percentile: " + latencies[(int) (NUM_OBJECTS * 0.75)]);
406 LOGGER.info("\t90th percentile: " + latencies[(int) (NUM_OBJECTS * 0.90)]);
407 LOGGER.info("\t99th percentile: " + latencies[(int) (NUM_OBJECTS * 0.99)]);
411 private static void storeRelationships(ChampGraph graph, boolean warmUp) {
412 final List<ChampObject> objects = retrieveBulkObjects(graph, warmUp);
413 final double[] latencies = new double[NUM_RELATIONSHIPS];
414 final long totalStartTime = System.nanoTime();
416 for (int i = 0; i < NUM_RELATIONSHIPS; i++) {
418 final long startTime = System.nanoTime();
420 graph.storeRelationship(
421 new ChampRelationship.Builder(
422 objects.get(i % objects.size()), objects.get((i + 1) % objects.size()), "bazz"
423 ).property("relationshipNumber", i)
427 final double elapsedMs = (System.nanoTime() - startTime) / 1000.0 / 1000.0;
429 latencies[i] = elapsedMs;
430 } catch (ChampMarshallingException e) {
431 throw new RuntimeException(e);
432 } catch (ChampObjectNotExistsException e) {
433 throw new RuntimeException(e);
434 } catch (ChampSchemaViolationException e) {
435 throw new RuntimeException(e);
436 } catch (ChampRelationshipNotExistsException e) {
437 throw new RuntimeException(e);
438 } catch (ChampUnmarshallingException e) {
439 throw new RuntimeException(e);
440 } catch (ChampTransactionException e) {
441 throw new RuntimeException(e);
445 final double totalElapsedTimeSecs = (System.nanoTime() - totalStartTime) / 1000.0 / 1000.0 / 1000.0;
446 LOGGER.info("Wrote " + NUM_RELATIONSHIPS + " relationships in " + totalElapsedTimeSecs + "s (" + NUM_RELATIONSHIPS / totalElapsedTimeSecs + " relationships/s)");
448 Arrays.sort(latencies);
451 LOGGER.info("Store relationship latencies");
452 LOGGER.info("\t50th percentile: " + latencies[(int) (NUM_RELATIONSHIPS * 0.50)]);
453 LOGGER.info("\t75th percentile: " + latencies[(int) (NUM_RELATIONSHIPS * 0.75)]);
454 LOGGER.info("\t90th percentile: " + latencies[(int) (NUM_RELATIONSHIPS * 0.90)]);
455 LOGGER.info("\t99th percentile: " + latencies[(int) (NUM_RELATIONSHIPS * 0.99)]);