Merge branch 'release-5.1' of github.com:apple/foundationdb into release-5.1

This commit is contained in:
Evan Tschannen 2018-04-10 16:01:03 -07:00
commit 8f300fdec9
98 changed files with 1100 additions and 10532 deletions

7
.gitignore vendored
View File

@ -6,8 +6,6 @@ bindings/flow/bin/
bindings/java/foundationdb-client*.jar bindings/java/foundationdb-client*.jar
bindings/java/foundationdb-tests*.jar bindings/java/foundationdb-tests*.jar
bindings/java/fdb-java-*-sources.jar bindings/java/fdb-java-*-sources.jar
bindings/nodejs/build/
bindings/nodejs/modules/
packaging/msi/FDBInstaller.msi packaging/msi/FDBInstaller.msi
# Generated source, build, and packaging files # Generated source, build, and packaging files
@ -24,7 +22,6 @@ bindings/java/src*/main/com/apple/foundationdb/StreamingMode.java
bindings/java/src*/main/com/apple/foundationdb/MutationType.java bindings/java/src*/main/com/apple/foundationdb/MutationType.java
bindings/java/src*/main/com/apple/foundationdb/ConflictRangeType.java bindings/java/src*/main/com/apple/foundationdb/ConflictRangeType.java
bindings/java/src*/main/com/apple/foundationdb/FDBException.java bindings/java/src*/main/com/apple/foundationdb/FDBException.java
bindings/nodejs/package.json
bindings/python/fdb/fdb*options.py bindings/python/fdb/fdb*options.py
bindings/python/dist/ bindings/python/dist/
bindings/python/setup.py bindings/python/setup.py
@ -52,12 +49,8 @@ bindings/go/godoc
bindings/java/.classstamp* bindings/java/.classstamp*
bindings/java/classes*/ bindings/java/classes*/
bindings/java/javadoc*/ bindings/java/javadoc*/
bindings/nodejs/fdb_node.stamp
bindings/nodejs/node_modules/
# Testing and logging # Testing and logging
bindings/nodejs/fdb_node*.log
bindings/nodejs/npm-debug.log
packaging/msi/*.log packaging/msi/*.log
packaging/msi/obj packaging/msi/obj
simfdb simfdb

View File

@ -93,7 +93,7 @@ VPATH += $(addprefix :,$(filter-out lib,$(patsubst -L%,%,$(filter -L%,$(LDFLAGS)
CS_PROJECTS := flow/actorcompiler flow/coveragetool fdbclient/vexillographer CS_PROJECTS := flow/actorcompiler flow/coveragetool fdbclient/vexillographer
CPP_PROJECTS := flow fdbrpc fdbclient fdbbackup fdbserver fdbcli bindings/c bindings/java fdbmonitor bindings/flow/tester bindings/flow CPP_PROJECTS := flow fdbrpc fdbclient fdbbackup fdbserver fdbcli bindings/c bindings/java fdbmonitor bindings/flow/tester bindings/flow
OTHER_PROJECTS := bindings/python bindings/ruby bindings/nodejs bindings/go OTHER_PROJECTS := bindings/python bindings/ruby bindings/go
CS_MK_GENERATED := $(CS_PROJECTS:=/generated.mk) CS_MK_GENERATED := $(CS_PROJECTS:=/generated.mk)
CPP_MK_GENERATED := $(CPP_PROJECTS:=/generated.mk) CPP_MK_GENERATED := $(CPP_PROJECTS:=/generated.mk)
@ -127,7 +127,7 @@ else
endif endif
@echo "#define FDB_VT_PACKAGE_NAME \"$(PACKAGE_NAME)\"" >> $@ @echo "#define FDB_VT_PACKAGE_NAME \"$(PACKAGE_NAME)\"" >> $@
bindings: fdb_c fdb_python fdb_ruby fdb_java fdb_node fdb_flow fdb_flow_tester fdb_go fdb_go_tester bindings: fdb_c fdb_python fdb_ruby fdb_java fdb_flow fdb_flow_tester fdb_go fdb_go_tester
Makefiles: $(MK_GENERATED) Makefiles: $(MK_GENERATED)

View File

@ -1,3 +1,21 @@
# FoundationDB # FoundationDB
FoundationDB is a distributed database designed to handle large volumes of structured data across clusters of commodity servers. It organizes data as an ordered key-value store and employs ACID transactions for all operations. It is especially well-suited for read/write workloads but also has excellent performance for write-intensive workloads. Users interact with the database using API language binding. FoundationDB is a distributed database designed to handle large volumes of structured data across clusters of commodity servers. It organizes data as an ordered key-value store and employs ACID transactions for all operations. It is especially well-suited for read/write workloads but also has excellent performance for write-intensive workloads. Users interact with the database using API language binding.
# Building Locally
## macOS
1. Check out this repo on your Mac.
1. Install the Xcode command-line tools.
1. Download version 1.52 of [Boost](https://sourceforge.net/projects/boost/files/boost/1.52.0/).
1. Set the BOOSTDIR environment variable to the location containing this boost installation.
1. Install [Mono](http://www.mono-project.com/download/stable/).
1. Install a [JDK](http://www.oracle.com/technetwork/java/javase/downloads/index.html). FoundationDB currently builds with Java 8.
1. Navigate to the directory where you checked out the foundationdb repo.
1. Run `make`.
This will build the fdbserver binary and the python bindings. If you
want to build our other bindings, you will need to install a runtime for the
language whose binding you want to build. Each binding has an `.mk` file
which provides specific targets for that binding.

View File

@ -58,8 +58,6 @@ _java_cmd = 'java -ea -cp %s:%s com.apple.foundationdb.test.' % (
testers = { testers = {
'python': Tester('python', 'python ' + _absolute_path('python/tests/tester.py'), 2040, 23, MAX_API_VERSION, types=ALL_TYPES), 'python': Tester('python', 'python ' + _absolute_path('python/tests/tester.py'), 2040, 23, MAX_API_VERSION, types=ALL_TYPES),
'python3': Tester('python3', 'python3 ' + _absolute_path('python/tests/tester.py'), 2040, 23, MAX_API_VERSION, types=ALL_TYPES), 'python3': Tester('python3', 'python3 ' + _absolute_path('python/tests/tester.py'), 2040, 23, MAX_API_VERSION, types=ALL_TYPES),
'node': Tester('node', _absolute_path('nodejs/tests/tester.js'), 53, 500, MAX_API_VERSION),
'streamline': Tester('streamline', _absolute_path('nodejs/tests/streamline_tester._js'), 53, 500, MAX_API_VERSION),
'ruby': Tester('ruby', _absolute_path('ruby/tests/tester.rb'), 64, 23, MAX_API_VERSION), 'ruby': Tester('ruby', _absolute_path('ruby/tests/tester.rb'), 64, 23, MAX_API_VERSION),
'java': Tester('java', _java_cmd + 'StackTester', 2040, 510, MAX_API_VERSION, types=ALL_TYPES), 'java': Tester('java', _java_cmd + 'StackTester', 2040, 510, MAX_API_VERSION, types=ALL_TYPES),
'java_async': Tester('java', _java_cmd + 'AsyncStackTester', 2040, 510, MAX_API_VERSION, types=ALL_TYPES), 'java_async': Tester('java', _java_cmd + 'AsyncStackTester', 2040, 510, MAX_API_VERSION, types=ALL_TYPES),

View File

@ -25,7 +25,7 @@ BREAKONERROR="${BREAKONERROR:-0}"
RUNSCRIPTS="${RUNSCRIPTS:-1}" RUNSCRIPTS="${RUNSCRIPTS:-1}"
RUNTESTS="${RUNTESTS:-1}" RUNTESTS="${RUNTESTS:-1}"
RANDOMTEST="${RANDOMTEST:-0}" RANDOMTEST="${RANDOMTEST:-0}"
BINDINGTESTS="${BINDINGTESTS:-python python3 java java_async ruby node go flow}" BINDINGTESTS="${BINDINGTESTS:-python python3 java java_async ruby go flow}"
LOGLEVEL="${LOGLEVEL:-INFO}" LOGLEVEL="${LOGLEVEL:-INFO}"
_BINDINGTESTS=(${BINDINGTESTS}) _BINDINGTESTS=(${BINDINGTESTS})
DISABLEDTESTS=() DISABLEDTESTS=()

View File

@ -24,7 +24,6 @@ function run_scripted() {
scripted ruby scripted ruby
scripted java scripted java
scripted java_async scripted java_async
scripted node
scripted go scripted go
scripted flow scripted flow
} }
@ -40,8 +39,6 @@ while `true`; do
run ruby run ruby
run java run java
run java_async run java_async
run node
#run streamline
run go run go
run flow run flow
done done

View File

@ -102,5 +102,3 @@ fdb_c_tests: packages/fdb-c-tests-$(VERSION)-$(PLATFORM).tar.gz
fdb_c_tests_clean: fdb_c_tests_clean:
@rm -f packages/fdb-c-tests-$(VERSION)-$(PLATFORM).tar.gz @rm -f packages/fdb-c-tests-$(VERSION)-$(PLATFORM).tar.gz
packages: fdb_c_tests

View File

@ -30,11 +30,6 @@
<property name="option" value="eol"/> <property name="option" value="eol"/>
<property name="ignoreEnums" value="false"/> <property name="ignoreEnums" value="false"/>
</module> </module>
<!-- We have about 76 errors with value = "alone" and 27 with value = "same". We should pick one.
<module name="RightCurly">
<property name="option" value="same"/>
</module>
-->
<!-- Design --> <!-- Design -->
<!-- We might get some helpful errors if we turned this on, but not right now. <!-- We might get some helpful errors if we turned this on, but not right now.

View File

@ -42,12 +42,12 @@ class JNIUtil {
OSX("osx", "x86_64", true); OSX("osx", "x86_64", true);
private final String name; private final String name;
private final String arch; private final String arch;
private final boolean canDeleteEager; private final boolean canDeleteEager;
OS(String name, String arch, boolean canDeleteEager) { OS(String name, String arch, boolean canDeleteEager) {
this.name = name; this.name = name;
this.arch = arch; this.arch = arch;
this.canDeleteEager = canDeleteEager; this.canDeleteEager = canDeleteEager;
} }
@ -55,9 +55,9 @@ class JNIUtil {
return this.name; return this.name;
} }
public String getArch() { public String getArch() {
return this.arch; return this.arch;
} }
} }
/** /**
@ -89,18 +89,18 @@ class JNIUtil {
OS os = getRunningOS(); OS os = getRunningOS();
String path = getPath(os, libName); String path = getPath(os, libName);
if ((os.getName().equals("linux") && !path.endsWith(".so")) || (os.getName().equals("windows") && !path.endsWith(".dll")) || (os.getName().equals("osx") && !path.endsWith(".jnilib") && !path.endsWith(".dylib"))) { if ((os.getName().equals("linux") && !path.endsWith(".so")) || (os.getName().equals("windows") && !path.endsWith(".dll")) || (os.getName().equals("osx") && !path.endsWith(".jnilib") && !path.endsWith(".dylib"))) {
throw new IllegalStateException("OS sanity check failed. System property os.name reports " + os.getName()+" but System.mapLibraryName is looking for " + getLibName(libName)); throw new IllegalStateException("OS sanity check failed. System property os.name reports " + os.getName()+" but System.mapLibraryName is looking for " + getLibName(libName));
} }
File exported; File exported;
try { try {
exported = exportResource(path); exported = exportResource(path);
} }
catch (IOException e) { catch (IOException e) {
throw new UnsatisfiedLinkError(e.getMessage()); throw new UnsatisfiedLinkError(e.getMessage());
} }
String filename = exported.getAbsolutePath(); String filename = exported.getAbsolutePath();
System.load(filename); System.load(filename);

View File

@ -21,17 +21,17 @@
package com.apple.foundationdb; package com.apple.foundationdb;
class RangeResultInfo { class RangeResultInfo {
RangeResultSummary getSummary() { RangeResultSummary getSummary() {
return f.getSummary(); return f.getSummary();
} }
RangeResult get() { RangeResult get() {
return f.getResults(); return f.getResults();
} }
RangeResultInfo(FutureResults f) { RangeResultInfo(FutureResults f) {
this.f = f; this.f = f;
} }
private FutureResults f; private FutureResults f;
} }

View File

@ -526,7 +526,7 @@ public class DirectoryLayer implements Directory {
return AsyncUtil.collect( return AsyncUtil.collect(
AsyncUtil.mapIterable(tr.getRange(subdir.range()), AsyncUtil.mapIterable(tr.getRange(subdir.range()),
kv -> subdir.unpack(kv.getKey()).getString(0) kv -> subdir.unpack(kv.getKey()).getString(0)
), ),
tr.getExecutor() tr.getExecutor()
); );

View File

@ -46,231 +46,231 @@ import com.apple.foundationdb.tuple.Versionstamp;
* </p> * </p>
*/ */
public class Subspace { public class Subspace {
static final Tuple EMPTY_TUPLE = Tuple.from(); static final Tuple EMPTY_TUPLE = Tuple.from();
static final byte[] EMPTY_BYTES = new byte[0]; static final byte[] EMPTY_BYTES = new byte[0];
private final byte[] rawPrefix; private final byte[] rawPrefix;
/** /**
* Constructor for a subspace formed with an empty prefix {@link Tuple}. * Constructor for a subspace formed with an empty prefix {@link Tuple}.
*/ */
public Subspace() { public Subspace() {
this(EMPTY_TUPLE, EMPTY_BYTES); this(EMPTY_TUPLE, EMPTY_BYTES);
} }
/** /**
* Constructor for a subspace formed with the specified prefix {@link Tuple}. * Constructor for a subspace formed with the specified prefix {@link Tuple}.
* Note that the {@link Tuple} {@code prefix} should not contain any incomplete * Note that the {@link Tuple} {@code prefix} should not contain any incomplete
* {@link Versionstamp}s as any of its entries. * {@link Versionstamp}s as any of its entries.
* *
* @param prefix a {@link Tuple} used to form the subspace * @param prefix a {@link Tuple} used to form the subspace
* @throws IllegalArgumentException if {@code prefix} contains any incomplete {@link Versionstamp}s * @throws IllegalArgumentException if {@code prefix} contains any incomplete {@link Versionstamp}s
*/ */
public Subspace(Tuple prefix) { public Subspace(Tuple prefix) {
this(prefix, EMPTY_BYTES); this(prefix, EMPTY_BYTES);
} }
/** /**
* Constructor for a subspace formed with the specified byte string, which will * Constructor for a subspace formed with the specified byte string, which will
* be prepended to all packed keys. * be prepended to all packed keys.
* *
* @param rawPrefix a byte array used as the prefix for all packed keys * @param rawPrefix a byte array used as the prefix for all packed keys
*/ */
public Subspace(byte[] rawPrefix) { public Subspace(byte[] rawPrefix) {
this(EMPTY_TUPLE, rawPrefix); this(EMPTY_TUPLE, rawPrefix);
} }
/** /**
* Constructor for a subspace formed with both a prefix {@link Tuple} and a * Constructor for a subspace formed with both a prefix {@link Tuple} and a
* prefix byte string. The prefix {@code Tuple} will be prepended to all * prefix byte string. The prefix {@code Tuple} will be prepended to all
* {@code Tuples} packed by the {@code Subspace}, and the byte string prefix * {@code Tuples} packed by the {@code Subspace}, and the byte string prefix
* will be prepended to the packed result. Note that the {@link Tuple} {@code prefix} * will be prepended to the packed result. Note that the {@link Tuple} {@code prefix}
* should not contain any incomplete {@link Versionstamp}s as any of its entries. * should not contain any incomplete {@link Versionstamp}s as any of its entries.
* *
* @param prefix a {@code Tuple} used to form the subspace * @param prefix a {@code Tuple} used to form the subspace
* @param rawPrefix a byte array used as the prefix for all packed keys * @param rawPrefix a byte array used as the prefix for all packed keys
* @throws IllegalArgumentException if {@code prefix} contains any incomplete {@link Versionstamp}s * @throws IllegalArgumentException if {@code prefix} contains any incomplete {@link Versionstamp}s
*/ */
public Subspace(Tuple prefix, byte[] rawPrefix) { public Subspace(Tuple prefix, byte[] rawPrefix) {
this.rawPrefix = join(rawPrefix, prefix.pack()); this.rawPrefix = join(rawPrefix, prefix.pack());
} }
/** /**
* Returns true if this {@code Subspace} is equal to {@code rhs}. * Returns true if this {@code Subspace} is equal to {@code rhs}.
* Two {@code Subspace}s are equal if they have the same prefix. * Two {@code Subspace}s are equal if they have the same prefix.
* *
* @param rhs the object to check for equality * @param rhs the object to check for equality
* @return {@code true} if this {@code Subspace} and {@code rhs} have equal prefixes * @return {@code true} if this {@code Subspace} and {@code rhs} have equal prefixes
*/ */
@Override @Override
public boolean equals(Object rhs) { public boolean equals(Object rhs) {
if(this == rhs) { if(this == rhs) {
return true; return true;
} }
if(rhs == null || getClass() != rhs.getClass()) { if(rhs == null || getClass() != rhs.getClass()) {
return false; return false;
} }
Subspace other = (Subspace)rhs; Subspace other = (Subspace)rhs;
return Arrays.equals(rawPrefix, other.rawPrefix); return Arrays.equals(rawPrefix, other.rawPrefix);
} }
/** /**
* Create a human-readable string representation of this subspace. This is * Create a human-readable string representation of this subspace. This is
* really only useful for debugging purposes, but it includes information * really only useful for debugging purposes, but it includes information
* on what raw prefix the subspace is using. * on what raw prefix the subspace is using.
* @return a printable representation of the subspace * @return a printable representation of the subspace
*/ */
@Override @Override
public String toString() { public String toString() {
return "Subspace(rawPrefix=" + printable(rawPrefix) + ")"; return "Subspace(rawPrefix=" + printable(rawPrefix) + ")";
} }
/** /**
* Returns a hash-table compatible hash of this subspace. This is based off * Returns a hash-table compatible hash of this subspace. This is based off
* of the hash of the underlying byte-array prefix. * of the hash of the underlying byte-array prefix.
* @return a hash of this subspace * @return a hash of this subspace
*/ */
@Override @Override
public int hashCode() { public int hashCode() {
return Arrays.hashCode(rawPrefix); return Arrays.hashCode(rawPrefix);
} }
/** /**
* Gets a new subspace which is equivalent to this subspace with its prefix {@link Tuple} extended by * Gets a new subspace which is equivalent to this subspace with its prefix {@link Tuple} extended by
* the specified {@code Object}. The object will be inserted into a {@link Tuple} and passed to {@link #get(Tuple)}. * the specified {@code Object}. The object will be inserted into a {@link Tuple} and passed to {@link #get(Tuple)}.
* *
* @param obj an {@code Object} compatible with {@code Tuple}s * @param obj an {@code Object} compatible with {@code Tuple}s
* @return a new subspace formed by joining this {@code Subspace}'s prefix to {@code obj} * @return a new subspace formed by joining this {@code Subspace}'s prefix to {@code obj}
*/ */
public Subspace get(Object obj) { public Subspace get(Object obj) {
return get(Tuple.from(obj)); return get(Tuple.from(obj));
} }
/** /**
* Gets a new subspace which is equivalent to this subspace with its prefix {@link Tuple} extended by * Gets a new subspace which is equivalent to this subspace with its prefix {@link Tuple} extended by
* the specified {@link Tuple}. * the specified {@link Tuple}.
* *
* @param tuple the {@link Tuple} used to form the new {@code Subspace} * @param tuple the {@link Tuple} used to form the new {@code Subspace}
* @return a new subspace formed by joining this {@code Subspace}'s prefix to {@code tuple} * @return a new subspace formed by joining this {@code Subspace}'s prefix to {@code tuple}
*/ */
public Subspace get(Tuple tuple) { public Subspace get(Tuple tuple) {
return subspace(tuple); return subspace(tuple);
} }
/** /**
* Gets the key encoding the prefix used for this {@code Subspace}. This is equivalent to * Gets the key encoding the prefix used for this {@code Subspace}. This is equivalent to
* {@link #pack}ing the empty {@link Tuple}. * {@link #pack}ing the empty {@link Tuple}.
* *
* @return the key encoding the prefix used for this {@code Subspace} * @return the key encoding the prefix used for this {@code Subspace}
*/ */
public byte[] getKey() { public byte[] getKey() {
return pack(); return pack();
} }
/** /**
* Gets the key encoding the prefix used for this {@code Subspace}. * Gets the key encoding the prefix used for this {@code Subspace}.
* *
* @return the key encoding the prefix used for this {@code Subspace} * @return the key encoding the prefix used for this {@code Subspace}
*/ */
public byte[] pack() { public byte[] pack() {
return Arrays.copyOf(rawPrefix, rawPrefix.length); return Arrays.copyOf(rawPrefix, rawPrefix.length);
} }
/** /**
* Gets the key encoding the specified {@code Object} in this {@code Subspace}. {@code obj} is * Gets the key encoding the specified {@code Object} in this {@code Subspace}. {@code obj} is
* inserted into a {@link Tuple} and packed with {@link #pack(Tuple)}. * inserted into a {@link Tuple} and packed with {@link #pack(Tuple)}.
* *
* @param obj an {@code Object} to be packed that is compatible with {@link Tuple}s * @param obj an {@code Object} to be packed that is compatible with {@link Tuple}s
* @return the key encoding the tuple derived from {@code obj} * @return the key encoding the tuple derived from {@code obj}
*/ */
public byte[] pack(Object obj) { public byte[] pack(Object obj) {
return pack(Tuple.from(obj)); return pack(Tuple.from(obj));
} }
/** /**
* Gets the key encoding the specified tuple in this {@code Subspace}. For example, if you have a {@code Subspace} * Gets the key encoding the specified tuple in this {@code Subspace}. For example, if you have a {@code Subspace}
* with prefix {@link Tuple} {@code ("users")} and you use it to pack the {@link Tuple} {@code ("Smith")}, * with prefix {@link Tuple} {@code ("users")} and you use it to pack the {@link Tuple} {@code ("Smith")},
* the result is the same as if you packed the {@link Tuple} {@code ("users", "Smith")}. * the result is the same as if you packed the {@link Tuple} {@code ("users", "Smith")}.
* *
* @param tuple the {@code Tuple} to be packed * @param tuple the {@code Tuple} to be packed
* @return the key encoding the specified tuple in this {@code Subspace} * @return the key encoding the specified tuple in this {@code Subspace}
*/ */
public byte[] pack(Tuple tuple) { public byte[] pack(Tuple tuple) {
return tuple.pack(rawPrefix); return tuple.pack(rawPrefix);
} }
/** /**
* Gets the key encoding the specified tuple in this {@code Subspace} for use with * Gets the key encoding the specified tuple in this {@code Subspace} for use with
* {@link com.apple.foundationdb.MutationType#SET_VERSIONSTAMPED_KEY MutationType.SET_VERSIONSTAMPED_KEY}. * {@link com.apple.foundationdb.MutationType#SET_VERSIONSTAMPED_KEY MutationType.SET_VERSIONSTAMPED_KEY}.
* There must be exactly one incomplete {@link Versionstamp} included in the given {@link Tuple}. It will * There must be exactly one incomplete {@link Versionstamp} included in the given {@link Tuple}. It will
* create a key that is within this {@code Subspace} that can be provided as the {@code key} argument to * create a key that is within this {@code Subspace} that can be provided as the {@code key} argument to
* {@link com.apple.foundationdb.Transaction#mutate(com.apple.foundationdb.MutationType, byte[], byte[]) Transaction.mutate()} * {@link com.apple.foundationdb.Transaction#mutate(com.apple.foundationdb.MutationType, byte[], byte[]) Transaction.mutate()}
* with the {@link com.apple.foundationdb.MutationType#SET_VERSIONSTAMPED_KEY SET_VERSIONSTAMPED_KEY} * with the {@link com.apple.foundationdb.MutationType#SET_VERSIONSTAMPED_KEY SET_VERSIONSTAMPED_KEY}
* mutation. This will throw an {@link IllegalArgumentException} if the {@link Tuple} does not * mutation. This will throw an {@link IllegalArgumentException} if the {@link Tuple} does not
* contain an incomplete {@link Versionstamp} or if it contains multiple. * contain an incomplete {@link Versionstamp} or if it contains multiple.
* *
* @param tuple the {@code Tuple} to be packed * @param tuple the {@code Tuple} to be packed
* @return the key encoding the specified tuple in this {@code Subspace} * @return the key encoding the specified tuple in this {@code Subspace}
* @throws IllegalArgumentException if {@code tuple} does not contain exactly one incomplete {@link Versionstamp} * @throws IllegalArgumentException if {@code tuple} does not contain exactly one incomplete {@link Versionstamp}
*/ */
public byte[] packWithVersionstamp(Tuple tuple) { public byte[] packWithVersionstamp(Tuple tuple) {
return tuple.packWithVersionstamp(rawPrefix); return tuple.packWithVersionstamp(rawPrefix);
} }
/** /**
* Gets the {@link Tuple} encoded by the given key, with this {@code Subspace}'s prefix {@link Tuple} and * Gets the {@link Tuple} encoded by the given key, with this {@code Subspace}'s prefix {@link Tuple} and
* {@code raw prefix} removed. * {@code raw prefix} removed.
* *
* @param key The key being decoded * @param key The key being decoded
* @return the {@link Tuple} encoded by {@code key} with the prefix removed * @return the {@link Tuple} encoded by {@code key} with the prefix removed
*/ */
public Tuple unpack(byte[] key) { public Tuple unpack(byte[] key) {
if(!contains(key)) if(!contains(key))
throw new IllegalArgumentException("Cannot unpack key that is not contained in subspace."); throw new IllegalArgumentException("Cannot unpack key that is not contained in subspace.");
return Tuple.fromBytes(Arrays.copyOfRange(key, rawPrefix.length, key.length)); return Tuple.fromBytes(Arrays.copyOfRange(key, rawPrefix.length, key.length));
} }
/** /**
* Gets a {@link Range} respresenting all keys strictly in the {@code Subspace}. * Gets a {@link Range} respresenting all keys strictly in the {@code Subspace}.
* *
* @return the {@link Range} of keyspace corresponding to this {@code Subspace} * @return the {@link Range} of keyspace corresponding to this {@code Subspace}
*/ */
public Range range() { public Range range() {
return range(EMPTY_TUPLE); return range(EMPTY_TUPLE);
} }
/** /**
* Gets a {@link Range} representing all keys in the {@code Subspace} strictly starting with * Gets a {@link Range} representing all keys in the {@code Subspace} strictly starting with
* the specified {@link Tuple}. * the specified {@link Tuple}.
* *
* @param tuple the {@code Tuple} whose sub-keys we are searching for * @param tuple the {@code Tuple} whose sub-keys we are searching for
* @return the {@link Range} of keyspace corresponding to {@code tuple} * @return the {@link Range} of keyspace corresponding to {@code tuple}
*/ */
public Range range(Tuple tuple) { public Range range(Tuple tuple) {
Range p = tuple.range(); Range p = tuple.range();
return new Range(join(rawPrefix, p.begin), join(rawPrefix, p.end)); return new Range(join(rawPrefix, p.begin), join(rawPrefix, p.end));
} }
/** /**
* Tests whether the specified key starts with this {@code Subspace}'s prefix, indicating that * Tests whether the specified key starts with this {@code Subspace}'s prefix, indicating that
* the {@code Subspace} logically contains key. * the {@code Subspace} logically contains key.
* *
* @param key the key to be tested * @param key the key to be tested
* @return {@code true} if {@code key} starts with {@code Subspace.key()} * @return {@code true} if {@code key} starts with {@code Subspace.key()}
*/ */
public boolean contains(byte[] key) { public boolean contains(byte[] key) {
return ByteArrayUtil.startsWith(key, rawPrefix); return ByteArrayUtil.startsWith(key, rawPrefix);
} }
/** /**
* Gets a new subspace which is equivalent to this subspace with its prefix {@link Tuple} extended by * Gets a new subspace which is equivalent to this subspace with its prefix {@link Tuple} extended by
* the specified {@link Tuple}. * the specified {@link Tuple}.
* *
* @param tuple the {@link Tuple} used to form the new {@code Subspace} * @param tuple the {@link Tuple} used to form the new {@code Subspace}
* @return a new subspace formed by joining this {@code Subspace}'s prefix to {@code tuple} * @return a new subspace formed by joining this {@code Subspace}'s prefix to {@code tuple}
*/ */
public Subspace subspace(Tuple tuple) { public Subspace subspace(Tuple tuple) {
return new Subspace(tuple, rawPrefix); return new Subspace(tuple, rawPrefix);
} }
} }

View File

@ -63,45 +63,45 @@ import java.util.Iterator;
* </ul> * </ul>
*/ */
public class IterableComparator implements Comparator<Iterable<?>> { public class IterableComparator implements Comparator<Iterable<?>> {
/** /**
* Creates a new {@code IterableComparator}. This {@link Comparator} has * Creates a new {@code IterableComparator}. This {@link Comparator} has
* no internal state. * no internal state.
*/ */
public IterableComparator() {} public IterableComparator() {}
/** /**
* Compare two {@link Iterable}s in a way consistent with their * Compare two {@link Iterable}s in a way consistent with their
* byte representation. This is done element-wise and is consistent * byte representation. This is done element-wise and is consistent
* with a number of other ways of sorting {@link Tuple}s. This will * with a number of other ways of sorting {@link Tuple}s. This will
* raise an {@link IllegalArgumentException} if any of the items * raise an {@link IllegalArgumentException} if any of the items
* of either {@link Iterable} cannot be serialized by a {@link Tuple}. * of either {@link Iterable} cannot be serialized by a {@link Tuple}.
* *
* @param iterable1 the first {@link Iterable} of items * @param iterable1 the first {@link Iterable} of items
* @param iterable2 the second {@link Iterable} of items * @param iterable2 the second {@link Iterable} of items
* @return a negative number if the first iterable would sort before the second * @return a negative number if the first iterable would sort before the second
* when serialized, a positive number if the opposite is true, and zero * when serialized, a positive number if the opposite is true, and zero
* if the two are equal * if the two are equal
*/ */
@Override @Override
public int compare(Iterable<?> iterable1, Iterable<?> iterable2) { public int compare(Iterable<?> iterable1, Iterable<?> iterable2) {
Iterator<?> i1 = iterable1.iterator(); Iterator<?> i1 = iterable1.iterator();
Iterator<?> i2 = iterable2.iterator(); Iterator<?> i2 = iterable2.iterator();
while(i1.hasNext() && i2.hasNext()) { while(i1.hasNext() && i2.hasNext()) {
int itemComp = TupleUtil.compareItems(i1.next(), i2.next()); int itemComp = TupleUtil.compareItems(i1.next(), i2.next());
if(itemComp != 0) { if(itemComp != 0) {
return itemComp; return itemComp;
} }
} }
if(i1.hasNext()) { if(i1.hasNext()) {
// iterable2 is a prefix of iterable1. // iterable2 is a prefix of iterable1.
return 1; return 1;
} }
if(i2.hasNext()) { if(i2.hasNext()) {
// iterable1 is a prefix of iterable2. // iterable1 is a prefix of iterable2.
return -1; return -1;
} }
return 0; return 0;
} }
} }

View File

@ -27,89 +27,89 @@ import com.apple.foundationdb.Database;
import com.apple.foundationdb.FDB; import com.apple.foundationdb.FDB;
public abstract class AbstractTester { public abstract class AbstractTester {
public static final int API_VERSION = 510; public static final int API_VERSION = 510;
protected static final int NUM_RUNS = 25; protected static final int NUM_RUNS = 25;
protected static final Charset ASCII = Charset.forName("ASCII"); protected static final Charset ASCII = Charset.forName("ASCII");
protected TesterArgs args; protected TesterArgs args;
protected Random random; protected Random random;
protected TestResult result; protected TestResult result;
protected FDB fdb; protected FDB fdb;
public AbstractTester() { public AbstractTester() {
args = null; args = null;
random = new Random(); random = new Random();
result = new TestResult(random); result = new TestResult(random);
} }
public void runTest() { public void runTest() {
try(Database db = fdb.open()) { try(Database db = fdb.open()) {
try { try {
testPerformance(db); testPerformance(db);
} }
catch (Exception e) { catch (Exception e) {
result.addError(wrapAndPrintError(e, "Failed to complete all tests")); result.addError(wrapAndPrintError(e, "Failed to complete all tests"));
} }
} }
catch (Exception e) { catch (Exception e) {
result.addError(wrapAndPrintError(e, "fdb.open failed")); result.addError(wrapAndPrintError(e, "fdb.open failed"));
return; return;
} }
} }
public abstract void testPerformance(Database db); public abstract void testPerformance(Database db);
public String multiVersionDescription() { public String multiVersionDescription() {
if (args == null) return ""; if (args == null) return "";
if (!args.useMultiversionApi()) { if (!args.useMultiversionApi()) {
return "multi-version API disabled"; return "multi-version API disabled";
} else if (args.useExternalClient()) { } else if (args.useExternalClient()) {
if (args.putCallbacksOnExternalThread()) { if (args.putCallbacksOnExternalThread()) {
return "external client on external thread"; return "external client on external thread";
} else { } else {
return "external client on main thread"; return "external client on main thread";
} }
} else { } else {
return "local client"; return "local client";
} }
} }
public void run(String[] argStrings) { public void run(String[] argStrings) {
args = TesterArgs.parseArgs(argStrings); args = TesterArgs.parseArgs(argStrings);
if (args == null) return; if (args == null) return;
fdb = FDB.selectAPIVersion(API_VERSION); fdb = FDB.selectAPIVersion(API_VERSION);
// Validate argument combinations and set options. // Validate argument combinations and set options.
if (!args.useMultiversionApi()) { if (!args.useMultiversionApi()) {
if (args.putCallbacksOnExternalThread() || args.useExternalClient()) { if (args.putCallbacksOnExternalThread() || args.useExternalClient()) {
throw new IllegalArgumentException("Invalid multi-version API argument combination"); throw new IllegalArgumentException("Invalid multi-version API argument combination");
} }
fdb.options().setDisableMultiVersionClientApi(); fdb.options().setDisableMultiVersionClientApi();
} }
if (args.putCallbacksOnExternalThread()) { if (args.putCallbacksOnExternalThread()) {
if (!args.useExternalClient()) { if (!args.useExternalClient()) {
throw new IllegalArgumentException("Cannot enable callbacks on external thread without using external client"); throw new IllegalArgumentException("Cannot enable callbacks on external thread without using external client");
} }
fdb.options().setCallbacksOnExternalThreads(); fdb.options().setCallbacksOnExternalThreads();
} }
if (args.useExternalClient()) { if (args.useExternalClient()) {
fdb.options().setDisableLocalClient(); fdb.options().setDisableLocalClient();
} }
try { try {
runTest(); runTest();
} catch (Exception e) { } catch (Exception e) {
result.addError(e); result.addError(e);
} }
result.save(args.getOutputDirectory()); result.save(args.getOutputDirectory());
} }
public RuntimeException wrapAndPrintError(Throwable t, String message) { public RuntimeException wrapAndPrintError(Throwable t, String message) {
String errorMessage = message + ": " + t.getClass() + ": " + t.getMessage() + "\n"; String errorMessage = message + ": " + t.getClass() + ": " + t.getMessage() + "\n";
t.printStackTrace(); t.printStackTrace();
return new RuntimeException(errorMessage, t); return new RuntimeException(errorMessage, t);
} }
} }

View File

@ -24,7 +24,6 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import com.apple.foundationdb.Cluster;
import com.apple.foundationdb.Database; import com.apple.foundationdb.Database;
import com.apple.foundationdb.FDB; import com.apple.foundationdb.FDB;
import com.apple.foundationdb.TransactionContext; import com.apple.foundationdb.TransactionContext;

View File

@ -22,7 +22,6 @@ package com.apple.foundationdb.test;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import com.apple.foundationdb.Cluster;
import com.apple.foundationdb.Database; import com.apple.foundationdb.Database;
import com.apple.foundationdb.FDB; import com.apple.foundationdb.FDB;
import com.apple.foundationdb.KeyValue; import com.apple.foundationdb.KeyValue;

View File

