Skip to content

Commit 32ada54

Browse files
committed
Updates to install, retry refactoring, and isolation level config.
1 parent 5500b9f commit 32ada54

17 files changed

+437
-307
lines changed

README.md

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,34 @@ JanusGraph, coupled with the FoundationDB storage adapter provides the following
1919

2020
# Getting started
2121

22+
The FoundationDB storage adapter requires a single FoundatoinDB instance or cluster and the FoundationDB client libraries. Downloads for server and client can be found [here](https://apple.github.io/foundationdb/downloads.html).
23+
24+
## Setting up FoundationDB
25+
2226
## Installing from a binary release
2327
Binary releases can be found on [GitHub](http://github.com/experoinc/janusgraph-foundationdb/releases).
2428

29+
This installation procedure will copy the necessary libraries, properties, and Gremlin Server configuration files into your JanusGraph installation.
30+
2531
1. Download the JanusGraph [release](https://github.com/JanusGraph/janusgraph/releases) that is compatible up with the FoundationDB storage adapter.
2632
2. Download the desired FoundationDB storage adapter release.
27-
3. Unzip the storage adapter zip and copy the contents of the `lib` directory into your JanusGraph `ext` directory.
28-
4. Copy the `janusgraph-foundationdb.properties` file into your JanusGraph `conf` directory.
33+
3. Unzip the storage adapter zip file and run `./install.sh $YOUR_JANUSGRAPH_INSTALL_DIRECTORY`
34+
35+
Assuming you have a FoundationDB cluster up and running, you can connect from the Gremlin console by running:
36+
37+
`gremlin> graph = JanusGraphFactory.open('conf/janusgraph-foundationdb.properties')`
38+
39+
To start Gremlin Server run `gremlin-server.sh` directly or `bin/janusgraph.sh start` which will also start a local Elasticsearch instance.
2940

3041
## Installing from source
3142

32-
1. Clone the repository from GitHub.
43+
Follow these steps if you'd like to use the latest version built from source.
44+
1. Clone the repository.
3345
`git clone http://github.com/experoinc/janusgraph-foundationdb`
46+
2. Build the distribution package.
47+
`mvn package -DskipTests`
48+
3. Follow the binary installation steps starting at step 3.
49+
3450
# Configuration Options
3551

3652
|Property|Description|Default|

conf/janusgraph-foundationdb.properties

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,11 @@ cache.db-cache-time = 180000
7070
cache.db-cache-size = 0.5
7171

7272
# Turn on storage adapter query parallelization
73-
query.batch = true
73+
query.batch = true
74+
75+
# The isolation level for the FoundationDB storage adapter. This configuration
76+
# is setup for interactive console sessions so it allows for writes that
77+
# can span more than the lifespan of one single FoundationDB transaction.
78+
# It is recommended that this be set to serializable or read_committed_no_write
79+
# for production scenarios to maintain referential integrity.
80+
storage.fdb.isolation-level=read_committed_with_write

conf/janusgraph.sh

Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
#!/bin/bash
2+
3+
# Returns the absolute path of this script regardless of symlinks
4+
abs_path() {
5+
# From: http://stackoverflow.com/a/246128
6+
# - To resolve finding the directory after symlinks
7+
SOURCE="${BASH_SOURCE[0]}"
8+
while [ -h "$SOURCE" ]; do
9+
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
10+
SOURCE="$(readlink "$SOURCE")"
11+
[[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE"
12+
done
13+
echo "$( cd -P "$( dirname "$SOURCE" )" && pwd )"
14+
}
15+
16+
BIN=`abs_path`
17+
GSRV_CONFIG_TAG=foundationdb-es
18+
: ${ELASTICSEARCH_STARTUP_TIMEOUT_S:=60}
19+
: ${ELASTICSEARCH_SHUTDOWN_TIMEOUT_S:=60}
20+
: ${ELASTICSEARCH_IP:=127.0.0.1}
21+
: ${ELASTICSEARCH_PORT:=9200}
22+
23+
: ${GSRV_STARTUP_TIMEOUT_S:=60}
24+
: ${GSRV_SHUTDOWN_TIMEOUT_S:=60}
25+
: ${GSRV_IP:=127.0.0.1}
26+
: ${GSRV_PORT:=8182}
27+
28+
: ${SLEEP_INTERVAL_S:=2}
29+
VERBOSE=
30+
COMMAND=
31+
32+
# Locate the jps command. Check $PATH, then check $JAVA_HOME/bin.
33+
# This does not need to by cygpath'd.
34+
JPS=
35+
for maybejps in jps "${JAVA_HOME}/bin/jps"; do
36+
type "$maybejps" >/dev/null 2>&1
37+
if [ $? -eq 0 ]; then
38+
JPS="$maybejps"
39+
break
40+
fi
41+
done
42+
43+
if [ -z "$JPS" ]; then
44+
echo "jps command not found. Put the JDK's jps binary on the command path." >&2
45+
exit 1
46+
fi
47+
48+
GREMLIN_FRIENDLY_NAME='Gremlin-Server'
49+
GREMLIN_CLASS_NAME=org.apache.tinkerpop.gremlin.server.GremlinServer
50+
ES_FRIENDLY_NAME=Elasticsearch
51+
ES_CLASS_NAME=org.elasticsearch.bootstrap.Elasticsearch
52+
53+
# wait_for_startup friendly_name host port timeout_s
54+
wait_for_startup() {
55+
local friendly_name="$1"
56+
local host="$2"
57+
local port="$3"
58+
local timeout_s="$4"
59+
60+
local now_s=`date '+%s'`
61+
local stop_s=$(( $now_s + $timeout_s ))
62+
local status=
63+
64+
echo -n "Connecting to $friendly_name ($host:$port)"
65+
while [ $now_s -le $stop_s ]; do
66+
echo -n .
67+
$BIN/checksocket.sh $host $port >/dev/null 2>&1
68+
if [ $? -eq 0 ]; then
69+
echo " OK (connected to $host:$port)."
70+
return 0
71+
fi
72+
sleep $SLEEP_INTERVAL_S
73+
now_s=`date '+%s'`
74+
done
75+
76+
echo " timeout exceeded ($timeout_s seconds): could not connect to $host:$port" >&2
77+
return 1
78+
}
79+
80+
# wait_for_shutdown friendly_name class_name timeout_s
81+
wait_for_shutdown() {
82+
local friendly_name="$1"
83+
local class_name="$2"
84+
local timeout_s="$3"
85+
86+
local now_s=`date '+%s'`
87+
local stop_s=$(( $now_s + $timeout_s ))
88+
89+
while [ $now_s -le $stop_s ]; do
90+
status_class "$friendly_name" $class_name >/dev/null
91+
if [ $? -eq 1 ]; then
92+
# Class not found in the jps output. Assume that it stopped.
93+
return 0
94+
fi
95+
sleep $SLEEP_INTERVAL_S
96+
now_s=`date '+%s'`
97+
done
98+
99+
echo "$friendly_name shutdown timeout exceeded ($timeout_s seconds)" >&2
100+
return 1
101+
}
102+
103+
start() {
104+
status_class $ES_FRIENDLY_NAME $ES_CLASS_NAME >/dev/null && status && echo "Stop services before starting" && exit 1
105+
echo "Forking Elasticsearch..."
106+
if [ -n "$VERBOSE" ]; then
107+
"$BIN"/../elasticsearch/bin/elasticsearch -d
108+
else
109+
"$BIN"/../elasticsearch/bin/elasticsearch -d >/dev/null 2>&1
110+
fi
111+
wait_for_startup Elasticsearch $ELASTICSEARCH_IP $ELASTICSEARCH_PORT $ELASTICSEARCH_STARTUP_TIMEOUT_S || {
112+
echo "See $BIN/../log/elasticsearch.log for Elasticsearch log output." >&2
113+
return 1
114+
}
115+
116+
status_class $GREMLIN_FRIENDLY_NAME $GREMLIN_CLASS_NAME >/dev/null && status && echo "Stop services before starting" && exit 1
117+
echo "Forking Gremlin-Server..."
118+
if [ -n "$VERBOSE" ]; then
119+
"$BIN"/gremlin-server.sh conf/gremlin-server/gremlin-server.yaml &
120+
else
121+
"$BIN"/gremlin-server.sh conf/gremlin-server/gremlin-server.yaml >/dev/null 2>&1 &
122+
fi
123+
wait_for_startup 'Gremlin-Server' $GSRV_IP $GSRV_PORT $GSRV_STARTUP_TIMEOUT_S || {
124+
echo "See $BIN/../log/gremlin-server.log for Gremlin-Server log output." >&2
125+
return 1
126+
}
127+
disown
128+
129+
echo "Run gremlin.sh to connect." >&2
130+
}
131+
132+
stop() {
133+
kill_class $GREMLIN_FRIENDLY_NAME $GREMLIN_CLASS_NAME
134+
wait_for_shutdown $GREMLIN_FRIENDLY_NAME $GREMLIN_CLASS_NAME $GSRV_SHUTDOWN_TIMEOUT_S
135+
kill_class $ES_FRIENDLY_NAME $ES_CLASS_NAME
136+
wait_for_shutdown $ES_FRIENDLY_NAME $ES_CLASS_NAME $ELASTICSEARCH_SHUTDOWN_TIMEOUT_S
137+
}
138+
139+
kill_class() {
140+
local p=`$JPS -l | grep "$2" | awk '{print $1}'`
141+
if [ -z "$p" ]; then
142+
echo "$1 ($2) not found in the java process table"
143+
return
144+
fi
145+
echo "Killing $1 (pid $p)..." >&2
146+
case "`uname`" in
147+
CYGWIN*) taskkill /F /PID "$p" ;;
148+
*) kill "$p" ;;
149+
esac
150+
}
151+
152+
status_class() {
153+
local p=`$JPS -l | grep "$2" | awk '{print $1}'`
154+
if [ -n "$p" ]; then
155+
echo "$1 ($2) is running with pid $p"
156+
return 0
157+
else
158+
echo "$1 ($2) does not appear in the java process table"
159+
return 1
160+
fi
161+
}
162+
163+
status() {
164+
status_class $GREMLIN_FRIENDLY_NAME $GREMLIN_CLASS_NAME
165+
status_class $ES_FRIENDLY_NAME $ES_CLASS_NAME
166+
}
167+
168+
clean() {
169+
echo -n "Are you sure you want to delete all stored data and logs? [y/N] " >&2
170+
read response
171+
if [ "$response" != "y" -a "$response" != "Y" ]; then
172+
echo "Response \"$response\" did not equal \"y\" or \"Y\". Canceling clean operation." >&2
173+
return 0
174+
fi
175+
176+
if cd "$BIN"/../db 2>/dev/null; then
177+
rm -rf es
178+
echo "Deleted data in `pwd`" >&2
179+
cd - >/dev/null
180+
else
181+
echo 'Data directory does not exist.' >&2
182+
fi
183+
184+
if cd "$BIN"/../log; then
185+
rm -f elasticsearch*.log
186+
rm -f gremlin-server.log
187+
echo "Deleted logs in `pwd`" >&2
188+
cd - >/dev/null
189+
fi
190+
}
191+
192+
usage() {
193+
echo "Usage: $0 [options] {start|stop|status|clean}" >&2
194+
echo " start: fork ES, and Gremlin-Server processes" >&2
195+
echo " stop: kill ES, and Gremlin-Server processes" >&2
196+
echo " status: print ES, and Gremlin-Server process status" >&2
197+
echo " clean: permanently delete all graph data (run when stopped)" >&2
198+
echo "Options:" >&2
199+
echo " -v enable logging to console in addition to logfiles" >&2
200+
}
201+
202+
find_verb() {
203+
if [ "$1" = 'start' -o \
204+
"$1" = 'stop' -o \
205+
"$1" = 'clean' -o \
206+
"$1" = 'status' ]; then
207+
COMMAND="$1"
208+
return 0
209+
fi
210+
return 1
211+
}
212+
213+
while [ 1 ]; do
214+
if find_verb ${!OPTIND}; then
215+
OPTIND=$(($OPTIND + 1))
216+
elif getopts 'c:v' option; then
217+
case $option in
218+
c) GSRV_CONFIG_TAG="${OPTARG}";;
219+
v) VERBOSE=yes;;
220+
*) usage; exit 1;;
221+
esac
222+
else
223+
break
224+
fi
225+
done
226+
227+
if [ -n "$COMMAND" ]; then
228+
$COMMAND
229+
else
230+
usage
231+
exit 1
232+
fi

