2 * ============LICENSE_START==========================================
4 * ===================================================================
5 * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
6 * Copyright © 2017-2018 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============================================
21 package org.onap.aai.champtitan.perf;
23 import com.thinkaurelius.titan.core.TitanFactory;
24 import com.thinkaurelius.titan.core.TitanFactory.Builder;
25 import com.thinkaurelius.titan.core.TitanGraph;
26 import com.thinkaurelius.titan.core.util.TitanCleanup;
28 import org.onap.aai.champcore.ChampGraph;
29 import org.onap.aai.champcore.exceptions.*;
30 import org.onap.aai.champcore.graph.impl.InMemoryChampGraphImpl;
31 import org.onap.aai.champcore.model.*;
32 import org.onap.aai.champcore.schema.ChampSchemaEnforcer;
33 import org.onap.aai.champtitan.graph.impl.TitanChampGraphImpl;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
38 import java.util.Map.Entry;
39 import java.util.stream.Collectors;
40 import java.util.stream.Stream;
42 public class ChampAPIPerformanceTest {
44 private static final Logger LOGGER = LoggerFactory.getLogger(ChampAPIPerformanceTest.class);
46 private static final int NUM_OBJECTS = 1000;
47 private static final int NUM_RELATIONSHIPS = 1000;
48 private static final String GRAPH_NAME = ChampAPIPerformanceTest.class.getSimpleName();
50 private static final String getGraphName() {
54 private static void cleanUp(String graphName, Map<String, String> settings) {
55 LOGGER.debug("Cleaning up graph {}", graphName);
58 final Builder graphBuilder = TitanFactory.build();
60 for (Entry<String, String> setting : settings.entrySet()) {
61 graphBuilder.set(setting.getKey(), setting.getValue());
64 final String storageBackend = settings.getOrDefault("storage.backend", "inmemory");
66 if (storageBackend.equals("cassandra") ||
67 storageBackend.equals("cassandrathrift") ||
68 storageBackend.equals("astyanax") ||
69 storageBackend.equals("embeddedcassandra")) {
70 graphBuilder.set("storage.cassandra.keyspace", graphName);
71 } else if (storageBackend.equals("hbase")) {
72 graphBuilder.set("storage.hbase.table", graphName);
75 final TitanGraph graph = graphBuilder.open();
78 TitanCleanup.clear(graph);
79 } catch (IllegalArgumentException e) {
80 LOGGER.warn("Could not clean up graph - unable to instantiate");
84 public static void main(String[] args) {
86 if (args.length < 1 || !args[0].startsWith("--champcore.graph.type=")) {
87 throw new RuntimeException("Must provide --champcore.graph.type=" + " as first parameter");
90 final String graphType = args[0].split("=")[1];
92 final Map<String, String> settings = new HashMap<String, String> ();
94 for (int i = 1; i < args.length; i++) {
95 if (!args[i].startsWith("--")) {
96 throw new RuntimeException("Bad command line argument: " + args[i]);
99 final String[] keyValue = args[i].replaceFirst("--", "").split("=");
101 if (keyValue.length != 2) {
102 throw new RuntimeException("Bad command line argument: " + args[i]);
105 settings.put(keyValue[0], keyValue[1]);
108 LOGGER.info("Provided graph settings: " + settings);
110 if (graphType.equals("TITAN")) {
111 cleanUp(getGraphName(), settings);
114 LOGGER.info("Graph cleaned, instantiating ChampGraph");
116 final ChampGraph graph;
120 final InMemoryChampGraphImpl.Builder inMemGraphBuilder = new InMemoryChampGraphImpl.Builder();
122 if (settings.containsKey("champcore.schema.enforcer")) {
123 final String schemaEnforcerClassStr = settings.get("champcore.schema.enforcer");
126 final Class<?> schemaEnforcer = Class.forName(schemaEnforcerClassStr);
128 if (!schemaEnforcer.isAssignableFrom(ChampSchemaEnforcer.class)) {
129 throw new RuntimeException("Unknown ChampSchemaEnforcer " + schemaEnforcer);
132 inMemGraphBuilder.schemaEnforcer((ChampSchemaEnforcer) schemaEnforcer.newInstance());
133 } catch (ClassNotFoundException e) {
134 throw new RuntimeException(e);
135 } catch (InstantiationException e) {
136 throw new RuntimeException(e);
137 } catch (IllegalAccessException e) {
138 throw new RuntimeException(e);
142 graph = inMemGraphBuilder.build();
145 final TitanChampGraphImpl.Builder graphBuilder = new TitanChampGraphImpl.Builder(getGraphName());
147 for (Entry<String, String> setting : settings.entrySet()) {
148 graphBuilder.property(setting.getKey(), setting.getValue());
151 graph = graphBuilder.build();
154 throw new RuntimeException("Unknown ChampGraph.Type " + graphType);
158 if (graph.queryObjects(Collections.emptyMap(), Optional.empty()).limit(1).count() > 0) {
160 throw new RuntimeException("Expected empty graph");
162 } catch (ChampTransactionException e) {
163 throw new RuntimeException("Transaction failure");
166 LOGGER.info("Graph instantiated, warming up JVM");
169 LOGGER.info("Warm up complete, starting to record performance measurements");
171 LOGGER.info("Performance without indexing/schema");
173 storeObjects(graph, false);
174 storeRelationships(graph, false);
175 retrieveIndividualObjects(graph, false);
176 retrieveBulkRelationships(graph, false);
177 retrieveIndividualRelationships(graph, false);
179 LOGGER.info("Storing indices + schema");
181 storeIndices(graph, false);
182 storeSchema(graph, false);
184 LOGGER.info("Stored indices + schema");
186 LOGGER.info("Performance with indexing + schema");
188 storeObjects(graph, false);
189 storeRelationships(graph, false);
190 retrieveIndividualObjects(graph, false);
191 retrieveBulkRelationships(graph, false);
192 retrieveIndividualRelationships(graph, false);
194 LOGGER.info("Performance test complete, shutting down graph");
198 LOGGER.info("Graph shutdown, JVM exiting");
201 private static void storeSchema(ChampGraph graph, boolean warmUp) {
205 .withObjectConstraint()
207 .withPropertyConstraint()
208 .onField("fooObjectNumber")
212 .withRelationshipConstraint()
214 .withPropertyConstraint()
215 .onField("barObjectNumber")
216 .ofType(ChampField.Type.INTEGER)
222 } catch (ChampSchemaViolationException e) {
223 throw new AssertionError(e);
227 private static void storeIndices(ChampGraph graph, boolean warmUp) {
228 graph.storeObjectIndex(
229 ChampObjectIndex.create()
230 .ofName("objectNumberIndex")
232 .forField("objectNumber")
236 graph.storeRelationshipIndex(ChampRelationshipIndex.create()
237 .ofName("relationshipNumberIndex")
239 .forField("relationshipNumber")
244 private static void warmUp(ChampGraph graph) {
245 storeObjects(graph, false);
246 storeRelationships(graph, false);
247 retrieveIndividualObjects(graph, false);
248 retrieveBulkRelationships(graph, false);
249 retrieveIndividualRelationships(graph, false);
252 private static void retrieveIndividualRelationships(ChampGraph graph, boolean warmUp) {
253 final double[] latencies = new double[NUM_RELATIONSHIPS];
254 final long totalStartTime = System.nanoTime();
256 for (int i = 0; i < NUM_RELATIONSHIPS; i++) {
257 final long startTime = System.nanoTime();
259 Stream<ChampRelationship> objects;
261 objects = graph.queryRelationships(Collections.singletonMap("relationshipNumber", i), Optional.empty());
262 } catch (ChampTransactionException e) {
263 throw new RuntimeException(e);
266 objects.findFirst().get();
267 final double elapsedMs = (System.nanoTime() - startTime) / 1000.0 / 1000.0;
268 latencies[i] = elapsedMs;
271 final double totalElapsedTimeSecs = (System.nanoTime() - totalStartTime) / 1000.0 / 1000.0 / 1000.0;
272 LOGGER.info("Individually read " + NUM_RELATIONSHIPS + " relationships in " + totalElapsedTimeSecs + "s (" + NUM_RELATIONSHIPS / totalElapsedTimeSecs + " relationships/s)");
274 Arrays.sort(latencies);
277 LOGGER.info("Retrieve individual relationship latencies");
278 LOGGER.info("\t50th percentile: " + latencies[(int) (NUM_RELATIONSHIPS * 0.50)]);
279 LOGGER.info("\t75th percentile: " + latencies[(int) (NUM_RELATIONSHIPS * 0.75)]);
280 LOGGER.info("\t90th percentile: " + latencies[(int) (NUM_RELATIONSHIPS * 0.90)]);
281 LOGGER.info("\t99th percentile: " + latencies[(int) (NUM_RELATIONSHIPS * 0.99)]);
285 private static void retrieveIndividualObjects(ChampGraph graph, boolean warmUp) {
287 final double[] latencies = new double[NUM_OBJECTS];
288 final long totalStartTime = System.nanoTime();
290 for (int i = 0; i < NUM_OBJECTS; i++) {
291 final long startTime = System.nanoTime();
292 Stream<ChampObject> objects;
294 objects = graph.queryObjects(Collections.singletonMap("objectNumber", i), Optional.empty());
295 } catch (ChampTransactionException e) {
296 throw new RuntimeException(e);
299 objects.findFirst().get();
301 final double elapsedMs = (System.nanoTime() - startTime) / 1000.0 / 1000.0;
303 latencies[i] = elapsedMs;
306 final double totalElapsedTimeSecs = (System.nanoTime() - totalStartTime) / 1000.0 / 1000.0 / 1000.0;
308 LOGGER.info("Individually read " + NUM_OBJECTS + " objects in " + totalElapsedTimeSecs + "s (" + NUM_OBJECTS / totalElapsedTimeSecs + " objects/s)");
309 Arrays.sort(latencies);
312 LOGGER.info("Retrieve individual object latencies");
313 LOGGER.info("\t50th percentile: " + latencies[(int) (NUM_OBJECTS * 0.50)]);
314 LOGGER.info("\t75th percentile: " + latencies[(int) (NUM_OBJECTS * 0.75)]);
315 LOGGER.info("\t90th percentile: " + latencies[(int) (NUM_OBJECTS * 0.90)]);
316 LOGGER.info("\t99th percentile: " + latencies[(int) (NUM_OBJECTS * 0.99)]);
320 private static List<ChampObject> retrieveBulkObjects(ChampGraph graph, boolean warmUp) {
322 final long startTime = System.nanoTime();
323 Stream<ChampObject> objects;
325 objects = graph.queryObjects(
326 Collections.singletonMap(
327 ChampObject.ReservedPropertyKeys.CHAMP_OBJECT_TYPE.toString(), "foo"
330 } catch (ChampTransactionException e) {
331 throw new RuntimeException(e);
334 final List<ChampObject> objectsAsList = objects.collect(Collectors.toList());
335 final double elapsedSecs = (System.nanoTime() - startTime) / 1000.0 / 1000.0 / 1000.0;
338 LOGGER.info("Bulk read " + objectsAsList.size() + " objects in " + elapsedSecs + "s (" + objectsAsList.size() / elapsedSecs + " objects/s)");
341 return objectsAsList;
344 private static List<ChampRelationship> retrieveBulkRelationships(ChampGraph graph, boolean warmUp) {
345 final long startTime = System.nanoTime();
346 Stream<ChampRelationship> relationships;
348 relationships = graph.queryRelationships(
349 Collections.singletonMap(
350 ChampRelationship.ReservedPropertyKeys.CHAMP_RELATIONSHIP_TYPE.toString(), "bazz"
353 } catch (ChampTransactionException e) {
354 throw new RuntimeException(e);
357 final List<ChampRelationship> relationshipsAsList = relationships.collect(Collectors.toList());
358 final double elapsedSecs = (System.nanoTime() - startTime) / 1000.0 / 1000.0 / 1000.0;
361 LOGGER.info("Bulk read " + relationshipsAsList.size() + " relationships in " + elapsedSecs + "s (" + relationshipsAsList.size() / elapsedSecs + " relationships/s)");
364 return relationshipsAsList;
367 private static void storeObjects(ChampGraph graph, boolean warmUp) {
368 final double[] latencies = new double[NUM_OBJECTS];
369 final long totalStartTime = System.nanoTime();
371 for (int i = 0; i < NUM_OBJECTS; i++) {
373 final long startTime = System.nanoTime();
379 .withProperty("objectNumber", i)
380 .build(), Optional.empty()
383 final double elapsedMs = (System.nanoTime() - startTime) / 1000.0 / 1000.0;
384 latencies[i] = elapsedMs;
385 } catch (ChampMarshallingException e) {
386 throw new RuntimeException(e);
387 } catch (ChampSchemaViolationException e) {
388 //Ignore, no schema set
389 } catch (ChampObjectNotExistsException e) {
390 //Ignore, not an update
391 } catch (ChampTransactionException e) {
392 throw new RuntimeException(e);
396 final double totalElapsedTimeSecs = (System.nanoTime() - totalStartTime) / 1000.0 / 1000.0 / 1000.0;
397 LOGGER.info("Wrote " + NUM_OBJECTS + " objects in " + totalElapsedTimeSecs + "s (" + NUM_OBJECTS / totalElapsedTimeSecs + " objects/s)");
399 Arrays.sort(latencies);
402 LOGGER.info("Store object latencies");
403 LOGGER.info("\t50th percentile: " + latencies[(int) (NUM_OBJECTS * 0.50)]);
404 LOGGER.info("\t75th percentile: " + latencies[(int) (NUM_OBJECTS * 0.75)]);
405 LOGGER.info("\t90th percentile: " + latencies[(int) (NUM_OBJECTS * 0.90)]);
406 LOGGER.info("\t99th percentile: " + latencies[(int) (NUM_OBJECTS * 0.99)]);
410 private static void storeRelationships(ChampGraph graph, boolean warmUp) {
411 final List<ChampObject> objects = retrieveBulkObjects(graph, warmUp);
412 final double[] latencies = new double[NUM_RELATIONSHIPS];
413 final long totalStartTime = System.nanoTime();
415 for (int i = 0; i < NUM_RELATIONSHIPS; i++) {
417 final long startTime = System.nanoTime();
419 graph.storeRelationship(
420 new ChampRelationship.Builder(
421 objects.get(i % objects.size()), objects.get((i + 1) % objects.size()), "bazz"
422 ).property("relationshipNumber", i)
426 final double elapsedMs = (System.nanoTime() - startTime) / 1000.0 / 1000.0;
428 latencies[i] = elapsedMs;
429 } catch (ChampMarshallingException e) {
430 throw new RuntimeException(e);
431 } catch (ChampObjectNotExistsException e) {
432 throw new RuntimeException(e);
433 } catch (ChampSchemaViolationException e) {
434 throw new RuntimeException(e);
435 } catch (ChampRelationshipNotExistsException e) {
436 throw new RuntimeException(e);
437 } catch (ChampUnmarshallingException e) {
438 throw new RuntimeException(e);
439 } catch (ChampTransactionException e) {
440 throw new RuntimeException(e);
444 final double totalElapsedTimeSecs = (System.nanoTime() - totalStartTime) / 1000.0 / 1000.0 / 1000.0;
445 LOGGER.info("Wrote " + NUM_RELATIONSHIPS + " relationships in " + totalElapsedTimeSecs + "s (" + NUM_RELATIONSHIPS / totalElapsedTimeSecs + " relationships/s)");
447 Arrays.sort(latencies);
450 LOGGER.info("Store relationship latencies");
451 LOGGER.info("\t50th percentile: " + latencies[(int) (NUM_RELATIONSHIPS * 0.50)]);
452 LOGGER.info("\t75th percentile: " + latencies[(int) (NUM_RELATIONSHIPS * 0.75)]);
453 LOGGER.info("\t90th percentile: " + latencies[(int) (NUM_RELATIONSHIPS * 0.90)]);
454 LOGGER.info("\t99th percentile: " + latencies[(int) (NUM_RELATIONSHIPS * 0.99)]);