@ -53,15 +53,14 @@ public class ParallelRandomScan {
} }
private static void runTest(Database database, private static void runTest(Database database,
int parallelism, int rows, int duration) throws InterruptedException int parallelism, int rows, int duration) throws InterruptedException {
{
final Random r = new Random(); final Random r = new Random();
final AtomicInteger readsCompleted = new AtomicInteger(0); final AtomicInteger readsCompleted = new AtomicInteger(0);
final AtomicInteger errors = new AtomicInteger(0); final AtomicInteger errors = new AtomicInteger(0);
final Semaphore coordinator = new Semaphore(parallelism); final Semaphore coordinator = new Semaphore(parallelism);
final ContinuousSample<Long> latencies = new ContinuousSample<>(1000); final ContinuousSample<Long> latencies = new ContinuousSample<>(1000);
try(final Transaction tr = database.createTransaction()) { try(Transaction tr = database.createTransaction()) {
tr.options().setReadYourWritesDisable(); tr.options().setReadYourWritesDisable();
// Clearing the whole database before starting means all reads are local // Clearing the whole database before starting means all reads are local

View File

@ -41,370 +41,370 @@ import com.apple.foundationdb.async.AsyncUtil;
import com.apple.foundationdb.tuple.ByteArrayUtil; import com.apple.foundationdb.tuple.ByteArrayUtil;
public class PerformanceTester extends AbstractTester { public class PerformanceTester extends AbstractTester {
private final int keyCount; private final int keyCount;
private final int keySize; private final int keySize;
private final int valueSize; private final int valueSize;
private final String keyFormat; private final String keyFormat;
private final byte[] valueBytes; private final byte[] valueBytes;
public static final int DEFAULT_KEY_COUNT = 10_000; public static final int DEFAULT_KEY_COUNT = 10_000;
public static final int DEFAULT_KEY_SIZE = 16; public static final int DEFAULT_KEY_SIZE = 16;
public static final int DEFAULT_VALUE_SIZE = 100; public static final int DEFAULT_VALUE_SIZE = 100;
private enum Tests { private enum Tests {
FUTURE_LATENCY("Java Completable API future throughput"), FUTURE_LATENCY("Java Completable API future throughput"),
SET("Java Completable API set throughput"), SET("Java Completable API set throughput"),
CLEAR("Java Completable API clear throughput"), CLEAR("Java Completable API clear throughput"),
CLEAR_RANGE("Java Completable API clear_range throughput"), CLEAR_RANGE("Java Completable API clear_range throughput"),
PARALLEL_GET("Java Completable API parallel get throughput"), PARALLEL_GET("Java Completable API parallel get throughput"),
SERIAL_GET("Java Completable API serial get throughput"), SERIAL_GET("Java Completable API serial get throughput"),
GET_RANGE("Java Completable API get_range throughput"), GET_RANGE("Java Completable API get_range throughput"),
GET_KEY("Java Completable API get_key throughput"), GET_KEY("Java Completable API get_key throughput"),
GET_SINGLE_KEY_RANGE("Java Completable API get_single_key_range throughput"), GET_SINGLE_KEY_RANGE("Java Completable API get_single_key_range throughput"),
ALTERNATING_GET_SET("Java Completable API alternating get and set throughput"), ALTERNATING_GET_SET("Java Completable API alternating get and set throughput"),
WRITE_TRANSACTION("Java Completable API single-key transaction throughput"); WRITE_TRANSACTION("Java Completable API single-key transaction throughput");
private String kpi; private String kpi;
private Function<? super Database, ? extends Double> function; private Function<? super Database, ? extends Double> function;
Tests(String kpi) { Tests(String kpi) {
this.kpi = kpi; this.kpi = kpi;
} }
public void setFunction(Function<?super Database, ? extends Double> function) { public void setFunction(Function<?super Database, ? extends Double> function) {
this.function = function; this.function = function;
} }
public Function<? super Database, ? extends Double> getFunction() { public Function<? super Database, ? extends Double> getFunction() {
return function; return function;
} }
public String getKpi() { public String getKpi() {
return kpi; return kpi;
} }
} }
public PerformanceTester() { public PerformanceTester() {
this(DEFAULT_KEY_COUNT, DEFAULT_KEY_SIZE, DEFAULT_VALUE_SIZE); this(DEFAULT_KEY_COUNT, DEFAULT_KEY_SIZE, DEFAULT_VALUE_SIZE);
} }
public PerformanceTester(int keyCount, int keySize, int valueSize) { public PerformanceTester(int keyCount, int keySize, int valueSize) {
super(); super();
this.keyCount = keyCount; this.keyCount = keyCount;
this.keySize = keySize; this.keySize = keySize;
this.valueSize = valueSize; this.valueSize = valueSize;
keyFormat = "%0" + keySize + "d"; keyFormat = "%0" + keySize + "d";
valueBytes = new byte[valueSize]; valueBytes = new byte[valueSize];
Arrays.fill(valueBytes, (byte)'x'); Arrays.fill(valueBytes, (byte)'x');
// Initialize tests. // Initialize tests.
Tests.FUTURE_LATENCY.setFunction(db -> futureLatency(db, 100_000)); Tests.FUTURE_LATENCY.setFunction(db -> futureLatency(db, 100_000));
Tests.SET.setFunction(db -> set(db, 100_000)); Tests.SET.setFunction(db -> set(db, 100_000));
Tests.CLEAR.setFunction(db -> clear(db, 100_000)); Tests.CLEAR.setFunction(db -> clear(db, 100_000));
Tests.CLEAR_RANGE.setFunction(db -> clearRange(db, 100_000)); Tests.CLEAR_RANGE.setFunction(db -> clearRange(db, 100_000));
Tests.PARALLEL_GET.setFunction(db -> parallelGet(db, 10_000)); Tests.PARALLEL_GET.setFunction(db -> parallelGet(db, 10_000));
Tests.SERIAL_GET.setFunction(db -> serialGet(db, 2_000)); Tests.SERIAL_GET.setFunction(db -> serialGet(db, 2_000));
Tests.GET_RANGE.setFunction(db -> getRange(db, 1_000)); Tests.GET_RANGE.setFunction(db -> getRange(db, 1_000));
Tests.GET_KEY.setFunction(db -> getKey(db, 2_000)); Tests.GET_KEY.setFunction(db -> getKey(db, 2_000));
Tests.GET_SINGLE_KEY_RANGE.setFunction(db -> getSingleKeyRange(db, 2_000)); Tests.GET_SINGLE_KEY_RANGE.setFunction(db -> getSingleKeyRange(db, 2_000));
Tests.ALTERNATING_GET_SET.setFunction(db -> alternatingGetSet(db, 2_000)); Tests.ALTERNATING_GET_SET.setFunction(db -> alternatingGetSet(db, 2_000));
Tests.WRITE_TRANSACTION.setFunction(db -> writeTransaction(db, 1_000)); Tests.WRITE_TRANSACTION.setFunction(db -> writeTransaction(db, 1_000));
} }
@Override @Override
public void testPerformance(Database db) { public void testPerformance(Database db) {
insertData(db); insertData(db);
List<String> testsToRun; List<String> testsToRun;
if (args.getTestsToRun().isEmpty()) { if (args.getTestsToRun().isEmpty()) {
testsToRun = Arrays.stream(Tests.values()).map(Tests::name).map(String::toLowerCase).sorted().collect(Collectors.toList()); testsToRun = Arrays.stream(Tests.values()).map(Tests::name).map(String::toLowerCase).sorted().collect(Collectors.toList());
} else { } else {
testsToRun = args.getTestsToRun(); testsToRun = args.getTestsToRun();
} }
for (String test : testsToRun) { for (String test : testsToRun) {
Tests testObj; Tests testObj;
try { try {
testObj = Tests.valueOf(test.toUpperCase()); testObj = Tests.valueOf(test.toUpperCase());
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
result.addError(new IllegalArgumentException("Test " + test + " not implemented")); result.addError(new IllegalArgumentException("Test " + test + " not implemented"));
continue; continue;
} }
Function<? super Database, ? extends Double> function = testObj.getFunction(); Function<? super Database, ? extends Double> function = testObj.getFunction();
try { try {
Thread.sleep(5_000); Thread.sleep(5_000);
} catch (InterruptedException e) { } catch (InterruptedException e) {
result.addError(wrapAndPrintError(e, "Interrupted while sleeping")); result.addError(wrapAndPrintError(e, "Interrupted while sleeping"));
} }
System.out.println("Running test " + test); System.out.println("Running test " + test);
List<Double> results = new ArrayList<>(NUM_RUNS); List<Double> results = new ArrayList<>(NUM_RUNS);
for (int i = 0; i < NUM_RUNS; i++) { for (int i = 0; i < NUM_RUNS; i++) {
try { try {
results.add(function.apply(db)); results.add(function.apply(db));
} catch (Exception e) { } catch (Exception e) {
result.addError(wrapAndPrintError(e, "Performance test failed: " + test)); result.addError(wrapAndPrintError(e, "Performance test failed: " + test));
break; break;
} }
} }
if (results.size() == NUM_RUNS) { if (results.size() == NUM_RUNS) {
Collections.sort(results); Collections.sort(results);
result.addKpi(String.format("%s (%s)", testObj.getKpi(), multiVersionDescription()), results.get(results.size()/2).intValue(), "keys/s"); result.addKpi(String.format("%s (%s)", testObj.getKpi(), multiVersionDescription()), results.get(results.size()/2).intValue(), "keys/s");
} }
} }
} }
public void insertData(Database db) { public void insertData(Database db) {
System.out.println("Loading database"); System.out.println("Loading database");
db.run(tr -> { db.run(tr -> {
byte[] subspacePrefix = args.getSubspace().pack(); byte[] subspacePrefix = args.getSubspace().pack();
if (subspacePrefix.length == 0) { if (subspacePrefix.length == 0) {
// Clear user space. // Clear user space.
tr.clear(new byte[0], new byte[]{(byte)0xff}); tr.clear(new byte[0], new byte[]{(byte)0xff});
} else { } else {
tr.clear(args.getSubspace().range()); tr.clear(args.getSubspace().range());
} }
return null; return null;
}); });
int keysPerActor = 100_000 / (keySize + valueSize); int keysPerActor = 100_000 / (keySize + valueSize);
int numActors = (int)Math.ceil(keyCount*1.0/keysPerActor); int numActors = (int)Math.ceil(keyCount*1.0/keysPerActor);
List<CompletableFuture<Void>> futures = IntStream.range(0, numActors).mapToObj(i -> { List<CompletableFuture<Void>> futures = IntStream.range(0, numActors).mapToObj(i -> {
int startKey = keysPerActor * i; int startKey = keysPerActor * i;
int endKey = (i + 1 == numActors) ? (keyCount) : (keysPerActor * (i+1)); int endKey = (i + 1 == numActors) ? (keyCount) : (keysPerActor * (i+1));
return db.runAsync(tr -> { return db.runAsync(tr -> {
IntStream.range(startKey, endKey).forEach(keyIndex -> tr.set(key(keyIndex), value(keyIndex))); IntStream.range(startKey, endKey).forEach(keyIndex -> tr.set(key(keyIndex), value(keyIndex)));
return CompletableFuture.completedFuture((Void)null); return CompletableFuture.completedFuture((Void)null);
}); });
}).collect(Collectors.toList()); }).collect(Collectors.toList());
try { try {
AsyncUtil.whenAll(futures).get(); AsyncUtil.whenAll(futures).get();
} catch (InterruptedException | ExecutionException e) { } catch (InterruptedException | ExecutionException e) {
result.addError(wrapAndPrintError(e, "Data insertion failed")); result.addError(wrapAndPrintError(e, "Data insertion failed"));
} }
// Give the database time to re-balance // Give the database time to re-balance
try { try {
Thread.sleep(15_000); Thread.sleep(15_000);
} catch (InterruptedException e) { } catch (InterruptedException e) {
result.addError(wrapAndPrintError(e, "Interrupted while waiting for quiescence")); result.addError(wrapAndPrintError(e, "Interrupted while waiting for quiescence"));
} }
} }
public Double futureLatency(Database db, int count) { public Double futureLatency(Database db, int count) {
return db.run(tr -> { return db.run(tr -> {
tr.options().setRetryLimit(5); tr.options().setRetryLimit(5);
tr.getReadVersion().join(); tr.getReadVersion().join();
long start = System.nanoTime(); long start = System.nanoTime();
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
tr.getReadVersion().join(); tr.getReadVersion().join();
} }
long end = System.nanoTime(); long end = System.nanoTime();
return count*1_000_000_000.0/(end - start); return count*1_000_000_000.0/(end - start);
}); });
} }
public Double clear(Database db, int count) { public Double clear(Database db, int count) {
try(Transaction tr = db.createTransaction()) { try(Transaction tr = db.createTransaction()) {
long start = System.nanoTime(); long start = System.nanoTime();
for(int i = 0; i < count; i++) { for(int i = 0; i < count; i++) {
tr.clear(randomKey()); tr.clear(randomKey());
} }
long end = System.nanoTime(); long end = System.nanoTime();
tr.cancel(); tr.cancel();
return count * 1_000_000_000.0 / (end - start); return count * 1_000_000_000.0 / (end - start);
} }
} }
public Double clearRange(Database db, int count) { public Double clearRange(Database db, int count) {
try(Transaction tr = db.createTransaction()) { try(Transaction tr = db.createTransaction()) {
long start = System.nanoTime(); long start = System.nanoTime();
for(int i = 0; i < count; i++) { for(int i = 0; i < count; i++) {
int keyIndex = randomKeyIndex(); int keyIndex = randomKeyIndex();
tr.clear(key(keyIndex), key(keyIndex + 1)); tr.clear(key(keyIndex), key(keyIndex + 1));
} }
long end = System.nanoTime(); long end = System.nanoTime();
tr.cancel(); tr.cancel();
return count * 1_000_000_000.0 / (end - start); return count * 1_000_000_000.0 / (end - start);
} }
} }
public Double set(Database db, int count) { public Double set(Database db, int count) {
try(Transaction tr = db.createTransaction()) { try(Transaction tr = db.createTransaction()) {
long start = System.nanoTime(); long start = System.nanoTime();
for(int i = 0; i < count; i++) { for(int i = 0; i < count; i++) {
int keyIndex = randomKeyIndex(); int keyIndex = randomKeyIndex();
tr.set(key(keyIndex), value(keyIndex)); tr.set(key(keyIndex), value(keyIndex));
} }
long end = System.nanoTime(); long end = System.nanoTime();
tr.cancel(); tr.cancel();
return count * 1_000_000_000.0 / (end - start); return count * 1_000_000_000.0 / (end - start);
} }
} }
public Double parallelGet(TransactionContext tcx, int count) { public Double parallelGet(TransactionContext tcx, int count) {
return tcx.run(tr -> { return tcx.run(tr -> {
tr.options().setRetryLimit(5); tr.options().setRetryLimit(5);
long start = System.nanoTime(); long start = System.nanoTime();
List<CompletableFuture<byte[]>> futures = IntStream.range(0, count) List<CompletableFuture<byte[]>> futures = IntStream.range(0, count)
.mapToObj(ignore -> tr.get(randomKey())) .mapToObj(ignore -> tr.get(randomKey()))
.collect(Collectors.toList()); .collect(Collectors.toList());
AsyncUtil.whenAll(futures).join(); AsyncUtil.whenAll(futures).join();
long end = System.nanoTime(); long end = System.nanoTime();
return count*1_000_000_000.0/(end - start); return count*1_000_000_000.0/(end - start);
}); });
} }
public Double alternatingGetSet(TransactionContext tcx, int count) { public Double alternatingGetSet(TransactionContext tcx, int count) {
return tcx.run(tr -> { return tcx.run(tr -> {
tr.options().setRetryLimit(5); tr.options().setRetryLimit(5);
long start = System.nanoTime(); long start = System.nanoTime();
List<CompletableFuture<byte[]>> futures = IntStream.range(0, count) List<CompletableFuture<byte[]>> futures = IntStream.range(0, count)
.mapToObj(ignore -> { .mapToObj(ignore -> {
int keyIndex = randomKeyIndex(); int keyIndex = randomKeyIndex();
byte[] keyBytes = key(keyIndex); byte[] keyBytes = key(keyIndex);
byte[] valBytes = value(keyIndex); byte[] valBytes = value(keyIndex);
tr.set(keyBytes, valBytes); tr.set(keyBytes, valBytes);
return tr.get(keyBytes); return tr.get(keyBytes);
}).collect(Collectors.toList()); }).collect(Collectors.toList());
AsyncUtil.whenAll(futures).join(); AsyncUtil.whenAll(futures).join();
long end = System.nanoTime(); long end = System.nanoTime();
return count*1_000_000_000.0/(end - start); return count*1_000_000_000.0/(end - start);
}); });
} }
public Double serialGet(TransactionContext tcx, int count) { public Double serialGet(TransactionContext tcx, int count) {
return tcx.run(tr -> { return tcx.run(tr -> {
tr.options().setRetryLimit(5); tr.options().setRetryLimit(5);
List<byte[]> keys; List<byte[]> keys;
if (count > keyCount/2) { if (count > keyCount/2) {
keys = Stream.generate(this::randomKey).limit(count).collect(Collectors.toList()); keys = Stream.generate(this::randomKey).limit(count).collect(Collectors.toList());
} else { } else {
Set<Integer> keySet = new HashSet<>(); Set<Integer> keySet = new HashSet<>();
while (keySet.size() < count) { while (keySet.size() < count) {
keySet.add(randomKeyIndex()); keySet.add(randomKeyIndex());
} }
keys = keySet.stream().map(this::key).collect(Collectors.toList()); keys = keySet.stream().map(this::key).collect(Collectors.toList());
} }
long start = System.nanoTime(); long start = System.nanoTime();
for (byte[] key : keys) { for (byte[] key : keys) {
tr.get(key).join(); tr.get(key).join();
} }
long end = System.nanoTime(); long end = System.nanoTime();
return count*1_000_000_000.0/(end - start); return count*1_000_000_000.0/(end - start);
}); });
} }
public Double getRange(TransactionContext tcx, int count) { public Double getRange(TransactionContext tcx, int count) {
return tcx.run(tr -> { return tcx.run(tr -> {
tr.options().setRetryLimit(5); tr.options().setRetryLimit(5);
int startIndex = random.nextInt(keyCount - count); int startIndex = random.nextInt(keyCount - count);
long start = System.nanoTime(); long start = System.nanoTime();
tr.getRange(key(startIndex), key(startIndex+count)).asList().join(); tr.getRange(key(startIndex), key(startIndex+count)).asList().join();
long end = System.nanoTime(); long end = System.nanoTime();
return count*1_000_000_000.0/(end - start); return count*1_000_000_000.0/(end - start);
}); });
} }
public Double getKey(TransactionContext tcx, int count) { public Double getKey(TransactionContext tcx, int count) {
return tcx.run(tr -> { return tcx.run(tr -> {
tr.options().setRetryLimit(5); tr.options().setRetryLimit(5);
long start = System.nanoTime(); long start = System.nanoTime();
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
tr.getKey(new KeySelector(randomKey(), true, random.nextInt(20) - 10)).join(); tr.getKey(new KeySelector(randomKey(), true, random.nextInt(20) - 10)).join();
} }
long end = System.nanoTime(); long end = System.nanoTime();
return count*1_000_000_000.0/(end - start); return count*1_000_000_000.0/(end - start);
}); });
} }
public Double getSingleKeyRange(TransactionContext tcx, int count) { public Double getSingleKeyRange(TransactionContext tcx, int count) {
return tcx.run(tr -> { return tcx.run(tr -> {
tr.options().setRetryLimit(5); tr.options().setRetryLimit(5);
long start = System.nanoTime(); long start = System.nanoTime();
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
int keyIndex = randomKeyIndex(); int keyIndex = randomKeyIndex();
tr.getRange(key(keyIndex), key(keyIndex + 1), 2).asList().join(); tr.getRange(key(keyIndex), key(keyIndex + 1), 2).asList().join();
} }
long end = System.nanoTime(); long end = System.nanoTime();
return count*1_000_000_000.0/(end - start); return count*1_000_000_000.0/(end - start);
}); });
} }
public Double writeTransaction(TransactionContext tcx, int count) { public Double writeTransaction(TransactionContext tcx, int count) {
long start = System.nanoTime(); long start = System.nanoTime();
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
tcx.run(tr -> { tcx.run(tr -> {
int keyIndex = randomKeyIndex(); int keyIndex = randomKeyIndex();
tr.set(key(keyIndex), value(keyIndex)); tr.set(key(keyIndex), value(keyIndex));
return null; return null;
}); });
} }
long end = System.nanoTime(); long end = System.nanoTime();
return count*1_000_000_000.0/(end - start); return count*1_000_000_000.0/(end - start);
} }
public byte[] key(int i) { public byte[] key(int i) {
return ByteArrayUtil.join(args.getSubspace().pack(), String.format(keyFormat, i).getBytes(ASCII)); return ByteArrayUtil.join(args.getSubspace().pack(), String.format(keyFormat, i).getBytes(ASCII));
} }
public int randomKeyIndex() { public int randomKeyIndex() {
return random.nextInt(keyCount); return random.nextInt(keyCount);
} }
public byte[] randomKey() { public byte[] randomKey() {
return key(randomKeyIndex()); return key(randomKeyIndex());
} }
public byte[] value(int key) { public byte[] value(int key) {
return valueBytes; return valueBytes;
} }
public static void main(String[] args) { public static void main(String[] args) {
System.out.println("Running Java performance test on Java version " + System.getProperty("java.version")); System.out.println("Running Java performance test on Java version " + System.getProperty("java.version"));
try { try {
new PerformanceTester().run(args); new PerformanceTester().run(args);
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
System.out.println("Could not run test due to malformed arguments."); System.out.println("Could not run test due to malformed arguments.");
System.out.println(e.getMessage()); System.out.println(e.getMessage());
System.exit(1); System.exit(1);
} catch (Exception e) { } catch (Exception e) {
System.out.println("Fatal error encountered during run: " + e); System.out.println("Fatal error encountered during run: " + e);
e.printStackTrace(); e.printStackTrace();
System.exit(2); System.exit(2);
} }
} }
} }

View File

@ -32,62 +32,62 @@ import com.apple.foundationdb.Transaction;
import com.apple.foundationdb.tuple.ByteArrayUtil; import com.apple.foundationdb.tuple.ByteArrayUtil;
public class RYWBenchmark extends AbstractTester { public class RYWBenchmark extends AbstractTester {
private int keyCount; private int keyCount;
public static final int DEFAULT_KEY_COUNT = 10_000; public static final int DEFAULT_KEY_COUNT = 10_000;
public static final int DEFAULT_KEY_SIZE = 16; public static final int DEFAULT_KEY_SIZE = 16;
private final String keyFormat; private final String keyFormat;
private enum Tests { private enum Tests {
GET_SINGLE("RYW Java Completable: get single cached value throughput"), GET_SINGLE("RYW Java Completable: get single cached value throughput"),
GET_MANY_SEQUENTIAL("RYW Java Completable: get sequential cached value throughput"), GET_MANY_SEQUENTIAL("RYW Java Completable: get sequential cached value throughput"),
GET_RANGE_BASIC("RYW Java Completable: get range cached values throughput"), GET_RANGE_BASIC("RYW Java Completable: get range cached values throughput"),
SINGLE_CLEAR_GET_RANGE("RYW Java Completable: get range cached values with clears throughput"), SINGLE_CLEAR_GET_RANGE("RYW Java Completable: get range cached values with clears throughput"),
CLEAR_RANGE_GET_RANGE("RYW Java Completable: get range cached values with clear ranges throughput"), CLEAR_RANGE_GET_RANGE("RYW Java Completable: get range cached values with clear ranges throughput"),
INTERLEAVED_SETS_GETS("RYW Java Completable: interleaved sets and gets on a single key throughput"); INTERLEAVED_SETS_GETS("RYW Java Completable: interleaved sets and gets on a single key throughput");
private String kpi; private String kpi;
private Function<? super Transaction, ? extends Double> function; private Function<? super Transaction, ? extends Double> function;
Tests(String kpi) { Tests(String kpi) {
this.kpi = kpi; this.kpi = kpi;
} }
public void setFunction(Function<?super Transaction, ? extends Double> function) { public void setFunction(Function<?super Transaction, ? extends Double> function) {
this.function = function; this.function = function;
} }
public Function<? super Transaction, ? extends Double> getFunction() { public Function<? super Transaction, ? extends Double> getFunction() {
return function; return function;
} }
public String getKpi() { public String getKpi() {
return kpi; return kpi;
} }
} }
public RYWBenchmark() { public RYWBenchmark() {
this(DEFAULT_KEY_COUNT, DEFAULT_KEY_SIZE); this(DEFAULT_KEY_COUNT, DEFAULT_KEY_SIZE);
} }
public RYWBenchmark(int keyCount, int keySize) { public RYWBenchmark(int keyCount, int keySize) {
super(); super();
this.keyCount = keyCount; this.keyCount = keyCount;
keyFormat = "%0" + keySize + "d"; keyFormat = "%0" + keySize + "d";
Tests.GET_SINGLE.setFunction(tr -> getSingle(tr, 10_000)); Tests.GET_SINGLE.setFunction(tr -> getSingle(tr, 10_000));
Tests.GET_MANY_SEQUENTIAL.setFunction(tr -> getManySequential(tr, 10_000)); Tests.GET_MANY_SEQUENTIAL.setFunction(tr -> getManySequential(tr, 10_000));
Tests.GET_RANGE_BASIC.setFunction(tr -> getRangeBasic(tr, 1_000)); Tests.GET_RANGE_BASIC.setFunction(tr -> getRangeBasic(tr, 1_000));
Tests.SINGLE_CLEAR_GET_RANGE.setFunction(tr -> singleClearGetRange(tr, 1_000)); Tests.SINGLE_CLEAR_GET_RANGE.setFunction(tr -> singleClearGetRange(tr, 1_000));
Tests.CLEAR_RANGE_GET_RANGE.setFunction(tr -> clearRangeGetRange(tr, 1_000)); Tests.CLEAR_RANGE_GET_RANGE.setFunction(tr -> clearRangeGetRange(tr, 1_000));
Tests.INTERLEAVED_SETS_GETS.setFunction(tr -> interleavedSetsGets(tr, 10_000)); Tests.INTERLEAVED_SETS_GETS.setFunction(tr -> interleavedSetsGets(tr, 10_000));
} }
@Override @Override
public void testPerformance(Database db) { public void testPerformance(Database db) {
try(Transaction tr = db.createTransaction()) { try(Transaction tr = db.createTransaction()) {
insertData(tr); insertData(tr);
List<String> testsToRun; List<String> testsToRun;
@ -139,103 +139,103 @@ public class RYWBenchmark extends AbstractTester {
tr.cancel(); tr.cancel();
} }
} }
public Double getSingle(Transaction tr, int count) { public Double getSingle(Transaction tr, int count) {
long start = System.nanoTime(); long start = System.nanoTime();
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
tr.get(key(5001)).join(); tr.get(key(5001)).join();
} }
long end = System.nanoTime(); long end = System.nanoTime();
return count*1_000_000_000.0/(end - start); return count*1_000_000_000.0/(end - start);
} }
public Double getManySequential(Transaction tr, int count) { public Double getManySequential(Transaction tr, int count) {
long start = System.nanoTime(); long start = System.nanoTime();
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
tr.get(key(i)).join(); tr.get(key(i)).join();
} }
long end = System.nanoTime(); long end = System.nanoTime();
return count*1_000_000_000.0/(end - start); return count*1_000_000_000.0/(end - start);
} }
public Double getRangeBasic(Transaction tr, int count) { public Double getRangeBasic(Transaction tr, int count) {
long start = System.nanoTime(); long start = System.nanoTime();
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
tr.getRange(key(0), key(keyCount)).asList().join(); tr.getRange(key(0), key(keyCount)).asList().join();
} }
long end = System.nanoTime(); long end = System.nanoTime();
return count * 1_000_000_000.0 * keyCount/(end - start); return count * 1_000_000_000.0 * keyCount/(end - start);
} }
public Double singleClearGetRange(Transaction tr, int count) { public Double singleClearGetRange(Transaction tr, int count) {
for (int i = 0; i < keyCount; i += 2) { for (int i = 0; i < keyCount; i += 2) {
tr.clear(("" + i).getBytes(ASCII)); tr.clear(("" + i).getBytes(ASCII));
} }
long start = System.nanoTime(); long start = System.nanoTime();
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
tr.getRange(key(0), key(keyCount)).asList().join(); tr.getRange(key(0), key(keyCount)).asList().join();
} }
long end = System.nanoTime(); long end = System.nanoTime();
Double kpi = count * 1_000_000_000.0 * keyCount / 2 / (end - start); Double kpi = count * 1_000_000_000.0 * keyCount / 2 / (end - start);
insertData(tr); insertData(tr);
return kpi; return kpi;
} }
public Double clearRangeGetRange(Transaction tr, int count) { public Double clearRangeGetRange(Transaction tr, int count) {
for (int i = 0; i < keyCount; i += 4) { for (int i = 0; i < keyCount; i += 4) {
tr.clear(key(i), key(i+1)); tr.clear(key(i), key(i+1));
} }
long start = System.nanoTime(); long start = System.nanoTime();
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
tr.getRange(key(0), key(keyCount)).asList().join(); tr.getRange(key(0), key(keyCount)).asList().join();
} }
long end = System.nanoTime(); long end = System.nanoTime();
Double kpi = count * 1_000_000_000.0 * keyCount * 3 / 4 / (end - start); Double kpi = count * 1_000_000_000.0 * keyCount * 3 / 4 / (end - start);
insertData(tr); insertData(tr);
return kpi; return kpi;
} }
public Double interleavedSetsGets(Transaction tr, int count) { public Double interleavedSetsGets(Transaction tr, int count) {
long start = System.nanoTime(); long start = System.nanoTime();
byte[] keyBytes = "foo".getBytes(ASCII); byte[] keyBytes = "foo".getBytes(ASCII);
tr.set(keyBytes, "1".getBytes(ASCII)); tr.set(keyBytes, "1".getBytes(ASCII));
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
int old = Integer.parseInt(new String(tr.get(keyBytes).join(), ASCII)); int old = Integer.parseInt(new String(tr.get(keyBytes).join(), ASCII));
tr.set(keyBytes, ("" + (old + 1)).getBytes(ASCII)); tr.set(keyBytes, ("" + (old + 1)).getBytes(ASCII));
} }
long end = System.nanoTime(); long end = System.nanoTime();
return count * 1_000_000_000.0/(end - start); return count * 1_000_000_000.0/(end - start);
} }
public void insertData(Transaction tr) { public void insertData(Transaction tr) {
tr.clear(new byte[0], new byte[]{(byte)0xff}); // Clear user space. tr.clear(new byte[0], new byte[]{(byte)0xff}); // Clear user space.
for (int i = 0; i < keyCount; i++) { for (int i = 0; i < keyCount; i++) {
tr.set(key(i), "foo".getBytes(ASCII)); tr.set(key(i), "foo".getBytes(ASCII));
} }
} }
public byte[] key(int i) { public byte[] key(int i) {
return ByteArrayUtil.join(args.getSubspace().pack(), String.format(keyFormat, i).getBytes(ASCII)); return ByteArrayUtil.join(args.getSubspace().pack(), String.format(keyFormat, i).getBytes(ASCII));
} }
public static void main(String[] args) { public static void main(String[] args) {
System.out.println("Running Java RYW benchmark on Java version " + System.getProperty("java.version")); System.out.println("Running Java RYW benchmark on Java version " + System.getProperty("java.version"));
try { try {
new RYWBenchmark().run(args); new RYWBenchmark().run(args);
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
System.out.println("Could not run test due to malformed arguments."); System.out.println("Could not run test due to malformed arguments.");
System.exit(1); System.exit(1);
} catch (Exception e) { } catch (Exception e) {
System.out.println("Fatal error encountered during run: " + e); System.out.println("Fatal error encountered during run: " + e);
e.printStackTrace(); e.printStackTrace();
System.exit(2); System.exit(2);
} }
} }
} }

View File

@ -22,7 +22,6 @@ package com.apple.foundationdb.test;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import com.apple.foundationdb.Cluster;
import com.apple.foundationdb.Database; import com.apple.foundationdb.Database;
import com.apple.foundationdb.FDB; import com.apple.foundationdb.FDB;
import com.apple.foundationdb.TransactionContext; import com.apple.foundationdb.TransactionContext;

View File

