Skip to content

Commit 76faf72

Browse files
authored
hide serialization stats recording behind flag (off by default) (#70)
* hide serialization stats recording behind flag (off by default) * use nanos in both serializer and deserializer * refactor: extract common stats code to BookKeeper * System.nanoTime is relatively expensive - only call if necessary
1 parent d58275d commit 76faf72

File tree

8 files changed

+80
-48
lines changed

8 files changed

+80
-48
lines changed

tinkerpop3/src/main/java/io/shiftleft/overflowdb/OdbConfig.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ public class OdbConfig {
66
private boolean overflowEnabled = true;
77
private int heapPercentageThreshold = 80;
88
private Optional<String> storageLocation = Optional.empty();
9+
private boolean serializationStatsEnabled = false;
910

1011
public static OdbConfig withDefaults() {
1112
return new OdbConfig();
@@ -37,6 +38,12 @@ public OdbConfig withStorageLocation(String path) {
3738
return this;
3839
}
3940

41+
/* If specified, OdbGraph will measure and report serialization / deserialization timing averages. */
42+
public OdbConfig withSerializationStatsEnabled() {
43+
this.serializationStatsEnabled = true;
44+
return this;
45+
}
46+
4047
public boolean isOverflowEnabled() {
4148
return overflowEnabled;
4249
}
@@ -48,4 +55,8 @@ public int getHeapPercentageThreshold() {
4855
public Optional<String> getStorageLocation() {
4956
return storageLocation;
5057
}
58+
59+
public boolean isSerializationStatsEnabled() {
60+
return serializationStatsEnabled;
61+
}
5162
}

tinkerpop3/src/main/java/io/shiftleft/overflowdb/OdbGraph.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,12 +95,17 @@ private OdbGraph(OdbConfig config,
9595
Optional.of(new HeapUsageMonitor(config.getHeapPercentageThreshold(), referenceManager)) :
9696
Optional.empty();
9797

98-
NodeDeserializer nodeDeserializer = new NodeDeserializer(this, nodeFactoryByLabelId);
98+
NodeDeserializer nodeDeserializer = new NodeDeserializer(
99+
this, nodeFactoryByLabelId, config.isSerializationStatsEnabled());
99100
if (config.getStorageLocation().isPresent()) {
100-
storage = OdbStorage.createWithSpecificLocation(nodeDeserializer, new File(config.getStorageLocation().get()));
101+
storage = OdbStorage.createWithSpecificLocation(
102+
nodeDeserializer,
103+
new File(config.getStorageLocation().get()),
104+
config.isSerializationStatsEnabled()
105+
);
101106
initElementCollections(storage);
102107
} else {
103-
storage = OdbStorage.createWithTempFile(nodeDeserializer);
108+
storage = OdbStorage.createWithTempFile(nodeDeserializer, config.isSerializationStatsEnabled());
104109
initEmptyElementCollections();
105110
}
106111
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package io.shiftleft.overflowdb.storage;
2+
3+
import org.slf4j.Logger;
4+
import org.slf4j.LoggerFactory;
5+
6+
public abstract class BookKeeper {
7+
protected final Logger logger = LoggerFactory.getLogger(getClass());
8+
public final boolean statsEnabled;
9+
private int totalCount = 0;
10+
private long totalTimeSpentNanos = 0;
11+
12+
protected BookKeeper(boolean statsEnabled) {
13+
this.statsEnabled = statsEnabled;
14+
}
15+
16+
protected final long getStartTimeNanos() {
17+
// System.nanoTime is relatively expensive - only go there if we're actually recording stats
18+
return statsEnabled ? System.nanoTime() : 0;
19+
}
20+
21+
protected void recordStatistics(long startTimeNanos) {
22+
totalCount++;
23+
totalTimeSpentNanos += System.nanoTime() - startTimeNanos;
24+
if (0 == (totalCount & 0x0001ffff)) {
25+
float avgSerializationTime = 1.0f-6 * totalTimeSpentNanos / (float) totalCount;
26+
logger.debug("stats: handled " + totalCount + " nodes in total (avg time: " + avgSerializationTime + "ms)");
27+
}
28+
}
29+
30+
public final int getSerializedCount() {
31+
if (statsEnabled) return totalCount;
32+
else throw new RuntimeException("serialization statistics not enabled");
33+
}
34+
}

tinkerpop3/src/main/java/io/shiftleft/overflowdb/storage/NodeDeserializer.java

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -22,31 +22,25 @@
2222
import java.util.Map;
2323
import java.util.concurrent.ConcurrentHashMap;
2424

25-
public class NodeDeserializer {
26-
private final Logger logger = LoggerFactory.getLogger(getClass());
25+
public class NodeDeserializer extends BookKeeper {
2726
protected final OdbGraph graph;
2827
private final Map<Integer, NodeFactory> nodeFactoryByLabelId;
29-
private int deserializedCount = 0;
30-
private long deserializationTimeSpentNanos = 0;
3128
private ConcurrentHashMap<String, String> interner;
3229

33-
34-
public NodeDeserializer(OdbGraph graph, Map<Integer, NodeFactory> nodeFactoryByLabelId) {
30+
public NodeDeserializer(OdbGraph graph, Map<Integer, NodeFactory> nodeFactoryByLabelId, boolean statsEnabled) {
31+
super(statsEnabled);
3532
this.graph = graph;
3633
this.nodeFactoryByLabelId = nodeFactoryByLabelId;
37-
this.interner = new ConcurrentHashMap<String, String>();
34+
this.interner = new ConcurrentHashMap<>();
3835
}
3936

4037
private final String intern(String s){
4138
String interned = interner.putIfAbsent(s, s);
4239
return interned == null ? s : interned;
4340
}
4441

45-
4642
public final OdbNode deserialize(byte[] bytes) throws IOException {
47-
// todo: only time when some config is set
48-
49-
long start = System.nanoTime();
43+
long startTimeNanos = getStartTimeNanos();
5044
if (null == bytes)
5145
return null;
5246

@@ -59,12 +53,7 @@ public final OdbNode deserialize(byte[] bytes) throws IOException {
5953

6054
OdbNode node = createNode(id, labelId, properties, edgeOffsets, adjacentNodesWithProperties);
6155

62-
deserializedCount++;
63-
deserializationTimeSpentNanos += System.nanoTime() - start;
64-
if (0 == (deserializedCount & 0x0001ffff)) {
65-
float avgDeserializationTime = 1.0f-6 * deserializationTimeSpentNanos / (float) deserializedCount;
66-
logger.debug("stats: deserialized " + deserializedCount + " nodes in total (avg time: " + avgDeserializationTime + "ms)");
67-
}
56+
if (statsEnabled) recordStatistics(startTimeNanos);
6857
return node;
6958
}
7059

tinkerpop3/src/main/java/io/shiftleft/overflowdb/storage/NodeSerializer.java

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,19 @@
66
import org.apache.commons.lang3.NotImplementedException;
77
import org.msgpack.core.MessageBufferPacker;
88
import org.msgpack.core.MessagePack;
9-
import org.slf4j.Logger;
10-
import org.slf4j.LoggerFactory;
119

1210
import java.io.IOException;
1311
import java.util.Iterator;
1412
import java.util.List;
1513
import java.util.Map;
1614

17-
public class NodeSerializer {
18-
19-
private final Logger logger = LoggerFactory.getLogger(getClass());
20-
private int serializedCount = 0;
21-
private long serializationTimeSpentMillis = 0;
15+
public class NodeSerializer extends BookKeeper {
16+
public NodeSerializer(boolean statsEnabled) {
17+
super(statsEnabled);
18+
}
2219

2320
public byte[] serialize(OdbNode node) throws IOException {
24-
long start = System.currentTimeMillis();
21+
long startTimeNanos = getStartTimeNanos();
2522
try (MessageBufferPacker packer = MessagePack.newDefaultBufferPacker()) {
2623
/* marking as clean *before* we start serializing - if node is modified any time afterwards it'll be marked as dirty */
2724
node.markAsClean();
@@ -33,12 +30,7 @@ public byte[] serialize(OdbNode node) throws IOException {
3330
packEdgeOffsets(packer, node.getEdgeOffsetsPackedArray());
3431
packAdjacentNodesWithProperties(packer, node.getAdjacentNodesWithProperties());
3532

36-
serializedCount++;
37-
serializationTimeSpentMillis += System.currentTimeMillis() - start;
38-
if (serializedCount % 131072 == 0) { //2^17
39-
float avgSerializationTime = serializationTimeSpentMillis / (float) serializedCount;
40-
logger.debug("stats: serialized " + serializedCount + " instances in total (avg time: " + avgSerializationTime + "ms)");
41-
}
33+
if (statsEnabled) recordStatistics(startTimeNanos);
4234
return packer.toByteArray();
4335
}
4436
}
@@ -120,7 +112,4 @@ private void packTypedValue(final MessageBufferPacker packer, final Object value
120112
}
121113
}
122114

123-
public final int getSerializedCount() {
124-
return serializedCount;
125-
}
126115
}

tinkerpop3/src/main/java/io/shiftleft/overflowdb/storage/OdbStorage.java

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,38 +17,41 @@
1717
public class OdbStorage implements AutoCloseable {
1818
private static final String INDEX_PREFIX = "index_";
1919
private final Logger logger = LoggerFactory.getLogger(getClass());
20-
protected final NodeSerializer nodeSerializer = new NodeSerializer();
20+
protected final NodeSerializer nodeSerializer;
2121
protected final Optional<NodeDeserializer> nodeDeserializer;
2222

2323
private final File mvstoreFile;
2424
private MVStore mvstore; // initialized in `getNodesMVMap`
2525
private MVMap<Long, byte[]> nodesMVMap;
2626
private boolean closed;
2727

28-
public static OdbStorage createWithTempFile(final NodeDeserializer nodeDeserializer) {
29-
return new OdbStorage(Optional.empty(), Optional.ofNullable(nodeDeserializer));
28+
public static OdbStorage createWithTempFile(
29+
final NodeDeserializer nodeDeserializer, final boolean enableSerializationStats) {
30+
return new OdbStorage(Optional.empty(), Optional.ofNullable(nodeDeserializer), enableSerializationStats);
3031
}
3132

3233
/**
3334
* create with specific mvstore file - which may or may not yet exist.
3435
* mvstoreFile won't be deleted at the end (unlike temp file constructors above)
3536
*/
3637
public static OdbStorage createWithSpecificLocation(
37-
final NodeDeserializer nodeDeserializer, final File mvstoreFile) {
38-
return new OdbStorage(Optional.ofNullable(mvstoreFile), Optional.ofNullable(nodeDeserializer));
38+
final NodeDeserializer nodeDeserializer, final File mvstoreFile, final boolean enableSerializationStats) {
39+
return new OdbStorage(Optional.ofNullable(mvstoreFile), Optional.ofNullable(nodeDeserializer), enableSerializationStats);
3940
}
4041

4142
/**
4243
* create with specific mvstore file - which may or may not yet exist.
4344
* mvstoreFile won't be deleted at the end (unlike temp file constructors above)
4445
*/
45-
public static OdbStorage createWithSpecificLocation(final File mvstoreFile) {
46-
return new OdbStorage(Optional.ofNullable(mvstoreFile), Optional.empty());
46+
public static OdbStorage createWithSpecificLocation(final File mvstoreFile, final boolean enableSerializationStats) {
47+
return new OdbStorage(Optional.ofNullable(mvstoreFile), Optional.empty(), enableSerializationStats);
4748
}
4849

4950
private OdbStorage(
5051
final Optional<File> mvstoreFileMaybe,
51-
final Optional<NodeDeserializer> nodeDeserializer) {
52+
final Optional<NodeDeserializer> nodeDeserializer,
53+
final boolean enableSerializationStats) {
54+
this.nodeSerializer = new NodeSerializer(enableSerializationStats);
5255
this.nodeDeserializer = nodeDeserializer;
5356

5457
if (mvstoreFileMaybe.isPresent()) {

tinkerpop3/src/test/java/io/shiftleft/overflowdb/storage/GraphSaveRestoreTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ private void modifyAndCloseGraph(File storageFile, Function<OdbGraph, Integer> g
169169

170170
private OdbGraph newGratefulDeadGraph(File overflowDb, boolean enableOverflow) {
171171
OdbConfig config = enableOverflow ? OdbConfig.withDefaults() : OdbConfig.withoutOverflow();
172+
config = config.withSerializationStatsEnabled();
172173
return GratefulDead.newGraph(config.withStorageLocation(overflowDb.getAbsolutePath()));
173174
}
174175

tinkerpop3/src/test/java/io/shiftleft/overflowdb/storage/SerializerTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public class SerializerTest {
2525
@Test
2626
public void serializeVertex() throws IOException {
2727
try (OdbGraph graph = SimpleDomain.newGraph()) {
28-
NodeSerializer serializer = new NodeSerializer();
28+
NodeSerializer serializer = new NodeSerializer(false);
2929
NodeDeserializer deserializer = newDeserializer(graph);
3030
TestNode testNode = (TestNode) graph.addVertex(
3131
T.label, TestNode.LABEL,
@@ -52,7 +52,7 @@ public void serializeVertex() throws IOException {
5252
@Test
5353
public void serializeWithEdge() throws IOException {
5454
try (OdbGraph graph = SimpleDomain.newGraph()) {
55-
NodeSerializer serializer = new NodeSerializer();
55+
NodeSerializer serializer = new NodeSerializer(true);
5656
NodeDeserializer deserializer = newDeserializer(graph);
5757

5858
TestNode testNode1 = (TestNode) graph.addVertex(T.label, TestNode.LABEL);
@@ -84,7 +84,7 @@ public void serializeWithEdge() throws IOException {
8484
private NodeDeserializer newDeserializer(OdbGraph graph) {
8585
Map<Integer, NodeFactory> vertexFactories = new HashMap();
8686
vertexFactories.put(TestNodeDb.layoutInformation.labelId, TestNode.factory);
87-
return new NodeDeserializer(graph, vertexFactories);
87+
return new NodeDeserializer(graph, vertexFactories, true);
8888
}
8989

9090
}

0 commit comments

Comments
 (0)