Skip to content

Commit

Permalink
Expose BerkeleyJE configs and setup default lock timeout to 30 second…
Browse files Browse the repository at this point in the history
…s [tp-tests]

Related to JanusGraph#1623 and JanusGraph#4425

Signed-off-by: Oleksandr Porunov <[email protected]>
  • Loading branch information
porunov committed Oct 17, 2024
1 parent 9f09767 commit 12b0915
Show file tree
Hide file tree
Showing 6 changed files with 131 additions and 32 deletions.
17 changes: 17 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,23 @@ For more information on features and bug fixes in 1.1.0, see the GitHub mileston
* [JanusGraph zip](https://github.com/JanusGraph/janusgraph/releases/download/v1.1.0/janusgraph-1.1.0.zip)
* [JanusGraph zip with embedded Cassandra and ElasticSearch](https://github.com/JanusGraph/janusgraph/releases/download/v1.1.0/janusgraph-full-1.1.0.zip)

#### Upgrade Instructions

##### BerkeleyJE lock timeout set to 30 seconds instead of default 500 ms

Many users reported that in multi-thread environment the default lock timeout of `500 ms` is not enough and often lead
to lock timeout errors. Thus, the new configuration is set to `30000 ms`.
It's possible to switch back to previous settings by setting configuration option
`storage.berkeleyje.ext.je.lock.timeout` to `500 ms`.

##### BerkeleyJE ability to overwrite arbitrary settings applied at `EnvironmentConfig` creation

The new namespace `storage.berkeleyje.ext` now allows to set custom configurations which were not directly exposed by
JanusGraph.
The full list of possible setting is available inside the Java class `com.sleepycat.je.EnvironmentConfig`.
All configurations values should be specified as `String` and be formated the same as specified in the official sleepycat
[documentation](https://docs.oracle.com/cd/E17277_02/html/java/com/sleepycat/je/EnvironmentConfig.html).

### Version 1.0.1 (Release Date: ???)

/// tab | Maven
Expand Down
42 changes: 42 additions & 0 deletions docs/storage-backend/bdb.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,45 @@ In order to not run out of memory, it is advised to disable transactions
transactions enabled requires BerkeleyDB to acquire read locks on the
data it is reading. When iterating over the entire graph, these read
locks can easily require more memory than is available.

## Additional BerkeleyDB JE configuration options

It's possible to set additional BerkeleyDB JE configuration which are not
directly exposed by JanusGraph by leveraging `storage.berkeleyje.ext`
namespace.

JanusGraph iterates over all properties prefixed with
`storage.berkeleyje.ext.`. It strips the prefix from each property key.
The remainder of the stripped key will be interpreted as a parameter
ke for `com.sleepycat.je.EnvironmentConfig`. The value associated with the
key is not modified.
This allows embedding arbitrary settings in JanusGraph’s properties. Here’s an
example configuration fragment that customizes three BerkeleyDB settings
using the `storage.berkeleyje.ext.` config mechanism:

```properties
storage.backend=berkeleyje
storage.berkeleyje.ext.je.lock.timeout=5000 ms
storage.berkeleyje.ext.je.lock.deadlockDetect=false
storage.berkeleyje.ext.je.txn.timeout=5000 ms
storage.berkeleyje.ext.je.log.fileMax=100000000
```

## Deadlock troubleshooting

In concurrent environment deadlocks are possible when using BerkeleyDB JE storage
backend.
It may be complicated to deal with deadlocks in use-cases when multiple threads are
modifying same vertices (including edges creation between affected vertices).
More insights on this topic can be found in the GitHub issue
[#1623](https://github.com/JanusGraph/janusgraph/issues/1623).

Some users suggest the following configuration to deal with deadlocks:
```properties
storage.berkeleyje.isolation-level=READ_UNCOMMITTED
storage.berkeleyje.lock-mode=LockMode.READ_UNCOMMITTED
storage.berkeleyje.ext.je.lock.timeout=0
storage.lock.wait-time=5000
ids.authority.wait-time=2000
tx.max-commit-time=30000
```
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
import org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration;
import org.janusgraph.graphdb.configuration.PreInitializeConfigOptions;
import org.janusgraph.graphdb.transaction.TransactionConfiguration;
import org.janusgraph.util.system.ConfigurationUtil;
import org.janusgraph.util.system.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -93,6 +94,23 @@ public class BerkeleyJEStoreManager extends LocalStoreManager implements Ordered
ConfigOption.Type.MASKABLE, String.class,
IsolationLevel.REPEATABLE_READ.toString(), disallowEmpty(String.class));

public static final ConfigNamespace BERKELEY_EXTRAS_NS =
new ConfigNamespace(BERKELEY_NS, "ext", "Overrides for arbitrary settings applied at `EnvironmentConfig` creation.\n" +
"The full list of possible setting is available inside the Java class `com.sleepycat.je.EnvironmentConfig`. " +
"All configurations values should be specified as `String` and be formated the same as specified in the following " +
"[documentation](https://docs.oracle.com/cd/E17277_02/html/java/com/sleepycat/je/EnvironmentConfig.html).");

// This setting isn't used directly in Java, but this setting will be picked up indirectly during parsing of the
// subset configuration of `BERKELEY_EXTRAS_NS` namespace
public static final ConfigOption<String> EXT_LOCK_TIMEOUT =
new ConfigOption<>(BERKELEY_EXTRAS_NS, EnvironmentConfig.LOCK_TIMEOUT,
"Lock timeout configuration. " +
"`0` disabled lock timeout completely. To set lock timeout via this configuration it's required to use " +
"String formated time representation. For example: `500 ms`, `5 min`, etc. See information about value " +
"constraints in the official " +
"[sleepycat documentation](https://docs.oracle.com/cd/E17277_02/html/java/com/sleepycat/je/EnvironmentConfig.html#LOCK_TIMEOUT)",
ConfigOption.Type.MASKABLE, "30000 ms");

private final ConcurrentMap<String, BerkeleyJEKeyValueStore> stores;

protected volatile Environment environment;
Expand Down Expand Up @@ -143,6 +161,9 @@ private synchronized void initialize() throws BackendException {
envConfig.setConfigParam(EnvironmentConfig.ENV_RUN_CLEANER, "false");
}

Map<String, String> extraSettings = getSettingsFromJanusGraphConf(storageConfig);
extraSettings.forEach(envConfig::setConfigParam);

// Open the environment
environment = new Environment(directory, envConfig);

Expand All @@ -153,7 +174,15 @@ private synchronized void initialize() throws BackendException {
} catch (DatabaseException e) {
throw new PermanentBackendException("Error during BerkeleyJE initialization: ", e);
}
}

static Map<String, String> getSettingsFromJanusGraphConf(Configuration config) {
final Map<String, String> settings = ConfigurationUtil.getSettingsFromJanusGraphConf(config, BERKELEY_EXTRAS_NS);
if(log.isDebugEnabled()){
settings.forEach((key, val) -> log.debug("[BERKELEY ext.* cfg] Set {}: {}", key, val));
log.debug("Loaded {} settings from the {} JanusGraph config namespace", settings.size(), BERKELEY_EXTRAS_NS);
}
return settings;
}

private synchronized void reInitialize(DatabaseException exception) throws BackendException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -275,5 +275,8 @@ public static Predicate<Long> positiveLong() {
return num -> num!=null && num>0;
}

public static Predicate<Long> nonnegativeLong() {
return num -> num!=null && num>=0;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

package org.janusgraph.util.system;

import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import org.apache.commons.configuration2.BaseConfiguration;
import org.apache.commons.configuration2.Configuration;
Expand All @@ -24,11 +25,14 @@
import org.apache.commons.configuration2.builder.fluent.PropertiesBuilderParameters;
import org.apache.commons.configuration2.convert.DefaultListDelimiterHandler;
import org.apache.commons.configuration2.ex.ConfigurationException;
import org.janusgraph.diskstorage.configuration.ConfigNamespace;

import java.io.File;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
Expand Down Expand Up @@ -169,4 +173,34 @@ private static PropertiesConfiguration loadPropertiesConfig(PropertiesBuilderPar
}
return builder.configure(newParams).getConfiguration();
}

public static Map<String, String> getSettingsFromJanusGraphConf(org.janusgraph.diskstorage.configuration.Configuration config, ConfigNamespace namespace) {

final Map<String, String> settings = new HashMap<>();

final Map<String,Object> configSub = config.getSubset(namespace);
for (Map.Entry<String,Object> entry : configSub.entrySet()) {
String key = entry.getKey();
Object val = entry.getValue();
if (null == val) continue;
if (List.class.isAssignableFrom(val.getClass())) {
// Pretty print lists using comma-separated values and no surrounding square braces for ES
List l = (List) val;
settings.put(key, Joiner.on(",").join(l));
} else if (val.getClass().isArray()) {
// As with Lists, but now for arrays
// The Object copy[] business lets us avoid repetitive primitive array type checking and casting
Object[] copy = new Object[Array.getLength(val)];
for (int i= 0; i < copy.length; i++) {
copy[i] = Array.get(val, i);
}
settings.put(key, Joiner.on(",").join(copy));
} else {
// Copy anything else unmodified
settings.put(key, val.toString());
}
}

return settings;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,15 @@

package org.janusgraph.diskstorage.es;

import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import org.janusgraph.diskstorage.configuration.Configuration;
import org.janusgraph.diskstorage.es.rest.RestClientSetup;
import org.janusgraph.util.system.ConfigurationUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.lang.reflect.Array;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
Expand Down Expand Up @@ -53,36 +51,12 @@ public Connection connect(Configuration config) throws IOException {
};

static Map<String, Object> getSettingsFromJanusGraphConf(Configuration config) {

final Map<String, Object> settings = new HashMap<>();

int keysLoaded = 0;
final Map<String,Object> configSub = config.getSubset(ElasticSearchIndex.ES_CREATE_EXTRAS_NS);
for (Map.Entry<String,Object> entry : configSub.entrySet()) {
String key = entry.getKey();
Object val = entry.getValue();
if (null == val) continue;
if (List.class.isAssignableFrom(val.getClass())) {
// Pretty print lists using comma-separated values and no surrounding square braces for ES
List l = (List) val;
settings.put(key, Joiner.on(",").join(l));
} else if (val.getClass().isArray()) {
// As with Lists, but now for arrays
// The Object copy[] business lets us avoid repetitive primitive array type checking and casting
Object[] copy = new Object[Array.getLength(val)];
for (int i= 0; i < copy.length; i++) {
copy[i] = Array.get(val, i);
}
settings.put(key, Joiner.on(",").join(copy));
} else {
// Copy anything else unmodified
settings.put(key, val.toString());
}
log.debug("[ES ext.* cfg] Set {}: {}", key, val);
keysLoaded++;
final Map<String, String> settings = ConfigurationUtil.getSettingsFromJanusGraphConf(config, ElasticSearchIndex.ES_CREATE_EXTRAS_NS);
if(log.isDebugEnabled()){
settings.forEach((key, val) -> log.debug("[ES ext.* cfg] Set {}: {}", key, val));
log.debug("Loaded {} settings from the {} JanusGraph config namespace", settings.size(), ElasticSearchIndex.ES_CREATE_EXTRAS_NS);
}
log.debug("Loaded {} settings from the {} JanusGraph config namespace", keysLoaded, ElasticSearchIndex.ES_CREATE_EXTRAS_NS);
return settings;
return new HashMap<>(settings);
}

private static final Logger log = LoggerFactory.getLogger(ElasticSearchSetup.class);
Expand Down

1 comment on commit 12b0915

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmark

Benchmark suite Current: 12b0915 Previous: 5402f5e Ratio
org.janusgraph.JanusGraphSpeedBenchmark.basicAddAndDelete 12916.326541285938 ms/op 12949.698505490105 ms/op 1.00
org.janusgraph.GraphCentricQueryBenchmark.getVertices 892.8461174073469 ms/op 892.5951053832812 ms/op 1.00
org.janusgraph.MgmtOlapJobBenchmark.runClearIndex 216.0361735152174 ms/op 215.59341250688408 ms/op 1.00
org.janusgraph.MgmtOlapJobBenchmark.runReindex 337.8430084901191 ms/op 335.2861736892857 ms/op 1.01
org.janusgraph.JanusGraphSpeedBenchmark.basicCount 252.90802179568936 ms/op 188.92540631309924 ms/op 1.34
org.janusgraph.CQLMultiQueryMultiSlicesBenchmark.getValuesAllPropertiesWithAllMultiQuerySlicesUnderMaxRequestsPerConnection 5003.692649112829 ms/op 4783.516855705014 ms/op 1.05
org.janusgraph.CQLMultiQueryBenchmark.getElementsWithUsingEmitRepeatSteps 16978.440000366387 ms/op 16371.585081654117 ms/op 1.04
org.janusgraph.CQLMultiQueryMultiSlicesBenchmark.getValuesMultiplePropertiesWithSmallBatch 19173.0948449899 ms/op 19821.94472387636 ms/op 0.97
org.janusgraph.CQLMultiQueryMultiSlicesBenchmark.vertexCentricPropertiesFetching 55601.585808699994 ms/op 55656.468643 ms/op 1.00
org.janusgraph.CQLMultiQueryDropBenchmark.dropVertices 1535.9703497247117 ms/op 1518.53597458366 ms/op 1.01
org.janusgraph.CQLMultiQueryBenchmark.getAllElementsTraversedFromOuterVertex 8290.915682708648 ms/op 7945.267944695057 ms/op 1.04
org.janusgraph.CQLMultiQueryBenchmark.getVerticesWithDoubleUnion 364.9336485765032 ms/op 382.6784147961901 ms/op 0.95
org.janusgraph.CQLMultiQueryMultiSlicesBenchmark.getValuesAllPropertiesWithUnlimitedBatch 4193.745841113841 ms/op 4111.55465671507 ms/op 1.02
org.janusgraph.CQLMultiQueryBenchmark.getNames 8397.906358247044 ms/op 8151.165947061362 ms/op 1.03
org.janusgraph.CQLMultiQueryMultiSlicesBenchmark.getValuesThreePropertiesWithAllMultiQuerySlicesUnderMaxRequestsPerConnection 5505.577542677107 ms/op 5673.677724947326 ms/op 0.97
org.janusgraph.CQLMultiQueryBenchmark.getLabels 7200.589186223835 ms/op 7082.457162937985 ms/op 1.02
org.janusgraph.CQLMultiQueryBenchmark.getVerticesFilteredByAndStep 416.31623249541144 ms/op 418.5346478931759 ms/op 0.99
org.janusgraph.CQLMultiQueryBenchmark.getVerticesFromMultiNestedRepeatStepStartingFromSingleVertex 12605.099895455442 ms/op 12294.636027679084 ms/op 1.03
org.janusgraph.CQLMultiQueryBenchmark.getVerticesWithCoalesceUsage 350.4614281335272 ms/op 356.39773890348994 ms/op 0.98
org.janusgraph.CQLMultiQueryMultiSlicesBenchmark.getValuesMultiplePropertiesWithAllMultiQuerySlicesUnderMaxRequestsPerConnection 14293.223097929049 ms/op 14473.400712190438 ms/op 0.99
org.janusgraph.CQLMultiQueryBenchmark.getIdToOutVerticesProjection 242.8811631505706 ms/op 246.2110184033701 ms/op 0.99
org.janusgraph.CQLMultiQueryMultiSlicesBenchmark.getValuesMultiplePropertiesWithUnlimitedBatch 14594.670094082929 ms/op 14452.906846572223 ms/op 1.01
org.janusgraph.CQLMultiQueryBenchmark.getNeighborNames 8391.029035375444 ms/op 8043.575015555463 ms/op 1.04
org.janusgraph.CQLMultiQueryBenchmark.getElementsWithUsingRepeatUntilSteps 8928.278174031884 ms/op 8740.071261503585 ms/op 1.02
org.janusgraph.CQLMultiQueryBenchmark.getAdjacentVerticesLocalCounts 8380.764480942062 ms/op 8370.81129934567 ms/op 1.00

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.