install.sh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ if [ -z "$*" ]; then
2121
fi
2222

2323
JANUS_INSTALL_PATH=$1
24+
JANUS_BIN_PATH=${JANUS_INSTALL_PATH}/bin
2425
JANUS_CONF_PATH=${JANUS_INSTALL_PATH}/conf
2526
JANUS_GREMLIN_SERVER_CONF=${JANUS_CONF_PATH}/gremlin-server
2627

@@ -36,3 +37,6 @@ cp conf/janusgraph-foundationdb-es-server.properties ${JANUS_GREMLIN_SERVER_CONF
3637
# backup Gremlin server config
3738
mv ${JANUS_GREMLIN_SERVER_CONF}/gremlin-server.yaml ${JANUS_GREMLIN_SERVER_CONF}/gremlin-server.yaml.orig
3839
cp conf/gremlin-server.yaml ${JANUS_GREMLIN_SERVER_CONF}/gremlin-server.yaml
40+
# backup janusgraph.sh
41+
mv ${JANUS_BIN_PATH}/janusgraph.sh ${JANUS_BIN_PATH}/janusgraph.sh.orig
42+
cp conf/janusgraph.sh ${JANUS_BIN_PATH}/janusgraph.sh

src/main/java/com/experoinc/janusgraph/diskstorage/foundationdb/FoundationDBConfigOptions.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,12 @@ public interface FoundationDBConfigOptions {
5454
ConfigOption.Type.LOCAL,
5555
"default");
5656

57-
ConfigOption<Boolean> SERIALIZABLE = new ConfigOption<>(
57+
ConfigOption<String> ISOLATION_LEVEL = new ConfigOption<>(
5858
FDB_NS,
59-
"serializable",
60-
"If true",
59+
"isolation-level",
60+
"Options are serializable, read_committed_no_write, read_committed_with_write",
6161
ConfigOption.Type.LOCAL,
62-
true);
62+
"serializable");
6363

6464

6565
}

