Skip to content

Commit e849077

Browse files
committed
Expose BerkeleyJE configs
Related to #1623 and #4425 Signed-off-by: Oleksandr Porunov <alexandr.porunov@gmail.com>
1 parent 213b754 commit e849077

9 files changed

Lines changed: 191 additions & 34 deletions

File tree

docs/changelog.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ For more information on features and bug fixes in 1.1.0, see the GitHub mileston
9898
* [JanusGraph zip](https://github.com/JanusGraph/janusgraph/releases/download/v1.1.0/janusgraph-1.1.0.zip)
9999
* [JanusGraph zip with embedded Cassandra and ElasticSearch](https://github.com/JanusGraph/janusgraph/releases/download/v1.1.0/janusgraph-full-1.1.0.zip)
100100

101-
##### Upgrade Instructions
101+
#### Upgrade Instructions
102102

103103
##### Inlining vertex properties into a Composite Index
104104

@@ -115,6 +115,15 @@ See [documentation](./schema/index-management/index-performance.md#inlining-vert
115115
It is critical that users carefully plan their migration to this new version, as there is no automated or manual rollback process
116116
to revert to an older version of JanusGraph once this feature is used.
117117

118+
##### BerkeleyJE ability to overwrite arbitrary settings applied at `EnvironmentConfig` creation
119+
120+
The new namespace `storage.berkeleyje.ext` now allows to set custom configurations which were not directly exposed by
121+
JanusGraph.
122+
The full list of possible setting is available inside the Java class `com.sleepycat.je.EnvironmentConfig`.
123+
All configurations values should be specified as `String` and be formated the same as specified in the official sleepycat
124+
[documentation](https://docs.oracle.com/cd/E17277_02/html/java/com/sleepycat/je/EnvironmentConfig.html).
125+
Example: `storage.berkeleyje.ext.je.lock.timeout=5000 ms`
126+
118127
### Version 1.0.1 (Release Date: ???)
119128

120129
/// tab | Maven

docs/configs/janusgraph-cfg.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,18 @@ BerkeleyDB JE configuration options
423423
| storage.berkeleyje.lock-mode | The BDB record lock mode used for read operations | String | LockMode.DEFAULT | MASKABLE |
424424
| storage.berkeleyje.shared-cache | If true, the shared cache is used for all graph instances | Boolean | true | MASKABLE |
425425

426+
### storage.berkeleyje.ext
427+
Overrides for arbitrary settings applied at `EnvironmentConfig` creation.
428+
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).
429+
Notice, for compatibility reasons, it's allowed to use `-` character instead of `.` for config keys. All dashes will be replaced by dots when passing those keys to `EnvironmentConfig`.
430+
431+
432+
| Name | Description | Datatype | Default Value | Mutability |
433+
| ---- | ---- | ---- | ---- | ---- |
434+
| storage.berkeleyje.ext.je-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.
435+
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).
436+
Notice, this option can be specified as `storage.berkeleyje.ext.je.lock.timeout` which will be treated the same as this configuration option. | String | (no default value) | MASKABLE |
437+
426438
### storage.cql
427439
CQL storage backend options
428440

docs/storage-backend/bdb.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,3 +85,49 @@ In order to not run out of memory, it is advised to disable transactions
8585
transactions enabled requires BerkeleyDB to acquire read locks on the
8686
data it is reading. When iterating over the entire graph, these read
8787
locks can easily require more memory than is available.
88+
89+
## Additional BerkeleyDB JE configuration options
90+
91+
It's possible to set additional BerkeleyDB JE configuration which are not
92+
directly exposed by JanusGraph by leveraging `storage.berkeleyje.ext`
93+
namespace.
94+
95+
JanusGraph iterates over all properties prefixed with
96+
`storage.berkeleyje.ext.`. It strips the prefix from each property key.
97+
Any dash character (`-`) wil be replaced by dot character (`.`) in the
98+
remainder of the stripped key. The final string will be interpreted as a parameter
99+
ke for `com.sleepycat.je.EnvironmentConfig`.
100+
Thus, both options `storage.berkeleyje.ext.je.lock.timeout` and
101+
`storage.berkeleyje.ext.je-lock-timeout` will be treated
102+
the same (as `storage.berkeleyje.ext.je.lock.timeout`).
103+
The value associated with the key is not modified.
104+
This allows embedding arbitrary settings in JanusGraph’s properties. Here’s an
105+
example configuration fragment that customizes three BerkeleyDB settings
106+
using the `storage.berkeleyje.ext.` config mechanism:
107+
108+
```properties
109+
storage.backend=berkeleyje
110+
storage.berkeleyje.ext.je.lock.timeout=5000 ms
111+
storage.berkeleyje.ext.je.lock.deadlockDetect=false
112+
storage.berkeleyje.ext.je.txn.timeout=5000 ms
113+
storage.berkeleyje.ext.je.log.fileMax=100000000
114+
```
115+
116+
## Deadlock troubleshooting
117+
118+
In concurrent environment deadlocks are possible when using BerkeleyDB JE storage
119+
backend.
120+
It may be complicated to deal with deadlocks in use-cases when multiple threads are
121+
modifying same vertices (including edges creation between affected vertices).
122+
More insights on this topic can be found in the GitHub issue
123+
[#1623](https://github.com/JanusGraph/janusgraph/issues/1623).
124+
125+
Some users suggest the following configuration to deal with deadlocks:
126+
```properties
127+
storage.berkeleyje.isolation-level=READ_UNCOMMITTED
128+
storage.berkeleyje.lock-mode=LockMode.READ_UNCOMMITTED
129+
storage.berkeleyje.ext.je.lock.timeout=0
130+
storage.lock.wait-time=5000
131+
ids.authority.wait-time=2000
132+
tx.max-commit-time=30000
133+
```

janusgraph-berkeleyje/src/main/java/org/janusgraph/diskstorage/berkeleyje/BerkeleyJEStoreManager.java

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414

1515
package org.janusgraph.diskstorage.berkeleyje;
1616

17-
1817
import com.google.common.base.Preconditions;
1918
import com.sleepycat.je.CacheMode;
2019
import com.sleepycat.je.Database;
@@ -44,6 +43,7 @@
4443
import org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration;
4544
import org.janusgraph.graphdb.configuration.PreInitializeConfigOptions;
4645
import org.janusgraph.graphdb.transaction.TransactionConfiguration;
46+
import org.janusgraph.util.system.ConfigurationUtil;
4747
import org.janusgraph.util.system.IOUtils;
4848
import org.slf4j.Logger;
4949
import org.slf4j.LoggerFactory;
@@ -88,6 +88,27 @@ public class BerkeleyJEStoreManager extends LocalStoreManager implements Ordered
8888
ConfigOption.Type.MASKABLE, String.class,
8989
IsolationLevel.REPEATABLE_READ.toString(), disallowEmpty(String.class));
9090

91+
public static final ConfigNamespace BERKELEY_EXTRAS_NS =
92+
new ConfigNamespace(BERKELEY_NS, "ext", "Overrides for arbitrary settings applied at `EnvironmentConfig` creation.\n" +
93+
"The full list of possible setting is available inside the Java class `com.sleepycat.je.EnvironmentConfig`. " +
94+
"All configurations values should be specified as `String` and be formated the same as specified in the following " +
95+
"[documentation](https://docs.oracle.com/cd/E17277_02/html/java/com/sleepycat/je/EnvironmentConfig.html).\n" +
96+
"Notice, for compatibility reasons, it's allowed to use `-` character instead of `.` for config keys. All dashes will " +
97+
"be replaced by dots when passing those keys to `EnvironmentConfig`.");
98+
99+
// This setting isn't used directly in Java, but this setting will be picked up indirectly during parsing of the
100+
// subset configuration of `BERKELEY_EXTRAS_NS` namespace
101+
public static final ConfigOption<String> EXT_LOCK_TIMEOUT =
102+
new ConfigOption<>(BERKELEY_EXTRAS_NS, toJanusGraphConfigKey(EnvironmentConfig.LOCK_TIMEOUT),
103+
String.format("Lock timeout configuration. `0` disabled lock timeout completely. " +
104+
"To set lock timeout via this configuration it's required to use " +
105+
"String formated time representation. For example: `500 ms`, `5 min`, etc. \nSee information about value " +
106+
"constraints in the official " +
107+
"[sleepycat documentation](https://docs.oracle.com/cd/E17277_02/html/java/com/sleepycat/je/EnvironmentConfig.html#LOCK_TIMEOUT).\n" +
108+
"Notice, this option can be specified as `%s` which will be treated the same as this configuration option.",
109+
BERKELEY_EXTRAS_NS.toStringWithoutRoot() + "." + EnvironmentConfig.LOCK_TIMEOUT
110+
), ConfigOption.Type.MASKABLE, String.class);
111+
91112
private final Map<String, BerkeleyJEKeyValueStore> stores;
92113

93114
protected Environment environment;
@@ -132,12 +153,32 @@ private void initialize(int cachePercent, final boolean sharedCache, final Cache
132153
}
133154

134155
//Open the environment
156+
Map<String, String> extraSettings = getSettingsFromJanusGraphConf(storageConfig);
157+
extraSettings.forEach((key, value) -> envConfig.setConfigParam(toBerkeleyConfigKey(key), value));
158+
159+
// Open the environment
135160
environment = new Environment(directory, envConfig);
136161

137162
} catch (DatabaseException e) {
138163
throw new PermanentBackendException("Error during BerkeleyJE initialization: ", e);
139164
}
165+
}
166+
167+
public static String toBerkeleyConfigKey(String janusGraphConfigKey){
168+
return janusGraphConfigKey.replace("-", ".");
169+
}
140170

