This document provides instructions for using Gatling to perform performance testing on Neo4j. It describes how to set up a Gatling simulation class to make HTTP requests to Neo4j, load test different Cypher queries, and view the results. It also covers using YourKit Java Profiler to profile the Cypher queries and view where time is being spent. Various Cypher queries to find weighted paths between users are demonstrated and improved through using different evaluators and expanders to filter relationships and prune the search space.
6. // Use a data file for our requests and repeat
values if we get to the end.
val feeder = csv("data/usernames.csv").circular
// The cypher queries we will test
val count = """CALL
com.maxdemarzi.network.count($username, 4)"""
val cypherQuery = """{"statements" : [{"statement"
: "%s", "parameters" : { "username": "${username}"
}}]}"""
.format(count)
9. /*
If you want to see the response from the
server, add the following to the .check
.check(bodyString.saveAs("BODY"))
)
.exec(session => {
val response =
session("BODY").as[String]
println(s"Response body:
n$response")
session
}
*/
69. MATCH p=(u:User)-[*1..2]-(u2:User)
WHERE u.username = 'Khloe17' AND
u2.username = 'Michelle21'
RETURN p, REDUCE(weight = 0.0, r in
relationships(p) |
weight + r.weight) /
length(p) AS weight
ORDER BY weight DESC
LIMIT 100
72. MATCH p=(u:User)-[*1..3]-(u2:User)
WHERE u.username = 'Khloe17' AND
u2.username = 'Michelle21'
RETURN p, REDUCE(weight = 0.0, r in
relationships(p) |
weight + r.weight) /
length(p) AS weight
ORDER BY weight DESC
LIMIT 100
75. MATCH p=(u:User)-[*1..4]-(u2:User)
WHERE u.username = 'Khloe17' AND
u2.username = 'Michelle21'
RETURN p, REDUCE(weight = 0.0, r in
relationships(p) |
weight + r.weight) /
length(p) AS weight
ORDER BY weight DESC
LIMIT 100
78. MATCH p=(u:User)-[*1..4]-(u2:User)
WHERE u.username = 'Khloe17' AND
u2.username = 'Michelle21' AND
ALL (r IN relationships(p) WHERE
r.weight >= 0.80)
RETURN p, REDUCE(weight = 0.0, r in
relationships(p) |
weight + r.weight) /
length(p) AS weight
ORDER BY length(p), weight DESC
LIMIT 100
84. package com.maxdemarzi.results;
import org.neo4j.graphdb.Path;
public class WeightedPathResult {
public Path path;
public double weight;
public WeightedPathResult(Path path, double
weight) {
this.path = path;
this.weight = weight;
}
public int getLength() {
return path.length();
}
public double getWeight() {
86. private static final Comparator<WeightedPathResult> comparator
=
Comparator.comparingInt(WeightedPathResult::getLength)
.thenComparing(Comparator.comparingDouble(WeightedPath
Result::getWeight)
.reversed());
This goes in Procedures.java inside the class, outside any methods.
104. public class GoodFriendExpander implements PathExpander {
@Override
public Iterable<Relationship> expand(Path path, BranchState
branchState) {
List<Relationship> rels = new ArrayList<>();
for (Relationship r : path.endNode()
.getRelationships(RelationshipType.wit
hName("FRIENDS"))) {
if ((double)r.getProperty("weight") >= 0.80) {
rels.add(r);
}
}
return rels;
}
@Override
public PathExpander reverse() {
// Doesn't matter, do the same thing.
return this;
}
}
135. REPORT
I want to know for every friend of
friends, how many friends they have
in common….Oh and I want it in
order by username alphabetically
and then the count in descending
order.
151. package com.maxdemarzi.results;
public class CountValueResult {
public final Long count;
public final Long value;
public CountValueResult(Long count,
Long value) {
this.count = count;
this.value = value;
}
}
152. @Procedure(name = "com.maxdemarzi.fic.distribution",
mode = Mode.READ)
@Description("com.maxdemarzi.fic.distribution")
public Stream<CountValueResult>
friendsInCommonDistribution() {
long start = System.nanoTime();
if (graph == null) {
graph = this.db;
}
ResourceIterator userIterator =
db.findNodes(Label.label("User"));
Map<Long, long[]> instances = new HashMap<>();
Roaring64NavigableMap seen = new
Roaring64NavigableMap();
153. int count = 0;
while (userIterator.hasNext()) {
Node user = (Node) userIterator.next();
RoaringBitmap friendsMap = nodeFriends.get((int)
user.getId());
int[] friends = friendsMap.toArray();
for (int i = 0; i < friends.length; i++) {
for (int j = i + 1; j < friends.length; j++) {
long key = (((long)friends[i]) << 32) |
(friends[j] & 0xffffffffL);
seen.add(key);
}
}
if(count++ % 1000 == 0) {
log.warn("On User # " + count + " at " +
TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - start));
}
}
154. count = 0;
Iterator<Long> iterator = seen.iterator();
long size = seen.getLongCardinality();
while(iterator.hasNext()) {
long l = iterator.next();
int firstNodeId = (int)(l >> 32);
int secondNodeId = (int)l;
long common = RoaringBitmap
.and(nodeFriends.get(firstNodeId),
nodeFriends.get(secondNodeId))
.getCardinality();
instances.putIfAbsent(common, new long[]{0L});
instances.get(common)[0]++;
if(count++ % 50_000_000 == 0) {
log.warn("On Combination # " + count + " of "
+ size + " at " +
TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() -
start));