src/main/java/com/experoinc/janusgraph/diskstorage/foundationdb/FoundationDBKeyValueStore.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@
1515
package com.experoinc.janusgraph.diskstorage.foundationdb;
1616

1717
import com.apple.foundationdb.KeyValue;
18-
import com.apple.foundationdb.Range;
19-
import com.apple.foundationdb.Transaction;
2018
import com.apple.foundationdb.directory.DirectorySubspace;
2119
import com.google.common.base.Preconditions;
2220
import org.janusgraph.diskstorage.BackendException;
@@ -33,7 +31,6 @@
3331
import org.slf4j.LoggerFactory;
3432

3533
import java.util.ArrayList;
36-
import java.util.HashMap;
3734
import java.util.Iterator;
3835
import java.util.LinkedList;
3936
import java.util.List;

src/main/java/com/experoinc/janusgraph/diskstorage/foundationdb/FoundationDBStoreManager.java

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343

4444
import static com.experoinc.janusgraph.diskstorage.foundationdb.FoundationDBConfigOptions.CLUSTER_FILE_PATH;
4545
import static com.experoinc.janusgraph.diskstorage.foundationdb.FoundationDBConfigOptions.DIRECTORY;
46-
import static com.experoinc.janusgraph.diskstorage.foundationdb.FoundationDBConfigOptions.SERIALIZABLE;
46+
import static com.experoinc.janusgraph.diskstorage.foundationdb.FoundationDBConfigOptions.ISOLATION_LEVEL;
4747
import static com.experoinc.janusgraph.diskstorage.foundationdb.FoundationDBConfigOptions.VERSION;
4848
import static org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration.GRAPH_NAME;
4949