171+
public static String toJanusGraphConfigKey(String berkeleyConfigKey){
172+
return berkeleyConfigKey.replace(".", "-");
173+
}
174+
175+
static Map<String, String> getSettingsFromJanusGraphConf(Configuration config) {
176+
final Map<String, String> settings = ConfigurationUtil.getSettingsFromJanusGraphConf(config, BERKELEY_EXTRAS_NS);
177+
if(log.isDebugEnabled()){
178+
settings.forEach((key, val) -> log.debug("[BERKELEY ext.* cfg] Set {}: {}", key, val));
179+
log.debug("Loaded {} settings from the {} JanusGraph config namespace", settings.size(), BERKELEY_EXTRAS_NS);
180+
}
181+
return settings;
141182
}
142183

143184
@Override
@@ -335,4 +376,8 @@ private TransactionBegin(String msg) {
335376
super(msg);
336377
}
337378
}
379+
380+
public Environment getEnvironment(){
381+
return environment;
382+
}
338383
}

janusgraph-berkeleyje/src/test/java/org/janusgraph/graphdb/berkeleyje/BerkeleyGraphTest.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
package org.janusgraph.graphdb.berkeleyje;
1616

1717
import com.google.common.base.Preconditions;
18+
import com.sleepycat.je.Environment;
19+
import com.sleepycat.je.EnvironmentConfig;
1820
import com.sleepycat.je.LockMode;
1921
import org.janusgraph.BerkeleyStorageSetup;
2022
import org.janusgraph.core.JanusGraphException;
@@ -27,6 +29,7 @@
2729
import org.janusgraph.diskstorage.configuration.ConfigOption;
2830
import org.janusgraph.diskstorage.configuration.ModifiableConfiguration;
2931
import org.janusgraph.diskstorage.configuration.WriteConfiguration;
32+
import org.janusgraph.diskstorage.keycolumnvalue.keyvalue.OrderedKeyValueStoreManagerAdapter;
3033
import org.janusgraph.graphdb.JanusGraphTest;
3134
import org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration;
3235
import org.junit.jupiter.api.Disabled;
@@ -36,6 +39,7 @@
3639