@ -439,15 +439,15 @@ public class StackTester {
byte[] prefix = (byte[]) params.get(0); byte[] prefix = (byte[]) params.get(0);
Map<Integer, StackEntry> entries = new HashMap<>(); Map<Integer, StackEntry> entries = new HashMap<>();
while(inst.size() > 0) { while(inst.size() > 0) {
entries.put(inst.size()-1, inst.pop()); entries.put(inst.size()-1, inst.pop());
if(entries.size() == 100) { if(entries.size() == 100) {
logStack(inst.context.db, entries, prefix); logStack(inst.context.db, entries, prefix);
entries.clear(); entries.clear();
} }
} }
logStack(inst.context.db, entries, prefix); logStack(inst.context.db, entries, prefix);
} }
else { else {
throw new IllegalArgumentException("Unrecognized (or unimplemented) operation"); throw new IllegalArgumentException("Unrecognized (or unimplemented) operation");
@ -576,7 +576,7 @@ public class StackTester {
} }
private static void logStack(Database db, Map<Integer, StackEntry> entries, byte[] prefix) { private static void logStack(Database db, Map<Integer, StackEntry> entries, byte[] prefix) {
db.run(tr -> { db.run(tr -> {
for(Map.Entry<Integer, StackEntry> it : entries.entrySet()) { for(Map.Entry<Integer, StackEntry> it : entries.entrySet()) {
byte[] pk = Tuple.from(it.getKey(), it.getValue().idx).pack(prefix); byte[] pk = Tuple.from(it.getKey(), it.getValue().idx).pack(prefix);
byte[] pv = Tuple.from(StackUtils.serializeFuture(it.getValue().value)).pack(); byte[] pv = Tuple.from(StackUtils.serializeFuture(it.getValue().value)).pack();
@ -588,7 +588,7 @@ public class StackTester {
} }
private static boolean checkWatches(List<CompletableFuture<Void>> watches, Database db, boolean expected) { private static boolean checkWatches(List<CompletableFuture<Void>> watches, Database db, boolean expected) {
for(CompletableFuture<Void> w : watches) { for(CompletableFuture<Void> w : watches) {
if(w.isDone() || expected) { if(w.isDone() || expected) {
try { try {
w.join(); w.join();
@ -647,8 +647,8 @@ public class StackTester {
return null; return null;
}); });
if(checkWatches(watches, db, true)) { if(checkWatches(watches, db, true)) {
return; return;
} }
} }
} }

View File

@ -62,7 +62,7 @@ public class StackUtils {
throw e; throw e;
} }
item = getErrorBytes(ex); item = getErrorBytes(ex);
} }
return item; return item;
} }

View File

@ -30,119 +30,119 @@ import java.util.Random;
import java.util.TreeMap; import java.util.TreeMap;
public class TestResult { public class TestResult {
private long id; private long id;
private Map<String,Map<String,Object>> kpis; private Map<String,Map<String,Object>> kpis;
private List<Throwable> errors; private List<Throwable> errors;
public TestResult(Random r) { public TestResult(Random r) {
id = Math.abs(r.nextLong()); id = Math.abs(r.nextLong());
kpis = new TreeMap<String,Map<String,Object>>(); // Tree map because we will have to print this out. kpis = new TreeMap<String,Map<String,Object>>(); // Tree map because we will have to print this out.
errors = new ArrayList<Throwable>(); errors = new ArrayList<Throwable>();
} }
public void addKpi(String name, Number value, String units) { public void addKpi(String name, Number value, String units) {
TreeMap<String,Object> kpi = new TreeMap<String,Object>(); TreeMap<String,Object> kpi = new TreeMap<String,Object>();
kpi.put("value", value); kpi.put("value", value);
kpi.put("units", units); kpi.put("units", units);
kpis.put(name, kpi); kpis.put(name, kpi);
} }
public void addError(Throwable t) { public void addError(Throwable t) {
errors.add(t); errors.add(t);
} }
public void save(String directory) { public void save(String directory) {
String file = "javaresult-" + id + ".json"; String file = "javaresult-" + id + ".json";
if(directory.length() > 0) { if(directory.length() > 0) {
file = directory + "/" + file; file = directory + "/" + file;
} }
// TODO: Should we use a really JSON library? // TODO: Should we use a really JSON library?
StringBuilder outputBuilder = new StringBuilder(); StringBuilder outputBuilder = new StringBuilder();
outputBuilder.append('{'); outputBuilder.append('{');
// Add KPIs: // Add KPIs:
outputBuilder.append("\"kpis\": {"); outputBuilder.append("\"kpis\": {");
boolean firstKpi = true; boolean firstKpi = true;
for (Map.Entry<String,Map<String,Object>> kpi : kpis.entrySet()) { for (Map.Entry<String,Map<String,Object>> kpi : kpis.entrySet()) {
if (firstKpi) { if (firstKpi) {
firstKpi = false; firstKpi = false;
} else { } else {
outputBuilder.append(", "); outputBuilder.append(", ");
} }
outputBuilder.append("\""); outputBuilder.append("\"");
outputBuilder.append(kpi.getKey()); outputBuilder.append(kpi.getKey());
outputBuilder.append("\": {"); outputBuilder.append("\": {");
boolean firstEntry = true; boolean firstEntry = true;
for (Map.Entry<String,Object> entry : kpi.getValue().entrySet()) { for (Map.Entry<String,Object> entry : kpi.getValue().entrySet()) {
if (firstEntry) { if (firstEntry) {
firstEntry = false; firstEntry = false;
} else { } else {
outputBuilder.append(", "); outputBuilder.append(", ");
} }
outputBuilder.append("\""); outputBuilder.append("\"");
outputBuilder.append(entry.getKey()); outputBuilder.append(entry.getKey());
outputBuilder.append("\": "); outputBuilder.append("\": ");
Object value = entry.getValue(); Object value = entry.getValue();
if (value instanceof String) { if (value instanceof String) {
outputBuilder.append("\""); outputBuilder.append("\"");
outputBuilder.append((String)value); outputBuilder.append((String)value);
outputBuilder.append("\""); outputBuilder.append("\"");
} else { } else {
outputBuilder.append(value.toString()); outputBuilder.append(value.toString());
} }
} }
outputBuilder.append("}"); outputBuilder.append("}");
} }
outputBuilder.append("}, "); outputBuilder.append("}, ");
// Add errors: // Add errors:
outputBuilder.append("\"errors\":["); outputBuilder.append("\"errors\":[");
boolean firstError = true; boolean firstError = true;
for (Throwable t : errors) { for (Throwable t : errors) {
if (firstError) { if (firstError) {
firstError = false; firstError = false;
} else { } else {
outputBuilder.append(", "); outputBuilder.append(", ");
} }
StringBuilder msgBuilder = new StringBuilder(); StringBuilder msgBuilder = new StringBuilder();
msgBuilder.append(t.getClass().toString()); msgBuilder.append(t.getClass().toString());
msgBuilder.append(": "); msgBuilder.append(": ");
msgBuilder.append(t.getMessage()); // Escaping quotes. Yeah, this won't work in the general case.... msgBuilder.append(t.getMessage()); // Escaping quotes. Yeah, this won't work in the general case....
StackTraceElement[] stackTraceElements = t.getStackTrace(); StackTraceElement[] stackTraceElements = t.getStackTrace();
for (StackTraceElement element : stackTraceElements) { for (StackTraceElement element : stackTraceElements) {
msgBuilder.append("\n "); msgBuilder.append("\n ");
msgBuilder.append(element.toString()); msgBuilder.append(element.toString());
} }
outputBuilder.append('"'); outputBuilder.append('"');
outputBuilder.append(msgBuilder.toString() outputBuilder.append(msgBuilder.toString()
.replace("\\", "\\\\") .replace("\\", "\\\\")
.replace("\"", "\\\"") .replace("\"", "\\\"")
.replace("\t", "\\t") .replace("\t", "\\t")
.replace("\r", "\\r") .replace("\r", "\\r")
.replace("\n", "\\n") .replace("\n", "\\n")
.replace("\f", "\\f") .replace("\f", "\\f")
.replace("\b", "\\b") .replace("\b", "\\b")
); );
outputBuilder.append('"'); outputBuilder.append('"');
} }
outputBuilder.append("]"); outputBuilder.append("]");
outputBuilder.append('}'); outputBuilder.append('}');
try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) { try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) {
writer.write(outputBuilder.toString()); writer.write(outputBuilder.toString());
} catch (IOException e) { } catch (IOException e) {
System.out.println("Could not write results to file " + file); System.out.println("Could not write results to file " + file);
throw new RuntimeException("Could not save results: " + e.getMessage(), e); throw new RuntimeException("Could not save results: " + e.getMessage(), e);
} }
} }
} }

View File

@ -27,126 +27,126 @@ import com.apple.foundationdb.subspace.Subspace;
import com.apple.foundationdb.tuple.Tuple; import com.apple.foundationdb.tuple.Tuple;
public class TesterArgs { public class TesterArgs {
private String outputDirectory; private String outputDirectory;
private boolean multiversionApi; private boolean multiversionApi;
private boolean callbacksOnExternalThread; private boolean callbacksOnExternalThread;
private boolean externalClient; private boolean externalClient;
private Subspace subspace; private Subspace subspace;
private List<String> testsToRun; private List<String> testsToRun;
private TesterArgs(String outputDirectory, boolean multiversionApi, boolean callbacksOnExternalThread, boolean externalClient, Subspace subspace, List<String> testsToRun) { private TesterArgs(String outputDirectory, boolean multiversionApi, boolean callbacksOnExternalThread, boolean externalClient, Subspace subspace, List<String> testsToRun) {
this.outputDirectory = outputDirectory; this.outputDirectory = outputDirectory;
this.multiversionApi = multiversionApi; this.multiversionApi = multiversionApi;
this.callbacksOnExternalThread = callbacksOnExternalThread; this.callbacksOnExternalThread = callbacksOnExternalThread;
this.externalClient = externalClient; this.externalClient = externalClient;
this.subspace = subspace; this.subspace = subspace;
this.testsToRun = testsToRun; this.testsToRun = testsToRun;
} }
public static void printUsage() { public static void printUsage() {
String usage = "Arguments: [-o/--output-directory DIR] [--disable-multiversion-api] [--enable-callbacks-on-external-threads] [--use-external-client] [--tests-to-run TEST [TEST ...]] [-h/--help]\n" + String usage = "Arguments: [-o/--output-directory DIR] [--disable-multiversion-api] [--enable-callbacks-on-external-threads] [--use-external-client] [--tests-to-run TEST [TEST ...]] [-h/--help]\n" +
"\n" + "\n" +
"Arguments:\n" + "Arguments:\n" +
" -o/--output-directory DIR Directory to store JSON output. If not set, the current directory is used.\n" + " -o/--output-directory DIR Directory to store JSON output. If not set, the current directory is used.\n" +
" --disable-multiversion-api Disables the multi-version client API\n" + " --disable-multiversion-api Disables the multi-version client API\n" +
" --enable-callbacks-on-external-threads Allows callbacks to be called on threads created by the client library.\n" + " --enable-callbacks-on-external-threads Allows callbacks to be called on threads created by the client library.\n" +
" --use-external-client Connect to the server using an external client.\n" + " --use-external-client Connect to the server using an external client.\n" +
" --tests-to-run TEST [TEST ...] List of test names to run.\n" + " --tests-to-run TEST [TEST ...] List of test names to run.\n" +
" -h/--help Print this help message and then quit.\n"; " -h/--help Print this help message and then quit.\n";
System.out.print(usage); System.out.print(usage);
} }
/** /**
* Parses the argument strings into a <code>TesterArgs</code> instance. * Parses the argument strings into a <code>TesterArgs</code> instance.
* This will return <code>null</code> if the args include an argument telling * This will return <code>null</code> if the args include an argument telling
* it to print the help message and it will throw an {@link IllegalArgumentException} * it to print the help message and it will throw an {@link IllegalArgumentException}
* if it can't parse the arguments. * if it can't parse the arguments.
* *
* @param args command-line args * @param args command-line args
* @return built instance or <code>null</code> * @return built instance or <code>null</code>
* @throws IllegalArgumentException if the arguments can't be parsed * @throws IllegalArgumentException if the arguments can't be parsed
*/ */
public static TesterArgs parseArgs(String[] args) { public static TesterArgs parseArgs(String[] args) {
String outputDirectory = ""; String outputDirectory = "";
boolean multiversionApi = true; boolean multiversionApi = true;
boolean callbacksOnExternalThread = false; boolean callbacksOnExternalThread = false;
boolean externalClient = false; boolean externalClient = false;
Subspace subspace = new Subspace(); Subspace subspace = new Subspace();
List<String> testsToRun = new ArrayList<String>(); List<String> testsToRun = new ArrayList<String>();
for (int i = 0; i < args.length; i++) { for (int i = 0; i < args.length; i++) {
String arg = args[i]; String arg = args[i];
if (arg.equals("-o") || arg.equals("--output-directory")) { if (arg.equals("-o") || arg.equals("--output-directory")) {
if (i + 1 < args.length) { if (i + 1 < args.length) {
outputDirectory = args[++i]; outputDirectory = args[++i];
} else { } else {
System.out.println("No output directory specified for argument " + arg + "\n"); System.out.println("No output directory specified for argument " + arg + "\n");
printUsage(); printUsage();
throw new IllegalArgumentException("No output directory specified for argument " + arg); throw new IllegalArgumentException("No output directory specified for argument " + arg);
} }
} else if (arg.equals("--subspace")) { } else if (arg.equals("--subspace")) {
if (i + 1 < args.length) { if (i + 1 < args.length) {
subspace = new Subspace(Tuple.from(args[++i])); subspace = new Subspace(Tuple.from(args[++i]));
} else { } else {
System.out.println("No subspace specified for argument " + arg + "\n"); System.out.println("No subspace specified for argument " + arg + "\n");
printUsage(); printUsage();
throw new IllegalArgumentException("Not subspace specified for argument " + arg); throw new IllegalArgumentException("Not subspace specified for argument " + arg);
} }
} else if (arg.equals("--disable-multiversion-api")) { } else if (arg.equals("--disable-multiversion-api")) {
multiversionApi = false; multiversionApi = false;
} else if (arg.equals("--enable-callbacks-on-external-threads")) { } else if (arg.equals("--enable-callbacks-on-external-threads")) {
callbacksOnExternalThread = true; callbacksOnExternalThread = true;
} else if (arg.equals("--use-external-client")) { } else if (arg.equals("--use-external-client")) {
externalClient = true; externalClient = true;
} else if (arg.equals("--tests-to-run")) { } else if (arg.equals("--tests-to-run")) {
if (i + 1 < args.length && args[i + 1].charAt(0) != '-') { if (i + 1 < args.length && args[i + 1].charAt(0) != '-') {
int j; int j;
for (j = i + 1; j < args.length && args[j].charAt(0) != '-'; j++) { for (j = i + 1; j < args.length && args[j].charAt(0) != '-'; j++) {
testsToRun.add(args[j]); testsToRun.add(args[j]);
} }
i = j - 1; i = j - 1;
} else { } else {
System.out.println("No tests specified with argument " + arg + "\n"); System.out.println("No tests specified with argument " + arg + "\n");
printUsage(); printUsage();
throw new IllegalArgumentException("No tests specified with argument " + arg); throw new IllegalArgumentException("No tests specified with argument " + arg);
} }
} else if (arg.equals("-h") || arg.equals("--help")) { } else if (arg.equals("-h") || arg.equals("--help")) {
printUsage(); printUsage();
return null; return null;
} else { } else {
System.out.println("Unknown argument " + arg + "\n"); System.out.println("Unknown argument " + arg + "\n");
printUsage(); printUsage();
throw new IllegalArgumentException("Unknown argument " + arg); throw new IllegalArgumentException("Unknown argument " + arg);
} }
} }
return new TesterArgs(outputDirectory, multiversionApi, callbacksOnExternalThread, externalClient, subspace, testsToRun); return new TesterArgs(outputDirectory, multiversionApi, callbacksOnExternalThread, externalClient, subspace, testsToRun);
} }
// Accessors. // Accessors.
public String getOutputDirectory() { public String getOutputDirectory() {
return outputDirectory; return outputDirectory;
} }
public boolean useMultiversionApi() { public boolean useMultiversionApi() {
return multiversionApi; return multiversionApi;
} }
public boolean putCallbacksOnExternalThread() { public boolean putCallbacksOnExternalThread() {
return callbacksOnExternalThread; return callbacksOnExternalThread;
} }
public boolean useExternalClient() { public boolean useExternalClient() {
return externalClient; return externalClient;
} }
public Subspace getSubspace() { public Subspace getSubspace() {
return subspace; return subspace;
} }
public List<String> getTestsToRun() { public List<String> getTestsToRun() {
return testsToRun; return testsToRun;
} }
} }

View File

@ -20,7 +20,6 @@
package com.apple.foundationdb.test; package com.apple.foundationdb.test;
import com.apple.foundationdb.Cluster;
import com.apple.foundationdb.Database; import com.apple.foundationdb.Database;
import com.apple.foundationdb.FDB; import com.apple.foundationdb.FDB;
import com.apple.foundationdb.TransactionContext; import com.apple.foundationdb.TransactionContext;

View File

@ -31,9 +31,9 @@ import com.apple.foundationdb.tuple.Tuple;
import com.apple.foundationdb.tuple.Versionstamp; import com.apple.foundationdb.tuple.Versionstamp;
public class VersionstampSmokeTest { public class VersionstampSmokeTest {
public static void main(String[] args) { public static void main(String[] args) {
FDB fdb = FDB.selectAPIVersion(510); FDB fdb = FDB.selectAPIVersion(510);
try(Database db = fdb.open()) { try(Database db = fdb.open()) {
db.run(tr -> { db.run(tr -> {
tr.clear(Tuple.from("prefix").range()); tr.clear(Tuple.from("prefix").range());
return null; return null;
@ -59,7 +59,7 @@ public class VersionstampSmokeTest {
System.out.println(Versionstamp.complete(trVersion)); System.out.println(Versionstamp.complete(trVersion));
assert v.equals(Versionstamp.complete(trVersion)); assert v.equals(Versionstamp.complete(trVersion));
} }
} }
private VersionstampSmokeTest() {} private VersionstampSmokeTest() {}
} }

View File

@ -26,13 +26,13 @@ import java.util.concurrent.atomic.AtomicInteger;
import com.apple.foundationdb.async.AsyncUtil; import com.apple.foundationdb.async.AsyncUtil;
public class WhileTrueTest { public class WhileTrueTest {
public static void main(String[] args) { public static void main(String[] args) {
// This should cause memory issues using the old implementation but not the new one. // This should cause memory issues using the old implementation but not the new one.
// Pro tip: Run with options -Xms16m -Xmx16m -XX:+HeadDumpOnOutOfMemoryError // Pro tip: Run with options -Xms16m -Xmx16m -XX:+HeadDumpOnOutOfMemoryError
AtomicInteger count = new AtomicInteger(1000000); AtomicInteger count = new AtomicInteger(1000000);
AsyncUtil.whileTrue(() -> CompletableFuture.completedFuture(count.decrementAndGet()).thenApplyAsync(c -> c > 0)).join(); AsyncUtil.whileTrue(() -> CompletableFuture.completedFuture(count.decrementAndGet()).thenApplyAsync(c -> c > 0)).join();
System.out.println("Final value: " + count.get()); System.out.println("Final value: " + count.get());
} }
private WhileTrueTest() {} private WhileTrueTest() {}
} }

View File

@ -1,3 +0,0 @@
Complete documentation of the FoundationDB Node.js API can be found at [https://www.foundationdb.org/documentation/api-node.html](https://www.foundationdb.org/documentation/api-node.html).
These bindings require the FoundationDB client. The client can be obtained from [https://www.foundationdb.org/downloads/fdb-c/](https://www.foundationdb.org/downloads/fdb-c/).

View File

@ -1,25 +0,0 @@
{
'targets': [
{
'target_name': 'fdblib',
'sources': [ 'src/FdbV8Wrapper.cpp', 'src/Database.cpp', 'src/Transaction.cpp', 'src/Cluster.cpp', 'src/FdbError.cpp', 'src/FdbOptions.cpp', 'src/FdbOptions.g.cpp', 'src/FdbUtil.cpp' ],
'include_dirs': ['../c'],
'conditions': [
['OS=="linux"', {
'link_settings': { 'libraries': ['-lfdb_c', '-L../../../lib'] },
}],
['OS=="mac"', {
'xcode_settings': {
'MACOSX_DEPLOYMENT_TARGET': '10.7', # -mmacosx-version-min=10.7
'OTHER_CFLAGS': ['-std=c++0x']
},
'link_settings': { 'libraries': ['-lfdb_c', '-L../../../lib'] },
}],
['OS=="win"', {
'link_settings': { 'libraries': ['../../../bin/Release/fdb_c.lib'] },
}],
],
'cflags': ['-std=c++0x'],
}
]
}

View File

@ -1,23 +0,0 @@
{
'targets': [
{
'target_name': 'fdblib',
'sources': [ 'src/FdbV8Wrapper.cpp', 'src/Database.cpp', 'src/Transaction.cpp', 'src/Cluster.cpp', 'src/FdbError.cpp', 'src/FdbOptions.cpp', 'src/FdbOptions.g.cpp', 'src/FdbUtil.cpp' ],
'conditions': [
['OS=="linux"', {
'link_settings': { 'libraries': ['-lfdb_c'] },
}],
['OS=="mac"', {
'xcode_settings': { 'OTHER_CFLAGS': ['-std=c++0x'] },
'include_dirs': ['/usr/local/include'],
'link_settings': { 'libraries': ['-lfdb_c', '-L/usr/local/lib'] },
}],
['OS=="win"', {
'link_settings': { 'libraries': ['<!(echo %FOUNDATIONDB_INSTALL_PATH%)\\lib\\foundationdb\\fdb_c.lib'] },
'include_dirs': ['<!(echo %FOUNDATIONDB_INSTALL_PATH%)\\include'],
}],
],
'cflags': ['-std=c++0x'],
}
]
}

View File

@ -1,28 +0,0 @@
/*
* FoundationDB Node.js API
* Copyright (c) 2012 FoundationDB, LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
"use strict";
var fdb = require('bindings')('fdblib.node');
module.exports = fdb;

View File

@ -1,157 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(SolutionDir)versions.target" />
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{9463CB25-DCA0-9D45-C46E-0A8E68EE7FAE}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>fdblib</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings" />
<ImportGroup Label="PropertySheets">
<Import Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<IgnoreImportLibrary>true</IgnoreImportLibrary>
<IntDir>$(SystemDrive)\temp\msvcfdb\$(Platform)$(Configuration)\$(MSBuildProjectName)\</IntDir>
<LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</LinkIncremental>
<LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</LinkIncremental>
<OutDir>$(SolutionDir)bin\$(Configuration)\</OutDir>
<TargetName>$(ProjectName)</TargetName>
<TargetPath>$(OutDir)$(ProjectName).node</TargetPath>
<TargetExt>.node</TargetExt>
</PropertyGroup>
<ItemDefinitionGroup>
<PreBuildEvent>
<Command>"$(ProgramW6432)\nodejs\node_modules\npm\bin\node-gyp-bin\node-gyp.cmd" install --ensure -v --target=$(NodeVersion)</Command>
</PreBuildEvent>
<PreBuildEvent>
<Message>Download and install node-gyp header and lib files</Message>
</PreBuildEvent>
<PostBuildEvent>
<Command>
mkdir modules
mkdir "modules\$(NodeVersionDir)"
copy "$(TargetPath)" "modules\$(NodeVersionDir)\fdblib.node"
</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<AdditionalIncludeDirectories>$(USERPROFILE)\.node-gyp\$(NodeVersion)\src;$(USERPROFILE)\.node-gyp\$(NodeVersion)\deps\uv\include;$(USERPROFILE)\.node-gyp\$(NodeVersion)\deps\v8\include;..\c;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<BufferSecurityCheck>true</BufferSecurityCheck>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<DisableSpecificWarnings>4251;%(DisableSpecificWarnings)</DisableSpecificWarnings>
<ExceptionHandling>Sync</ExceptionHandling>
<MinimalRebuild>false</MinimalRebuild>
<OmitFramePointers>false</OmitFramePointers>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;BUILDING_V8_SHARED=1;BUILDING_UV_SHARED=1;DEBUG;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<StringPooling>true</StringPooling>
<SuppressStartupBanner>true</SuppressStartupBanner>
<TreatWarningAsError>false</TreatWarningAsError>
<WarningLevel>Level3</WarningLevel>
</ClCompile>
<Link>
<AdditionalDependencies>$(USERPROFILE)\.node-gyp\$(NodeVersion)\$(Platform)\node.lib;..\..\bin\$(Configuration)\fdb_c.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AllowIsolation>true</AllowIsolation>
<DataExecutionPrevention>true</DataExecutionPrevention>
<GenerateDebugInformation>true</GenerateDebugInformation>
<OutputFile>$(OutDir)$(ProjectName).node</OutputFile>
<RandomizedBaseAddress>true</RandomizedBaseAddress>
<SuppressStartupBanner>true</SuppressStartupBanner>
<TargetMachine>MachineX64</TargetMachine>
</Link>
<ResourceCompile>
<AdditionalIncludeDirectories>$(USERPROFILE)\.node-gyp\$(NodeVersion)\src;$(USERPROFILE)\.node-gyp\$(NodeVersion)\deps\uv\include;$(USERPROFILE)\.node-gyp\$(NodeVersion)\deps\v8\include;..\..\c;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;BUILDING_V8_SHARED=1;BUILDING_UV_SHARED=1;DEBUG;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ResourceCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<AdditionalIncludeDirectories>$(USERPROFILE)\.node-gyp\$(NodeVersion)\src;$(USERPROFILE)\.node-gyp\$(NodeVersion)\deps\uv\include;$(USERPROFILE)\.node-gyp\$(NodeVersion)\deps\v8\include;..\c;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalOptions>/MP %(AdditionalOptions)</AdditionalOptions>
<BufferSecurityCheck>true</BufferSecurityCheck>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<DisableSpecificWarnings>4251;%(DisableSpecificWarnings)</DisableSpecificWarnings>
<ExceptionHandling>false</ExceptionHandling>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<FunctionLevelLinking>true</FunctionLevelLinking>
<InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
<IntrinsicFunctions>true</IntrinsicFunctions>
<OmitFramePointers>true</OmitFramePointers>
<Optimization>Full</Optimization>
<PreprocessorDefinitions>WIN32;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;BUILDING_V8_SHARED=1;BUILDING_UV_SHARED=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<StringPooling>true</StringPooling>
<SuppressStartupBanner>true</SuppressStartupBanner>
<TreatWarningAsError>false</TreatWarningAsError>
<WarningLevel>Level3</WarningLevel>
<WholeProgramOptimization>true</WholeProgramOptimization>
</ClCompile>
<Lib>
<AdditionalOptions>/LTCG %(AdditionalOptions)</AdditionalOptions>
</Lib>
<Link>
<AdditionalDependencies>$(USERPROFILE)\.node-gyp\$(NodeVersion)\$(Platform)\node.lib;..\..\bin\$(Configuration)\fdb_c.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AllowIsolation>true</AllowIsolation>
<DataExecutionPrevention>true</DataExecutionPrevention>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<GenerateDebugInformation>true</GenerateDebugInformation>
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
<OptimizeReferences>true</OptimizeReferences>
<OutputFile>$(OutDir)$(ProjectName).node</OutputFile>
<RandomizedBaseAddress>true</RandomizedBaseAddress>
<SuppressStartupBanner>true</SuppressStartupBanner>
<TargetMachine>MachineX64</TargetMachine>
</Link>
<ResourceCompile>
<AdditionalIncludeDirectories>$(USERPROFILE)\.node-gyp\$(NodeVersion)\src;$(USERPROFILE)\.node-gyp\$(NodeVersion)\deps\uv\include;$(USERPROFILE)\.node-gyp\$(NodeVersion)\deps\v8\include;..\..\c;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;BUILDING_V8_SHARED=1;BUILDING_UV_SHARED=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ResourceCompile>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="src\FdbOptions.g.cpp" />
<ClCompile Include="src\Database.cpp" />
<ClCompile Include="src\Transaction.cpp" />
<ClCompile Include="src\FdbError.cpp" />
<ClCompile Include="src\Cluster.cpp" />
<ClCompile Include="src\FdbV8Wrapper.cpp" />
<ClCompile Include="src\FdbOptions.cpp" />
<ClCompile Include="src\FdbUtil.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\Cluster.h" />
<ClInclude Include="src\Database.h" />
<ClInclude Include="src\FdbError.h" />
<ClInclude Include="src\FdbOptions.h" />
<ClInclude Include="src\FdbUtil.h" />
<ClInclude Include="src\NodeCallback.h" />
<ClInclude Include="src\Transaction.h" />
<ClInclude Include="src\Version.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets" />
<Target Name="AfterClean">
<Message Text="Cleaning $(NodeVersionDir)\fdblib module" Importance="high" />
<Delete Files="modules\$(NodeVersionDir)\fdblib.node" />
</Target>
</Project>

View File

@ -1,64 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- Load version information from the central versions file, creating -PRERELEASE if needed -->
<Import Project="$(SolutionDir)versions.target" />
<PropertyGroup Condition="'$(Release)' != 'true' ">
<PreReleaseDecoration>-PRERELEASE</PreReleaseDecoration>
</PropertyGroup>
<PropertyGroup Condition="'$(Release)' == 'true' ">
<PreReleaseDecoration>
</PreReleaseDecoration>
</PropertyGroup>
<!-- Set up VS minimal configuation -->
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{9463CB25-DCA0-9D45-C46E-0A8E68EE7FAE}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<PlatformToolset>v140_xp</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<PropertyGroup>
<IntDir>$(SystemDrive)\temp\msvcfdb\$(Platform)$(Configuration)\$(MSBuildProjectName)\</IntDir>
</PropertyGroup>
<!-- Meat of the work to create proper node output. -->
<ItemDefinitionGroup>
<!-- Clean up before a build -->
<PreBuildEvent>
<Command>del "$(SolutionDir)bin\$(Configuration)\fdb-node*.tar.gz"</Command>
</PreBuildEvent>
<PostBuildEvent>
<!-- After build, create package.json from input -->
<Command>
c:\python27\python -c "print open(\"package.json.in\").read().replace(\"VERSION\",\"$(Version)$(PreReleaseDecoration)\")" &gt; package.json
copy ..\..\LICENSE
c:\python27\python "$(SolutionDir)build/tarball.py" -r nodejs "$(SolutionDir)bin\$(Configuration)\fdb-node-$(Version)$(PreReleaseDecoration)-windows-$(Platform).tar.gz" lib modules package.json README.md LICENSE
del LICENSE</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<Target Name="AfterClean">
<!-- Add output of which VS was not aware to "clean" step -->
<ItemGroup>
<FilesToDelete Include="$(ProjectDir)package.json">
<Visible>false</Visible>
</FilesToDelete>
<FilesToDelete Include="$(SolutionDir)bin\$(Configuration)\fdb-node-$(Version)$(PreReleaseDecoration)-windows-$(Platform).tar.gz">
<Visible>false</Visible>
</FilesToDelete>
</ItemGroup>
<Message Text="Cleaning package.json and tar.gz file" Importance="high" />
<Delete Files="@(FilesToDelete)" />
</Target>
</Project>

View File

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Label="Globals">
<ProjectGuid>{E22D4EF8-E75D-4281-93F9-A9F73936DE54}</ProjectGuid>
</PropertyGroup>
<PropertyGroup Label="Configuration">
<PlatformToolset>v140_xp</PlatformToolset>
</PropertyGroup>
<PropertyGroup>
<NodeVersion>0.10.0</NodeVersion>
<NodeVersionDir>0.10</NodeVersionDir>
</PropertyGroup>
<Import Project="fdb_node.target" />
</Project>

View File

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Label="Globals">
<ProjectGuid>{E936E200-689E-49FD-8463-32FE763F1860}</ProjectGuid>
</PropertyGroup>
<PropertyGroup Label="Configuration">
<PlatformToolset>v140_xp</PlatformToolset>
</PropertyGroup>
<PropertyGroup>
<NodeVersion>0.8.22</NodeVersion>
<NodeVersionDir>0.8</NodeVersionDir>
</PropertyGroup>
<Import Project="fdb_node.target" />
</Project>

View File

@ -1,99 +0,0 @@
#
# include.mk
#
# This source file is part of the FoundationDB open source project
#
# Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# -*- mode: makefile-gmake; -*-
TARGETS += fdb_node fdb_node_npm
CLEAN_TARGETS += fdb_node_clean fdb_node_npm_clean
NODE_VERSIONS := 0.8.22 0.10.0
NODE_DIST_URL ?= https://nodejs.org/dist
NODE_REGISTRY_URL ?= https://registry.npmjs.org/
ifeq ($(RELEASE),true)
NPMVER = $(VERSION)
else
NPMVER = $(VERSION)-PRERELEASE
endif
packages: fdb_node_npm
packages_clean: fdb_node_npm_clean
fdb_node: fdb_c bindings/nodejs/fdb_node.stamp
bindings/nodejs/fdb_node.stamp: bindings/nodejs/src/FdbOptions.g.cpp bindings/nodejs/src/*.cpp bindings/nodejs/src/*.h bindings/nodejs/binding.gyp lib/libfdb_c.$(DLEXT) bindings/nodejs/package.json
@echo "Building $@"
@rm -f $@
@cd bindings/nodejs && \
mkdir -p modules && \
rm -rf modules/* && \
for ver in $(NODE_VERSIONS); do \
MMVER=`echo $$ver | sed -e 's,\., ,g' | awk '{print $$1 "." $$2}'` && \
mkdir modules/$$MMVER && \
node-gyp configure --dist-url=$(NODE_DIST_URL) --target=$$ver && \
node-gyp -v build && \
cp build/Release/fdblib.node modules/$${MMVER} ; \
done
@touch $@
bindings/nodejs/src/FdbOptions.g.cpp: bin/vexillographer.exe fdbclient/vexillographer/fdb.options
@echo "Building $@"
@$(MONO) bin/vexillographer.exe fdbclient/vexillographer/fdb.options nodejs $@
fdb_node_clean:
@echo "Cleaning fdb_node"
@rm -f bindings/nodejs/src/FdbOptions.g.cpp
@rm -rf bindings/nodejs/modules
@(cd bindings/nodejs && node-gyp clean)
@rm -f bindings/nodejs/fdb_node.stamp
bindings/nodejs/package.json: bindings/nodejs/package.json.in $(ALL_MAKEFILES) versions.target
@m4 -DVERSION=$(NPMVER) $< > $@
@echo "Updating Node dependencies"
@cd bindings/nodejs && \
npm config set registry "$(NODE_REGISTRY_URL)" && \
npm update
fdb_node_npm: fdb_node versions.target bindings/nodejs/README.md bindings/nodejs/lib/*.js bindings/nodejs/src/* bindings/nodejs/binding.gyp LICENSE
@echo "Packaging NPM"
@mkdir -p packages
@rm -f packages/fdb-node-*
@rm -rf packages/nodejs.tmp
@mkdir -p packages/nodejs.tmp/nodejs
@cp LICENSE packages/nodejs.tmp/nodejs/LICENSE
@tar -C bindings -czf packages/fdb-node-$(NPMVER)-$(PLATFORM)-$(ARCH).tar.gz nodejs/lib nodejs/modules nodejs/package.json nodejs/README.md -C ../packages/nodejs.tmp nodejs/LICENSE
@rm -rf packages/nodejs.tmp
ifeq ($(PLATFORM),linux)
@echo "Packaging NPM (unbuilt)"
@rm -rf packages/nodejs.tmp
@mkdir -p packages/nodejs.tmp/npmsrc/nodejs
@cat bindings/nodejs/package.json | grep -v private | grep -v engineStrict | awk '/"semver"/ {print " \"bindings\": \"*\""; next} {print}' > packages/nodejs.tmp/npmsrc/nodejs/package.json
@cp -r bindings/nodejs/lib bindings/nodejs/src bindings/nodejs/README.md LICENSE packages/nodejs.tmp/npmsrc/nodejs
@cp bindings/nodejs/binding.gyp.npmsrc packages/nodejs.tmp/npmsrc/nodejs/binding.gyp
@cp bindings/nodejs/fdbModule.js.npmsrc packages/nodejs.tmp/npmsrc/nodejs/lib/fdbModule.js
@tar -C packages/nodejs.tmp/npmsrc -czf packages/fdb-node-$(NPMVER).tar.gz nodejs
@rm -rf packages/nodejs.tmp
endif
fdb_node_npm_clean:
@echo "Cleaning NPM"
@rm -f packages/fdb-node-* bindings/nodejs/package.json

View File

@ -1,26 +0,0 @@
/*
* apiVersion.js
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
"use strict";
var apiVersion;
module.exports = { value: apiVersion };

View File

@ -1,86 +0,0 @@
/*
* bufferConversion.js
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
"use strict";
var toBuffer = function(obj) {
if(Buffer.isBuffer(obj))
return obj;
if(obj instanceof ArrayBuffer)
obj = new Uint8Array(obj);
if(obj instanceof Uint8Array) {
var buf = new Buffer(obj.length);
for(var i = 0; i < obj.length; ++i)
buf[i] = obj[i];
return buf;
}
if(typeof obj === 'string')
return new Buffer(obj, 'utf8');
throw new TypeError('toBuffer function expects a string, buffer, ArrayBuffer, or Uint8Array');
};
toBuffer.fromByteLiteral = function(str) {
if(typeof str === 'string') {
var buf = new Buffer(str.length);
for(var i = 0; i < str.length; ++i) {
if(str[i] > 255)
throw new RangeError('fromByteLiteral string argument cannot have codepoints larger than 1 byte');
buf[i] = str.charCodeAt(i);
}
return buf;
}
else
throw new TypeError('fromByteLiteral function expects a string');
};
toBuffer.toByteLiteral = function(buf) {
if(Buffer.isBuffer(buf))
return String.fromCharCode.apply(null, buf);
else
throw new TypeError('toByteLiteral function expects a buffer');
};
toBuffer.printable = function(buf) {
buf = toBuffer(buf);
var out = '';
for(var i = 0; i < buf.length; ++i) {
if(buf[i] >= 32 && buf[i] < 127 && buf[i] !== 92)
out += String.fromCharCode(buf[i]);
else if(buf[i] === 92)
out += '\\\\';
else {
var str = buf[i].toString(16);
out += '\\x';
if(str.length == 1)
out += '0';
out += str;
}
}
return out;
};
module.exports = toBuffer;

View File

@ -1,38 +0,0 @@
/*
* cluster.js
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
"use strict";
var future = require('./future');
var Database = require('./database');
var Cluster = function(_cluster) {
this._cluster = _cluster;
this.options = _cluster.options;
};
Cluster.prototype.openDatabase = function() {
return new Database(this._cluster.openDatabase());
};
module.exports = Cluster;

View File

@ -1,172 +0,0 @@
/*
* database.js
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
"use strict";
var Transaction = require('./transaction');
var future = require('./future');
var fdb = require('./fdbModule');
var fdbUtil = require('./fdbUtil');
var onError = function(tr, err, func, cb) {
tr.onError(err, function(retryErr, retryRes) {
if(retryErr)
cb(retryErr, retryRes);
else
retryLoop(tr, func, cb);
});
};
var retryLoop = function(tr, func, cb) {
func(tr, function(err, res) {
if(err) {
onError(tr, err, func, cb);
}
else {
tr.commit(function(commitErr, commitRes) {
if(commitErr)
onError(tr, commitErr, func, cb);
else
cb(commitErr, res);
});
}
});
};
var atomic = function(db, op) {
return function(key, value, cb) {
return db.doTransaction(function(tr, innerCb) {
fdb.atomic[op].call(tr.tr, fdbUtil.keyToBuffer(key), fdbUtil.valueToBuffer(value));
innerCb();
}, cb);
};
};
var Database = function(_db) {
this._db = _db;
this.options = _db.options;
for(var op in fdb.atomic)
this[op] = atomic(this, op);
};
Database.prototype.createTransaction = function() {
return new Transaction(this, this._db.createTransaction());
};
Database.prototype.doTransaction = function(func, cb) {
var tr = this.createTransaction();
return future.create(function(futureCb) {
retryLoop(tr, func, futureCb);
}, cb);
};
Database.prototype.get = function(key, cb) {
return this.doTransaction(function(tr, innerCb) {
tr.get(key, innerCb);
}, cb);
};
Database.prototype.getKey = function(keySelector, cb) {
return this.doTransaction(function(tr, innerCb) {
tr.getKey(keySelector, innerCb);
}, cb);
};
Database.prototype.getRange = function(start, end, options, cb) {
return this.doTransaction(function(tr, innerCb) {
tr.getRange(start, end, options).toArray(innerCb);
}, cb);
};
Database.prototype.getRangeStartsWith = function(prefix, options, cb) {
return this.doTransaction(function(tr, innerCb) {
try {
tr.getRangeStartsWith(prefix, options).toArray(innerCb);
}
catch(e) {
innerCb(e);
}
}, cb);
};
Database.prototype.getAndWatch = function(key, cb) {
return this.doTransaction(function(tr, innerCb) {
tr.get(key, function(err, val) {
if(err)
innerCb(err);
else
innerCb(undefined, { value: val, watch: tr.watch(key) });
});
}, cb);
};
Database.prototype.setAndWatch = function(key, value, cb) {
return this.doTransaction(function(tr, innerCb) {
tr.set(key, value);
var watchObj = tr.watch(key);
innerCb(undefined, { watch: watchObj });
}, cb);
};
Database.prototype.clearAndWatch = function(key, cb) {
return this.doTransaction(function(tr, innerCb) {
tr.clear(key);
var watchObj = tr.watch(key);
innerCb(undefined, { watch: watchObj });
}, cb);
};
Database.prototype.set = function(key, value, cb) {
return this.doTransaction(function(tr, innerCb) {
tr.set(key, value);
innerCb();
}, cb);
};
Database.prototype.clear = function(key, cb) {
return this.doTransaction(function(tr, innerCb) {
tr.clear(key);
innerCb();
}, cb);
};
Database.prototype.clearRange = function(start, end, cb) {
return this.doTransaction(function(tr, innerCb) {
tr.clearRange(start, end);
innerCb();
}, cb);
};
Database.prototype.clearRangeStartsWith = function(prefix, cb) {
return this.doTransaction(function(tr, innerCb) {
try {
tr.clearRangeStartsWith(prefix);
innerCb();
}
catch(e) {
innerCb(e);
}
}, cb);
};
module.exports = Database;

View File

@ -1,850 +0,0 @@
/*
* directory.js
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
"use strict";
var util = require('util');
var buffer = require('./bufferConversion');
var future = require('./future');
var transactional = require('./retryDecorator');
var tuple = require('./tuple');
var Subspace = require('./subspace');
var fdbUtil = require('./fdbUtil');
/*************
* Utilities *
*************/
function whileLoop(func, cb) {
return future.create(function(futureCb) {
fdbUtil.whileLoop(function(f) {
func()(f);
}, futureCb);
}, cb);
}
function startsWith(str, prefix) {
return str.length >= prefix.length && fdbUtil.buffersEqual(str.slice(0, prefix.length), prefix);
}
function valueOrDefault(value, defaultValue) {
if(typeof value === 'undefined')
return defaultValue;
return value;
}
function pathsEqual(path1, path2) {
if(path1.length !== path2.length)
return false;
for(var i = 0; i < path1.length; ++i)
if(path1[i] !== path2[i])
return false;
return true;
}
function tuplifyPath(path) {
if(!(path instanceof Array))
path = [path];
return path;
}
function checkLayer(layer, required) {
if(layer && layer.length > 0 && !fdbUtil.buffersEqual(layer, required))
throw new Error('The directory was created with an incompatible layer.');
}
/***************************
* HighContentionAllocator *
***************************/
var HighContentionAllocator = function(subspace) {
this.counters = subspace.subspace([0]);
this.recent = subspace.subspace([1]);
};
HighContentionAllocator.prototype.allocate = transactional(function(tr) {
var self = this;
var windowStart = 0;
return whileLoop(function() {
return tr.snapshot.getRange(self.counters.range().begin, self.counters.range().end, { limit: 1, reverse: true })
.toArray()
.then(function(arr) {
if(arr.length > 0) {
windowStart = self.counters.unpack(arr[0].key)[0];
}
})
.then(function() {
return self.chooseWindow(tr, windowStart);
})
.then(function(window) {
return self.choosePrefix(tr, window);
})
.then(function(prefix) {
if(prefix !== null) {
prefix = tuple.pack([prefix]); // exit the loop
return prefix;
}
});
})
});
HighContentionAllocator.prototype.chooseWindow = function(tr, windowStart) {
var self = this;
var increment = new Buffer(8);
increment.fill(0);
increment.writeUInt32LE(1, 0);
var window = { start: windowStart, size: 0 };
return whileLoop(function() {
// Cannot yield to event loop in this block {
if(window.start > windowStart) {
tr.clearRange(self.counters, self.counters.get(window.start));
tr.options.setNextWriteNoWriteConflictRange();
tr.clearRange(self.recent, self.recent.get(window.start));
}
tr.add(self.counters.pack([window.start]), increment);
return tr.snapshot.get(self.counters.get(window.start))
// }
.then(function(newCountBuffer) {
var newCount = (newCountBuffer === null) ? 0 : newCountBuffer.readUInt32LE(0);
window.size = windowSize(window.start);
if(newCount * 2 < window.size) {
return window; // exit the loop
}
window.start += window.size;
});
});
};
HighContentionAllocator.prototype.choosePrefix = function(tr, window) {
var self = this;
return whileLoop(function() {
var candidate = Math.floor(Math.random() * window.size) + window.start;
var allocationKey = self.recent.pack([candidate]);
// Cannot yield to event loop in this block {
var counterRange = tr.snapshot.getRange(self.counters.range().begin, self.counters.range().end, { limit: 1, reverse: true }).toArray();
var allocation = tr.get(allocationKey);
tr.options.setNextWriteNoWriteConflictRange();
tr.set(allocationKey, buffer(''));
// }
return future.all([counterRange, allocation])
.then(function(vals) {
var currentWindowStart = vals[0].length > 0 ? self.counters.unpack(vals[0][0].key)[0] : 0;
if(currentWindowStart > window.start) {
return null; // exit the loop and force find() to retry
}
if(vals[1] === null) {
tr.addWriteConflictKey(allocationKey);
return candidate; // exit the loop
}
});
});
};
function windowSize(start) {
if(start < 255)
return 64;
if(start < 65535)
return 1024;
return 8192;
}
/******************
* DirectoryLayer *
******************/
var VERSION = [1, 0, 0];
var SUBDIRS = 0;
var DirectoryLayer = function(options) {
options = valueOrDefault(options, {});
this._nodeSubspace = valueOrDefault(options.nodeSubspace, new Subspace([], buffer.fromByteLiteral('\xfe')));
// If specified, new automatically allocated prefixes will all fall within the contentSubspace
this._contentSubspace = valueOrDefault(options.contentSubspace, new Subspace());
this._allowManualPrefixes = valueOrDefault(options.allowManualPrefixes, false);
this._rootNode = this._nodeSubspace.subspace([this._nodeSubspace.key()]);
this._allocator = new HighContentionAllocator(this._rootNode.subspace([buffer('hca')]));
this._path = [];
};
var createOrOpen = transactional(function(tr, self, path, options, allowCreate, allowOpen, cb) {
options = valueOrDefault(options, {});
var layer = valueOrDefault(options.layer, buffer(''));
var prefix = options.prefix;
allowCreate = valueOrDefault(allowCreate, true);
allowOpen = valueOrDefault(allowOpen, true);
return checkVersion(self, tr, false)
.then(function() {
if(typeof prefix !== 'undefined') {
if(allowCreate && allowOpen)
throw new Error('Cannot specify a prefix when calling create_or_open.');
else if(!self._allowManualPrefixes) {
if(self._path.length === 0)
throw new Error('Cannot specify a prefix unless manual prefixes are enabled.');
else
throw new Error('Cannot specify a prefix in a partition.');
}
}
path = toUnicodePath(path);
if(path.length === 0)
throw new Error('The root directory cannot be opened.');
return find(self, tr, path).then(loadMetadata(tr));
})
.then(function(existingNode) {
if(existingNode.exists()) {
if(existingNode.isInPartition(false)) {
var subpath = existingNode.getPartitionSubpath();
var directoryLayer = existingNode.getContents(self)._directoryLayer;
return createOrOpen(tr,
existingNode.getContents(self)._directoryLayer,
subpath,
options,
allowCreate,
allowOpen);
}
return openDirectory(tr, self, path, layer, existingNode, allowOpen);
}
else
return createDirectory(tr, self, path, layer, prefix, allowCreate);
})(cb);
});
var openDirectory = function(tr, self, path, layer, existingNode, allowOpen) {
if(!allowOpen)
throw new Error('The directory already exists.');
checkLayer(layer, existingNode.layer);
return existingNode.getContents(self);
};
var createDirectory = function(tr, self, path, layer, prefix, allowCreate) {
if(!allowCreate)
throw new Error('The directory does not exist.');
var prefixIsAllocated = typeof(prefix) === 'undefined';
return checkVersion(self, tr, true)
.then(function() {
return getPrefix(self, tr, prefix);
})
.then(function(prefix) {
return isPrefixFree(self, prefixIsAllocated ? tr.snapshot : tr, prefix)
.then(function(isFree) {
if(!isFree) {
if(prefixIsAllocated)
throw new Error('The directory layer has manually allocated prefixes that conflict with the automatic prefix allocator.');
else
throw new Error('The given prefix is already in use.');
}
return getParentNode(self, tr, path);
})
.then(function(parentNode) {
if(!parentNode)
throw new Error('The parent directory doesn\'t exist.');
var node = nodeWithPrefix(self, prefix);
tr.set(parentNode.subspace([SUBDIRS]).pack([path[path.length-1]]), prefix);
tr.set(node.pack([buffer('layer')]), layer);
return contentsOfNode(self, node, path, layer);
});
});
};
DirectoryLayer.prototype.getLayer = function() {
return new Buffer(0);
};
DirectoryLayer.prototype.getPath = function() {
return this._path.slice(0);
};
DirectoryLayer.prototype.createOrOpen = function(databaseOrTransaction, path, options, cb) {
return createOrOpen(databaseOrTransaction, this, path, options, true, true, cb);
};
DirectoryLayer.prototype.open = function(databaseOrTransaction, path, options, cb) {
return createOrOpen(databaseOrTransaction, this, path, options, false, true, cb);
};
DirectoryLayer.prototype.create = function(databaseOrTransaction, path, options, cb) {
return createOrOpen(databaseOrTransaction, this, path, options, true, false, cb);
};
DirectoryLayer.prototype.moveTo = function(databaseOrTransaction, newAbsolutePath, cb) {
return future.reject(new Error('The root directory cannot be moved.'))(cb);
};
DirectoryLayer.prototype.move = transactional(function(tr, oldPath, newPath, cb) {
var self = this;
var oldNode, newNode;
return checkVersion(self, tr, true)
.then(function() {
oldPath = toUnicodePath(oldPath);
newPath = toUnicodePath(newPath);
if(pathsEqual(oldPath, newPath.slice(0, oldPath.length)))
throw new Error('The destination directory cannot be a subdirectory of the source directory.');
var oldNodeFuture = find(self, tr, oldPath).then(loadMetadata(tr));
var newNodeFuture = find(self, tr, newPath).then(loadMetadata(tr));
return future.all([oldNodeFuture, newNodeFuture]);
})
.then(function(nodes) {
oldNode = nodes[0];
newNode = nodes[1];
if(!oldNode.exists())
throw new Error('The source directory does not exist.');
if(oldNode.isInPartition(false) || newNode.isInPartition(false)) {
if(!oldNode.isInPartition(false) || !newNode.isInPartition(false) || !pathsEqual(oldNode.path, newNode.path))
throw new Error('Cannot move between partitions.');
return newNode.getContents(self).move(tr, oldNode.getPartitionSubpath(), newNode.getPartitionSubpath());
}
if(newNode.exists())
throw new Error('The destination directory already exists. Remove it first.');
return find(self, tr, newPath.slice(0, newPath.length-1))
.then(function(parentNode) {
if(!parentNode.exists())
throw new Error('The parent of the destination directory does not exist. Create it first.');
tr.set(parentNode.subspace.subspace([SUBDIRS]).pack([newPath[newPath.length-1]]),
self._nodeSubspace.unpack(oldNode.subspace.key())[0]);
return removeFromParent(self, tr, oldPath);
})
.then(function() {
return contentsOfNode(self, oldNode.subspace, newPath, oldNode.layer);
});
})(cb);
});
DirectoryLayer.prototype.remove = transactional(function(tr, path, cb) {
return removeInternal(this, tr, path, true)(cb);
});
DirectoryLayer.prototype.removeIfExists = transactional(function(tr, path, cb) {
return removeInternal(this, tr, path, false)(cb);
});
function removeInternal(self, tr, path, failOnNonexistent) {
return checkVersion(self, tr, true)
.then(function() {
path = valueOrDefault(path, []);
if(path.length === 0)
return future.reject(new Error('The root directory cannot be removed.'));
path = toUnicodePath(path);
return find(self, tr, path).then(loadMetadata(tr));
})
.then(function(node) {
if(!node.exists()) {
if(failOnNonexistent)
throw new Error('The directory doesn\'t exist');
else
return false;
}
if(node.isInPartition(false)) {
return removeInternal(node.getContents(self)._directoryLayer,
tr,
node.getPartitionSubpath(),
failOnNonexistent);
}
return removeRecursive(self, tr, node.subspace)
.then(function() {
return removeFromParent(self, tr, path);
}).
then(function() {
return true;
});
});
}
DirectoryLayer.prototype.list = transactional(function(tr, path, cb) {
var self = this;
return checkVersion(self, tr, false)
.then(function() {
path = valueOrDefault(path, []);
path = toUnicodePath(path);
return find(self, tr, path).then(loadMetadata(tr));
})
.then(function(node) {
if(!node.exists())
throw new Error('The given directory does not exist');
if(node.isInPartition(true))
return node.getContents(self).list(tr, node.getPartitionSubpath());
var subdir = node.subspace.subspace([SUBDIRS]);
return tr.getRange(subdir.range().begin, subdir.range().end).toArray()
.then(function(arr) {
return arr.map(function(kv) { return subdir.unpack(kv.key)[0].toString('utf8'); });
});
})(cb);
});
DirectoryLayer.prototype.exists = transactional(function(tr, path, cb) {
var self = this;
return checkVersion(self, tr, false)
.then(function() {
path = valueOrDefault(path, []);
path = toUnicodePath(path);
return find(self, tr, path).then(loadMetadata(tr));
})
.then(function(node) {
if(!node.exists())
return false;
if(node.isInPartition(false))
return node.getContents(self).exists(tr, node.getPartitionSubpath());
return true;
})(cb);
});
// Private functions:
function checkVersion(self, tr, writeAccess) {
return tr.get(self._rootNode.pack([buffer('version')]))
.then(function(versionBuf) {
if(!versionBuf) {
if(writeAccess)
initializeDirectory(self, tr);
return;
}
var version = [];
for(var i = 0; i < 3; ++i)
version.push(versionBuf.readInt32LE(4*i));
var dirVersion = util.format('%d.%d.%d', version[0], version[1], version[2]);
var layerVersion = util.format('%d.%d.%d', VERSION[0], VERSION[1], VERSION[2]);
if(version[0] > VERSION[0]) {
throw new Error(util.format('Cannot load directory with version %s using directory layer %s',
dirVersion,
layerVersion));
}
if(version[1] > VERSION[1]) {
throw new Error(util.format('Directory with version %s is read-only when opened using directory layer %s',
dirVersion,
layerVersion));
}
});
}
function initializeDirectory(self, tr) {
var versionBuf = new Buffer(12);
for(var i = 0; i < 3; ++i)
versionBuf.writeUInt32LE(VERSION[i], i*4);
tr.set(self._rootNode.pack([buffer('version')]), versionBuf);
}
function nodeWithPrefix(self, prefix) {
if(prefix === null)
return null;
return self._nodeSubspace.subspace([prefix]);
}
function find(self, tr, path) {
var pathIndex = 0;
var node = new Node(self._rootNode, [], path);
return whileLoop(function() {
if(pathIndex === path.length)
return future.resolve(node);
return tr.get(node.subspace.subspace([SUBDIRS]).pack([path[pathIndex++]]))
.then(function(val) {
node = new Node(nodeWithPrefix(self, val), path.slice(0, pathIndex), path);
if(!node.exists())
return node;
return node.loadMetadata(tr)
.then(function() {
if(fdbUtil.buffersEqual(node.layer, buffer('partition')))
return node;
});
});
});
}
function contentsOfNode(self, node, path, layer) {
var prefix = self._nodeSubspace.unpack(node.key())[0];
if(fdbUtil.buffersEqual(layer, buffer('partition')))
return new DirectoryPartition(self._path.concat(path), prefix, self);
else
return new DirectorySubspace(self._path.concat(path), prefix, self, layer);
}
function getPrefix(self, tr, prefix) {
if(typeof prefix === 'undefined') {
return self._allocator.allocate(tr)
.then(function(prefix) {
var allocated = Buffer.concat([self._contentSubspace.key(), prefix], self._contentSubspace.key().length + prefix.length);
return tr.getRangeStartsWith(allocated, { limit: 1 })
.toArray()
.then(function(arr) {
if(arr.length > 0)
throw new Error('The database has keys stored at the prefix chosen by the automatic prefix allocator: ' + prefix);
return allocated;
});
});
}
else
return future.resolve(buffer(prefix));
}
function getNodeContainingKey(self, tr, key) {
if(self._nodeSubspace.contains(key))
return future.resolve(self._rootNode);
return tr.getRange(self._nodeSubspace.range([]).begin,
self._nodeSubspace.subspace([key]).range().begin,
{ limit: 1, reverse: true })
.toArray()
.then(function(arr) {
if(arr.length > 0) {
var prevPrefix = self._nodeSubspace.unpack(arr[0].key)[0];
if(startsWith(key, prevPrefix))
return nodeWithPrefix(self, prevPrefix);
}
return null;
});
}
function isPrefixFree(self, tr, prefix) {
if(!prefix || prefix.length === 0)
return future.resolve(false);
return getNodeContainingKey(self, tr, prefix)
.then(function(node) {
if(node)
return false;
return tr.getRange(self._nodeSubspace.pack([prefix]),
self._nodeSubspace.pack([fdbUtil.strinc(prefix)]),
{ limit: 1 })
.toArray()
.then(function(arr) {
return arr.length === 0;
});
});
}
function getParentNode(self, tr, path) {
if(path.length > 1) {
return self.createOrOpen(tr, path.slice(0, path.length-1))
.then(function(dir) {
return nodeWithPrefix(self, dir.key());
});
}
else
return future.resolve(self._rootNode);
}
function removeFromParent(self, tr, path) {
return find(self, tr, path.slice(0, path.length-1))
.then(function(parentNode) {
tr.clear(parentNode.subspace.subspace([SUBDIRS]).pack([path[path.length-1]]));
});
}
function removeRecursive(self, tr, node) {
var subdir = node.subspace([SUBDIRS]);
return tr.getRange(subdir.range().begin, subdir.range().end)
.forEach(function(kv, loopCb) {
removeRecursive(self, tr, nodeWithPrefix(self, kv.value))(loopCb);
})
.then(function() {
tr.clearRangeStartsWith(self._nodeSubspace.unpack(node.key())[0]);
tr.clearRange(node.range().begin, node.range().end);
});
}
function toUnicodePath(path) {
if(Buffer.isBuffer(path) || path instanceof ArrayBuffer || path instanceof Uint8Array)
path = buffer(path).toString('utf8');
if(typeof path === 'string')
return [path];
if(path instanceof Array) {
for(var i = 0; i < path.length; ++i) {
if(Buffer.isBuffer(path[i]) || path[i] instanceof ArrayBuffer || path[i] instanceof Uint8Array)
path[i] = buffer(path[i]).toString('utf8');
if(typeof path[i] !== 'string')
throw new TypeError('Invalid path: must be a string, Buffer, ArrayBuffer, Uint8Array, or an array of such items');
}
return path;
}
throw new TypeError('Invalid path: must be a string, Buffer, ArrayBuffer, Uint8Array, or an array of such items');
}
/*********************
* DirectorySubspace *
*********************/
var DirectorySubspace = function(path, prefix, directoryLayer, layer) {
Subspace.call(this, undefined, prefix);
this._path = path;
this._directoryLayer = directoryLayer;
this._layer = layer;
};
DirectorySubspace.prototype = new Subspace();
DirectorySubspace.constructor = DirectorySubspace;
DirectorySubspace.prototype.getLayer = function() {
return this._layer;
};
DirectorySubspace.prototype.getPath = function() {
return this._path.slice(0);
};
DirectorySubspace.prototype.createOrOpen = function(databaseOrTransaction, nameOrPath, options, cb) {
var path = tuplifyPath(nameOrPath);
return this._directoryLayer.createOrOpen(databaseOrTransaction, partitionSubpath(this, path), options, cb);
};
DirectorySubspace.prototype.open = function(databaseOrTransaction, nameOrPath, options, cb) {
var path = tuplifyPath(nameOrPath);
return this._directoryLayer.open(databaseOrTransaction, partitionSubpath(this, path), options, cb);
};
DirectorySubspace.prototype.create = function(databaseOrTransaction, nameOrPath, options, cb) {
var path = tuplifyPath(nameOrPath);
return this._directoryLayer.create(databaseOrTransaction, partitionSubpath(this, path), options, cb);
};
DirectorySubspace.prototype.list = function(databaseOrTransaction, nameOrPath, cb) {
var path = tuplifyPath(valueOrDefault(nameOrPath, []));
return this._directoryLayer.list(databaseOrTransaction, partitionSubpath(this, path), cb);
};
DirectorySubspace.prototype.move = function(databaseOrTransaction, oldNameOrPath, newNameOrPath, cb) {
var oldPath = tuplifyPath(oldNameOrPath);
var newPath = tuplifyPath(newNameOrPath);
return this._directoryLayer.move(databaseOrTransaction,
partitionSubpath(this, oldPath),
partitionSubpath(this, newPath),
cb);
};
DirectorySubspace.prototype.moveTo = function(databaseOrTransaction, newAbsoluteNameOrPath, cb) {
var directoryLayer;
var newAbsolutePath;
try {
directoryLayer = getLayerForPath(this, []);
newAbsolutePath = toUnicodePath(newAbsoluteNameOrPath);
var partitionPath = newAbsolutePath.slice(0, directoryLayer._path.length);
if(!pathsEqual(partitionPath, directoryLayer._path))
throw new Error('Cannot move between partitions.');
}
catch(err) {
return future.reject(err)(cb);
}
return directoryLayer.move(databaseOrTransaction,
this._path.slice(directoryLayer._path.length),
newAbsolutePath.slice(directoryLayer._path.length),
cb);
};
DirectorySubspace.prototype.remove = function(databaseOrTransaction, nameOrPath, cb) {
var path = tuplifyPath(valueOrDefault(nameOrPath, []));
var directoryLayer = getLayerForPath(this, path);
return directoryLayer.remove(databaseOrTransaction, partitionSubpath(this, path, directoryLayer), cb);
};
DirectorySubspace.prototype.removeIfExists = function(databaseOrTransaction, nameOrPath, cb) {
var path = tuplifyPath(valueOrDefault(nameOrPath, []));
var directoryLayer = getLayerForPath(this, path);
return directoryLayer.removeIfExists(databaseOrTransaction, partitionSubpath(this, path, directoryLayer), cb);
};
DirectorySubspace.prototype.exists = function(databaseOrTransaction, nameOrPath, cb) {
var path = tuplifyPath(valueOrDefault(nameOrPath, []));
var directoryLayer = getLayerForPath(this, path);
return directoryLayer.exists(databaseOrTransaction, partitionSubpath(this, path, directoryLayer), cb);
};
var partitionSubpath = function(directorySubspace, path, directoryLayer) {
directoryLayer = valueOrDefault(directoryLayer, directorySubspace._directoryLayer);
return directorySubspace._path.slice(directoryLayer._path.length).concat(path);
};
/**********************
* DirectoryPartition *
**********************/
var DirectoryPartition = function(path, prefix, parentDirectoryLayer) {
var directoryLayer = new DirectoryLayer({
nodeSubspace: new Subspace(undefined, Buffer.concat([prefix, buffer.fromByteLiteral('\xfe')], prefix.length+1)),
contentSubspace: new Subspace(undefined, prefix)
});
directoryLayer._path = path;
DirectorySubspace.call(this, path, prefix, directoryLayer, buffer('partition'));
this._parentDirectoryLayer = parentDirectoryLayer;
};
DirectoryPartition.prototype = new DirectorySubspace();
DirectoryPartition.constructor = DirectoryPartition;
DirectoryPartition.prototype.key = function() {
throw new Error('Cannot get key for the root of a directory partition.');
};
DirectoryPartition.prototype.pack = function(arr) {
throw new Error('Cannot pack keys using the root of a directory partition.');
};
DirectoryPartition.prototype.unpack = function(arr) {
throw new Error('Cannot unpack keys using the root of a directory partition.');
};
DirectoryPartition.prototype.range = function(arr) {
throw new Error('Cannot get range for the root of a directory partition.');
};
DirectoryPartition.prototype.contains = function(key) {
throw new Error('Cannot check whether a key belongs to the root of a directory partition.');
};
DirectoryPartition.prototype.get = function(name) {
throw new Error('Cannot open subspace in the root of a directory partition.');
};
DirectoryPartition.prototype.subspace = function(arr) {
throw new Error('Cannot open subspace in the root of a directory partition.');
};
DirectoryPartition.prototype.asFoundationDBKey = function() {
throw new Error('Cannot use the root of a directory partition as a key.');
};
var getLayerForPath = function(directorySubspace, path) {
if(directorySubspace instanceof DirectoryPartition && path.length === 0)
return directorySubspace._parentDirectoryLayer;
else
return directorySubspace._directoryLayer;
};
/********
* Node *
********/
var Node = function(subspace, path, targetPath) {
this.subspace = subspace;
this.path = path;
this.targetPath = targetPath;
};
Node.prototype.exists = function() {
return typeof(this.subspace) !== 'undefined' && this.subspace !== null;
};
Node.prototype.loadMetadata = function(tr) {
var self = this;
if(!self.exists()) {
self.loadedMetadata = true;
return future.resolve(self);
}
return tr.get(self.subspace.pack([buffer('layer')]))
.then(function(layer) {
self.loadedMetadata = true;
self.layer = layer;
return self;
});
};
Node.prototype.ensureMetadataLoaded = function() {
if(!this.loadedMetadata)
throw new Error('Metadata for node has not been loaded');
};
Node.prototype.isInPartition = function(includeEmptySubpath) {
this.ensureMetadataLoaded();
return this.exists() &&
fdbUtil.buffersEqual(this.layer, buffer('partition')) &&
(includeEmptySubpath || this.targetPath.length > this.path.length);
};
Node.prototype.getPartitionSubpath = function() {
this.ensureMetadataLoaded();
return this.targetPath.slice(this.path.length);
};
Node.prototype.getContents = function(directoryLayer) {
this.ensureMetadataLoaded();
return contentsOfNode(directoryLayer, this.subspace, this.path, this.layer);
};
var loadMetadata = function(tr) {
return function(node) {
return node.loadMetadata(tr);
};
};
module.exports = { directory: new DirectoryLayer(), DirectoryLayer: DirectoryLayer };

View File

@ -1,34 +0,0 @@
/*
* error.js
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
"use strict";
function FDBError(description, code) {
Error.captureStackTrace(this, this.constructor);
this.message = description;
this.code = code;
}
FDBError.prototype = new Error();
FDBError.constructor = FDBError;
FDBError.prototype.name = "FDBError"; // affects error messages, also needed for compatibility with older bindings
module.exports = FDBError;

View File

@ -1,115 +0,0 @@
/*
* fdb.js
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
"use strict";
var KeySelector = require('./keySelector');
var Cluster = require('./cluster');
var future = require('./future');
var Transactional = require('./retryDecorator');
var tuple = require('./tuple');
var buffer = require('./bufferConversion');
var fdb = require('./fdbModule');
var FDBError = require('./error');
var locality = require('./locality');
var directory = require('./directory');
var Subspace = require('./subspace');
var selectedApiVersion = require('./apiVersion');
var fdbModule = {};
module.exports = {
FDBError: FDBError,
apiVersion: function(version) {
if(selectedApiVersion.value && version !== selectedApiVersion.value)
throw new Error('Cannot select multiple different FDB API versions');
if(version < 500)
throw new RangeError('FDB API versions before 500 are not supported');
if(version > 510)
throw new RangeError('Latest known FDB API version is 510');
if(!selectedApiVersion.value) {
fdb.apiVersion(version);
fdbModule.FDBError = this.FDBError;
fdbModule.KeySelector = KeySelector;
fdbModule.future = future;
fdbModule.transactional = Transactional;
fdbModule.tuple = tuple;
fdbModule.buffer = buffer;
fdbModule.locality = locality;
fdbModule.directory = directory.directory;
fdbModule.DirectoryLayer = directory.DirectoryLayer;
fdbModule.Subspace = Subspace;
fdbModule.options = fdb.options;
fdbModule.streamingMode = fdb.streamingMode;
var dbCache = {};
var doInit = function() {
fdb.startNetwork();
process.on('exit', function() {
//Clearing out the caches makes memory debugging a little easier
dbCache = null;
fdb.stopNetwork();
});
//Subsequent calls do nothing
doInit = function() { };
};
fdbModule.init = function() {
doInit();
};
fdbModule.createCluster = function(clusterFile, cb) {
if(!clusterFile)
clusterFile = '';
return new Cluster(fdb.createCluster(clusterFile));
};
fdbModule.open = function(clusterFile, cb) {
if(clusterFile)
fdb.options.setClusterFile(clusterFile);
this.init();
var database = dbCache[clusterFile];
if(!database) {
var cluster = fdbModule.createCluster(clusterFile);
database = cluster.openDatabase();
dbCache[clusterFile] = database;
}
return database;
};
}
selectedApiVersion.value = version;
return fdbModule;
}
};
fdb.FDBError = module.exports.FDBError;

View File

@ -1,33 +0,0 @@
/*
* fdbModule.js
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
"use strict";
var semver = require('semver');
if(semver.satisfies(process.version, '0.10.x'))
var fdb = require('../modules/0.10/fdblib');
else if(semver.satisfies(process.version, '0.8.x'))
var fdb = require('../modules/0.8/fdblib');
else
throw new Error('FoundationDB binary NPM does not support Node.js ' + process.version + '; only v0.8.x and v0.10.x are supported');
module.exports = fdb;

View File

@ -1,104 +0,0 @@
/*
* fdbUtil.js
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
"use strict";
var buffer = require('./bufferConversion');
var future = require('./future');
var strinc = function(str) {
var buf = buffer(str);
var lastNonFFByte;
for(lastNonFFByte = buf.length-1; lastNonFFByte >= 0; --lastNonFFByte)
if(buf[lastNonFFByte] != 0xFF)
break;
if(lastNonFFByte < 0)
throw new Error('invalid argument \'' + str + '\': prefix must have at least one byte not equal to 0xFF');
var copy = new Buffer(lastNonFFByte + 1);
str.copy(copy, 0, 0, copy.length);
++copy[lastNonFFByte];
return copy;
};
var whileLoop = function(func, cb) {
return future.create(function(futureCb) {
var calledCallback = true;
function outer(err, res) {
if(err || typeof(res) !== 'undefined') {
futureCb(err, res);
}
else if(!calledCallback) {
calledCallback = true;
}
else {
while(calledCallback) {
calledCallback = false;
func(outer);
}
calledCallback = true;
}
}
outer();
}, cb);
};
var keyToBuffer = function(key) {
if(typeof(key.asFoundationDBKey) == 'function')
return buffer(key.asFoundationDBKey());
return buffer(key);
};
var valueToBuffer = function(val) {
if(typeof(val.asFoundationDBValue) == 'function')
return buffer(val.asFoundationDBValue());
return buffer(val);
};
var buffersEqual = function(buf1, buf2) {
if(!buf1 || !buf2)
return buf1 === buf2;
if(buf1.length !== buf2.length)
return false;
for(var i = 0; i < buf1.length; ++i)
if(buf1[i] !== buf2[i])
return false;
return true;
};
module.exports = {
strinc: strinc,
whileLoop: whileLoop,
keyToBuffer: keyToBuffer,
valueToBuffer: valueToBuffer,
buffersEqual: buffersEqual
};

View File

@ -1,244 +0,0 @@
/*
* future.js
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
"use strict";
var semver = require('semver');
function isFunction(f) {
return typeof(f) == 'function';
}
function isObject(o) {
return o === Object(o);
}
var resolvePromise = function(promise, value) {
var called = false;
try {
if(promise === value)
promise._state.reject(new TypeError('promise.then cannot be fulfilled with itself as the argument.'));
if(isObject(value)) {
var then = value.then;
if(isFunction(then)) {
then.call(value, function(res) {
if(!called) {
called = true;
resolvePromise(promise, res, promise);
}
}, function(err) {
if(!called) {
called = true;
promise._state.reject(err);
}
});
}
else
promise._state.fulfill(value);
}
else
promise._state.fulfill(value);
}
catch(error) {
if(!called)
promise._state.reject(error);
}
};
var FuturePrototype = {
cancel: function() {
//cancel is not implemented for most futures
},
then: function(onFulfilled, onRejected) {
var self = this;
var future = create();
this._state.addCallback(function(err, res) {
var setImmediateFunc;
if(semver.satisfies(process.version, '>=0.10.0')) {
setImmediateFunc = setImmediate;
}
else {
setImmediateFunc = process.nextTick;
}
setImmediateFunc(function() {
try {
if(self._state.rejected) {
if(isFunction(onRejected))
res = onRejected(err);
else {
future._state.reject(err);
return;
}
}
else if(isFunction(onFulfilled))
res = onFulfilled(res);
resolvePromise(future, res);
}
catch(error) {
future._state.reject(error);
}
});
});
return future;
},
"catch": function(onRejected) {
this.then(undefined, onRejected);
}
};
FuturePrototype.__proto__ = Function.__proto__;
var FutureState = function() {
this.callbacks = [];
this.fulfilled = false;
this.rejected = false;
};
FutureState.prototype.triggerCallbacks = function() {
for(var i = 0; i < this.callbacks.length; ++i)
this.callbacks[i](this.error, this.value);
this.callbacks = [];
};
FutureState.prototype.addCallback = function(cb) {
if(!this.rejected && !this.fulfilled)
this.callbacks.push(cb);
else
cb(this.error, this.value);
};
FutureState.prototype.fulfill = function(value) {
if(!this.fulfilled && !this.rejected) {
this.fulfilled = true;
this.value = value;
this.triggerCallbacks();
}
};
FutureState.prototype.reject = function(reason) {
if(!this.fulfilled && !this.rejected) {
this.rejected = true;
this.error = reason;
this.triggerCallbacks();
}
};
var getFutureCallback = function(futureState) {
return function(err, val) {
if(err)
futureState.reject(err);
else
futureState.fulfill(val);
};
};
var create = function(func, cb) {
if(cb)
func(cb);
else {
// This object is used to break a reference cycle with C++ objects
var futureState = new FutureState();
var future = function(callback) {
if(typeof callback === 'undefined')
return future;
future.then(function(val) { callback(undefined, val); }, callback);
};
future._state = futureState;
future.__proto__ = FuturePrototype;
if(func)
func.call(future, getFutureCallback(futureState));
return future;
}
};
var resolve = function(value) {
var f = create();
f._state.fulfill(value);
return f;
};
var reject = function(reason) {
var f = create();
f._state.reject(reason);
return f;
};
var all = function(futures) {
var future = create(function(futureCb) {
var count = futures.length;
if(count === 0)
futureCb(undefined, []);
var successCallback = function() {
if(--count === 0)
futureCb(undefined, futures.map(function(f) { return f._state.value; }));
};
for(var i = 0; i < futures.length; ++i) {
if(futures[i] && isFunction(futures[i].then))
futures[i].then(successCallback, futureCb);
else
successCallback();
}
});
return future;
};
var race = function(futures) {
var future = create(function(futureCb) {
var successCallback = function(val) {
futureCb(undefined, val);
};
for(var i = 0; i < futures.length; ++i) {
if(futures[i] && isFunction(futures[i].then))
futures[i].then(successCallback, futureCb);
else {
futureCb(undefined, futures[i]);
break;
}
}
});
return future;
};
module.exports = {
FuturePrototype: FuturePrototype,
create: create,
resolve: resolve,
reject: reject,
all: all,
race: race
};

View File

@ -1,65 +0,0 @@
/*
* keySelector.js
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
"use strict";
var fdbUtil = require('./fdbUtil');
var KeySelector = function(key, orEqual, offset) {
this.key = fdbUtil.keyToBuffer(key);
this.orEqual = orEqual;
this.offset = offset;
};
KeySelector.prototype.next = function() {
return this.add(1);
};
KeySelector.prototype.prev = function() {
return this.add(-1);
};
KeySelector.prototype.add = function(addOffset) {
return new KeySelector(this.key, this.orEqual, this.offset + addOffset);
};
KeySelector.isKeySelector = function(sel) {
return sel instanceof KeySelector;
};
KeySelector.lastLessThan = function(key) {
return new KeySelector(key, false, 0);
};
KeySelector.lastLessOrEqual = function(key) {
return new KeySelector(key, true, 0);
};
KeySelector.firstGreaterThan = function(key) {
return new KeySelector(key, true, 1);
};
KeySelector.firstGreaterOrEqual = function(key) {
return new KeySelector(key, false, 1);
};
module.exports = KeySelector;

View File

@ -1,182 +0,0 @@
/*
* lazyIterator.js
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
"use strict";
var fdbUtil = require('./fdbUtil');
var future = require('./future');
function fetch(state, cb) {
if(cb)
state.fetchCallbacks.push(cb);
if(!state.fetching) {
state.fetching = true;
state.fetcher.fetch(function(err, res) {
var cbs = state.fetchCallbacks;
state.fetching = false;
state.fetchCallbacks = [];
state.results = res;
state.index = -1;
state.finished = !res || res.length === 0;
for(var i = 0; i < cbs.length; ++i)
cbs[i](err);
});
}
}
function iterState(fetcher) {
return {
index: -1,
results: undefined,
fetching: false,
fetchCallbacks: [],
fetcher: fetcher
};
}
var LazyIterator = function(Fetcher) {
this.Fetcher = Fetcher;
this.stateForNext = undefined;
this.startState = iterState(new Fetcher());
var startState = this.startState;
fetch(this.startState);
};
function copyState(state, wantAll) {
var newState = iterState();
newState.index = state.index;
newState.results = state.results;
newState.fetching = state.fetching;
if(state.fetching) {
state.fetchCallbacks.push(function(err) {
var cbs = newState.fetchCallbacks;
newState.index = state.index;
newState.results = state.results;
newState.fetching = false;
newState.fetchCallbacks = [];
newState.finished = state.finished;
newState.fetcher = state.fetcher.clone(wantAll);
for(var i = 0; i < cbs.length; ++i)
cbs[i](err);
});
}
else {
newState.fetcher = state.fetcher.clone(wantAll);
}
return newState;
}
function nextImpl(state, cb) {
if(state.finished)
cb();
else if(state.results && (state.index + 1) < state.results.length)
cb(null, state.results[++state.index]);
else {
fetch(state, function(err) {
if(err)
cb(err);
else if(state.finished)
cb();
else
nextImpl(state, cb);
});
}
}
LazyIterator.prototype.next = function(cb) {
var itr = this;
return future.create(function(futureCb) {
if(!itr.stateForNext)
itr.stateForNext = copyState(itr.startState);
nextImpl(itr.stateForNext, futureCb);
}, cb);
};
LazyIterator.prototype.forEach = function(func, cb) {
var itr = this;
return future.create(function(futureCb) {
var state = copyState(itr.startState);
fdbUtil.whileLoop(function(loopCb) {
nextImpl(state, function(err, res) {
if(err || !res)
loopCb(err, null);
else
func(res, loopCb);
});
}, futureCb);
}, cb);
};
function forEachBatchImpl(state, func, cb) {
function loopBody(loopCb) {
function processBatch(err) {
if(err || state.finished)
loopCb(err, null);
else {
state.index = state.results.length;
func(state.results, loopCb);
}
}
if(!state.results || state.index === state.results.length)
fetch(state, processBatch);
else
processBatch();
}
fdbUtil.whileLoop(loopBody, cb);
}
LazyIterator.prototype.forEachBatch = function(func, cb) {
var itr = this;
return future.create(function(futureCb) {
forEachBatchImpl(copyState(itr.startState), func, futureCb);
}, cb);
};
LazyIterator.prototype.toArray = function(cb) {
var itr = this;
return future.create(function(futureCb) {
var state = copyState(itr.startState, true);
var result = [];
forEachBatchImpl(state, function(arr, itrCb) {
result = result.concat(arr);
itrCb();
}, function(err, res) {
if(err)
futureCb(err);
else
futureCb(null, result);
});
}, cb);
};
module.exports = LazyIterator;

View File

@ -1,131 +0,0 @@
/*
* locality.js
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
"use strict";
var buffer = require('./bufferConversion');
var transactional = require('./retryDecorator');
var Database = require('./database');
var LazyIterator = require('./lazyIterator');
var fdb = require('./fdbModule');
var fdbUtil = require('./fdbUtil');
var KEY_SERVERS_PREFIX = buffer.fromByteLiteral('\xff/keyServers/');
var PAST_VERSION_ERROR_CODE = 1007;
function getBoundaryKeysImpl(tr, begin, end, callback) {
function BoundaryFetcher(wantAll) {
this.tr = tr;
this.begin = begin;
this.end = end;
this.lastBegin = undefined;
var fetcher = this;
if(wantAll)
this.streamingMode = fdb.streamingMode.wantAll;
else
this.streamingMode = fdb.streamingMode.iterator;
function iteratorCb(err, res) {
if(err) {
if(err.code === PAST_VERSION_ERROR_CODE && fetcher.begin !== fetcher.lastBegin) {
fetcher.tr = fetcher.tr.db.createTransaction();
readKeys();
}
else {
fetcher.tr.onError(err, function(e) {
if(e)
fetcher.fetchCb(e);
else
readKeys();
});
}
}
else
fetcher.fetchCb();
}
function readKeys() {
fetcher.lastBegin = fetcher.begin;
fetcher.tr.options.setReadSystemKeys();
fetcher.tr.options.setLockAware();
fetcher.tr.snapshot.getRange(fetcher.begin, fetcher.end, {streamingMode: fetcher.streamingMode}).forEachBatch(function(kvs, innerCb) {
fetcher.forEachCb = innerCb;
var keys = kvs.map(function(kv) { return kv.key.slice(13); });
var last = kvs[kvs.length-1].key;
fetcher.begin = Buffer.concat([last, buffer.fromByteLiteral('\x00')], last.length + 1);
fetcher.fetchCb(undefined, keys);
}, iteratorCb);
fetcher.streamingMode = fdb.streamingMode.wantAll;
}
this.fetch = function(cb) {
this.fetchCb = cb;
if(this.read)
this.forEachCb();
else {
this.read = true;
readKeys();
}
};
this.clone = function(wantAll) {
var clone = new BoundaryFetcher(wantAll);
clone.tr = this.tr.db.createTransaction();
clone.begin = this.begin;
clone.end = this.end;
clone.lastBegin = this.lastBegin;
return clone;
};
}
callback(null, new LazyIterator(BoundaryFetcher));
}
function getBoundaryKeys(databaseOrTransaction, begin, end, callback) {
begin = fdbUtil.keyToBuffer(begin);
end = fdbUtil.keyToBuffer(end);
begin = Buffer.concat([KEY_SERVERS_PREFIX, begin], KEY_SERVERS_PREFIX.length + begin.length);
end = Buffer.concat([KEY_SERVERS_PREFIX, end], KEY_SERVERS_PREFIX.length + end.length);
if(databaseOrTransaction instanceof Database) {
getBoundaryKeysImpl(databaseOrTransaction.createTransaction(), begin, end, callback);
}
else {
var tr = databaseOrTransaction.db.createTransaction();
databaseOrTransaction.getReadVersion(function(err, ver) {
tr.setReadVersion(ver);
getBoundaryKeysImpl(tr, begin, end, callback);
});
}
}
var getAddressesForKey = transactional(function (tr, key, cb) {
key = fdbUtil.keyToBuffer(key);
tr.tr.getAddressesForKey(key, cb);
});
module.exports = {getBoundaryKeys: getBoundaryKeys, getAddressesForKey: getAddressesForKey};

View File

@ -1,106 +0,0 @@
/*
* rangeIterator.js
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
"use strict";
var KeySelector = require('./keySelector');
var Future = require('./future');
var fdb = require('./fdbModule');
var LazyIterator = require('./lazyIterator');
function getStreamingMode(requestedMode, limit, wantAll) {
if(wantAll && requestedMode === fdb.streamingMode.iterator) {
if(limit)
return fdb.streamingMode.exact;
else
return fdb.streamingMode.wantAll;
}
return requestedMode;
}
module.exports = function(tr, start, end, options, snapshot) {
if(!options)
options = {};
if(!options.limit)
options.limit = 0;
if(!options.reverse)
options.reverse = false;
if(!options.streamingMode && options.streamingMode !== 0)
options.streamingMode = fdb.streamingMode.iterator;
var RangeFetcher = function(wantAll) {
this.finished = false;
this.limit = options.limit;
this.iterStart = start;
this.iterEnd = end;
this.iterationCount = 1;
this.streamingMode = getStreamingMode(options.streamingMode, this.limit, wantAll);
};
RangeFetcher.prototype.clone = function(wantAll) {
var clone = new RangeFetcher(wantAll);
clone.finished = this.finished;
clone.limit = this.limit;
clone.iterStart = this.iterStart;
clone.iterEnd = this.iterEnd;
clone.iterationCount = this.iterationCount;
return clone;
};
RangeFetcher.prototype.fetch = function(cb) {
var fetcher = this;
if(fetcher.finished) {
cb();
}
else {
tr.getRange(fetcher.iterStart.key, fetcher.iterStart.orEqual, fetcher.iterStart.offset, fetcher.iterEnd.key, fetcher.iterEnd.orEqual, fetcher.iterEnd.offset, fetcher.limit, fetcher.streamingMode, fetcher.iterationCount++, snapshot, options.reverse, function(err, res)
{
if(!err) {
var results = res.array;
if(results.length > 0) {
if(!options.reverse)
fetcher.iterStart = KeySelector.firstGreaterThan(results[results.length-1].key);
else
fetcher.iterEnd = KeySelector.firstGreaterOrEqual(results[results.length-1].key);
}
if(fetcher.limit !== 0) {
fetcher.limit -= results.length;
if(fetcher.limit <= 0)
fetcher.finished = true;
}
if(!res.more)
fetcher.finished = true;
cb(undefined, results);
}
else {
cb(err);
}
});
}
};
return new LazyIterator(RangeFetcher);
};

View File

@ -1,50 +0,0 @@
/*
* retryDecorator.js
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
"use strict";
var Database = require('./database');
var Transaction = require('./transaction');
module.exports = function(func) {
return function(db) {
var self = this;
if(db instanceof Database) {
var cb = arguments[func.length - 1];
if(typeof cb !== "undefined" && !(cb instanceof Function))
throw new TypeError("fdb.transactional function must declare a callback function as last argument");
else {
var args = Array.prototype.slice.call(arguments);
return db.doTransaction(function(tr, innerCb) {
args[0] = tr;
args[func.length - 1] = innerCb;
func.apply(self, args);
})(cb);
}
}
else if(db instanceof Transaction || db instanceof Transaction.SnapshotTransaction)
return func.apply(self, arguments);
else
throw new TypeError("fdb.transactional function must pass a Database, Transaction, or SnapshotTransaction as first argument");
};
};

View File

@ -1,85 +0,0 @@
/*
* subspace.js
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
"use strict";
var buffer = require('./bufferConversion');
var fdbUtil = require('./fdbUtil');
var tuple = require('./tuple');
var Subspace = function(prefixArray, rawPrefix) {
if(typeof rawPrefix === 'undefined')
rawPrefix = new Buffer(0);
if(typeof prefixArray === 'undefined')
prefixArray = [];
rawPrefix = fdbUtil.keyToBuffer(rawPrefix);
var packed = tuple.pack(prefixArray);
this.rawPrefix = Buffer.concat([rawPrefix, packed], rawPrefix.length + packed.length);
};
Subspace.prototype.key = function() {
return this.rawPrefix;
};
Subspace.prototype.pack = function(arr) {
var packed = tuple.pack(arr);
return Buffer.concat([this.rawPrefix, packed], this.rawPrefix.length + packed.length) ;
};
Subspace.prototype.unpack = function(key) {
key = fdbUtil.keyToBuffer(key);
if(!this.contains(key))
throw new Error('Cannot unpack key that is not in subspace.');
return tuple.unpack(key.slice(this.rawPrefix.length));
};
Subspace.prototype.range = function(arr) {
if(typeof arr === 'undefined')
arr = [];
var range = tuple.range(arr);
return {
begin: Buffer.concat([this.rawPrefix, range.begin], this.rawPrefix.length + range.begin.length),
end: Buffer.concat([this.rawPrefix, range.end], this.rawPrefix.length + range.end.length)
};
};
Subspace.prototype.contains = function(key) {
key = fdbUtil.keyToBuffer(key);
return key.length >= this.rawPrefix.length && fdbUtil.buffersEqual(key.slice(0, this.rawPrefix.length), this.rawPrefix);
};
Subspace.prototype.get = function(item) {
return this.subspace([item]);
};
Subspace.prototype.subspace = function(arr) {
return new Subspace(arr, this.rawPrefix);
};
Subspace.prototype.asFoundationDBKey = function() {
return this.key();
};
module.exports = Subspace;

View File

@ -1,203 +0,0 @@
/*
* transaction.js
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
"use strict";
var future = require('./future');
var KeySelector = require('./keySelector');
var rangeIterator = require('./rangeIterator');
var buffer = require('./bufferConversion');
var FDBError = require('./error');
var fdb = require('./fdbModule');
var fdbUtil = require('./fdbUtil');
function addReadOperations(object, snapshot) {
object.prototype.get = function(key, cb) {
var tr = this.tr;
key = fdbUtil.keyToBuffer(key);
return future.create(function(futureCb) {
tr.get(key, snapshot, futureCb);
}, cb);
};
object.prototype.getKey = function(keySelector, cb) {
var tr = this.tr;
return future.create(function(futureCb) {
tr.getKey(keySelector.key, keySelector.orEqual, keySelector.offset, snapshot, futureCb);
}, cb);
};
object.prototype.getRange = function(start, end, options) {
if(!KeySelector.isKeySelector(start))
start = KeySelector.firstGreaterOrEqual(start);
if(!KeySelector.isKeySelector(end))
end = KeySelector.firstGreaterOrEqual(end);
return rangeIterator(this.tr, start, end, options, snapshot);
};
object.prototype.getRangeStartsWith = function(prefix, options) {
prefix = fdbUtil.keyToBuffer(prefix);
return this.getRange(prefix, fdbUtil.strinc(prefix), options, snapshot);
};
object.prototype.getReadVersion = function(cb) {
var tr = this.tr;
return future.create(function(futureCb) {
tr.getReadVersion(futureCb, snapshot);
}, cb);
};
}
var atomic = function(tr, op) {
return function(key, value) { fdb.atomic[op].call(tr, fdbUtil.keyToBuffer(key), fdbUtil.valueToBuffer(value)); };
};
var Transaction = function(db, tr) {
this.db = db;
this.tr = tr;
this.options = tr.options;
this.snapshot = new Transaction.SnapshotTransaction(tr);
for(var op in fdb.atomic)
this[op] = atomic(tr, op);
};
Transaction.SnapshotTransaction = function(tr) {
this.tr = tr;
};
addReadOperations(Transaction, false);
addReadOperations(Transaction.SnapshotTransaction, true);
Transaction.prototype.doTransaction = function(func, cb) {
var self = this;
return future.create(function(futureCb) {
func(self, futureCb);
}, cb);
};
Transaction.prototype.set = function(key, value) {
key = fdbUtil.keyToBuffer(key);
value = fdbUtil.valueToBuffer(value);
this.tr.set(key, value);
};
Transaction.prototype.clear = function(key) {
key = fdbUtil.keyToBuffer(key);
this.tr.clear(key);
};
Transaction.prototype.clearRange = function(start, end) {
start = fdbUtil.keyToBuffer(start);
end = fdbUtil.keyToBuffer(end);
this.tr.clearRange(start, end);
};
Transaction.prototype.clearRangeStartsWith = function(prefix) {
prefix = fdbUtil.keyToBuffer(prefix);
this.clearRange(prefix, fdbUtil.strinc(prefix));
};
Transaction.prototype.watch = function(key) {
key = fdbUtil.keyToBuffer(key);
var self = this;
var watchFuture = future.create(function(futureCb) {
// 'this' is the future that is being created.
// We set its cancel method to cancel the watch.
this._watch = self.tr.watch(key, futureCb);
this.cancel = function() { this._watch.cancel(); };
});
return watchFuture;
};
Transaction.prototype.addReadConflictRange = function(start, end) {
start = fdbUtil.keyToBuffer(start);
end = fdbUtil.keyToBuffer(end);
this.tr.addReadConflictRange(start, end);
};
Transaction.prototype.addReadConflictKey = function(key) {
key = fdbUtil.keyToBuffer(key);
this.tr.addReadConflictRange(key, Buffer.concat([key, buffer.fromByteLiteral('\x00')], key.length + 1));
};
Transaction.prototype.addWriteConflictRange = function(start, end) {
start = fdbUtil.keyToBuffer(start);
end = fdbUtil.keyToBuffer(end);
this.tr.addWriteConflictRange(start, end);
};
Transaction.prototype.addWriteConflictKey = function(key) {
key = fdbUtil.keyToBuffer(key);
this.tr.addWriteConflictRange(key, Buffer.concat([key, buffer.fromByteLiteral('\x00')], key.length + 1));
};
Transaction.prototype.commit = function(cb) {
var tr = this.tr;
return future.create(function(futureCb) {
tr.commit(futureCb);
}, cb);
};
Transaction.prototype.onError = function(fdbError, cb) {
var tr = this.tr;
return future.create(function(futureCb) {
if(fdbError instanceof FDBError)
tr.onError(fdbError.code, futureCb);
else
futureCb(fdbError, null);
}, cb);
};
Transaction.prototype.reset = function() {
this.tr.reset();
};
Transaction.prototype.setReadVersion = function(version) {
this.tr.setReadVersion(version);
};
Transaction.prototype.getCommittedVersion = function() {
return this.tr.getCommittedVersion();
};
Transaction.prototype.getVersionstamp = function(cb) {
var tr = this.tr;
return future.create(function(futureCb) {
tr.getVersionstamp(futureCb);
}, cb);
};
Transaction.prototype.cancel = function() {
this.tr.cancel();
};
module.exports = Transaction;

View File

@ -1,481 +0,0 @@
/*
* tuple.js
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
"use strict";
var assert = require('assert');
var buffer = require('./bufferConversion');
var fdbUtil = require('./fdbUtil');
var fdb = require('./fdbModule');
var FDBError = require('./error');
var sizeLimits = new Array(8);
function setupSizeLimits() {
sizeLimits[0] = 1;
for(var i = 1; i < sizeLimits.length; i++) {
sizeLimits[i] = sizeLimits[i-1] * 256;
sizeLimits[i-1] -= 1;
}
sizeLimits[7] -= 1;
}
setupSizeLimits();
var maxInt = Math.pow(2, 53) - 1;
var minInt = -Math.pow(2, 53);
var nullByte = new Buffer('00', 'hex');
var BYTES_CODE = 0x01;
var STRING_CODE = 0x02;
var NESTED_CODE = 0x05;
var INT_ZERO_CODE = 0x14;
var POS_INT_END = 0x1d;
var NEG_INT_END = 0x0b;
var FLOAT_CODE = 0x20;
var DOUBLE_CODE = 0x21;
var FALSE_CODE = 0x26;
var TRUE_CODE = 0x27;
var UUID_CODE = 0x30;
function validateData(data, length) {
if(!(data instanceof Buffer) && !(data instanceof Array)) {
throw new TypeError('Data for FDB tuple type not array or buffer.');
} else if(data.length != length) {
throw new RangeError('Data for FDB tuple type has length ' + data.length + ' instead of expected length ' + length);
}
}
// If encoding and sign bit is 1 (negative), flip all of the bits. Otherwise, just flip sign.
// If decoding and sign bit is 0 (negative), flip all of the bits. Otherwise, just flip sign.
function adjustFloat(data, start, encode) {
if((encode && (data[start] & 0x80) === 0x80) || (!encode && (data[start] & 0x80) === 0x00)) {
for(var i = start; i < data.length; i++) {
data[i] = data[i] ^ 0xff;
}
} else {
data[start] = data[start] ^ 0x80;
}
}
function Float(value) {
this.value = value;
this.toBytes = function () {
if (this.rawData !== undefined) {
return this.rawData;
} else {
var buf = new Buffer(4);
buf.writeFloatBE(fdb.toFloat(this.value), 0);
return buf;
}
};
}
Float.fromBytes = function (buf) {
validateData(buf, 4);
var f = new Float(buf.readFloatBE(0));
if(isNaN(f.value)) {
f.rawData = buf;
}
return f;
}
function Double(value) {
this.value = value;
this.toBytes = function () {
if (this.rawData !== undefined) {
return this.rawData;
} else {
var buf = new Buffer(8);
buf.writeDoubleBE(this.value, 0);
return buf;
}
};
}
Double.fromBytes = function (buf) {
validateData(buf, 8);
var d = new Double(buf.readDoubleBE(0));
if(isNaN(d.value)) {
d.rawData = buf;
}
return d;
}
function UUID(data) {
if (data.length != 16) {
// There's a special code for this, so we check it first and throw the error if appropriate.
throw new FDBError("invalid_uuid_size", 2268);
}
validateData(data, 16);
this.data = new Buffer(data);
}
function findNullBytes(buf, pos, searchForTerminators) {
var nullBytes = [];
var found;
for(pos; pos < buf.length; ++pos) {
if(searchForTerminators && found && buf[pos] !== 255) {
break;
}
found = false;
if(buf[pos] === 0) {
found = true;
nullBytes.push(pos);
}
}
if(!found && searchForTerminators) {
nullBytes.push(buf.length);
}
return nullBytes;
}
function encode(item, buf, pos) {
var encodedString;
if(typeof item === 'undefined')
throw new TypeError('Packed element cannot be undefined');
else if(item === null)
return nullByte;
//byte string or unicode
else if(Buffer.isBuffer(item) || item instanceof ArrayBuffer || item instanceof Uint8Array || typeof item === 'string') {
var unicode = typeof item === 'string';
if(unicode) {
item = new Buffer(item, 'utf8');
}
else {
item = buffer(item);
}
var nullBytes = findNullBytes(item, 0);
encodedString = new Buffer(2 + item.length + nullBytes.length);
encodedString[0] = unicode ? STRING_CODE : BYTES_CODE;
var srcPos = 0;
var targetPos = 1;
for(var i = 0; i < nullBytes.length; ++i) {
item.copy(encodedString, targetPos, srcPos, nullBytes[i]+1);
targetPos += nullBytes[i]+1 - srcPos;
srcPos = nullBytes[i]+1;
encodedString[targetPos++] = 255;
}
item.copy(encodedString, targetPos, srcPos);
encodedString[encodedString.length-1] = 0;
return encodedString;
}
//64-bit integer
else if((typeof item === 'number' || item instanceof Number) && item % 1 === 0) {
var negative = item < 0;
var posItem = Math.abs(item);
var length = 0;
for(; length < sizeLimits.length; ++length) {
if(posItem <= sizeLimits[length])
break;
}
if(item > maxInt || item < minInt)
throw new RangeError('Cannot pack signed integer larger than 54 bits');
var prefix = negative ? INT_ZERO_CODE - length : INT_ZERO_CODE + length;
var outBuf = new Buffer(length+1);
outBuf[0] = prefix;
for(var byteIdx = length-1; byteIdx >= 0; --byteIdx) {
var b = posItem & 0xff;
if(negative)
outBuf[byteIdx+1] = ~b;
else {
outBuf[byteIdx+1] = b;
}
posItem = (posItem - b) / 0x100;
}
return outBuf;
}
// Floats
else if(item instanceof Float) {
var outBuf = new Buffer(5);
outBuf[0] = FLOAT_CODE;
if (isNaN(item.value) && item.rawData !== undefined) {
item.rawData.copy(outBuf, 1, 0, 4);
} else {
outBuf.writeFloatBE(fdb.toFloat(item.value), 1);
}
adjustFloat(outBuf, 1, true);
return outBuf;
}
// Doubles
else if(item instanceof Double) {
var outBuf = new Buffer(9);
outBuf[0] = DOUBLE_CODE;
if (isNaN(item.value) && item.rawData !== undefined) {
item.rawData.copy(outBuf, 1, 0, 8);
} else {
outBuf.writeDoubleBE(item.value, 1);
}
adjustFloat(outBuf, 1, true);
return outBuf;
}
// UUIDs
else if(item instanceof UUID) {
var outBuf = new Buffer(17);
outBuf[0] = UUID_CODE;
item.data.copy(outBuf, 1);
return outBuf;
}
// booleans
else if(item instanceof Boolean || typeof item === 'boolean') {
var outBuf = new Buffer(1);
var boolItem;
if(item instanceof Boolean) {
boolItem = item.valueOf();
} else {
boolItem = item;
}
if(boolItem) {
outBuf[0] = TRUE_CODE;
} else {
outBuf[0] = FALSE_CODE;
}
return outBuf;
}
// nested tuples
else if(item instanceof Array) {
var totalLength = 2;
var outArr = [new Buffer('05', 'hex')];
for(var i = 0; i < item.length; ++i) {
if(item[i] === null) {
outArr.push(new Buffer('00ff', 'hex'));
totalLength += 2;
} else {
outArr.push(encode(item[i]));
totalLength += outArr[i+1].length;
}
}
outArr.push(new Buffer('00', 'hex'))
return Buffer.concat(outArr, totalLength);
}
else
throw new TypeError('Packed element must either be a string, a buffer, an integer, or null');
}
function pack(arr) {
if(!(arr instanceof Array))
throw new TypeError('fdb.tuple.pack must be called with a single array argument');
var totalLength = 0;
var outArr = [];
for(var i = 0; i < arr.length; ++i) {
outArr.push(encode(arr[i]));
totalLength += outArr[i].length;
}
return Buffer.concat(outArr, totalLength);
}
function decodeNumber(buf, offset, bytes) {
var negative = bytes < 0;
bytes = Math.abs(bytes);
var num = 0;
var mult = 1;
var odd;
for(var i = bytes-1; i >= 0; --i) {
var b = buf[offset+i];
if(negative)
b = -(~b & 0xff);
if(i == bytes-1)
odd = b & 0x01;
num += b * mult;
mult *= 0x100;
}
if(num > maxInt || num < minInt || (num === minInt && odd))
throw new RangeError('Cannot unpack signed integers larger than 54 bits');
return num;
}
function decode(buf, pos, nested) {
if(typeof nested === 'undefined') nested = false;
var code = buf[pos];
var value;
if(code === 0) {
value = null;
if(nested) {
pos += 2;
} else {
pos++;
}
}
else if(code === BYTES_CODE || code === STRING_CODE) {
var nullBytes = findNullBytes(buf, pos+1, true);
var start = pos+1;
var end = nullBytes[nullBytes.length-1];
if(code === STRING_CODE && nullBytes.length === 1) {
value = buf.toString('utf8', start, end);
}
else {
value = new Buffer(end-start-(nullBytes.length-1));
var valuePos = 0;
for(var i=0; i < nullBytes.length && start < end; ++i) {
buf.copy(value, valuePos, start, nullBytes[i]);
valuePos += nullBytes[i] - start;
start = nullBytes[i] + 2;
if(start <= end) {
value[valuePos++] = 0;
}
}
if(code === STRING_CODE)
value = value.toString('utf8');
}
pos = end + 1;
}
else if(Math.abs(code-INT_ZERO_CODE) <= 7) {
if(code === INT_ZERO_CODE)
value = 0;
else
value = decodeNumber(buf, pos+1, code-INT_ZERO_CODE);
pos += Math.abs(INT_ZERO_CODE-code) + 1;
}
else if(Math.abs(code-INT_ZERO_CODE) <= 8)
throw new RangeError('Cannot unpack signed integers larger than 54 bits');
else if(code === FLOAT_CODE) {
var valBuf = new Buffer(4);
buf.copy(valBuf, 0, pos+1, pos+5);
adjustFloat(valBuf, 0, false);
value = Float.fromBytes(valBuf);
pos += 5;
}
else if(code === DOUBLE_CODE) {
var valBuf = new Buffer(8);
buf.copy(valBuf, 0, pos+1, pos+9);
adjustFloat(valBuf, 0, false);
value = Double.fromBytes(valBuf);
pos += 9;
}
else if(code === UUID_CODE) {
var valBuf = new Buffer(16);
buf.copy(valBuf, 0, pos+1, pos+17);
value = new UUID(valBuf);
pos += 17;
}
else if(code === FALSE_CODE) {
pos++;
value = false;
}
else if(code === TRUE_CODE) {
pos++;
value = true;
}
else if(code === NESTED_CODE) {
pos++;
value = []
while (buf[pos] != 0 || pos+1 < buf.length && buf[pos+1] === 0xff) {
var nestedVal = decode(buf, pos, true)
pos = nestedVal.pos
value.push(nestedVal.value)
}
pos++;
}
else
throw new TypeError('Unknown data type in DB: ' + buf + ' at ' + pos);
return { pos: pos, value: value };
}
function unpack(key) {
var res = { pos: 0 };
var arr = [];
key = fdbUtil.keyToBuffer(key);
while(res.pos < key.length) {
res = decode(key, res.pos);
arr.push(res.value);
}
return arr;
}
function range(arr) {
var packed = pack(arr);
return { begin: Buffer.concat([packed, nullByte]), end: Buffer.concat([packed, new Buffer('ff', 'hex')]) };
}
function compare(arr1, arr2) {
// NOTE: There is built-in comparison function included in 0.11.13 that we might want to switch to.
var buf1 = pack(arr1);
var buf2 = pack(arr2);
var pos = 0;
while(pos < buf1.length && pos < buf2.length) {
if(buf1[pos] != buf2[pos]) {
if(buf1[pos] < buf2[pos]) {
return -1;
} else {
return 1;
}
}
pos += 1;
}
// The two arrays begin with a common prefix.
if(buf1.length < buf2.length) {
return -1;
} else if(buf1.length == buf2.length) {
return 0;
} else {
return 1;
}
}
module.exports = {pack: pack, unpack: unpack, range: range, compare: compare, Float: Float, Double: Double, UUID: UUID};

View File

@ -1,26 +0,0 @@
{
"name": "@apple/fdb",
"publishConfig": {
"registry": "https://registry.npmjs.org"
},
"version": "VERSION",
"author": "FoundationDB <fdb-dist@apple.com> (https://www.foundationdb.org)",
"description": "Node.js bindings for the FoundationDB database",
"keywords": [ "FoundationDB", "database", "NoSQL", "ACID" ],
"homepage": "http://17.199.145.104",
"license": "Apache v2",
"main": "./lib/fdb.js",
"cpu": [ "x64" ],
"dependencies": {
"semver": "~ 4.1"
},
"devDependencies": {
"jshint": ">= 2.5.6",
"promises-aplus-tests": ">= 2.1.0"
},
"engineStrict": true,
"private": true,
"engines": {
"node" : "0.8.x || 0.10.x"
}
}

View File

@ -1,96 +0,0 @@
/*
* Cluster.cpp
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <node.h>
#include <iostream>
#include <string>
#include <cstring>
#include <node_version.h>
#include "Cluster.h"
#include "Database.h"
#include "FdbOptions.h"
#include "NodeCallback.h"
using namespace v8;
using namespace std;
Cluster::Cluster() { }
Cluster::~Cluster() {
fdb_cluster_destroy(cluster);
}
Persistent<Function> Cluster::constructor;
Handle<Value> Cluster::OpenDatabase(const Arguments &args) {
HandleScope scope;
Cluster *clusterPtr = ObjectWrap::Unwrap<Cluster>(args.Holder());
const char *dbName = "DB";
FDBFuture *f = fdb_cluster_create_database(clusterPtr->cluster, (uint8_t*)dbName, (int)strlen(dbName));
fdb_error_t errorCode = fdb_future_block_until_ready(f);
FDBDatabase *database;
if(errorCode == 0)
errorCode = fdb_future_get_database(f, &database);
if(errorCode != 0)
return ThrowException(FdbError::NewInstance(errorCode, fdb_get_error(errorCode)));
Handle<Value> jsValue = Database::NewInstance(database);
return scope.Close(jsValue);
}
void Cluster::Init() {
HandleScope scope;
Local<FunctionTemplate> tpl = FunctionTemplate::New(New);
tpl->InstanceTemplate()->SetInternalFieldCount(1);
tpl->SetClassName(String::NewSymbol("Cluster"));
tpl->PrototypeTemplate()->Set(String::NewSymbol("openDatabase"), FunctionTemplate::New(OpenDatabase)->GetFunction());
constructor = Persistent<Function>::New(tpl->GetFunction());
}
Handle<Value> Cluster::New(const Arguments &args) {
HandleScope scope;
Cluster *c = new Cluster();
c->Wrap(args.Holder());
return scope.Close(args.Holder());
}
Handle<Value> Cluster::NewInstance(FDBCluster *ptr) {
HandleScope scope;
Local<Object> instance = constructor->NewInstance(0, NULL);
Cluster *clusterObj = ObjectWrap::Unwrap<Cluster>(instance);
clusterObj->cluster = ptr;
instance->Set(String::NewSymbol("options"), FdbOptions::CreateOptions(FdbOptions::ClusterOption, instance));
return scope.Close(instance);
}

View File

@ -1,47 +0,0 @@
/*
* Cluster.h
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FDB_NODE_CLUSTER_H
#define FDB_NODE_CLUSTER_H
#include "Version.h"
#include <foundationdb/fdb_c.h>
#include <node.h>
class Cluster : public node::ObjectWrap {
public:
static void Init();
static v8::Handle<v8::Value> NewInstance(FDBCluster *ptr);
static v8::Handle<v8::Value> New(const v8::Arguments &args);
static v8::Handle<v8::Value> OpenDatabase(const v8::Arguments &args);
static v8::Handle<v8::Value> Destroy(const v8::Arguments &args);
FDBCluster* GetCluster() { return cluster; }
private:
Cluster();
~Cluster();
static v8::Persistent<v8::Function> constructor;
FDBCluster *cluster;
};
#endif

View File

@ -1,86 +0,0 @@
/*
* Database.cpp
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <node.h>
#include <iostream>
#include <string>
#include <cstring>
#include "Database.h"
#include "FdbOptions.h"
#include "NodeCallback.h"
using namespace v8;
using namespace std;
Database::Database() { };
Database::~Database() {
fdb_database_destroy(db);
};
Persistent<Function> Database::constructor;
void Database::Init() {
Local<FunctionTemplate> tpl = FunctionTemplate::New(New);
tpl->SetClassName(String::NewSymbol("Database"));
tpl->InstanceTemplate()->SetInternalFieldCount(1);
tpl->PrototypeTemplate()->Set(String::NewSymbol("createTransaction"), FunctionTemplate::New(CreateTransaction)->GetFunction());
constructor = Persistent<Function>::New(tpl->GetFunction());
}
Handle<v8::Value> Database::CreateTransaction(const v8::Arguments &args) {
HandleScope scope;
Database *dbPtr = node::ObjectWrap::Unwrap<Database>(args.Holder());
FDBDatabase *db = dbPtr->db;
FDBTransaction *tr;
fdb_error_t err = fdb_database_create_transaction(db, &tr);
if (err) {
ThrowException(FdbError::NewInstance(err, fdb_get_error(err)));
return scope.Close(Undefined());
}
return scope.Close(Transaction::NewInstance(tr));
}
Handle<Value> Database::New(const Arguments &args) {
HandleScope scope;
Database *db = new Database();
db->Wrap(args.Holder());
return scope.Close(args.Holder());
}
Handle<Value> Database::NewInstance(FDBDatabase *ptr) {
HandleScope scope;
Local<Object> instance = constructor->NewInstance(0, NULL);
Database *dbObj = ObjectWrap::Unwrap<Database>(instance);
dbObj->db = ptr;
instance->Set(String::NewSymbol("options"), FdbOptions::CreateOptions(FdbOptions::DatabaseOption, instance));
return scope.Close(instance);
}

View File

@ -1,49 +0,0 @@
/*
* Database.h
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FDB_NODE_DATABASE_H
#define FDB_NODE_DATABASE_H
#include "Version.h"
#include "Transaction.h"
#include <foundationdb/fdb_c.h>
#include <node.h>
class Database: public node::ObjectWrap {
public:
static void Init();
static v8::Handle<v8::Value> NewInstance(FDBDatabase *ptr);
static v8::Handle<v8::Value> New(const v8::Arguments &args);
FDBDatabase* GetDatabase() { return db; }
private:
Database();
~Database();
static v8::Handle<v8::Value> CreateTransaction(const v8::Arguments &args);
static v8::Persistent<v8::Function> constructor;
FDBDatabase *db;
};
#endif

View File

@ -1,48 +0,0 @@
/*
* FdbError.cpp
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <node.h>
#include "FdbError.h"
using namespace v8;
using namespace node;
static Persistent<Object> module;
void FdbError::Init( Handle<Object> module ) {
::module = Persistent<Object>::New( module );
}
Handle<Value> FdbError::NewInstance(fdb_error_t code, const char *description) {
HandleScope scope;
Local<Value> constructor = module->Get( String::NewSymbol("FDBError") );
Local<Object> instance;
if (!constructor.IsEmpty() && constructor->IsFunction()) {
Local<Value> constructorArgs[] = { String::New(description), Integer::New(code) };
instance = Local<Function>::Cast(constructor)->NewInstance(2, constructorArgs);
} else {
// We can't find the (javascript) FDBError class, so construct and throw *something*
instance = Exception::Error(String::New("FDBError class not found. Unable to deliver error."))->ToObject();
}
return scope.Close(instance);
}

View File

@ -1,40 +0,0 @@
/*
* FdbError.h
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FDB_NODE_FDB_ERROR_H
#define FDB_NODE_FDB_ERROR_H
#include "Version.h"
#include <foundationdb/fdb_c.h>
#include <node.h>
class FdbError {
public:
static v8::Handle<v8::Value> NewInstance(fdb_error_t code, const char *description);
static void Init( v8::Handle<v8::Object> module );
private:
FdbError(); // not implemented by design
};
#endif

View File

@ -1,241 +0,0 @@
/*
* FdbOptions.cpp
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "FdbOptions.h"
#include "Cluster.h"
#include "Database.h"
#include "Transaction.h"
#include "FdbError.h"
#include <algorithm>
#include <node_buffer.h>
#define INVALID_OPTION_VALUE_ERROR_CODE (fdb_error_t)2006
using namespace v8;
using namespace node;
std::map<FdbOptions::Scope, Persistent<FunctionTemplate>> FdbOptions::optionTemplates;
std::map<FdbOptions::Scope, ScopeInfo> FdbOptions::scopeInfo;
std::map<FdbOptions::Scope, std::map<int, FdbOptions::ParameterType>> FdbOptions::parameterTypes;
FdbOptions::FdbOptions() { }
void FdbOptions::InitOptionsTemplate(Persistent<FunctionTemplate> &tpl, const char *className) {
tpl = Persistent<FunctionTemplate>::New(FunctionTemplate::New(New));
tpl->SetClassName(String::NewSymbol(className));
tpl->InstanceTemplate()->SetInternalFieldCount(1);
}
void FdbOptions::AddOption(Scope scope, std::string name, int value, ParameterType type) {
if(scope == NetworkOption || scope == ClusterOption || scope == DatabaseOption || scope == TransactionOption || scope == MutationType) {
bool isSetter = scope != MutationType;
optionTemplates[scope]->PrototypeTemplate()->Set(v8::String::NewSymbol(ToJavaScriptName(name, isSetter).c_str()),
v8::FunctionTemplate::New(scopeInfo[scope].optionFunction, v8::Integer::New(value))->GetFunction());
parameterTypes[scope][value] = type;
}
else if(scope == StreamingMode) {
optionTemplates[scope]->PrototypeTemplate()->Set(v8::String::NewSymbol(ToJavaScriptName(name, false).c_str()), v8::Integer::New(value));
}
else if(scope == ConflictRangeType) {
//Conflict range type enum is not exposed to JS code
}
}
Handle<Value> FdbOptions::New(const Arguments &args) {
FdbOptions *options = new FdbOptions();
options->Wrap(args.Holder());
return args.Holder();
}
void FdbOptions::WeakCallback(Persistent<Value> value, void *data) { }
Handle<Value> FdbOptions::NewInstance(Persistent<FunctionTemplate> optionsTemplate, Handle<Value> source) {
HandleScope scope;
Local<Object> instance = optionsTemplate->GetFunction()->NewInstance();
FdbOptions *optionsObj = ObjectWrap::Unwrap<FdbOptions>(instance);
optionsObj->source = Persistent<Value>::New(source);
optionsObj->source.MakeWeak(optionsObj, WeakCallback);
return scope.Close(instance);
}
Handle<Value> FdbOptions::CreateOptions(Scope scope, Handle<Value> source) {
return NewInstance(optionTemplates[scope], source);
}
Handle<Value> FdbOptions::CreateEnum(Scope scope) {
return optionTemplates[scope]->GetFunction()->NewInstance();
}
Parameter GetStringParameter(const Arguments &args, int index) {
if(args.Length() <= index || (!Buffer::HasInstance(args[index]) && !args[index]->IsString()))
return INVALID_OPTION_VALUE_ERROR_CODE;
else if(args[index]->IsString()) {
String::Utf8Value val(args[index]);
return std::string(*val, val.length());
}
else
return std::string(Buffer::Data(args[index]->ToObject()), Buffer::Length(args[index]->ToObject()));
};
Parameter FdbOptions::GetOptionParameter(const Arguments &args, Scope scope, int optionValue, int index) {
if(args.Length() > index) {
int64_t val;
switch(parameterTypes[scope][optionValue]) {
case FdbOptions::String:
return GetStringParameter(args, index);
case FdbOptions::Bytes:
if(!Buffer::HasInstance(args[index]))
return INVALID_OPTION_VALUE_ERROR_CODE;
return std::string(Buffer::Data(args[index]->ToObject()), Buffer::Length(args[index]->ToObject()));
case FdbOptions::Int:
if(!args[index]->IsNumber())
return INVALID_OPTION_VALUE_ERROR_CODE;
val = args[index]->IntegerValue();
return std::string((const char*)&val, 8);
case FdbOptions::None:
return Parameter();
}
}
return Parameter();
}
v8::Handle<v8::Value> SetNetworkOption(const Arguments &args) {
FDBNetworkOption op = (FDBNetworkOption)args.Data()->Uint32Value();
Parameter param = FdbOptions::GetOptionParameter(args, FdbOptions::NetworkOption, op);
fdb_error_t errorCode = param.errorCode;
if(errorCode == 0)
errorCode = fdb_network_set_option(op, param.getValue(), param.getLength());
if(errorCode)
return ThrowException(FdbError::NewInstance(errorCode, fdb_get_error(errorCode)));
return Null();
}
v8::Handle<v8::Value> SetClusterOption(const Arguments &args) {
FdbOptions *options = ObjectWrap::Unwrap<FdbOptions>(args.Holder());
Cluster *cluster = ObjectWrap::Unwrap<Cluster>(options->GetSource()->ToObject());
FDBClusterOption op = (FDBClusterOption)args.Data()->Uint32Value();
Parameter param = FdbOptions::GetOptionParameter(args, FdbOptions::ClusterOption, op);
fdb_error_t errorCode = param.errorCode;
if(errorCode == 0)
errorCode = fdb_cluster_set_option(cluster->GetCluster(), op, param.getValue(), param.getLength());
if(errorCode)
return ThrowException(FdbError::NewInstance(errorCode, fdb_get_error(errorCode)));
return Null();
}
v8::Handle<v8::Value> SetDatabaseOption(const Arguments &args) {
FdbOptions *options = ObjectWrap::Unwrap<FdbOptions>(args.Holder());
Database *db = ObjectWrap::Unwrap<Database>(options->GetSource()->ToObject());
FDBDatabaseOption op = (FDBDatabaseOption)args.Data()->Uint32Value();
Parameter param = FdbOptions::GetOptionParameter(args, FdbOptions::DatabaseOption, op);
fdb_error_t errorCode = param.errorCode;
if(errorCode == 0)
errorCode = fdb_database_set_option(db->GetDatabase(), op, param.getValue(), param.getLength());
if(errorCode)
return ThrowException(FdbError::NewInstance(errorCode, fdb_get_error(errorCode)));
return Null();
}
v8::Handle<v8::Value> SetTransactionOption(const Arguments &args) {
FdbOptions *options = ObjectWrap::Unwrap<FdbOptions>(args.Holder());
Transaction *tr = ObjectWrap::Unwrap<Transaction>(options->GetSource()->ToObject());
FDBTransactionOption op = (FDBTransactionOption)args.Data()->Uint32Value();
Parameter param = FdbOptions::GetOptionParameter(args, FdbOptions::TransactionOption, op);
fdb_error_t errorCode = param.errorCode;
if(errorCode == 0)
errorCode = fdb_transaction_set_option(tr->GetTransaction(), op, param.getValue(), param.getLength());
if(errorCode)
return ThrowException(FdbError::NewInstance(errorCode, fdb_get_error(errorCode)));
return Null();
}
v8::Handle<v8::Value> CallAtomicOperation(const Arguments &args) {
Transaction *tr = ObjectWrap::Unwrap<Transaction>(args.Holder());
Parameter key = GetStringParameter(args, 0);
Parameter value = GetStringParameter(args, 1);
fdb_error_t errorCode = key.errorCode > 0 ? key.errorCode : value.errorCode;
if(errorCode > 0)
return ThrowException(FdbError::NewInstance(errorCode, fdb_get_error(errorCode)));
fdb_transaction_atomic_op(tr->GetTransaction(), key.getValue(), key.getLength(), value.getValue(), value.getLength(), (FDBMutationType)args.Data()->Uint32Value());
return Null();
}
//Converts names using underscores as word separators to camel case (but preserves existing capitalization, if present). If isSetter, prepends the word 'set' to each name
std::string FdbOptions::ToJavaScriptName(std::string optionName, bool isSetter) {
if(isSetter)
optionName = "set_" + optionName;
size_t start = 0;
while(start < optionName.size()) {
if(start != 0)
optionName[start] = ::toupper(optionName[start]);
size_t index = optionName.find_first_of('_', start);
if(index == std::string::npos)
break;
optionName.erase(optionName.begin() + index);
start = index;
}
return optionName;
}
void FdbOptions::Init() {
scopeInfo[NetworkOption] = ScopeInfo("FdbNetworkOptions", SetNetworkOption);
scopeInfo[ClusterOption] = ScopeInfo("FdbClusterOptions", SetClusterOption);
scopeInfo[DatabaseOption] = ScopeInfo("FdbDatabaseOptions", SetDatabaseOption);
scopeInfo[TransactionOption] = ScopeInfo("FdbTransactionOptions", SetTransactionOption);
scopeInfo[StreamingMode] = ScopeInfo("FdbStreamingMode", NULL);
scopeInfo[MutationType] = ScopeInfo("AtomicOperations", CallAtomicOperation);
//scopeInfo[ConflictRangeType] = ScopeInfo("ConflictRangeType", NULL);
for(auto itr = scopeInfo.begin(); itr != scopeInfo.end(); ++itr)
InitOptionsTemplate(optionTemplates[itr->first], itr->second.templateClassName.c_str());
InitOptions();
}

View File

@ -1,109 +0,0 @@
/*
* FdbOptions.h
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FDB_NODE_FDB_OPTIONS_H
#define FDB_NODE_FDB_OPTIONS_H
#define ADD_OPTION(scope, name, value, type) AddOption(scope, name, value, type)
#include "Version.h"
#include <foundationdb/fdb_c.h>
#include <node.h>
#include <string>
#include <map>
struct Parameter {
Parameter() : isNull(true), errorCode(0) { }
Parameter(std::string param) : param(param), isNull(false), errorCode(0) { }
Parameter(fdb_error_t errorCode) : isNull(false), errorCode(errorCode) { }
std::string param;
bool isNull;
fdb_error_t errorCode;
uint8_t const* getValue() { return isNull ? NULL : (uint8_t const*)param.c_str(); }
int getLength() { return isNull ? 0 : (int)param.size(); }
};
struct ScopeInfo {
std::string templateClassName;
v8::Handle<v8::Value> (*optionFunction) (const v8::Arguments &args);
ScopeInfo() { }
ScopeInfo(std::string templateClassName, v8::Handle<v8::Value> (*optionFunction) (const v8::Arguments &args)) {
this->templateClassName = templateClassName;
this->optionFunction = optionFunction;
}
};
class FdbOptions : node::ObjectWrap {
public:
static void Init();
enum ParameterType {
None,
Int,
String,
Bytes
};
enum Scope {
NetworkOption,
ClusterOption,
DatabaseOption,
TransactionOption,
StreamingMode,
MutationType,
ConflictRangeType
};
static v8::Handle<v8::Value> CreateOptions(Scope scope, v8::Handle<v8::Value> source = v8::Null());
static v8::Handle<v8::Value> CreateEnum(Scope scope);
static Parameter GetOptionParameter(const v8::Arguments &args, Scope scope, int optionValue, int index = 0);
v8::Persistent<v8::Value> GetSource() {
return source;
}
private:
static v8::Handle<v8::Value> New(const v8::Arguments &args);
static v8::Handle<v8::Value> NewInstance(v8::Persistent<v8::FunctionTemplate> optionsTemplate, v8::Handle<v8::Value> source);
FdbOptions();
static void InitOptionsTemplate(v8::Persistent<v8::FunctionTemplate> &tpl, const char *className);
static void InitOptions();
static void AddOption(Scope scope, std::string name, int value, ParameterType type);
static void WeakCallback(v8::Persistent<v8::Value> value, void *data);
static std::string ToJavaScriptName(std::string optionName, bool isSetter);
static std::map<Scope, ScopeInfo> scopeInfo;
static std::map<Scope, v8::Persistent<v8::FunctionTemplate>> optionTemplates;
static std::map<Scope, std::map<int, ParameterType>> parameterTypes;
v8::Persistent<v8::Value> source;
};
#endif

View File

@ -1,42 +0,0 @@
/*
* FdbUtil.cpp
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <node.h>
#include "FdbUtil.h"
using namespace v8;
Handle<Value> ToFloat(const Arguments &args) {
HandleScope scope;
if (args.Length() != 1) {
return ThrowException(Exception::TypeError(String::NewSymbol("Wrong number of arguments (must be exactly 1)")));
}
if (!args[0]->IsNumber()) {
return ThrowException(Exception::TypeError(String::NewSymbol("Argument is not a Number")));
}
float value = (float)args[0]->NumberValue();
Handle<Value> jsValue = Number::New(value);
return scope.Close(jsValue);
}

View File

@ -1,29 +0,0 @@
/*
* FdbUtil.h
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FDB_NODE_FDB_UTIL_H
#define FDB_NODE_FDB_UTIL_H
#include <node.h>
v8::Handle<v8::Value> ToFloat(const v8::Arguments &args);
#endif

View File

@ -1,151 +0,0 @@
/*
* FdbV8Wrapper.cpp
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <string>
#include "node.h"
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <node_version.h>
#include "Database.h"
#include "NodeCallback.h"
#include "Cluster.h"
#include "Version.h"
#include "FdbError.h"
#include "FdbOptions.h"
#include "FdbUtil.h"
uv_thread_t fdbThread;
using namespace v8;
using namespace std;
bool networkStarted = false;
Handle<Value> ApiVersion(const Arguments &args) {
int apiVersion = args[0]->Int32Value();
fdb_error_t errorCode = fdb_select_api_version(apiVersion);
if(errorCode != 0) {
if(errorCode == 2203) {
int maxSupportedVersion = fdb_get_max_api_version();
ostringstream errorStr;
if(FDB_API_VERSION > maxSupportedVersion) {
errorStr << "This version of the FoundationDB Node.js binding is not supported by the installed FoundationDB "
<< "C library. The binding requires a library that supports API version " << FDB_API_VERSION
<< ", but the installed library supports a maximum version of " << maxSupportedVersion << ".";
}
else {
errorStr << "API version " << apiVersion << " is not supported by the installed FoundationDB C library.";
}
return ThrowException(FdbError::NewInstance(errorCode, errorStr.str().c_str()));
}
return ThrowException(FdbError::NewInstance(errorCode, fdb_get_error(errorCode)));
}
return Null();
}
static void networkThread(void *arg) {
fdb_error_t errorCode = fdb_run_network();
if(errorCode != 0)
fprintf(stderr, "Unhandled error in FoundationDB network thread: %s (%d)\n", fdb_get_error(errorCode), errorCode);
}
static Handle<Value> runNetwork() {
fdb_error_t errorCode = fdb_setup_network();
if(errorCode != 0)
return ThrowException(FdbError::NewInstance(errorCode, fdb_get_error(errorCode)));
uv_thread_create(&fdbThread, networkThread, NULL); // FIXME: Return code?
return Null();
}
Handle<Value> CreateCluster(const Arguments &args) {
HandleScope scope;
FDBFuture *f = fdb_create_cluster(*String::AsciiValue(args[0]->ToString()));
fdb_error_t errorCode = fdb_future_block_until_ready(f);
FDBCluster *cluster;
if(errorCode == 0)
errorCode = fdb_future_get_cluster(f, &cluster);
if(errorCode != 0)
return ThrowException(FdbError::NewInstance(errorCode, fdb_get_error(errorCode)));
Handle<Value> jsValue = Local<Value>::New(Cluster::NewInstance(cluster));
return scope.Close(jsValue);
}
Handle<Value> StartNetwork(const Arguments &args) {
if(!networkStarted) {
networkStarted = true;
return runNetwork();
}
return Null();
}
Handle<Value> StopNetwork(const Arguments &args) {
fdb_error_t errorCode = fdb_stop_network();
if(errorCode != 0)
return ThrowException(FdbError::NewInstance(errorCode, fdb_get_error(errorCode)));
uv_thread_join(&fdbThread);
//This line forces garbage collection. Useful for doing valgrind tests
//while(!V8::IdleNotification());
return Null();
}
void init(Handle<Object> target){
FdbError::Init( target );
Database::Init();
Transaction::Init();
Cluster::Init();
FdbOptions::Init();
Watch::Init();
target->Set(String::NewSymbol("apiVersion"), FunctionTemplate::New(ApiVersion)->GetFunction());
target->Set(String::NewSymbol("createCluster"), FunctionTemplate::New(CreateCluster)->GetFunction());
target->Set(String::NewSymbol("startNetwork"), FunctionTemplate::New(StartNetwork)->GetFunction());
target->Set(String::NewSymbol("stopNetwork"), FunctionTemplate::New(StopNetwork)->GetFunction());
target->Set(String::NewSymbol("options"), FdbOptions::CreateOptions(FdbOptions::NetworkOption));
target->Set(String::NewSymbol("streamingMode"), FdbOptions::CreateEnum(FdbOptions::StreamingMode));
target->Set(String::NewSymbol("atomic"), FdbOptions::CreateOptions(FdbOptions::MutationType));
target->Set(String::NewSymbol("toFloat"), FunctionTemplate::New(ToFloat)->GetFunction());
}
#if NODE_VERSION_AT_LEAST(0, 8, 0)
NODE_MODULE(fdblib, init);
#else
#error "Node.js versions before v0.8.0 are not supported"
#endif

View File

@ -1,149 +0,0 @@
/*
* NodeCallback.h
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FDB_NODE_NODE_CALLBACK_H
#define FDB_NODE_NODE_CALLBACK_H
#include "FdbError.h"
#include <v8.h>
#include <cstdlib>
#include <stdio.h>
#include <string.h>
#include <node.h>
#include <node_buffer.h>
#include <node_version.h>
#include <foundationdb/fdb_c.h>
#if NODE_VERSION_AT_LEAST(0, 7, 9)
#else
#error Node version too old
#endif
using namespace std;
using namespace v8;
using namespace node;
struct NodeCallback {
public:
NodeCallback(FDBFuture *future, Persistent<Function> cbFunc) : future(future), cbFunc(cbFunc), refCount(1) {
uv_async_init(uv_default_loop(), &handle, &NodeCallback::nodeThreadCallback);
uv_ref((uv_handle_t*)&handle);
handle.data = this;
}
void start() {
if (fdb_future_set_callback(future, &NodeCallback::futureReadyCallback, this)) {
fprintf(stderr, "fdb_future_set_callback failed.\n");
abort();
}
}
virtual ~NodeCallback() {
cbFunc.Dispose();
fdb_future_destroy(future);
}
void addRef() {
++refCount;
}
void delRef() {
if(--refCount == 0) {
delete this;
}
}
FDBFuture* getFuture() {
return future;
}
private:
void close() {
uv_close((uv_handle_t*)&handle, &NodeCallback::closeCallback);
}
static void closeCallback(uv_handle_s *handle) {
NodeCallback *nc = (NodeCallback*)((uv_async_t*)handle)->data;
nc->delRef();
}
static void futureReadyCallback(FDBFuture *f, void *ptr) {
NodeCallback *nc = (NodeCallback*)ptr;
uv_async_send(&nc->handle);
}
static void nodeThreadCallback(uv_async_t *handle, int status) {
HandleScope scope;
NodeCallback *nc = (NodeCallback*)handle->data;
FDBFuture *future = nc->future;
uv_unref((uv_handle_t*)handle);
Handle<Value> jsError;
Handle<Value> jsValue;
fdb_error_t errorCode;
jsValue = nc->extractValue(future, errorCode);
if (errorCode == 0)
jsError = Null();
else
jsError = FdbError::NewInstance(errorCode, fdb_get_error(errorCode));
Handle<Value> args[2] = { jsError, jsValue };
v8::TryCatch ex;
nc->cbFunc->Call(Context::GetCurrent()->Global(), 2, args);
if(ex.HasCaught())
fprintf(stderr, "\n%s\n", *String::AsciiValue(ex.StackTrace()->ToString()));
nc->close();
}
FDBFuture* future;
uv_async_t handle;
Persistent<Function> cbFunc;
int refCount;
protected:
virtual Handle<Value> extractValue(FDBFuture* future, fdb_error_t& outErr) = 0;
static Handle<Value> makeBuffer(const char *arr, int length) {
HandleScope scope;
Buffer *buf = Buffer::New(length);
Local<Object> slowBufferHandle = Local<Object>::New( buf->handle_ ); // Else the buffer, which has only a weak handle to itself, could be freed by GC in one of the below calls...
memcpy(Buffer::Data(buf), (const char*)arr, length);
Local<Object> globalObj = Context::GetCurrent()->Global();
Local<Function> bufferConstructor = Local<Function>::Cast(globalObj->Get(String::NewSymbol("Buffer")));
Handle<Value> constructorArgs[3] = { slowBufferHandle, Integer::New(length), Integer::New(0) };
Handle<Object> actualBuffer = bufferConstructor->NewInstance(3, constructorArgs);
return scope.Close(actualBuffer);
}
};
#endif

View File

@ -1,485 +0,0 @@
/*
* Transaction.cpp
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <node.h>
#include <iostream>
#include <string>
#include <cstring>
#include <vector>
#include <node_buffer.h>
#include <node_version.h>
#include "Transaction.h"
#include "NodeCallback.h"
#include "FdbError.h"
#include "FdbOptions.h"
using namespace v8;
using namespace std;
using namespace node;
// Transaction Implementation
Transaction::Transaction() { };
Transaction::~Transaction() {
fdb_transaction_destroy(tr);
};
Persistent<Function> Transaction::constructor;
struct NodeValueCallback : NodeCallback {
NodeValueCallback(FDBFuture *future, Persistent<Function> cbFunc) : NodeCallback(future, cbFunc) { }
virtual Handle<Value> extractValue(FDBFuture* future, fdb_error_t& outErr) {
HandleScope scope;
const char *value;
int valueLength;
int valuePresent;
outErr = fdb_future_get_value(future, &valuePresent, (const uint8_t**)&value, &valueLength);
if (outErr) return scope.Close(Undefined());
Handle<Value> jsValue;
if(!valuePresent)
jsValue = Null();
else
jsValue = makeBuffer(value, valueLength);
return scope.Close(jsValue);
}
};
struct NodeKeyCallback : NodeCallback {
NodeKeyCallback(FDBFuture *future, Persistent<Function> cbFunc) : NodeCallback(future, cbFunc) { }
virtual Handle<Value> extractValue(FDBFuture* future, fdb_error_t& outErr) {
HandleScope scope;
const char *key;
int keyLength;
outErr = fdb_future_get_key(future, (const uint8_t**)&key, &keyLength);
if (outErr) return scope.Close(Undefined());
Handle<Value> jsValue = makeBuffer(key, keyLength);
return scope.Close(jsValue);
}
};
struct NodeVoidCallback : NodeCallback {
NodeVoidCallback(FDBFuture *future, Persistent<Function> cbFunc) : NodeCallback(future, cbFunc) { }
virtual Handle<Value> extractValue(FDBFuture* future, fdb_error_t& outErr) {
outErr = fdb_future_get_error(future);
return Undefined();
}
};
struct NodeKeyValueCallback : NodeCallback {
NodeKeyValueCallback(FDBFuture *future, Persistent<Function> cbFunc) : NodeCallback(future, cbFunc) { }
virtual Handle<Value> extractValue(FDBFuture* future, fdb_error_t& outErr) {
HandleScope scope;
const FDBKeyValue *kv;
int len;
fdb_bool_t more;
outErr = fdb_future_get_keyvalue_array(future, &kv, &len, &more);
if (outErr) return scope.Close(Undefined());
/*
* Constructing a JavaScript array of KeyValue objects:
* {
* key: "some key",
* value: "some value"
* }
*
*/
Handle<Object> returnObj = Object::New();
Handle<Array> jsValueArray = Array::New(len);
Handle<String> keySymbol = String::NewSymbol("key");
Handle<String> valueSymbol = String::NewSymbol("value");
for(int i = 0; i < len; i++) {
Local<Object> jsKeyValue = Object::New();
Handle<Value> jsKeyBuffer = makeBuffer((const char*)kv[i].key, kv[i].key_length);
Handle<Value> jsValueBuffer = makeBuffer((const char*)kv[i].value, kv[i].value_length);
jsKeyValue->Set(keySymbol, jsKeyBuffer);
jsKeyValue->Set(valueSymbol, jsValueBuffer);
jsValueArray->Set(Number::New(i), jsKeyValue);
}
returnObj->Set(String::NewSymbol("array"), jsValueArray);
if(more)
returnObj->Set(String::NewSymbol("more"), Number::New(1));
return scope.Close(returnObj);
}
};
struct NodeVersionCallback : NodeCallback {
NodeVersionCallback(FDBFuture *future, Persistent<Function> cbFunc) : NodeCallback(future, cbFunc) { }
virtual Handle<Value> extractValue(FDBFuture* future, fdb_error_t& outErr) {
HandleScope scope;
int64_t version;
outErr = fdb_future_get_version(future, &version);
if (outErr) return scope.Close(Undefined());
//SOMEDAY: This limits the version to 53-bits. Do something different here?
Handle<Value> jsValue = Number::New((double)version);
return scope.Close(jsValue);
}
};
struct NodeStringArrayCallback : NodeCallback {
NodeStringArrayCallback(FDBFuture *future, Persistent<Function> cbFunc) : NodeCallback(future, cbFunc) { }
virtual Handle<Value> extractValue(FDBFuture *future, fdb_error_t& outErr) {
HandleScope scope;
const char **strings;
int stringCount;
outErr = fdb_future_get_string_array(future, &strings, &stringCount);
if (outErr) return scope.Close(Undefined());
Handle<Array> jsArray = Array::New(stringCount);
for(int i = 0; i < stringCount; i++)
jsArray->Set(Number::New(i), makeBuffer(strings[i], (int)strlen(strings[i])));
return scope.Close(jsArray);
}
};
struct StringParams {
uint8_t *str;
int len;
/*
* String arguments always have to be buffers to
* preserve bytes. Otherwise, stuff gets converted
* to UTF-8.
*/
StringParams(Handle<Value> keyVal) {
str = (uint8_t*)(Buffer::Data(keyVal->ToObject()));
len = (int)Buffer::Length(keyVal->ToObject());
}
};
FDBTransaction* Transaction::GetTransactionFromArgs(const Arguments &args) {
return node::ObjectWrap::Unwrap<Transaction>(args.Holder())->tr;
}
Persistent<Function> Transaction::GetCallback(Handle<Value> funcVal) {
return Persistent<Function>::New(Handle<Function>(Function::Cast(*funcVal)));
}
Handle<Value> Transaction::Set(const Arguments &args){
StringParams key(args[0]);
StringParams val(args[1]);
fdb_transaction_set(GetTransactionFromArgs(args), key.str, key.len, val.str, val.len);
return Null();
}
Handle<Value> Transaction::Commit(const Arguments &args) {
FDBFuture *f = fdb_transaction_commit(GetTransactionFromArgs(args));
(new NodeVoidCallback(f, GetCallback(args[0])))->start();
return Null();
}
Handle<Value> Transaction::Clear(const Arguments &args) {
StringParams key(args[0]);
fdb_transaction_clear(GetTransactionFromArgs(args), key.str, key.len);
return Null();
}
/*
* ClearRange takes two key strings.
*/
Handle<Value> Transaction::ClearRange(const Arguments &args) {
StringParams begin(args[0]);
StringParams end(args[1]);
fdb_transaction_clear_range(GetTransactionFromArgs(args), begin.str, begin.len, end.str, end.len);
return Null();
}
/*
* This function takes a KeySelector and returns a future.
*/
Handle<Value> Transaction::GetKey(const Arguments &args) {
StringParams key(args[0]);
int selectorOrEqual = args[1]->Int32Value();
int selectorOffset = args[2]->Int32Value();
bool snapshot = args[3]->BooleanValue();
FDBFuture *f = fdb_transaction_get_key(GetTransactionFromArgs(args), key.str, key.len, (fdb_bool_t)selectorOrEqual, selectorOffset, snapshot);
(new NodeKeyCallback(f, GetCallback(args[4])))->start();
return Null();
}
Handle<Value> Transaction::Get(const Arguments &args) {
StringParams key(args[0]);
bool snapshot = args[1]->BooleanValue();
FDBFuture *f = fdb_transaction_get(GetTransactionFromArgs(args), key.str, key.len, snapshot);
(new NodeValueCallback(f, GetCallback(args[2])))->start();
return Null();
}
Handle<Value> Transaction::GetRange(const Arguments &args) {
StringParams start(args[0]);
int startOrEqual = args[1]->Int32Value();
int startOffset = args[2]->Int32Value();
StringParams end(args[3]);
int endOrEqual = args[4]->Int32Value();
int endOffset = args[5]->Int32Value();
int limit = args[6]->Int32Value();
FDBStreamingMode mode = (FDBStreamingMode)args[7]->Int32Value();
int iteration = args[8]->Int32Value();
bool snapshot = args[9]->BooleanValue();
bool reverse = args[10]->BooleanValue();
FDBFuture *f = fdb_transaction_get_range(GetTransactionFromArgs(args), start.str, start.len, (fdb_bool_t)startOrEqual, startOffset,
end.str, end.len, (fdb_bool_t)endOrEqual, endOffset, limit, 0, mode, iteration, snapshot, reverse);
(new NodeKeyValueCallback(f, GetCallback(args[11])))->start();
return Null();
}
Handle<Value> Transaction::Watch(const Arguments &args) {
HandleScope scope;
Transaction *trPtr = node::ObjectWrap::Unwrap<Transaction>(args.Holder());
uint8_t *keyStr = (uint8_t*)(Buffer::Data(args[0]->ToObject()));
int keyLen = (int)Buffer::Length(args[0]->ToObject());
Persistent<Function> cb = Persistent<Function>::New(Handle<Function>(Function::Cast(*args[1])));
FDBFuture *f = fdb_transaction_watch(trPtr->tr, keyStr, keyLen);
NodeVoidCallback *callback = new NodeVoidCallback(f, cb);
Handle<Value> watch = Watch::NewInstance(callback);
callback->start();
return scope.Close(watch);
}
Handle<Value> Transaction::AddConflictRange(const Arguments &args, FDBConflictRangeType type) {
StringParams start(args[0]);
StringParams end(args[1]);
fdb_error_t errorCode = fdb_transaction_add_conflict_range(GetTransactionFromArgs(args), start.str, start.len, end.str, end.len, type);
if(errorCode != 0) {
ThrowException(FdbError::NewInstance(errorCode, fdb_get_error(errorCode)));
return Undefined();
}
return Null();
}
Handle<Value> Transaction::AddReadConflictRange(const Arguments &args) {
return AddConflictRange(args, FDB_CONFLICT_RANGE_TYPE_READ);
}
Handle<Value> Transaction::AddWriteConflictRange(const Arguments &args) {
return AddConflictRange(args, FDB_CONFLICT_RANGE_TYPE_WRITE);
}
Handle<Value> Transaction::OnError(const Arguments &args) {
fdb_error_t errorCode = args[0]->Int32Value();
FDBFuture *f = fdb_transaction_on_error(GetTransactionFromArgs(args), errorCode);
(new NodeVoidCallback(f, GetCallback(args[1])))->start();
return Null();
}
Handle<Value> Transaction::Reset(const Arguments &args) {
fdb_transaction_reset(GetTransactionFromArgs(args));
return Null();
}
Handle<Value> Transaction::SetReadVersion(const Arguments &args) {
int64_t version = args[0]->IntegerValue();
fdb_transaction_set_read_version(GetTransactionFromArgs(args), version);
return Null();
}
Handle<Value> Transaction::GetReadVersion(const Arguments &args) {
FDBFuture *f = fdb_transaction_get_read_version(GetTransactionFromArgs(args));
(new NodeVersionCallback(f, GetCallback(args[0])))->start();
return Null();
}
Handle<Value> Transaction::GetCommittedVersion(const Arguments &args) {
HandleScope scope;
int64_t version;
fdb_error_t errorCode = fdb_transaction_get_committed_version(GetTransactionFromArgs(args), &version);
if(errorCode != 0) {
ThrowException(FdbError::NewInstance(errorCode, fdb_get_error(errorCode)));
return scope.Close(Undefined());
}
return scope.Close(Number::New((double)version));
}
Handle<Value> Transaction::GetVersionstamp(const Arguments &args) {
FDBFuture *f = fdb_transaction_get_versionstamp(GetTransactionFromArgs(args));
(new NodeKeyCallback(f, GetCallback(args[0])))->start();
return Null();
}
Handle<Value> Transaction::Cancel(const Arguments &args) {
fdb_transaction_cancel(GetTransactionFromArgs(args));
return Null();
}
Handle<Value> Transaction::GetAddressesForKey(const Arguments &args) {
StringParams key(args[0]);
FDBFuture *f = fdb_transaction_get_addresses_for_key(GetTransactionFromArgs(args), key.str, key.len);
(new NodeStringArrayCallback(f, GetCallback(args[1])))->start();
return Null();
}
Handle<Value> Transaction::New(const Arguments &args) {
Transaction *tr = new Transaction();
tr->Wrap(args.Holder());
return args.Holder();
}
Handle<Value> Transaction::NewInstance(FDBTransaction *ptr) {
HandleScope scope;
Local<Object> instance = constructor->NewInstance();
Transaction *trObj = ObjectWrap::Unwrap<Transaction>(instance);
trObj->tr = ptr;
instance->Set(String::NewSymbol("options"), FdbOptions::CreateOptions(FdbOptions::TransactionOption, instance));
return scope.Close(instance);
}
void Transaction::Init() {
Local<FunctionTemplate> tpl = FunctionTemplate::New(New);
tpl->SetClassName(String::NewSymbol("Transaction"));
tpl->InstanceTemplate()->SetInternalFieldCount(1);
tpl->PrototypeTemplate()->Set(String::NewSymbol("get"), FunctionTemplate::New(Get)->GetFunction());
tpl->PrototypeTemplate()->Set(String::NewSymbol("getRange"), FunctionTemplate::New(GetRange)->GetFunction());
tpl->PrototypeTemplate()->Set(String::NewSymbol("getKey"), FunctionTemplate::New(GetKey)->GetFunction());
tpl->PrototypeTemplate()->Set(String::NewSymbol("watch"), FunctionTemplate::New(Watch)->GetFunction());
tpl->PrototypeTemplate()->Set(String::NewSymbol("set"), FunctionTemplate::New(Set)->GetFunction());
tpl->PrototypeTemplate()->Set(String::NewSymbol("commit"), FunctionTemplate::New(Commit)->GetFunction());
tpl->PrototypeTemplate()->Set(String::NewSymbol("clear"), FunctionTemplate::New(Clear)->GetFunction());
tpl->PrototypeTemplate()->Set(String::NewSymbol("clearRange"), FunctionTemplate::New(ClearRange)->GetFunction());
tpl->PrototypeTemplate()->Set(String::NewSymbol("addReadConflictRange"), FunctionTemplate::New(AddReadConflictRange)->GetFunction());
tpl->PrototypeTemplate()->Set(String::NewSymbol("addWriteConflictRange"), FunctionTemplate::New(AddWriteConflictRange)->GetFunction());
tpl->PrototypeTemplate()->Set(String::NewSymbol("onError"), FunctionTemplate::New(OnError)->GetFunction());
tpl->PrototypeTemplate()->Set(String::NewSymbol("reset"), FunctionTemplate::New(Reset)->GetFunction());
tpl->PrototypeTemplate()->Set(String::NewSymbol("getReadVersion"), FunctionTemplate::New(GetReadVersion)->GetFunction());
tpl->PrototypeTemplate()->Set(String::NewSymbol("setReadVersion"), FunctionTemplate::New(SetReadVersion)->GetFunction());
tpl->PrototypeTemplate()->Set(String::NewSymbol("getCommittedVersion"), FunctionTemplate::New(GetCommittedVersion)->GetFunction());
tpl->PrototypeTemplate()->Set(String::NewSymbol("getVersionstamp"), FunctionTemplate::New(GetVersionstamp)->GetFunction());
tpl->PrototypeTemplate()->Set(String::NewSymbol("cancel"), FunctionTemplate::New(Cancel)->GetFunction());
tpl->PrototypeTemplate()->Set(String::NewSymbol("getAddressesForKey"), FunctionTemplate::New(GetAddressesForKey)->GetFunction());
constructor = Persistent<Function>::New(tpl->GetFunction());
}
// Watch implementation
Watch::Watch() : callback(NULL) { };
Watch::~Watch() {
if(callback) {
if(callback->getFuture())
fdb_future_cancel(callback->getFuture());
callback->delRef();
}
};
Persistent<Function> Watch::constructor;
Handle<Value> Watch::NewInstance(NodeCallback *callback) {
HandleScope scope;
Local<Object> instance = constructor->NewInstance();
Watch *watchObj = ObjectWrap::Unwrap<Watch>(instance);
watchObj->callback = callback;
callback->addRef();
return scope.Close(instance);
}
Handle<Value> Watch::New(const Arguments &args) {
Watch *c = new Watch();
c->Wrap(args.Holder());
return args.Holder();
}
Handle<Value> Watch::Cancel(const Arguments &args) {
NodeCallback *callback = node::ObjectWrap::Unwrap<Watch>(args.Holder())->callback;
if(callback && callback->getFuture())
fdb_future_cancel(callback->getFuture());
return Null();
}
void Watch::Init() {
Local<FunctionTemplate> tpl = FunctionTemplate::New(New);
tpl->SetClassName(String::NewSymbol("Watch"));
tpl->InstanceTemplate()->SetInternalFieldCount(1);
tpl->PrototypeTemplate()->Set(String::NewSymbol("cancel"), FunctionTemplate::New(Cancel)->GetFunction());
constructor = Persistent<Function>::New(tpl->GetFunction());
}

View File

@ -1,92 +0,0 @@
/*
* Transaction.h
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FDB_NODE_TRANSACTION_H
#define FDB_NODE_TRANSACTION_H
#include "Version.h"
#include <foundationdb/fdb_c.h>
#include <node.h>
#include "NodeCallback.h"
class Transaction: public node::ObjectWrap {
public:
static void Init();
static v8::Handle<v8::Value> NewInstance(FDBTransaction *ptr);
static v8::Handle<v8::Value> New(const v8::Arguments &args);
static v8::Handle<v8::Value> Get(const v8::Arguments &args);
static v8::Handle<v8::Value> GetKey(const v8::Arguments &args);
static v8::Handle<v8::Value> Set(const v8::Arguments &args);
static v8::Handle<v8::Value> Commit(const v8::Arguments &args);
static v8::Handle<v8::Value> Clear(const v8::Arguments &args);
static v8::Handle<v8::Value> ClearRange(const v8::Arguments &args);
static v8::Handle<v8::Value> GetRange(const v8::Arguments &args);
static v8::Handle<v8::Value> Watch(const v8::Arguments &args);
static v8::Handle<v8::Value> AddConflictRange(const v8::Arguments &args, FDBConflictRangeType type);
static v8::Handle<v8::Value> AddReadConflictRange(const v8::Arguments &args);
static v8::Handle<v8::Value> AddWriteConflictRange(const v8::Arguments &args);
static v8::Handle<v8::Value> OnError(const v8::Arguments &args);
static v8::Handle<v8::Value> Reset(const v8::Arguments &args);
static v8::Handle<v8::Value> SetReadVersion(const v8::Arguments &args);
static v8::Handle<v8::Value> GetReadVersion(const v8::Arguments &args);
static v8::Handle<v8::Value> GetCommittedVersion(const v8::Arguments &args);
static v8::Handle<v8::Value> GetVersionstamp(const v8::Arguments &args);
static v8::Handle<v8::Value> Cancel(const v8::Arguments &args);
static v8::Handle<v8::Value> GetAddressesForKey(const v8::Arguments &args);
FDBTransaction* GetTransaction() { return tr; }
private:
Transaction();
~Transaction();
static v8::Persistent<v8::Function> constructor;
FDBTransaction *tr;
static FDBTransaction* GetTransactionFromArgs(const v8::Arguments &args);
static v8::Persistent<v8::Function> GetCallback(const v8::Handle<v8::Value> funcVal);
};
class Watch : public node::ObjectWrap {
public:
static void Init();
static v8::Handle<v8::Value> NewInstance(NodeCallback *callback);
static v8::Handle<v8::Value> New(const v8::Arguments &args);
static v8::Handle<v8::Value> Cancel(const v8::Arguments &args);
private:
Watch();
~Watch();
static v8::Persistent<v8::Function> constructor;
NodeCallback *callback;
};
#endif

View File

@ -1,27 +0,0 @@
/*
* Version.h
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FDB_NODE_VERSION_H
#define FDB_NODE_VERSION_H
#define FDB_API_VERSION 510
#endif

View File

@ -1,51 +0,0 @@
/*
* async_test.js
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var fdb = require('../lib/fdb').apiVersion(200);
fdb.open(null, null, function(dbErr, dbVal) {
fdb.open(null, null, function(dbErr, dbVal) {
if(dbVal == null)
console.log("database is null");
console.log("created database", dbErr);
console.log(JSON.stringify(dbVal));
var tr = dbVal.createTransaction();
console.log("created transaction");
tr.get('foo', function(err, val) {
console.log("get called", val, err);
tr.set('foo', 'bar');
tr.commit(function(err) {
console.log("commit called", err);
var x = tr.get('foo');
x(function(err, val) {
console.log("get called", val.toString(), err);
tr.clear('foo')
tr.commit(function(err) {
console.log("commit called", err);
tr.get('foo', function(err, val) {
console.log("get called", val, err);
});
});
});
});
});
});
});

View File

@ -1,304 +0,0 @@
/*
* directory_extension.js
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
"use strict";
var util = require('util');
var fdb = require('../lib/fdb.js').apiVersion(parseInt(process.argv[3]));
var fdbUtil = require('../lib/fdbUtil.js');
var dirUtil = require('./directory_util.js');
var logAll = false;
var logInstructions = false;
var logOps = false;
var logDirs = false;
var logErrors = false;
var logOp = function(message, force) {
if(logOps || logAll || force)
console.log(message);
};
var DirectoryExtension = function() {
this.dirList = [fdb.directory];
this.dirIndex = 0;
this.errorIndex = 0;
};
DirectoryExtension.prototype.processInstruction = function(inst, cb) {
var self = this;
var directory = this.dirList[this.dirIndex];
var promiseCb = function(err) {
if(err && (logErrors || logAll)) {
console.log(err);
//console.log(err.stack);
}
dirUtil.pushError(self, inst, err);
cb();
};
var appendDir = function(dir) {
if(logDirs || logAll)
console.log(util.format('pushed at %d (op=%s)', self.dirList.length, inst.op));
self.dirList.push(dir);
};
if(logAll || logInstructions)
console.log(inst.context.instructionIndex, inst.tokens[0].toString());
if(inst.op === 'DIRECTORY_CREATE_SUBSPACE') {
dirUtil.popTuples(inst)
.then(function(path) {
return inst.pop()
.then(function(rawPrefix) {
logOp(util.format('created subspace at (%s): %s', path, fdb.buffer.printable(rawPrefix)));
appendDir(new fdb.Subspace(path, rawPrefix));
});
})(promiseCb);
}
else if(inst.op === 'DIRECTORY_CREATE_LAYER') {
inst.pop({count: 3})
.then(function(params) {
var index1 = params[0];
var index2 = params[1];
var allowManualPrefixes = params[2];
var dir1 = self.dirList[params[0]];
var dir2 = self.dirList[params[1]];
if(dir1 === null || dir2 === null) {
logOp('create directory layer: None');
appendDir(null);
}
else {
logOp(util.format('create directory layer: node_subspace (%d) = %s, content_subspace (%d) = %s, allow_manual_prefixes = %d', index1, fdb.buffer.printable(dir1.rawPrefix), index2, fdb.buffer.printable(dir2.rawPrefix), allowManualPrefixes));
appendDir(new fdb.DirectoryLayer({ nodeSubspace: dir1,
contentSubspace: dir2,
allowManualPrefixes: allowManualPrefixes === 1 }));
}
})(promiseCb);
}
else if(inst.op === 'DIRECTORY_CHANGE') {
inst.pop()
.then(function(index) {
if(self.dirList[index] === null)
self.dirIndex = self.errorIndex;
else
self.dirIndex = index;
if(logDirs || logAll) {
var dir = self.dirList[self.dirIndex];
console.log(util.format('changed directory to %d ((%s))', self.dirIndex, dir._path));
}
})(promiseCb);
}
else if(inst.op === 'DIRECTORY_SET_ERROR_INDEX') {
inst.pop()
.then(function(errorIndex) {
self.errorIndex = errorIndex;
})(promiseCb);
}
else if(inst.op === 'DIRECTORY_CREATE_OR_OPEN') {
dirUtil.popTuples(inst)
.then(function(path) {
return inst.pop()
.then(function(layer) {
logOp(util.format('create_or_open (%s): layer=%s', directory._path + path, fdb.buffer.printable(layer) || ''));
return directory.createOrOpen(inst.tr, path, {'layer': layer || undefined});
})
.then(function(dir) {
appendDir(dir);
});
})(promiseCb);
}
else if(inst.op === 'DIRECTORY_CREATE') {
dirUtil.popTuples(inst)
.then(function(path) {
return inst.pop({count: 2})
.then(function(params) {
logOp(util.format('create (%s): layer=%s, prefix=%s', directory._path + path, fdb.buffer.printable(params[0]) || '', fdb.buffer.printable(params[1] || '')));
return directory.create(inst.tr, path, {'layer': params[0] || undefined, 'prefix': params[1] || undefined});
})
.then(function(dir) {
appendDir(dir);
});
})(promiseCb);
}
else if(inst.op === 'DIRECTORY_OPEN') {
dirUtil.popTuples(inst)
.then(function(path) {
return inst.pop()
.then(function(layer) {
logOp(util.format('open (%s): layer=%s', directory._path + path, fdb.buffer.printable(layer) || ''));
return directory.open(inst.tr, path, {'layer': layer});
})
.then(function(dir) {
appendDir(dir);
});
})(promiseCb);
}
else if(inst.op === 'DIRECTORY_MOVE') {
dirUtil.popTuples(inst, 2)
.then(function(paths) {
logOp(util.format('move (%s) to (%s)', directory._path + paths[0], directory._path + paths[1]));
return directory.move(inst.tr, paths[0], paths[1])
.then(function(dir) {
appendDir(dir);
});
})(promiseCb);
}
else if(inst.op === 'DIRECTORY_MOVE_TO') {
dirUtil.popTuples(inst)
.then(function(newAbsolutePath) {
logOp(util.format('move (%s) to (%s)', directory._path, newAbsolutePath));
return directory.moveTo(inst.tr, newAbsolutePath)
.then(function(dir) {
appendDir(dir);
});
})(promiseCb);
}
else if(inst.op === 'DIRECTORY_REMOVE') {
inst.pop()
.then(function(count) {
return dirUtil.popTuples(inst, count)
.then(function(path) {
logOp(util.format('remove (%s)', directory._path + (path ? path : '')));
return directory.remove(inst.tr, path);
});
})(promiseCb);
}
else if(inst.op === 'DIRECTORY_REMOVE_IF_EXISTS') {
inst.pop()
.then(function(count) {
return dirUtil.popTuples(inst, count)
.then(function(path) {
logOp(util.format('remove_if_exists (%s)', directory._path + (path ? path : '')));
return directory.removeIfExists(inst.tr, path);
});
})(promiseCb);
}
else if(inst.op === 'DIRECTORY_LIST') {
inst.pop()
.then(function(count) {
return dirUtil.popTuples(inst, count)
.then(function(path) {
return directory.list(inst.tr, path);
})
.then(function(children) {
inst.push(fdb.tuple.pack(children));
});
})(promiseCb);
}
else if(inst.op === 'DIRECTORY_EXISTS') {
var path;
inst.pop()
.then(function(count) {
return dirUtil.popTuples(inst, count)
.then(function(p) {
path = p;
return directory.exists(inst.tr, path);
})
.then(function(exists) {
logOp(util.format('exists (%s): %d', directory._path + (path ? path : ''), exists ? 1 : 0));
inst.push(exists ? 1 : 0);
});
})(promiseCb);
}
else if(inst.op === 'DIRECTORY_PACK_KEY') {
dirUtil.popTuples(inst)
.then(function(keyTuple) {
inst.push(directory.pack(keyTuple));
})(promiseCb);
}
else if(inst.op === 'DIRECTORY_UNPACK_KEY') {
inst.pop()
.then(function(key) {
logOp(util.format('unpack %s in subspace with prefix %s', fdb.buffer.printable(key), fdb.buffer.printable(directory.rawPrefix)));
var tup = directory.unpack(key);
for(var i = 0; i < tup.length; ++i)
inst.push(tup[i]);
})(promiseCb);
}
else if(inst.op === 'DIRECTORY_RANGE') {
dirUtil.popTuples(inst)
.then(function(tup) {
var rng = directory.range(tup);
inst.push(rng.begin);
inst.push(rng.end);
})(promiseCb);
}
else if(inst.op === 'DIRECTORY_CONTAINS') {
inst.pop()
.then(function(key) {
inst.push(directory.contains(key) ? 1 : 0);
})(promiseCb);
}
else if(inst.op === 'DIRECTORY_OPEN_SUBSPACE') {
dirUtil.popTuples(inst)
.then(function(path) {
logOp(util.format('open_subspace (%s)', path));
appendDir(directory.subspace(path));
})(promiseCb);
}
else if(inst.op === 'DIRECTORY_LOG_SUBSPACE') {
inst.pop()
.then(function(prefix) {
inst.tr.set(Buffer.concat([prefix, fdb.tuple.pack([self.dirIndex])]), directory.key());
})(promiseCb);
}
else if(inst.op === 'DIRECTORY_LOG_DIRECTORY') {
inst.pop()
.then(function(prefix) {
var exists;
return directory.exists(inst.tr)
.then(function(e) {
exists = e;
if(exists)
return directory.list(inst.tr);
else
return [];
})
.then(function(children) {
var logSubspace = new fdb.Subspace([self.dirIndex], prefix);
inst.tr.set(logSubspace.get('path'), fdb.tuple.pack(directory.getPath()));
inst.tr.set(logSubspace.get('layer'), fdb.tuple.pack([directory.getLayer()]));
inst.tr.set(logSubspace.get('exists'), fdb.tuple.pack([exists ? 1 : 0]));
inst.tr.set(logSubspace.get('children'), fdb.tuple.pack(children));
});
})(promiseCb);
}
else if(inst.op === 'DIRECTORY_STRIP_PREFIX') {
inst.pop()
.then(function(str) {
if(!fdbUtil.buffersEqual(fdb.buffer(str).slice(0, directory.key().length), directory.key()))
throw new Error('String ' + str + ' does not start with raw prefix ' + directory.key());
inst.push(str.slice(directory.key().length));
})(promiseCb);
}
else {
throw new Error('Unknown op: ' + inst.op);
}
};
module.exports = DirectoryExtension;

View File

@ -1,77 +0,0 @@
/*
* directory_util.js
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
"use strict";
var fdb = require('../lib/fdb.js').apiVersion(parseInt(process.argv[3]));
var util = require('../lib/fdbUtil.js');
var opsThatCreateDirs = [
'DIRECTORY_CREATE_SUBSPACE',
'DIRECTORY_CREATE_LAYER',
'DIRECTORY_CREATE_OR_OPEN',
'DIRECTORY_CREATE',
'DIRECTORY_OPEN',
'DIRECTORY_MOVE',
'DIRECTORY_MOVE_TO',
'DIRECTORY_OPEN_SUBSPACE'
];
var popTuples = function(inst, num, cb) {
if(typeof num === 'undefined')
num = 1;
return fdb.future.create(function(futureCb) {
var tuples = [];
if(num === 0) return futureCb();
util.whileLoop(function(loopCb) {
inst.pop()
.then(function(count) {
return inst.pop({count: count})
.then(function(tuple) {
tuples.push(tuple);
if(--num === 0)
return null;
});
})(loopCb);
}, function() {
if(tuples.length == 1)
futureCb(undefined, tuples[0]);
else
futureCb(undefined, tuples);
});
})(cb);
};
var pushError = function(self, inst, err) {
if(err) {
//console.log(err.toString());
//console.log(err.stack);
inst.push(fdb.buffer('DIRECTORY_ERROR'));
if(opsThatCreateDirs.indexOf(inst.op) >= 0)
self.dirList.push(null);
}
return err;
};
module.exports = {
popTuples: popTuples,
pushError: pushError
};

View File

@ -1,62 +0,0 @@
/*
* get_range.js
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var fdb = require('../lib/fdb').apiVersion(200);
var db = fdb.open(null, null)
var tr = db.createTransaction();
for(var i = 0; i < 10000; i++)
tr.set('foo' + i, 'bar' + i)
tr.commit(function(err) {
if(err)
console.log('commit error', err);
console.log('get range: foo-fooa');
var itr = tr.getRange('foo', 'fooa', null);
itr.forEach(
function(val, cb) {
console.log(val.key.toString(), val.value.toString());
cb();
},
function(err, res) {
if(err)
console.log(err);
else {
itr.forEach(
function(val, cb) {
console.log('pass2: ' + val.key.toString(), val.value.toString());
cb();
},
function(err, res) {
if(err)
console.log(err);
else { }
}
);
}
}
);
});

View File

@ -1,59 +0,0 @@
/*
* get_versionstamp.js
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var bufferEqual = function (a, b) {
if (!Buffer.isBuffer(a)) return undefined;
if (!Buffer.isBuffer(b)) return undefined;
if (typeof a.equals === 'function') return a.equals(b);
if (a.length !== b.length) return false;
for (var i = 0; i < a.length; i++) {
if (a[i] !== b[i]) return false;
}
return true;
};
var fdb = require('../lib/fdb').apiVersion(410);
var db = fdb.open(null, null)
var tr = db.createTransaction();
tr.getVersionstamp(function(error, vs) {
db.get('foo', function(error, val) {
if(bufferEqual(val, vs))
console.log('versionstamps match!')
else {
console.log("versionstamps don't match!")
console.log("database verionstamp: " + val)
console.log("transaction versionstamp: " + vs)
}
});
});
tr.setVersionstampedValue('foo', 'blahblahbl')
tr.commit(function(err) {
if(err)
console.log('commit error', err);
console.log(tr.getCommittedVersion())
});

View File

@ -1,58 +0,0 @@
#!/usr/bin/env node
/*
* promise_aplus_test.js
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
"use strict";
var promisesAplusTests = require('promises-aplus-tests');
var future = require('../lib/future.js');
var adapter = {
resolved: function(value) {
var f = future.create();
f._state.fulfill(value);
return f;
},
rejected: function(reason) {
var f = future.create();
f._state.reject(reason);
return f;
},
deferred: function() {
var f = future.create();
return {
promise: f,
resolve: function(value) {
f._state.fulfill(value);
},
reject: function(reason) {
f._state.reject(reason);
},
};
}
};
promisesAplusTests(adapter, function(err) {
console.log('Finished tests:', err);
});

View File

@ -1,58 +0,0 @@
/*
* retry_test.js
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var fdb = require('../lib/fdb').apiVersion(200);
function set(db, key, value, cb) {
db.doTransaction(function(tr, cb2) {
console.log("setting key");
tr.set(new Buffer(key), new Buffer(value));
cb2(null);
}, cb);
};
function setTxn(tr, key, value, cb) {
console.log('calling set');
tr.set(new Buffer(key), new Buffer(value));
cb();
};
setTxn = fdb.transactional(setTxn);
function getAndClear(db, key, cb) {
db.doTransaction(function(tr, cb2) {
console.log("getting key");
tr.get(new Buffer(key), function(err, res) {
tr.clear(new Buffer(key));
//setTimeout(function() { cb2(err, res); }, 6000);
cb2(err, res);
});
}, cb);
};
fdb.open(null, null, function(dbErr, db) {
console.log("created database", dbErr);
setTxn(db, 'foo', 'bar', function(err) {
console.log("Called transactional function", err);
getAndClear(db, 'foo', function(err, res) {
console.log("Called get and clear", err, res.toString());
});
});
});

View File

@ -1,33 +0,0 @@
var fdb = require('../lib/fdb').apiVersion(200);
function doSomething(_){
console.log("start");
db = fdb.open(null, null);
db = db(_);
db = fdb.open(null, null);
db = db(_);
a = db.get('foo');
console.log('foo = ', a(_));
db.clear('foo', 'bar', _);
console.log('foo = ', db.get('foo', _));
b = db.set('foo', 'bar');
b(_);
console.log('foo = ', db.get('foo', _));
/*var tr = db.createTransaction();
tr.set(new Buffer('foo'), new Buffer('bar'));
tr.commit(_);
var a = tr.get(new Buffer('foo'))
var b = tr.get(new Buffer('bar'))
console.log(a(_));
console.log(b(_));
var c = tr.get(new Buffer('a'));
console.log(c(_));*/
}
doSomething(_);
console.log("after");

View File

@ -1,148 +0,0 @@
"use strict";
var fdb = require('../lib/fdb.js').apiVersion(parseInt(process.argv[3]));
var fdbUtil = require('../lib/fdbUtil.js');
var dirUtil = require('./directory_util.js');
var StreamlineDirectoryExtension = function() {
this.dirList = [fdb.directory];
this.dirIndex = 0;
this.errorIndex = 0;
};
StreamlineDirectoryExtension.prototype.processInstruction = function(inst, _) {
var directory = this.dirList[this.dirIndex];
try {
//console.log(inst.op);
if(inst.op === 'DIRECTORY_CREATE_SUBSPACE') {
var path = dirUtil.popTuples(inst)(_);
var rawPrefix = inst.pop()(_);
this.dirList.push(new fdb.Subspace(path, rawPrefix));
}
else if(inst.op === 'DIRECTORY_CREATE_LAYER') {
var params = inst.pop({count: 3})(_);
if(this.dirList[params[0]] === null || this.dirList[params[1]] === null)
this.dirList.push(null);
else {
this.dirList.push(new fdb.DirectoryLayer({ nodeSubspace: this.dirList[params[0]],
contentSubspace: this.dirList[params[1]],
allowManualPrefixes: params[2] }));
}
}
else if(inst.op === 'DIRECTORY_CHANGE') {
var index = inst.pop()(_);
if(this.dirList[index] === null)
this.dirIndex = this.errorIndex;
else
this.dirIndex = index;
}
else if(inst.op === 'DIRECTORY_SET_ERROR_INDEX') {
this.errorIndex = inst.pop()(_);
}
else if(inst.op === 'DIRECTORY_CREATE_OR_OPEN') {
var path = dirUtil.popTuples(inst)(_);
var layer = inst.pop()(_);
var dir = directory.createOrOpen(inst.tr, path, {'layer': layer || undefined})(_);
this.dirList.push(dir);
}
else if(inst.op === 'DIRECTORY_CREATE') {
var path = dirUtil.popTuples(inst)(_);
var params = inst.pop({count: 2})(_);
var dir = directory.create(inst.tr, path, {'layer': params[0] || undefined, 'prefix': params[1] || undefined})(_);
this.dirList.push(dir);
}
else if(inst.op === 'DIRECTORY_OPEN') {
var path = dirUtil.popTuples(inst)(_);
var layer = inst.pop()(_);
var dir = directory.open(inst.tr, path, {'layer': layer || undefined})(_);
this.dirList.push(dir);
}
else if(inst.op === 'DIRECTORY_MOVE') {
var paths = dirUtil.popTuples(inst, 2)(_);
var movedDir = directory.move(inst.tr, paths[0], paths[1])(_);
this.dirList.push(movedDir);
}
else if(inst.op === 'DIRECTORY_MOVE_TO') {
var newAbsolutePath = dirUtil.popTuples(inst)(_);
var movedDir = directory.moveTo(inst.tr, newAbsolutePath)(_);
this.dirList.push(movedDir);
}
else if(inst.op === 'DIRECTORY_REMOVE') {
var count = inst.pop()(_);
var path = dirUtil.popTuples(inst, count)(_);
directory.remove(inst.tr, path)(_);
}
else if(inst.op === 'DIRECTORY_REMOVE_IF_EXISTS') {
var count = inst.pop()(_);
var path = dirUtil.popTuples(inst, count)(_);
directory.removeIfExists(inst.tr, path)(_);
}
else if(inst.op === 'DIRECTORY_LIST') {
var count = inst.pop()(_);
var path = dirUtil.popTuples(inst, count)(_);
var children = directory.list(inst.tr, path)(_);
inst.push(fdb.tuple.pack(children));
}
else if(inst.op === 'DIRECTORY_EXISTS') {
var count = inst.pop()(_);
var path = dirUtil.popTuples(inst, count)(_);
var exists = directory.exists(inst.tr, path)(_);
inst.push(exists ? 1 : 0);
}
else if(inst.op === 'DIRECTORY_PACK_KEY') {
var keyTuple = dirUtil.popTuples(inst)(_);
inst.push(directory.pack(keyTuple));
}
else if(inst.op === 'DIRECTORY_UNPACK_KEY') {
var key = inst.pop()(_);
var tup = directory.unpack(key);
for(var i = 0; i < tup.length; ++i)
inst.push(tup[i]);
}
else if(inst.op === 'DIRECTORY_RANGE') {
var tup = dirUtil.popTuples(inst)(_);
var rng = directory.range(tup);
inst.push(rng.begin);
inst.push(rng.end);
}
else if(inst.op === 'DIRECTORY_CONTAINS') {
var key = inst.pop()(_);
inst.push(directory.contains(key) ? 1 : 0);
}
else if(inst.op === 'DIRECTORY_OPEN_SUBSPACE') {
var path = dirUtil.popTuples(inst)(_);
this.dirList.push(directory.subspace(path));
}
else if(inst.op === 'DIRECTORY_LOG_SUBSPACE') {
var prefix = inst.pop()(_);
inst.tr.set(Buffer.concat([prefix, fdb.tuple.pack([this.dirIndex])]), directory.key());
}
else if(inst.op === 'DIRECTORY_LOG_DIRECTORY') {
var prefix = inst.pop()(_);
var exists = directory.exists(inst.tr)(_);
var children = exists ? directory.list(inst.tr)(_) : [];
var logSubspace = new fdb.Subspace([this.dirIndex], prefix);
inst.tr.set(logSubspace.get('path'), fdb.tuple.pack(directory.getPath()));
inst.tr.set(logSubspace.get('layer'), fdb.tuple.pack([directory.getLayer()]));
inst.tr.set(logSubspace.get('exists'), fdb.tuple.pack([exists ? 1 : 0]));
inst.tr.set(logSubspace.get('children'), fdb.tuple.pack(children));
}
else if(inst.op === 'DIRECTORY_STRIP_PREFIX') {
var str = inst.pop()(_);
if(!fdbUtil.buffersEqual(fdb.buffer(str).slice(0, directory.key().length), directory.key()))
throw new Error('String ' + str + ' does not start with raw prefix ' + directory.key());
inst.push(str.slice(directory.key().length));
}
else {
throw new Error('Unknown op: ' + inst.op);
}
}
catch(err) {
dirUtil.pushError(this, inst, err);
}
};
module.exports = StreamlineDirectoryExtension;

View File

@ -1,41 +0,0 @@
var fdb = require('../lib/fdb').apiVersion(200);
db = fdb.open(null, null, _);
var tr = db.createTransaction();
tr.set('foo1', 'bar1');
tr.set('foo2', 'bar2');
tr.set('foo3', 'bar3');
tr.set('foo4', 'bar4');
tr.set('foo5', 'bar5');
tr.set('bar1', 'foo1');
tr.set('bar2', 'foo2');
tr.commit(_);
console.log('get range: foo1-foo4');
var itr = tr.getRange('foo1', 'foo4', null);
a = itr.forEach(function(val, cb) {
console.log(val.key.toString(), val.value.toString());
cb(null, null);
});
console.log('get range starts with: foo');
itr = tr.getRangeStartsWith('foo');
b = itr.forEachBatch(function(arr, cb) {
console.log('processing array', arr.length);
for(var i in arr)
console.log(arr[i].key.toString(), arr[i].value.toString());
cb(null, null);
});
c = itr.forEachBatch(function(arr, cb) {
console.log('processing array concurrent', arr.length);
for(var i in arr)
console.log(arr[i].key.toString(), arr[i].value.toString());
cb(null, null);
});
console.log(a(_) + b(_) + c(_));

View File

@ -1,48 +0,0 @@
var fdb = require('../lib/fdb').apiVersion(200);
function set(db, key, value, _) {
db.doTransaction(function(tr, _) {
console.log("setting key");
tr.set(key, value);
return;
}, _);
};
function getAndClear(db, key, _) {
a = db.doTransaction(function(tr, _) {
console.log("getting key");
res = tr.get(key, _);
tr.clear(key);
return res;
});
b = db.doTransaction(function(tr, _) {
console.log("getting key");
res = tr.get(key, _);
tr.clear(key);
return res;
});
return a(_) + b(_);
};
getAndClearTxn = fdb.transactional(function(tr, key, _) {
console.log("getting key");
tr.getKey(fdb.KeySelector.firstGreaterOrEqual(key));
res = tr.get(key, _);
tr.clear(key);
return res;
});
db = fdb.open(null, null, _);
set(db, 'foo', 'bar', _);
//res = getAndClear(db, 'foo');
//console.log("Called get and clear", res(_).toString());
a = getAndClearTxn(db, 'foo');
b = getAndClearTxn(db, 'foo');
res = a(_) + b(_);
console.log("Result:", res.toString());

View File

@ -1,464 +0,0 @@
#!/usr/bin/env _node
"use strict";
//cmd line: _node streamline_tester._js <test_prefix> <optional_cluster_file>
var startTestPrefix = process.argv[2];
var assert = require('assert');
var fdb = require('../lib/fdb.js').apiVersion(parseInt(process.argv[3]));
var fdbUtil = require('../lib/fdbUtil.js');
var testerUtil = require('./util.js');
var DirectoryExtension = require('./streamline_directory_extension._js');
var db = fdb.open(process.argv[4]);
function pushError(inst, err) {
if(err) {
if(!err.code)
throw err;
inst.push(fdb.tuple.pack([fdb.buffer('ERROR'), fdb.buffer(err.code.toString())]));
}
return err;
}
var rangeChoice = 0;
function pushRange(itr, inst, prefixFilter, _) {
var outArray = [];
function pushKV(kv) {
if(typeof prefixFilter === 'undefined' || prefixFilter === null || fdbUtil.buffersEqual(kv.key.slice(0, prefixFilter.length), prefixFilter)) {
outArray.push(kv.key);
outArray.push(kv.value);
}
}
//Test the different methods for getting data from a range
if(inst.isDatabase) {
for(var i = 0; i < itr.length; ++i)
pushKV(itr[i]);
}
else if(rangeChoice % 4 === 0) {
itr.forEachBatch(function(res, _) {
for(var i = 0; i < res.length; ++i)
pushKV(res[i]);
if(rangeChoice % 8 === 0)
setTimeout(_, 0);
}, _);
}
else if(rangeChoice % 4 === 1) {
itr.forEach(function(res, _) {
pushKV(res);
if(rangeChoice % 8 === 1)
setTimeout(_, 0);
}, _);
}
else if(rangeChoice % 4 === 2) {
var arr = itr.toArray(_);
for(var i = 0; i < arr.length; ++i)
pushKV(arr[i]);
}
else {
fdbUtil.whileLoop(function(_) {
var kv = itr.next(_);
if(!kv)
return null;
else
pushKV(kv);
}, _);
}
rangeChoice++;
inst.push(fdb.tuple.pack(outArray));
}
var waitEmpty = fdb.transactional(function(tr, prefix, _) {
var itr = tr.getRangeStartsWith(prefix, { limit: 1 });
var arr = itr.toArray(_);
if(arr.length > 0)
throw new fdb.FDBError('', 1020);
});
var testWatches = function(db, _) {
db.set('w0', '0', _);
db.set('w3', '3', _);
var ready = [ false, false, false, false ];
var watches = [];
watches[0] = db.doTransaction(function(tr, _) {
return tr.watch('w0');
}, _);
watches[1] = db.clearAndWatch('w1', _).watch;
watches[2] = db.setAndWatch('w2', '2', _).watch;
watches[3] = db.getAndWatch('w3', _);
assert.strictEqual(watches[3].value.toString(), '3', 'get and watch');
watches[3] = watches[3].watch;
for(var i = 0; i < watches.length; ++i) {
(function(i) {
watches[i](function(err) { if(!err) ready[i] = true; });
})(i);
}
function checkWatches(expected, testName) {
for(var i = 0; i < watches.length; ++i)
assert.strictEqual(ready[i], expected, 'testName' + i);
}
setTimeout(_, 1000);
checkWatches(false, 'test 1');
db.set('w0', '0', _);
db.clear('w1', _);
setTimeout(_, 5000);
checkWatches(false, 'test 2');
db.set('w0', 'a', _);
db.set('w1', 'b', _);
db.clear('w2', _);
db.xor('w3', fdb.buffer.fromByteLiteral('\xff\xff'), _);
setTimeout(_, 2000);
checkWatches(true, 'test 3');
};
var testLocality = function(_) {
db.doTransaction(function(tr, _) {
tr.options.setTimeout(60*1000);
tr.options.setReadSystemKeys();
var boundaryKeys = fdb.locality.getBoundaryKeys(tr, '', fdb.buffer.fromByteLiteral('\xff\xff'), _).toArray(_);
var success = true;
for(var i = 0; i < boundaryKeys.length-1; ++i) {
var start = boundaryKeys[i];
var end = tr.getKey(fdb.KeySelector.lastLessThan(boundaryKeys[i+1]), _);
var startAddresses = fdb.locality.getAddressesForKey(tr, start, _);
var endAddresses = fdb.locality.getAddressesForKey(tr, end, _);
for(var j = 0; j < startAddresses.length; ++j) {
var found = false;
for(var k = 0; k < endAddresses.length; ++k) {
if(startAddresses[j].toString() === endAddresses[k].toString()) {
found = true;
break;
}
}
if(!found) {
success = false;
break;
}
}
if(!success)
break;
}
if(!success)
throw(new Error('Locality not internally consistent'));
}, _);
}
var numOperations = 0;
function processOperation(context, inst, _) {
//if(inst.op !== 'SWAP' && inst.op !== 'PUSH')
//console.log(context.prefix + ':', context.instructionIndex + '.', inst.op);
var params, numParams, res, itr;
try {
if(inst.op === 'PUSH')
inst.push(inst.tokens[1]);
else if(inst.op === 'POP')
inst.pop()(_);
else if(inst.op === 'DUP')
context.stack.pushEntry(context.stack.get(context.stack.length()-1));
else if(inst.op === 'EMPTY_STACK')
context.stack = new testerUtil.Stack();
else if(inst.op === 'SWAP') {
var index = inst.pop()(_);
assert.strictEqual(context.stack.length() > index, true, 'Cannot swap; stack too small');
index = context.stack.length() - index - 1;
if(context.stack.length() > index + 1) {
var tmp = context.stack.get(index);
context.stack.set(index, context.stack.popEntry());
context.stack.pushEntry(tmp);
}
}
else if(inst.op === 'WAIT_FUTURE') {
var stackEntry = inst.pop({withMetadata: true})(_);
context.stack.pushEntry(stackEntry);
}
else if(inst.op === 'WAIT_EMPTY') {
var waitKey = inst.pop()(_);
waitEmpty(db, waitKey, _);
inst.push('WAITED_FOR_EMPTY');
}
else if(inst.op === 'START_THREAD') {
var prefix = inst.pop()(_);
processTest(prefix, function(err, res) {
if(err) {
console.error('ERROR in Thread', prefix + ':');
console.error(err.stack);
process.exit(1);
}
});
}
else if(inst.op === 'NEW_TRANSACTION') {
context.newTransaction();
}
else if(inst.op === 'USE_TRANSACTION') {
var name = inst.pop()(_);
context.switchTransaction(name);
}
else if(inst.op === 'SET') {
params = inst.pop({count: 2})(_);
res = inst.tr.set(params[0], params[1]);
if(inst.isDatabase)
inst.push(res, true);
}
else if(inst.op === 'CLEAR') {
var key = inst.pop()(_);
res = inst.tr.clear(key);
if(inst.isDatabase)
inst.push(res, true);
}
else if(inst.op === 'CLEAR_RANGE') {
params = inst.pop({count: 2})(_);
res = inst.tr.clearRange(params[0], params[1]);
if(inst.isDatabase)
inst.push(res, true);
}
else if(inst.op === 'CLEAR_RANGE_STARTS_WITH') {
var prefix = inst.pop()(_);
res = inst.tr.clearRangeStartsWith(prefix);
if(inst.isDatabase)
inst.push(res, true);
}
else if(inst.op === 'ATOMIC_OP') {
params = inst.pop({count: 3})(_);
res = inst.tr[testerUtil.toJavaScriptName(params[0])](params[1], params[2]);
if(inst.isDatabase)
inst.push(res, true);
}
else if(inst.op === 'COMMIT') {
inst.push(inst.tr.commit(), true);
}
else if(inst.op === 'RESET')
inst.tr.reset();
else if(inst.op === 'CANCEL')
inst.tr.cancel();
else if(inst.op === 'GET') {
var key = inst.pop()(_);
inst.push(inst.tr.get(key), true);
}
else if(inst.op === 'GET_RANGE') {
params = inst.pop({count: 5})(_);
if(inst.isDatabase)
itr = inst.tr.getRange(params[0], params[1], { limit: params[2], reverse: params[3], streamingMode: params[4] }, _);
else
itr = inst.tr.getRange(params[0], params[1], { limit: params[2], reverse: params[3], streamingMode: params[4] });
pushRange(itr, inst, undefined, _);
}
else if(inst.op === 'GET_RANGE_SELECTOR') {
params = inst.pop({count: 10})(_);
var start = new fdb.KeySelector(params[0], params[1], params[2]);
var end = new fdb.KeySelector(params[3], params[4], params[5]);
if(inst.isDatabase)
itr = inst.tr.getRange(start, end, { limit: params[6], reverse: params[7], streamingMode: params[8] }, _);
else
itr = inst.tr.getRange(start, end, { limit: params[6], reverse: params[7], streamingMode: params[8] });
pushRange(itr, inst, params[9], _);
}
else if(inst.op === 'GET_RANGE_STARTS_WITH') {
params = inst.pop({count: 4})(_);
if(inst.isDatabase)
itr = inst.tr.getRangeStartsWith(params[0], { limit: params[1], reverse: params[2], streamingMode: params[3] }, _);
else
itr = inst.tr.getRangeStartsWith(params[0], { limit: params[1], reverse: params[2], streamingMode: params[3] });
pushRange(itr, inst, undefined, _);
}
else if(inst.op === 'GET_KEY') {
params = inst.pop({count: 4})(_);
var key = inst.tr.getKey(new fdb.KeySelector(params[0], params[1], params[2]), _);
if(fdbUtil.buffersEqual(key.slice(0, params[3].length), params[3])) {
inst.push(key);
}
else if(fdb.buffer.toByteLiteral(key) < fdb.buffer.toByteLiteral(params[3])) {
inst.push(params[3]);
}
else {
inst.push(fdbUtil.strinc(params[3]));
}
}
else if(inst.op === 'READ_CONFLICT_RANGE') {
params = inst.pop({count: 2})(_);
inst.tr.addReadConflictRange(params[0], params[1]);
inst.push(fdb.buffer('SET_CONFLICT_RANGE'));
}
else if(inst.op === 'WRITE_CONFLICT_RANGE') {
params = inst.pop({count: 2})(_);
inst.tr.addWriteConflictRange(params[0], params[1]);
inst.push(fdb.buffer('SET_CONFLICT_RANGE'));
}
else if(inst.op === 'READ_CONFLICT_KEY') {
var key = inst.pop()(_);
inst.tr.addReadConflictKey(key);
inst.push(fdb.buffer('SET_CONFLICT_KEY'));
}
else if(inst.op === 'WRITE_CONFLICT_KEY') {
var key = inst.pop()(_);
inst.tr.addWriteConflictKey(key);
inst.push(fdb.buffer('SET_CONFLICT_KEY'));
}
else if(inst.op === 'DISABLE_WRITE_CONFLICT') {
inst.tr.options.setNextWriteNoWriteConflictRange();
}
else if(inst.op === 'GET_READ_VERSION') {
context.lastVersion = inst.tr.getReadVersion(_);
inst.push(fdb.buffer('GOT_READ_VERSION'));
}
else if(inst.op === 'GET_COMMITTED_VERSION') {
context.lastVersion = inst.tr.getCommittedVersion();
inst.push(fdb.buffer('GOT_COMMITTED_VERSION'));
}
else if(inst.op === 'GET_VERSIONSTAMP') {
inst.push(inst.tr.getVersionstamp(), true);
}
else if(inst.op === 'SET_READ_VERSION') {
assert.notStrictEqual(typeof context.lastVersion, 'undefined', 'Cannot set read version; version has never been read');
inst.tr.setReadVersion(context.lastVersion);
}
else if(inst.op === 'ON_ERROR') {
var errorCode = inst.pop()(_);
var testErr = new fdb.FDBError('', errorCode);
inst.push(inst.tr.onError(testErr), true);
}
else if(inst.op === 'TUPLE_PACK') {
numParams = inst.pop()(_);
params = inst.pop({count: numParams})(_);
inst.push(fdb.tuple.pack(params));
}
else if(inst.op === 'TUPLE_UNPACK') {
var packedTuple = inst.pop()(_);
var arr = fdb.tuple.unpack(packedTuple);
for(var i = 0; i < arr.length; ++i)
inst.push(fdb.tuple.pack([arr[i]]));
}
else if(inst.op === 'TUPLE_RANGE') {
numParams = inst.pop()(_);
params = inst.pop({count: numParams})(_);
var range = fdb.tuple.range(params);
inst.push(range.begin);
inst.push(range.end);
}
else if(inst.op === 'SUB') {
params = inst.pop({count: 2})(_);
inst.push(params[0] - params[1]);
}
else if(inst.op === 'CONCAT') {
params = inst.pop({count: 2})(_);
if(Buffer.isBuffer(params[0])) {
inst.push(Buffer.concat([params[0], params[1]]))
}
else {
inst.push(params[0] + params[1]);
}
}
else if(inst.op === 'LOG_STACK') {
var prefix = inst.pop()(_);
var items = inst.pop({count: context.stack.length(), withMetadata: true})(_);
for(var i = 0; i < items.length; ++i) {
if(i % 100 === 0)
inst.tr.commit(_);
inst.tr.reset();
var entry = items[items.length - i -1];
var packedSubKey = fdb.tuple.pack([i, entry.instructionIndex]);
var packedValue = fdb.tuple.pack([entry.item]);
if(packedValue.length > 40000)
packedValue = packedValue.slice(0, 40000);
inst.tr.set(Buffer.concat([prefix, packedSubKey], prefix.length + packedSubKey.length), packedValue);
}
inst.tr.commit(_);
inst.tr.reset();
}
else if(inst.op === 'UNIT_TESTS') {
try {
db.options.setLocationCacheSize(100001);
db.doTransaction(function(tr, _) {
tr.options.setPrioritySystemImmediate();
tr.options.setPriorityBatch();
tr.options.setCausalReadRisky();
tr.options.setCausalWriteRisky();
tr.options.setReadYourWritesDisable();
tr.options.setReadAheadDisable();
tr.options.setReadSystemKeys();
tr.options.setAccessSystemKeys();
tr.options.setDurabilityDevNullIsWebScale();
tr.options.setTimeout(1000);
tr.options.setRetryLimit(5);
tr.options.setMaxRetryDelay(100);
tr.options.setUsedDuringCommitProtectionDisable();
tr.options.setTransactionLoggingEnable('my_transaction');
tr.get(fdb.buffer.fromByteLiteral('\xff'), _);
}, _);
testWatches(db, _);
testLocality(_);
}
catch(err) {
throw('Unit tests failed: ' + err);
}
}
else if(testerUtil.startsWith(inst.op, 'DIRECTORY_')) {
context.directoryExtension.processInstruction(inst, _);
}
else
throw new Error('Unrecognized operation');
}
catch(err) {
pushError(inst, err);
}
}
function processTest(prefix, _) {
var context = new testerUtil.Context(db, prefix, processOperation, new DirectoryExtension());
try {
context.run(_);
}
catch(err) {
console.error('ERROR during operation \'' + context.ops[context.current].value.toString() + '\':');
console.error(err.stack);
process.exit(1);
}
}
processTest(startTestPrefix, _);

View File

@ -1,754 +0,0 @@
#!/usr/bin/env node
/*
* tester.js
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
"use strict";
//cmd line: node tester.js <test_prefix> <optional_cluster_file>
var startTestPrefix = process.argv[2];
if(process.argv.length === 5)
var clusterFile = process.argv[4];
else
var clusterFile = '';
var assert = require('assert');
var fdb = require('../lib/fdb.js').apiVersion(parseInt(process.argv[3]));
var fdbUtil = require('../lib/fdbUtil.js');
var testerUtil = require('./util.js');
var DirectoryExtension = require('./directory_extension.js');
//fdb.options.setTraceEnable()
var db = fdb.open(clusterFile);
function pushError(inst, err) {
if(err) {
if(!err.code) {
console.error('ERROR during operation \'' + inst.op + '\':');
console.error(err.stack);
context.cb(err);
}
inst.push(fdb.tuple.pack([fdb.buffer('ERROR'), fdb.buffer(err.code.toString())]));
}
return err;
}
var rangeChoice = 0;
function pushRange(itr, inst, prefixFilter, cb) {
return fdb.future.create(function(futureCb) {
var outArray = [];
function pushKV(kv) {
if(typeof prefixFilter === 'undefined' || prefixFilter === null || fdbUtil.buffersEqual(kv.key.slice(0, prefixFilter.length), prefixFilter)) {
outArray.push(kv.key);
outArray.push(kv.value);
}
}
function finish(err) {
if(!pushError(inst, err))
inst.push(fdb.tuple.pack(outArray));
futureCb();
}
//Test different methods for getting a range
if(inst.isDatabase) {
for(var i = 0; i < itr.length; ++i)
pushKV(itr[i]);
finish();
}
else if(rangeChoice % 4 === 0) {
itr.forEachBatch(function(res, itrCb) {
for(var i = 0 ; i < res.length; ++i)
pushKV(res[i]);
if(rangeChoice % 8 === 0)
setTimeout(itrCb, 0);
else
itrCb();
}, function(err, res) {
finish(err);
});
}
else if(rangeChoice % 4 === 1) {
itr.forEach(function(res, itrCb) {
pushKV(res);
if(rangeChoice % 8 === 1)
setTimeout(itrCb, 0);
else
itrCb();
}, function(err, res) {
finish(err);
});
}
else if(rangeChoice % 4 === 2) {
itr.toArray(function(err, arr) {
if(!err) {
for(var i = 0; i < arr.length; ++i)
pushKV(arr[i]);
}
finish(err);
});
}
else {
fdbUtil.whileLoop(function(loopCb) {
itr.next(function(err, res) {
if(err)
loopCb(err);
else if(!res)
loopCb(undefined, null);
else {
pushKV(res);
loopCb();
}
});
}, finish);
}
rangeChoice++;
})(cb);
}
var waitEmpty = fdb.transactional(function(tr, prefix, cb) {
var itr = tr.getRangeStartsWith(prefix, { limit: 1 });
itr.toArray(function(err, res) {
if(err)
cb(err, null);
else if(res.length > 0)
cb(new fdb.FDBError('', 1020), null);
else
cb(null, null);
});
});
var timeoutFuture = function(time) {
return fdb.future.create(function(futureCb) {
setTimeout(futureCb, time);
});
};
var checkWatches = function(db, watches, ready, error, expected, cb) {
var i = 0;
return fdbUtil.whileLoop(function(loopCb) {
if(i == watches.length) return loopCb(undefined, true); // terminate loop
if(!ready[i] && expected) {
return watches[i]
.then(function() {
loopCb(); // Recheck this watch when it finishes
})
.catch(function() {
loopCb(); // Check the error
});
}
assert.strictEqual(!ready[i] || expected, true, 'watch shouldnt be ready: ' + i);
if(typeof error[i] !== 'undefined') {
var tr = db.createTransaction();
return tr.onError(error[i])
.then(function() {
return false;
})(loopCb);
}
i++;
loopCb();
})(cb);
}
var testWatches = function(db, cb) {
return fdbUtil.whileLoop(function(loopCb) {
var ready = [ false, false, false, false ];
var error = [ undefined, undefined, undefined, undefined ];
var watches = [];
db.doTransaction(function(tr, innerCb) {
tr.set('w0', '0')
tr.set('w3', '3');
innerCb();
})
.then(function() {
return db.doTransaction(function(tr, innerCb) {
watches[0] = tr.watch('w0');
innerCb();
});
})
.then(function() {
return db.clearAndWatch('w1');
})
.then(function(w) {
watches[1] = w.watch;
return db.setAndWatch('w2', '2');
})
.then(function(w) {
watches[2] = w.watch;
return db.getAndWatch('w3');
})
.then(function(w) {
assert.strictEqual(w.value.toString(), '3', 'get and watch');
watches[3] = w.watch;
for(var i = 0; i < watches.length; ++i) {
(function(i) {
watches[i](function(err) {
if(!err) ready[i] = true;
else error[i] = err;
});
})(i);
}
return timeoutFuture(1000);
})
.then(function() {
return checkWatches(db, watches, ready, error, false);
})
.then(function(result) {
if(!result) return; // go around the loop again
return db.doTransaction(function(tr, innerCb) {
tr.set('w0', '0');
innerCb();
})
.then(function() {
return db.clear('w1');
})
.then(function() {
return timeoutFuture(5000);
})
.then(function() {
return checkWatches(db, watches, ready, error, false);
})
.then(function(result) {
if(!result) return; // go around the loop again
return db.set('w0', 'a')
.then(function() {
return db.set('w1', 'b');
})
.then(function() {
return db.clear('w2');
})
.then(function() {
return db.xor('w3', fdb.buffer.fromByteLiteral('\xff\xff'));
})
.then(function() {
return timeoutFuture(2000);
})
.then(function() {
return checkWatches(db, watches, ready, error, true);
})
.then(function(result) {
if(result) return null; //terminate loop
return;
});
});
})(loopCb);
})(cb);
};
var testLocality = function(db, cb) {
return db.doTransaction(function(tr, innerCb) {
tr.options.setTimeout(60*1000);
tr.options.setReadSystemKeys();
fdb.locality.getBoundaryKeys(tr, '', fdb.buffer.fromByteLiteral('\xff\xff'), function(err, itr) {
if(err) return innerCb(err);
var index = 0;
var start;
var end;
itr.forEach(function(boundaryKey, loopCb) {
if(err) return loopCb(err);
start = end;
end = boundaryKey;
if(index++ == 0)
return loopCb();
tr.getKey(fdb.KeySelector.lastLessThan(end), function(err, end) {
if(err) return loopCb(err);
fdb.locality.getAddressesForKey(tr, start, function(err, startAddresses) {
if(err) return loopCb(err);
fdb.locality.getAddressesForKey(tr, end, function(err, endAddresses) {
if(err) return loopCb(err);
for(var j = 0; j < startAddresses.length; ++j) {
var found = false;
for(var k = 0; k < endAddresses.length; ++k) {
if(startAddresses[j].toString() === endAddresses[k].toString()) {
found = true;
break;
}
}
if(!found) {
return loopCb(new Error('Locality not internally consistent'));
}
}
loopCb();
});
});
});
}, innerCb);
});
}, cb);
};
var numOperations = 0;
function processOperation(context, inst, cb) {
//if(inst.op !== 'SWAP' && inst.op !== 'PUSH')
//console.log(context.prefix + ':', context.instructionIndex + '.', inst.op);
var promiseCb = function(err) {
pushError(inst, err);
cb();
};
if(inst.op === 'PUSH') {
inst.push(inst.tokens[1]);
cb();
}
else if(inst.op === 'POP') {
inst.pop()(promiseCb);
}
else if(inst.op === 'DUP') {
context.stack.pushEntry(context.stack.get(context.stack.length()-1));
cb();
}
else if(inst.op === 'EMPTY_STACK') {
context.stack = new testerUtil.Stack();
cb();
}
else if(inst.op === 'SWAP') {
inst.pop()
.then(function(index) {
assert.strictEqual(context.stack.length() > index, true, 'Cannot swap; stack too small');
index = context.stack.length() - index - 1;
if(context.stack.length() > index + 1) {
var tmp = context.stack.get(index);
context.stack.set(index, context.stack.popEntry());
context.stack.pushEntry(tmp);
}
})(promiseCb);
}
else if(inst.op === 'WAIT_FUTURE') {
inst.pop({withMetadata: true})
.then(function(stackEntry) {
context.stack.pushEntry(stackEntry);
})(promiseCb);
}
else if(inst.op === 'WAIT_EMPTY') {
inst.pop()
.then(function(waitKey) {
return waitEmpty(db, waitKey)
.then(function() {
inst.push('WAITED_FOR_EMPTY');
});
})(promiseCb);
}
else if(inst.op === 'START_THREAD') {
inst.pop()
.then(function(prefix) {
processTest(prefix, function(err, res) {
if(err) {
console.error('ERROR in Thread', prefix + ':');
console.error(err.stack);
process.exit(1);
}
});
})(promiseCb);
}
else if(inst.op === 'NEW_TRANSACTION') {
context.newTransaction();
cb();
}
else if(inst.op === 'USE_TRANSACTION') {
inst.pop()
.then(function(name) {
context.switchTransaction(name);
})(promiseCb);
}
else if(inst.op === 'SET') {
inst.pop({count: 2})
.then(function(params) {
var res = inst.tr.set(params[0], params[1]);
if(inst.isDatabase)
inst.push(res, true);
})(promiseCb);
}
else if(inst.op === 'CLEAR') {
inst.pop()
.then(function(key) {
var res = inst.tr.clear(key);
if(inst.isDatabase)
inst.push(res, true);
})(promiseCb);
}
else if(inst.op === 'CLEAR_RANGE') {
inst.pop({count: 2})
.then(function(params) {
var res = inst.tr.clearRange(params[0], params[1]);
if(inst.isDatabase)
inst.push(res, true);
})(promiseCb);
}
else if(inst.op === 'CLEAR_RANGE_STARTS_WITH') {
inst.pop()
.then(function(prefix) {
var res = inst.tr.clearRangeStartsWith(prefix);
if(inst.isDatabase)
inst.push(res, true);
})(promiseCb);
}
else if(inst.op === 'ATOMIC_OP') {
inst.pop({count: 3})
.then(function(params) {
var res = inst.tr[testerUtil.toJavaScriptName(params[0])](params[1], params[2]);
if(inst.isDatabase)
inst.push(res, true);
})(promiseCb);
}
else if(inst.op === 'COMMIT') {
inst.push(inst.tr.commit(), true);
cb();
}
else if(inst.op === 'RESET') {
inst.tr.reset();
cb();
}
else if(inst.op === 'CANCEL') {
inst.tr.cancel();
cb();
}
else if(inst.op === 'GET') {
inst.pop()
.then(function(key) {
inst.push(inst.tr.get(key), true);
})(promiseCb);
}
else if(inst.op === 'GET_RANGE') {
inst.pop({count: 5})
.then(function(params) {
var itr = inst.tr.getRange(params[0], params[1], { limit: params[2], reverse: params[3], streamingMode: params[4] });
if(inst.isDatabase) {
return itr.then(function(arr) {
return pushRange(arr, inst);
});
}
else {
return pushRange(itr, inst);
}
})(promiseCb);
}
else if(inst.op === 'GET_RANGE_SELECTOR') {
inst.pop({count: 10})
.then(function(params) {
var start = new fdb.KeySelector(params[0], params[1], params[2]);
var end = new fdb.KeySelector(params[3], params[4], params[5]);
var itr = inst.tr.getRange(start, end, { limit: params[6], reverse: params[7], streamingMode: params[8] });
if(inst.isDatabase) {
return itr.then(function(arr) {
return pushRange(arr, inst, params[9]);
});
}
else {
return pushRange(itr, inst, params[9]);
}
})(promiseCb);
}
else if(inst.op === 'GET_RANGE_STARTS_WITH') {
inst.pop({count: 4})
.then(function(params) {
var itr = inst.tr.getRangeStartsWith(params[0], { limit: params[1], reverse: params[2], streamingMode: params[3] });
if(inst.isDatabase) {
return itr.then(function(arr) {
return pushRange(arr, inst);
});
}
else {
return pushRange(itr, inst);
}
})(promiseCb);
}
else if(inst.op === 'GET_KEY') {
inst.pop({count: 4})
.then(function(params) {
var result = inst.tr.getKey(new fdb.KeySelector(params[0], params[1], params[2]))
.then(function(key) {
if(fdbUtil.buffersEqual(key.slice(0, params[3].length), params[3])) {
return key;
}
else if(fdb.buffer.toByteLiteral(key) < fdb.buffer.toByteLiteral(params[3])) {
return params[3];
}
else {
return fdbUtil.strinc(params[3]);
}
});
inst.push(result, true);
})(promiseCb);
}
else if(inst.op === 'READ_CONFLICT_RANGE') {
inst.pop({count: 2})
.then(function(params) {
inst.tr.addReadConflictRange(params[0], params[1]);
inst.push(fdb.buffer('SET_CONFLICT_RANGE'));
})(promiseCb);
}
else if(inst.op === 'WRITE_CONFLICT_RANGE') {
inst.pop({count: 2})
.then(function(params) {
inst.tr.addWriteConflictRange(params[0], params[1]);
inst.push(fdb.buffer('SET_CONFLICT_RANGE'));
})(promiseCb);
}
else if(inst.op === 'READ_CONFLICT_KEY') {
inst.pop()
.then(function(key) {
inst.tr.addReadConflictKey(key);
inst.push(fdb.buffer('SET_CONFLICT_KEY'));
})(promiseCb);
}
else if(inst.op === 'WRITE_CONFLICT_KEY') {
inst.pop()
.then(function(key) {
inst.tr.addWriteConflictKey(key);
inst.push(fdb.buffer('SET_CONFLICT_KEY'));
})(promiseCb);
}
else if(inst.op === 'DISABLE_WRITE_CONFLICT') {
inst.tr.options.setNextWriteNoWriteConflictRange();
cb();
}
else if(inst.op === 'GET_READ_VERSION') {
inst.tr.getReadVersion(function(err, res) {
if(!pushError(inst, err)) {
context.lastVersion = res;
inst.push(fdb.buffer('GOT_READ_VERSION'));
}
cb();
});
}
else if(inst.op === 'GET_COMMITTED_VERSION') {
try {
context.lastVersion = inst.tr.getCommittedVersion();
inst.push(fdb.buffer('GOT_COMMITTED_VERSION'));
cb();
}
catch(err) {
pushError(inst, err);
cb();
}
}
else if(inst.op === 'GET_VERSIONSTAMP') {
try {
inst.push(inst.tr.getVersionstamp(), true)
cb();
}
catch(err) {
pushError(inst, err);
cb();
}
}
else if(inst.op === 'SET_READ_VERSION') {
assert.notStrictEqual(typeof context.lastVersion, 'undefined', 'Cannot set read version; version has never been read');
inst.tr.setReadVersion(context.lastVersion);
cb();
}
else if(inst.op === 'ON_ERROR') {
inst.pop()
.then(function(errorCode) {
var testErr = new fdb.FDBError('', errorCode);
inst.push(inst.tr.onError(testErr), true);
})(promiseCb);
}
else if(inst.op === 'TUPLE_PACK') {
inst.pop()
.then(function(numParams) {
return inst.pop({count: numParams})
.then(function(params) {
inst.push(fdb.tuple.pack(params));
});
})(promiseCb);
}
else if(inst.op === 'TUPLE_UNPACK') {
inst.pop()
.then(function(packedTuple) {
var arr = fdb.tuple.unpack(packedTuple);
for(var i = 0; i < arr.length; ++i)
inst.push(fdb.tuple.pack([arr[i]]));
})(promiseCb);
}
else if(inst.op === 'TUPLE_RANGE') {
inst.pop()
.then(function(numParams) {
return inst.pop({count: numParams})
.then(function(params) {
var range = fdb.tuple.range(params);
inst.push(range.begin);
inst.push(range.end);
});
})(promiseCb);
}
else if(inst.op === 'TUPLE_SORT') {
inst.pop()
.then(function(numParams) {
return inst.pop({count: numParams})
.then(function(params) {
var tuples = [];
for(var i = 0; i < params.length; ++i)
tuples.push(fdb.tuple.unpack(params[i]));
tuples.sort(fdb.tuple.compare);
for(var i = 0; i < tuples.length; ++i)
inst.push(fdb.tuple.pack(tuples[i]));
});
})(promiseCb);
}
else if(inst.op === 'SUB') {
inst.pop({count: 2})
.then(function(params) {
inst.push(params[0] - params[1]);
})(promiseCb);
}
else if(inst.op === 'ENCODE_FLOAT') {
inst.pop()
.then(function(fBytes) {
inst.push(fdb.tuple.Float.fromBytes(fBytes));
})(promiseCb);
}
else if(inst.op === 'ENCODE_DOUBLE') {
inst.pop()
.then(function(dBytes) {
inst.push(fdb.tuple.Double.fromBytes(dBytes));
})(promiseCb);
}
else if(inst.op === 'DECODE_FLOAT') {
inst.pop()
.then(function(fVal) {
inst.push(fVal.toBytes());
})(promiseCb);
}
else if(inst.op === 'DECODE_DOUBLE') {
inst.pop()
.then(function(dVal) {
inst.push(dVal.toBytes());
})(promiseCb);
}
else if(inst.op === 'CONCAT') {
inst.pop({count: 2})
.then(function(params) {
if(Buffer.isBuffer(params[0])) {
inst.push(Buffer.concat([params[0], params[1]]))
}
else {
inst.push(params[0] + params[1]);
}
})(promiseCb);
}
else if(inst.op === 'LOG_STACK') {
inst.pop()
.then(function(prefix) {
return fdbUtil.whileLoop(function(loopCb) {
inst.pop({count: 100, withMetadata: true})
.then(function(items) {
if(items.length == 0) {
return null
}
return db.doTransaction(function(tr, innerCb) {
for(var index = 0; index < items.length; ++index) {
var entry = items[items.length - index - 1];
var packedSubKey = fdb.tuple.pack([context.stack.length() + index, entry.instructionIndex]);
var packedValue = fdb.tuple.pack([entry.item]);
if(packedValue.length > 40000)
packedValue = packedValue.slice(0, 40000);
tr.set(Buffer.concat([prefix, packedSubKey], prefix.length + packedSubKey.length), packedValue);
}
innerCb();
});
})(loopCb);
});
})(promiseCb);
}
else if(inst.op === 'UNIT_TESTS') {
db.options.setLocationCacheSize(100001);
db.doTransaction(function(tr, innerCb) {
tr.options.setPrioritySystemImmediate();
tr.options.setPriorityBatch();
tr.options.setCausalReadRisky();
tr.options.setCausalWriteRisky();
tr.options.setReadYourWritesDisable();
tr.options.setReadAheadDisable();
tr.options.setReadSystemKeys();
tr.options.setAccessSystemKeys();
tr.options.setDurabilityDevNullIsWebScale();
tr.options.setTimeout(60*1000);
tr.options.setRetryLimit(50);
tr.options.setMaxRetryDelay(100);
tr.options.setUsedDuringCommitProtectionDisable();
tr.options.setTransactionLoggingEnable('my_transaction');
tr.options.setReadLockAware()
tr.options.setLockAware()
tr.get(fdb.buffer.fromByteLiteral('\xff'), innerCb);
})
.then(function() {
return testWatches(db);
})
.then(function() {
return testLocality(db);
})
.then(cb)
.catch(function(err) {
cb('Unit tests failed: ' + err + "\n" + err.stack);
});
}
else if(testerUtil.startsWith(inst.op, 'DIRECTORY_')) {
context.directoryExtension.processInstruction(inst, cb);
}
else {
cb('Unrecognized operation');
}
}
function processTest(prefix, cb) {
var context = new testerUtil.Context(db, prefix, processOperation, new DirectoryExtension());
context.run(cb);
}
processTest(startTestPrefix, function(err, res) {
if(err)
process.exit(1);
});

View File

@ -1,101 +0,0 @@
/*
* tuple_test.js
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var fdb = require('../lib/fdb.js').apiVersion(510);
var fdbModule = require('../lib/fdbModule.js');
console.log(fdb.tuple.pack([-Math.pow(2,53)]));
console.log(fdb.tuple.pack([-Math.pow(2,53)+1]));
console.log(fdb.tuple.unpack(fdb.tuple.pack([-Math.pow(2,53)])));
console.log(fdb.tuple.unpack(fdb.tuple.pack([-Math.pow(2,53)+1])));
try {
console.log(fdb.tuple.unpack(fdb.buffer.fromByteLiteral('\x0d\xdf\xff\xff\xff\xff\xff\xfe')));
}
catch(err) {
console.log(err);
}
console.log(fdb.tuple.pack([0xff * 0xff]));
console.log(fdb.tuple.pack([0xffffffff + 100 ]));
console.log(fdb.buffer.printable(fdb.tuple.pack(['begin', [true, null, false], 'end'])))
console.log(fdb.tuple.unpack(fdb.buffer.fromByteLiteral('\x1a\xff\xff\xff\xff\xff\xff')));
console.log(fdb.tuple.unpack(fdb.tuple.pack(['TEST', 'herp', 1, -10, 393493, '\u0000abc', 0xffffffff + 100, true, false, [new Boolean(true), null, new Boolean(false), 0, 'asdf'], null])));
console.log(fdb.buffer.printable(fdb.tuple.pack([[[[['three']]], 'two'], 'one'])))
console.log(fdb.tuple.range(['TEST', 1]));
console.log(fdb.buffer.printable(fdb.tuple.pack([fdb.tuple.Float.fromBytes(new Buffer('402df854', 'hex')), fdb.tuple.Double.fromBytes(new Buffer('4005BF0A8B145769', 'hex')), new fdb.tuple.UUID(new Buffer('deadc0deba5eba115ca1ab1edeadc0de', 'hex'))])))
console.log(fdb.tuple.unpack(fdb.tuple.pack([fdb.tuple.Float.fromBytes(new Buffer('2734236f', 'hex'))])))
tuples = [
[1,2],
[1],
[2],
[true],
[false],
[1,true],
[1,false],
[1, []],
[1, [null]],
[1, [0]],
[1, [1]],
[1, [0,1,2]],
[null],
[]
];
tuples.sort(fdb.tuple.compare);
console.log(tuples);
tuples = [
[fdb.tuple.Float.fromBytes(new Buffer('2734236f', 'hex'))], // A really small value.
[fdb.tuple.Float.fromBytes(new Buffer('80000000', 'hex'))], // -0.0
[new fdb.tuple.Float(0.0)],
[new fdb.tuple.Float(3.14)],
[new fdb.tuple.Float(-3.14)],
[new fdb.tuple.Float(2.7182818)],
[new fdb.tuple.Float(-2.7182818)],
[fdb.tuple.Float.fromBytes(new Buffer('7f800000', 'hex'))], // Infinity
[fdb.tuple.Float.fromBytes(new Buffer('7fffffff', 'hex'))], // NaN
[fdb.tuple.Float.fromBytes(new Buffer('ffffffff', 'hex'))], // -NaN
];
tuples.sort(fdb.tuple.compare);
console.log(tuples);
// Float overruns.
const floats = [ 2.037036e90, -2.037036e90, 4.9090935e-91, -4.9090935e-91, 2.345624805922133125e14, -2.345624805922133125e14 ];
for (var i = 0; i < floats.length; i++) {
var f = floats[i];
console.log(f + " -> " + fdb.tuple.Float.fromBytes((new fdb.tuple.Float(f)).toBytes()).value);
}
// Float type errors.
try {
console.log((new fdb.tuple.Float("asdf")).toBytes());
} catch (e) {
console.log("Caught!");
console.log(e);
}
try {
console.log(fdbModule.toFloat(3.14, 2.718));
} catch (e) {
console.log("Caught!");
console.log(e);
}

View File

@ -1,276 +0,0 @@
/*
* util.js
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
"use strict";
var fdb = require('../lib/fdb.js').apiVersion(parseInt(process.argv[3]));
var util = require('../lib/fdbUtil.js');
function Stack() {
this.stack = [];
}
Stack.prototype.length = function() {
return this.stack.length;
};
Stack.prototype.get = function(index) {
return this.stack[index];
};
Stack.prototype.set = function(index, val) {
this.stack[index] = val;
};
Stack.prototype.push = function(instructionIndex, item, isFuture) {
if(typeof isFuture === 'undefined')
isFuture = false;
this.pushEntry({ instructionIndex: instructionIndex, item: item, isFuture: isFuture });
};
Stack.prototype.pushEntry = function(entry) {
this.stack.push(entry);
};
Stack.prototype.pop = function(options, callback) {
var self = this;
return fdb.future.create(function(futureCb) {
if(typeof options === 'undefined')
options = {};
var count = options.count;
if(typeof count === 'undefined')
count = 1;
var params = self.stack.slice(self.stack.length-count).reverse();
self.stack = self.stack.slice(0, self.stack.length-count);
var index = 0;
var itemCallback = function(err, val) {
if(err) {
//console.log(err);
params[index].item = fdb.tuple.pack([fdb.buffer('ERROR'), fdb.buffer(err.code.toString())]);
}
else if(val)
params[index].item = val;
else
params[index].item = fdb.buffer('RESULT_NOT_PRESENT');
params[index].isFuture = false;
if(!options.withMetadata)
params[index] = params[index].item;
index++;
processNext();
};
var processNext = function() {
while(true) {
if(index >= params.length) {
if(typeof options.count === 'undefined')
futureCb(undefined, params[0]);
else
futureCb(undefined, params);
return;
}
if(params[index].isFuture) {
params[index].item(itemCallback);
return;
}
if(!options.withMetadata)
params[index] = params[index].item;
index++;
}
};
processNext();
})(callback);
};
Stack.prototype.popEntry = function() {
return this.stack.pop();
};
function Context(db, prefix, processInstruction, directoryExtension) {
var range = fdb.tuple.range([fdb.buffer(prefix)]);
this.prefix = prefix;
this.stack = new Stack();
this.db = db;
this.next = range.begin;
this.end = range.end;
this.processInstruction = processInstruction;
this.instructionIndex = -1;
this.directoryExtension = directoryExtension;
this.trName = prefix;
}
Context.trMap = {}
Context.prototype.newTransaction = function() {
Context.trMap[this.trName] = this.db.createTransaction();
};
Context.prototype.switchTransaction = function(name) {
this.trName = name;
if(typeof Context.trMap[this.trName] === 'undefined') {
this.newTransaction();
}
};
Context.prototype.updateResults = function(results) {
this.ops = results;
this.current = 0;
this.next = fdb.KeySelector.firstGreaterThan(results[results.length-1].key);
};
var issueInstruction = function(context, cb) {
try {
var tokens = fdb.tuple.unpack(context.ops[context.current].value);
var op = tokens[0].toString();
var snapshotStr = '_SNAPSHOT';
var databaseStr = '_DATABASE';
var isSnapshot = endsWith(op, snapshotStr);
var isDatabase = endsWith(op, databaseStr);
var tr = Context.trMap[context.trName];
if(isSnapshot) {
op = op.substr(0, op.length - snapshotStr.length);
tr = tr.snapshot;
}
else if(isDatabase) {
op = op.substr(0, op.length - databaseStr.length);
tr = context.db;
}
var inst = new Instruction(context, tr, op, tokens, isDatabase, isSnapshot);
context.processInstruction(context, inst, cb);
}
catch(e) {
cb(e);
}
};
Context.prototype.run = function(cb) {
var self = this;
function getInstructions(instCb) {
self.db.doTransaction(function(tr, trCb) {
tr.getRange(self.next, self.end, { limit: 1000 } ).toArray(function(rangeErr, rangeRes) {
if(rangeErr) return trCb(rangeErr);
trCb(undefined, rangeRes);
});
}, function(err, rangeRes) {
if(err) return instCb(err);
if(rangeRes.length > 0)
self.updateResults(rangeRes);
instCb();
});
}
function readAndExecuteInstructions(loopCb) {
++self.instructionIndex;
if(!self.ops || ++self.current === self.ops.length) {
getInstructions(function(err) {
if(err) return loopCb(err);
if(self.current < self.ops.length)
issueInstruction(self, loopCb);
else
loopCb(undefined, null); // terminate the loop
});
}
else
issueInstruction(self, loopCb);
}
util.whileLoop(readAndExecuteInstructions, function(err) {
if(err) {
if(self.ops && self.current < self.ops.length)
console.error('ERROR during operation \'' + self.ops[self.current].value.toString() + '\':');
else
console.error('ERROR getting operations:');
if(err.stack)
console.error(err.stack);
else
console.error(err);
}
cb(err);
});
};
function Instruction(context, tr, op, tokens, isDatabase, isSnapshot) {
this.context = context;
this.tr = tr;
this.op = op;
this.tokens = tokens;
this.isDatabase = isDatabase;
this.isSnapshot = isSnapshot;
}
Instruction.prototype.pop = function(options, callback) {
return this.context.stack.pop(options, callback);
};
Instruction.prototype.push = function(item, isFuture) {
this.context.stack.push(this.context.instructionIndex, item, isFuture);
};
function toJavaScriptName(name) {
name = name.toString().toLowerCase();
var start = 0;
while(start < name.length) {
start = name.indexOf('_', start);
if(start === -1)
break;
name = name.slice(0, start) + name[start+1].toUpperCase() + name.slice(start+2);
}
return name.replace(/_/g, '');
}
function startsWith(str, prefixStr) {
return str.length >= prefixStr.length && str.substr(0, prefixStr.length) === prefixStr;
}
function endsWith(str, endStr) {
return str.length >= endStr.length && str.substr(str.length - endStr.length) === endStr;
}
module.exports = {
Stack: Stack,
Context: Context,
Instruction: Instruction,
toJavaScriptName: toJavaScriptName,
startsWith: startsWith,
endsWith: endsWith
};

View File

@ -472,7 +472,7 @@ module FDB
prefix && prefix &&
prefix.length > 0 && prefix.length > 0 &&
!node_containing_key(tr, prefix) && !node_containing_key(tr, prefix) &&
tr.get_range(@node_subspace.pack([prefix]), @node_subspace.pack([tr.send(:strinc, prefix)]), tr.get_range(@node_subspace.pack([prefix]), @node_subspace.pack([FDB.strinc(prefix)]),
{ :limit => 1 }).to_a.empty? { :limit => 1 }).to_a.empty?
end end

File diff suppressed because one or more lines are too long

View File

@ -1,47 +0,0 @@
#!/usr/bin/python
#
# tarball.py
#
# This source file is part of the FoundationDB open source project
#
# Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import tarfile
import argparse
import glob
def createTarball(outFile, rootDir, inFiles):
tar = tarfile.open(outFile, 'w:gz')
for fStr in inFiles:
for f in glob.glob(fStr):
if rootDir is None:
tar.add(f)
else:
tar.add(f, rootDir + "/" + f)
tar.close()
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Python tar utility')
parser.add_argument('-r', dest='rootDir', type=str, help='The root directory for files in the tarball')
parser.add_argument('outFile', type=str, help='The output tarball')
parser.add_argument('inFile', type=str, nargs='+', help='The files to put into the tarball')
args = parser.parse_args()
createTarball(args.outFile, args.rootDir, args.inFile)

View File

@ -611,7 +611,7 @@ For **RHEL/CentOS**, perform the upgrade using the rpm command:
user@host$ sudo rpm -Uvh |package-rpm-clients| \\ user@host$ sudo rpm -Uvh |package-rpm-clients| \\
|package-rpm-server| |package-rpm-server|
The ``foundationdb-clients`` package also installs the :doc:`Python <api-python>` and :doc:`C <api-c>` APIs. If your clients use :doc:`Ruby <api-ruby>`, `Java <javadoc/index.html>`_, :doc:`Node.js <api-node>`, or `Go <godoc/fdb.html>`_, follow the instructions in the corresponding language documentation to install the APIs. The ``foundationdb-clients`` package also installs the :doc:`Python <api-python>` and :doc:`C <api-c>` APIs. If your clients use :doc:`Ruby <api-ruby>`, `Java <javadoc/index.html>`_, or `Go <https://godoc.org/github.com/apple/foundationdb/bindings/go/src/fdb>`_, follow the instructions in the corresponding language documentation to install the APIs.
Test the database Test the database
----------------- -----------------

View File

@ -443,7 +443,6 @@
.. |directory-create-or-open-blurb| replace:: .. |directory-create-or-open-blurb| replace::
Opens the directory with ``path`` specified as |dir-path-type| of strings. ``path`` can also be a string, in which case it will be automatically wrapped in |dir-path-type|. All string values in a path will be converted to unicode. If the directory does not exist, it is created (creating parent directories if necessary). Opens the directory with ``path`` specified as |dir-path-type| of strings. ``path`` can also be a string, in which case it will be automatically wrapped in |dir-path-type|. All string values in a path will be converted to unicode. If the directory does not exist, it is created (creating parent directories if necessary).
.. **NOTE** this blurb is not used in api-node
.. |directory-create-or-open-return-blurb| replace:: .. |directory-create-or-open-return-blurb| replace::
Returns the directory and its contents as a |directory-subspace|. Returns the directory and its contents as a |directory-subspace|.
@ -456,7 +455,6 @@
.. |directory-move-blurb| replace:: .. |directory-move-blurb| replace::
Moves the directory at ``old_path`` to ``new_path``. There is no effect on the physical prefix of the given directory or on clients that already have the directory open. The method will |error-raise-type| an |error-type| if a directory does not exist at ``old_path``, a directory already exists at ``new_path``, or the parent directory of ``new_path`` does not exist. Moves the directory at ``old_path`` to ``new_path``. There is no effect on the physical prefix of the given directory or on clients that already have the directory open. The method will |error-raise-type| an |error-type| if a directory does not exist at ``old_path``, a directory already exists at ``new_path``, or the parent directory of ``new_path`` does not exist.
.. **NOTE** this blurb is not used in api-node
.. |directory-move-return-blurb| replace:: .. |directory-move-return-blurb| replace::
Returns the directory at its new location as a |directory-subspace|. Returns the directory at its new location as a |directory-subspace|.

File diff suppressed because it is too large Load Diff

View File

@ -12,7 +12,6 @@ The following documents give detailed descriptions of the API for each language:
api-python api-python
api-ruby api-ruby
Node.js API <api-node>
Java API <relative://javadoc/index.html> Java API <relative://javadoc/index.html>
Go API <relative://godoc/fdb.html> Go API <relative://godoc/fdb.html>
api-c api-c

View File

@ -110,7 +110,7 @@ Managing the FoundationDB service
Next steps Next steps
========== ==========
* Install the APIs for :doc:`Ruby <api-ruby>`, `Java <javadoc/index.html>`_, or :doc:`Node.js <api-node>` if you intend to use those languages. :doc:`Python <api-python>` and :doc:`C <api-c>` APIs were installed along with the ``foundationdb-clients`` package above. * Install the APIs for :doc:`Ruby <api-ruby>`, `Java <javadoc/index.html>`_, or `Go <https://godoc.org/github.com/apple/foundationdb/bindings/go/src/fdb>`_ if you intend to use those languages. :doc:`Python <api-python>` and :doc:`C <api-c>` APIs were installed along with the ``foundationdb-clients`` package above.
* See :doc:`tutorials` for samples of developing applications with FoundationDB. * See :doc:`tutorials` for samples of developing applications with FoundationDB.
* See :doc:`developer-guide` for information of interest to developers, including common design patterns and performance considerations. * See :doc:`developer-guide` for information of interest to developers, including common design patterns and performance considerations.
* See :doc:`administration` for detailed administration information. * See :doc:`administration` for detailed administration information.

View File

@ -101,7 +101,7 @@ Managing the FoundationDB service
Next steps Next steps
========== ==========
* Install the APIs for :doc:`Ruby <api-ruby>`, `Java <javadoc/index.html>`_, or :doc:`Node.js <api-node>` if you intend to use those languages. :doc:`Python <api-python>` and :doc:`C <api-c>` APIs were installed using the FoundationDB installer above. * Install the APIs for :doc:`Ruby <api-ruby>`, `Java <javadoc/index.html>`_, or `Go <https://godoc.org/github.com/apple/foundationdb/bindings/go/src/fdb>`_ if you intend to use those languages. :doc:`Python <api-python>` and :doc:`C <api-c>` APIs were installed using the FoundationDB installer above.
* See :doc:`tutorials` for samples of developing applications with FoundationDB. * See :doc:`tutorials` for samples of developing applications with FoundationDB.
* See :doc:`developer-guide` for information of interest to developers, including common design patterns and performance considerations. * See :doc:`developer-guide` for information of interest to developers, including common design patterns and performance considerations.
* See :doc:`administration` for detailed administration information. * See :doc:`administration` for detailed administration information.

View File

@ -1,90 +0,0 @@
/*
* nodejs.cs
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace vexillographer
{
class nodejs : BindingWriter
{
private static string getNodeLine(Option o)
{
string comment = "";
if(o.comment.Length > 0)
comment = String.Format("\t// {0}\n", o.comment);
string parameterComment = "";
if (o.getParameterComment().Length > 0)
parameterComment = String.Format("\t// {0}\n", "Parameter: " + o.getParameterComment());
return String.Format("{0}{1}\tADD_OPTION({2}, \"{3}\", {4}, {5});", comment, parameterComment, o.scope.ToString(), o.name, o.code, o.paramType.ToString());
}
public void writeFiles(string filePath, IEnumerable<Option> options)
{
using (var nodeFile = System.IO.File.Open(filePath,
System.IO.FileMode.Create, System.IO.FileAccess.Write))
{
TextWriter outFile = new StreamWriter(nodeFile);
outFile.NewLine = "\n";
outFile.WriteLine(@"/*
* FoundationDB Node.js API
* Copyright (c) 2013-2018 Apple Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the ""Software""), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include ""FdbOptions.h""
void FdbOptions::InitOptions() {");
foreach (Option option in options) {
if (!option.hidden && option.scope != Scope.ErrorPredicate) {
outFile.WriteLine(getNodeLine(option) + "\n");
}
}
outFile.WriteLine("}");
outFile.Flush();
}
}
}
}

View File

@ -113,7 +113,7 @@ namespace vexillographer
private static void usage() private static void usage()
{ {
Console.WriteLine("{0} inputFile {{c,cpp,java,ruby,python,nodejs}} <outputDirectory/outputFile>", Console.WriteLine("{0} inputFile {{c,cpp,java,ruby,python}} <outputDirectory/outputFile>",
Environment.GetCommandLineArgs()[0]); Environment.GetCommandLineArgs()[0]);
} }

View File

@ -42,7 +42,6 @@
<Compile Include="c.cs" /> <Compile Include="c.cs" />
<Compile Include="cpp.cs" /> <Compile Include="cpp.cs" />
<Compile Include="java.cs" /> <Compile Include="java.cs" />
<Compile Include="nodejs.cs" />
<Compile Include="python.cs" /> <Compile Include="python.cs" />
<Compile Include="ruby.cs" /> <Compile Include="ruby.cs" />
<Compile Include="vexillographer.cs" /> <Compile Include="vexillographer.cs" />
@ -74,7 +73,6 @@
<Exec Command="&quot;$(SolutionDir)bin\$(Configuration)\vexillographer.exe&quot; &quot;$(ProjectDir)fdb.options&quot; c &quot;$(ProjectDir)..\..\bindings\c\foundationdb\fdb_c_options.g.h&quot;" /> <Exec Command="&quot;$(SolutionDir)bin\$(Configuration)\vexillographer.exe&quot; &quot;$(ProjectDir)fdb.options&quot; c &quot;$(ProjectDir)..\..\bindings\c\foundationdb\fdb_c_options.g.h&quot;" />
<Exec Command="&quot;$(SolutionDir)bin\$(Configuration)\vexillographer.exe&quot; &quot;$(ProjectDir)fdb.options&quot; cpp &quot;$(ProjectDir)..\..\fdbclient\FDBOptions.g&quot;" /> <Exec Command="&quot;$(SolutionDir)bin\$(Configuration)\vexillographer.exe&quot; &quot;$(ProjectDir)fdb.options&quot; cpp &quot;$(ProjectDir)..\..\fdbclient\FDBOptions.g&quot;" />
<Exec Command="&quot;$(SolutionDir)bin\$(Configuration)\vexillographer.exe&quot; &quot;$(ProjectDir)fdb.options&quot; python &quot;$(ProjectDir)..\..\bindings\python\fdb\fdboptions.py&quot;" /> <Exec Command="&quot;$(SolutionDir)bin\$(Configuration)\vexillographer.exe&quot; &quot;$(ProjectDir)fdb.options&quot; python &quot;$(ProjectDir)..\..\bindings\python\fdb\fdboptions.py&quot;" />
<Exec Command="&quot;$(SolutionDir)bin\$(Configuration)\vexillographer.exe&quot; &quot;$(ProjectDir)fdb.options&quot; nodejs &quot;$(ProjectDir)..\..\bindings\nodejs\src\FdbOptions.g.cpp&quot;" />
<Exec Command="&quot;$(SolutionDir)bin\$(Configuration)\vexillographer.exe&quot; &quot;$(ProjectDir)fdb.options&quot; ruby &quot;$(ProjectDir)..\..\bindings\ruby\lib\fdboptions.rb&quot;" /> <Exec Command="&quot;$(SolutionDir)bin\$(Configuration)\vexillographer.exe&quot; &quot;$(ProjectDir)fdb.options&quot; ruby &quot;$(ProjectDir)..\..\bindings\ruby\lib\fdboptions.rb&quot;" />
</Target> </Target>
</Project> </Project>

View File

@ -64,22 +64,6 @@ Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "MSIInstaller", "packaging\m
{E2939DAA-238E-4970-96C4-4C57980F93BD} = {E2939DAA-238E-4970-96C4-4C57980F93BD} {E2939DAA-238E-4970-96C4-4C57980F93BD} = {E2939DAA-238E-4970-96C4-4C57980F93BD}
EndProjectSection EndProjectSection
EndProject EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fdb_node_0_8", "bindings\nodejs\fdb_node_0_8.vcxproj", "{E936E200-689E-49FD-8463-32FE763F1860}"
ProjectSection(ProjectDependencies) = postProject
{CACB2C8E-3E55-4309-A411-2A9C56C6C1CB} = {CACB2C8E-3E55-4309-A411-2A9C56C6C1CB}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fdb_node_0_10", "bindings\nodejs\fdb_node_0_10.vcxproj", "{E22D4EF8-E75D-4281-93F9-A9F73936DE54}"
ProjectSection(ProjectDependencies) = postProject
{CACB2C8E-3E55-4309-A411-2A9C56C6C1CB} = {CACB2C8E-3E55-4309-A411-2A9C56C6C1CB}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fdb_node", "bindings\nodejs\fdb_node.vcxproj", "{9463CB25-DCA0-9D45-C46E-0A8E68EE7FAE}"
ProjectSection(ProjectDependencies) = postProject
{E936E200-689E-49FD-8463-32FE763F1860} = {E936E200-689E-49FD-8463-32FE763F1860}
{E22D4EF8-E75D-4281-93F9-A9F73936DE54} = {E22D4EF8-E75D-4281-93F9-A9F73936DE54}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fdb_flow", "bindings\flow\fdb_flow.vcxproj", "{2BA0A5E2-EB4C-4A32-948C-CBAABD77AF87}" Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fdb_flow", "bindings\flow\fdb_flow.vcxproj", "{2BA0A5E2-EB4C-4A32-948C-CBAABD77AF87}"
ProjectSection(ProjectDependencies) = postProject ProjectSection(ProjectDependencies) = postProject
{00AC9087-0378-4872-9992-DF267CF12ACB} = {00AC9087-0378-4872-9992-DF267CF12ACB} {00AC9087-0378-4872-9992-DF267CF12ACB} = {00AC9087-0378-4872-9992-DF267CF12ACB}