@@ -63,7 +63,7 @@ public class FoundationDBStoreManager extends AbstractStoreManager implements Or
6363
protected final StoreFeatures features;
6464
protected DirectorySubspace rootDirectory;
6565
protected final String rootDirectoryName;
66-
protected final boolean serializable;
66+
protected final FoundationDBTx.IsolationLevel isolationLevel;
6767

6868
public FoundationDBStoreManager(Configuration configuration) throws BackendException {
6969
super(configuration);
@@ -73,7 +73,20 @@ public FoundationDBStoreManager(Configuration configuration) throws BackendExcep
7373
rootDirectoryName = determineRootDirectoryName(configuration);
7474
db = !"default".equals(configuration.get(CLUSTER_FILE_PATH)) ?
7575
fdb.open(configuration.get(CLUSTER_FILE_PATH)) : fdb.open();
76-
serializable = configuration.get(SERIALIZABLE);
76+
final String isolationLevelStr = configuration.get(ISOLATION_LEVEL);
77+
switch (isolationLevelStr.toLowerCase().trim()) {
78+
case "serializable":
79+
isolationLevel = FoundationDBTx.IsolationLevel.SERIALIZABLE;
80+
break;
81+
case "read_committed_no_write":
82+
isolationLevel = FoundationDBTx.IsolationLevel.READ_COMMITTED_NO_WRITE;
83+
break;
84+
case "read_committed_with_write":
85+
isolationLevel = FoundationDBTx.IsolationLevel.READ_COMMITTED_WITH_WRITE;
86+
break;
87+
default:
88+
throw new PermanentBackendException("Unrecognized isolation level " + isolationLevelStr);
89+
}
7790
initialize(rootDirectoryName);
7891

7992
features = new StandardStoreFeatures.Builder()
@@ -112,8 +125,7 @@ public StoreTransaction beginTransaction(final BaseTransactionConfig txCfg) thro
112125
try {
113126
final Transaction tx = db.createTransaction();
114127

115-
final StoreTransaction fdbTx = serializable ?
116-
new FoundationDBTx(db, tx, txCfg) : new ReadCommittedFoundationDBTx(db, tx, txCfg);
128+
final StoreTransaction fdbTx = new FoundationDBTx(db, tx, txCfg, isolationLevel);
117129

118130
if (log.isTraceEnabled()) {
119131
log.trace("FoundationDB tx created", new TransactionBegin(fdbTx.toString()));

0 commit comments

Comments
 (0)