3740
import java.time.Duration;
3841
import java.time.temporal.ChronoUnit;
42+
import java.util.concurrent.TimeUnit;
3943

4044
import static org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration.ALLOW_SETTING_VERTEX_ID;
4145
import static org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration.ALLOW_CUSTOM_VERTEX_ID_TYPES;
@@ -49,6 +53,12 @@ public class BerkeleyGraphTest extends JanusGraphTest {
4953
private static final Logger log =
5054
LoggerFactory.getLogger(BerkeleyGraphTest.class);
5155

56+
public EnvironmentConfig getCurrentEnvironmentConfig() {
57+
BerkeleyJEStoreManager storeManager = (BerkeleyJEStoreManager) ((OrderedKeyValueStoreManagerAdapter) graph.getBackend().getStoreManager()).getManager();
58+
Environment environment = storeManager.getEnvironment();
59+
return environment.getConfig();
60+
}
61+
5262
@Override
5363
public WriteConfiguration getConfiguration() {
5464
ModifiableConfiguration modifiableConfiguration = BerkeleyStorageSetup.getBerkeleyJEConfiguration();
@@ -162,4 +172,24 @@ public void testCannotUseCustomStringId() {
162172
() -> clopen(option(ALLOW_SETTING_VERTEX_ID), true, option(ALLOW_CUSTOM_VERTEX_ID_TYPES), true));
163173
assertEquals("allow-custom-vid-types is not supported for OrderedKeyValueStore", ex.getMessage());
164174
}
175+
176+
@Test
177+
public void testExposedConfigurations() throws BackendException {
178+
clopen(option(BerkeleyJEStoreManager.EXT_LOCK_TIMEOUT), "4321 ms");
179+
assertEquals(4321, getCurrentEnvironmentConfig().getLockTimeout(TimeUnit.MILLISECONDS));
180+
close();
181+
WriteConfiguration configuration = getConfiguration();
182+
clearGraph(configuration);
183+
configuration.set(BerkeleyJEStoreManager.BERKELEY_EXTRAS_NS.toStringWithoutRoot()+"."+EnvironmentConfig.LOCK_TIMEOUT, "12345 ms");
184+
open(configuration);
185+
assertEquals(12345, getCurrentEnvironmentConfig().getLockTimeout(TimeUnit.MILLISECONDS));
186+
close();
187+
clearGraph(configuration);
188+
configuration.set(BerkeleyJEStoreManager.BERKELEY_EXTRAS_NS.toStringWithoutRoot()+"."+EnvironmentConfig.ENV_IS_TRANSACTIONAL, "true");
189+
open(configuration);
190+
assertTrue(getCurrentEnvironmentConfig().getTransactional());
191+
close();
192+
clearGraph(configuration);
193+
}
194+
165195
}

janusgraph-core/src/main/java/org/janusgraph/diskstorage/configuration/ConfigOption.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,5 +275,8 @@ public static Predicate<Long> positiveLong() {
275275
return num -> num!=null && num>0;
276276
}
277277

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

279282
}

janusgraph-core/src/main/java/org/janusgraph/diskstorage/keycolumnvalue/keyvalue/OrderedKeyValueStoreManagerAdapter.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,4 +155,8 @@ public List<KeyRange> getLocalKeyPartition() throws BackendException {
155155
public String getName() {
156156
return manager.getName();
157157
}
158+
159+
public OrderedKeyValueStoreManager getManager() {
160+
return manager;
161+
}
158162
}

janusgraph-core/src/main/java/org/janusgraph/util/system/ConfigurationUtil.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
package org.janusgraph.util.system;
1616

17+
import com.google.common.base.Joiner;
1718
import com.google.common.base.Preconditions;
1819
import org.apache.commons.configuration2.BaseConfiguration;
1920
import org.apache.commons.configuration2.Configuration;
@@ -24,11 +25,14 @@
2425
import org.apache.commons.configuration2.builder.fluent.PropertiesBuilderParameters;
2526
import org.apache.commons.configuration2.convert.DefaultListDelimiterHandler;
2627
import org.apache.commons.configuration2.ex.ConfigurationException;
28+
import org.janusgraph.diskstorage.configuration.ConfigNamespace;
2729

2830
import java.io.File;
31+
import java.lang.reflect.Array;
2932
import java.lang.reflect.Constructor;
3033
import java.lang.reflect.InvocationTargetException;
3134
import java.util.ArrayList;
35+
import java.util.HashMap;
3236
import java.util.HashSet;
3337
import java.util.Iterator;
3438
import java.util.List;
@@ -169,4 +173,34 @@ private static PropertiesConfiguration loadPropertiesConfig(PropertiesBuilderPar
169173
}
170174
return builder.configure(newParams).getConfiguration();
171175
}
176+
177+
public static Map<String, String> getSettingsFromJanusGraphConf(org.janusgraph.diskstorage.configuration.Configuration config, ConfigNamespace namespace) {
178+
179+
final Map<String, String> settings = new HashMap<>();
180+
181+
final Map<String,Object> configSub = config.getSubset(namespace);
182+
for (Map.Entry<String,Object> entry : configSub.entrySet()) {
183+
String key = entry.getKey();
184+
Object val = entry.getValue();
185+
if (null == val) continue;
186+
if (List.class.isAssignableFrom(val.getClass())) {
187+
// Pretty print lists using comma-separated values and no surrounding square braces for ES
188+
List l = (List) val;
189+
settings.put(key, Joiner.on(",").join(l));
190+
} else if (val.getClass().isArray()) {
191+
// As with Lists, but now for arrays
192+
// The Object copy[] business lets us avoid repetitive primitive array type checking and casting
193+
Object[] copy = new Object[Array.getLength(val)];
194+
for (int i= 0; i < copy.length; i++) {
195+
copy[i] = Array.get(val, i);
196+
}
197+
settings.put(key, Joiner.on(",").join(copy));
198+
} else {
199+
// Copy anything else unmodified
200+
settings.put(key, val.toString());
201+
}
202+
}
203+
204+
return settings;
205+
}
172206
}

janusgraph-es/src/main/java/org/janusgraph/diskstorage/es/ElasticSearchSetup.java

Lines changed: 6 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,15 @@
1414

1515
package org.janusgraph.diskstorage.es;
1616

17-
import com.google.common.base.Joiner;
1817
import com.google.common.base.Preconditions;
1918
import org.janusgraph.diskstorage.configuration.Configuration;
2019
import org.janusgraph.diskstorage.es.rest.RestClientSetup;
20+
import org.janusgraph.util.system.ConfigurationUtil;
2121
import org.slf4j.Logger;
2222
import org.slf4j.LoggerFactory;
2323

2424
import java.io.IOException;
25-
import java.lang.reflect.Array;
2625
import java.util.HashMap;
27-
import java.util.List;
2826
import java.util.Map;
2927

3028
/**
@@ -53,36 +51,12 @@ public Connection connect(Configuration config) throws IOException {
5351
};
5452

5553
static Map<String, Object> getSettingsFromJanusGraphConf(Configuration config) {
56-
57-
final Map<String, Object> settings = new HashMap<>();
58-
59-
int keysLoaded = 0;
60-
final Map<String,Object> configSub = config.getSubset(ElasticSearchIndex.ES_CREATE_EXTRAS_NS);
61-
for (Map.Entry<String,Object> entry : configSub.entrySet()) {
62-
String key = entry.getKey();
63-
Object val = entry.getValue();
64-
if (null == val) continue;
65-
if (List.class.isAssignableFrom(val.getClass())) {
66-
// Pretty print lists using comma-separated values and no surrounding square braces for ES
67-
List l = (List) val;
68-
settings.put(key, Joiner.on(",").join(l));
69-
} else if (val.getClass().isArray()) {
70-
// As with Lists, but now for arrays
71-
// The Object copy[] business lets us avoid repetitive primitive array type checking and casting
72-
Object[] copy = new Object[Array.getLength(val)];
73-
for (int i= 0; i < copy.length; i++) {
74-
copy[i] = Array.get(val, i);
75-
}
76-
settings.put(key, Joiner.on(",").join(copy));
77-
} else {
78-
// Copy anything else unmodified
79-
settings.put(key, val.toString());
80-
}
81-
log.debug("[ES ext.* cfg] Set {}: {}", key, val);
82-
keysLoaded++;
54+
final Map<String, String> settings = ConfigurationUtil.getSettingsFromJanusGraphConf(config, ElasticSearchIndex.ES_CREATE_EXTRAS_NS);
55+
if(log.isDebugEnabled()){
56+
settings.forEach((key, val) -> log.debug("[ES ext.* cfg] Set {}: {}", key, val));
57+
log.debug("Loaded {} settings from the {} JanusGraph config namespace", settings.size(), ElasticSearchIndex.ES_CREATE_EXTRAS_NS);
8358
}
84-
log.debug("Loaded {} settings from the {} JanusGraph config namespace", keysLoaded, ElasticSearchIndex.ES_CREATE_EXTRAS_NS);
85-
return settings;
59+
return new HashMap<>(settings);
8660
}
8761

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

0 commit comments

Comments
 (0)