Merge pull request #207 from cie/java-add-missing-dispose

Update the Java bindings to call add missing dispose calls.
This commit is contained in:
Alec Grieser 2017-12-14 13:38:15 -08:00 committed by GitHub Enterprise
commit 24c6439456
80 changed files with 2493 additions and 2593 deletions

View File

@ -64,8 +64,8 @@ testers = {
'ruby' : Tester('ruby', _absolute_path('ruby/tests/tester.rb'), 64, 23, MAX_API_VERSION),
'java' : Tester('java', _java_cmd + 'StackTester', 2040, 500, MAX_API_VERSION, types=ALL_TYPES),
'java_async' : Tester('java', _java_cmd + 'AsyncStackTester', 2040, 500, MAX_API_VERSION, types=ALL_TYPES),
'java_completable' : Tester('java', _java_completable_cmd + 'StackTester', 2040, 500, MAX_API_VERSION, types=ALL_TYPES),
'java_completable_async' : Tester('java', _java_completable_cmd + 'AsyncStackTester', 2040, 500, MAX_API_VERSION, types=ALL_TYPES),
'java_completable' : Tester('java', _java_completable_cmd + 'StackTester', 2040, 510, MAX_API_VERSION, types=ALL_TYPES),
'java_completable_async' : Tester('java', _java_completable_cmd + 'AsyncStackTester', 2040, 510, MAX_API_VERSION, types=ALL_TYPES),
'go' : Tester('go', _absolute_path('go/build/bin/_stacktester'), 63, 200, MAX_API_VERSION),
'flow' : Tester('flow', _absolute_path('flow/bin/fdb_flow_tester'), 63, 500, MAX_API_VERSION),
}

View File

@ -0,0 +1,146 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
The style for code written within the FDB Java bindings.
Note that this style guide grew up somewhat organically from
the idiosyncracies of the committers involved. It aims to
be at least a little idiomatically Java while at the same time
trying not to look too incongruous when compared to the style
of our core products (e.g., fdbserver). It also isn't
borrowed from some other style guide, because that would
be too easy.
-->
<!DOCTYPE module PUBLIC
"-//Puppy Crawl//DTD Check Configuration 1.3//EN"
"http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
<module name="Checker">
<module name="SuppressionFilter">
<property name="file" value="suppressions.xml"/>
</module>
<module name="TreeWalker">
<property name="tabWidth" value="4"/>
<!-- Blocks -->
<module name="AvoidNestedBlocks"/>
<module name="EmptyBlock"/>
<module name="EmptyCatchBlock"/>
<module name="LeftCurly">
<property name="option" value="eol"/>
<property name="ignoreEnums" value="false"/>
</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 -->
<!-- We might get some helpful errors if we turned this on, but not right now.
<module name="DesignForExtension"/>
-->
<!-- We have some classes violating this. It seems like a reasonable thing to add, but it is technically API breaking.
<module name="FinalClass"/>
-->
<module name="HideUtilityClassConstructor"/>
<module name="MutableException"/>
<module name="OneTopLevelClass"/>
<!-- Coding -->
<module name="CovariantEquals"/>
<module name="DefaultComesLast"/>
<module name="EmptyStatement"/>
<module name="EqualsHashCode"/>
<module name="FallThrough"/>
<!-- We should probably clean these up at some point, but not today.
<module name="MagicNumber">
<property name="ignoreNumbers" value="-1, 0, 1, 2, 255, 65535"/>
<property name="ignoreHashCodeMethod" value="true"/>
</module>
-->
<module name="MissingSwitchDefault"/>
<module name="NoClone"/>
<module name="PackageDeclaration"/>
<module name="SimplifyBooleanExpression"/>
<module name="SimplifyBooleanReturn"/>
<module name="StringLiteralEquality"/>
<module name="SuperClone"/>
<module name="SuperFinalize"/>
<!-- Imports -->
<module name="CustomImportOrder">
<property name="customImportOrderRules" value="STATIC###STANDARD_JAVA_PACKAGE###SAME_PACKAGE(3)"/>
</module>
<module name="AvoidStarImport"/>
<module name="UnusedImports"/>
<module name="RedundantImport"/>
<!-- Javadoc -->
<!-- TODO -->
<!-- Miscellaneous -->
<module name="ArrayTypeStyle"/>
<module name="CommentsIndentation"/>
<module name="Indentation"/>
<module name="OuterTypeFilename"/>
<module name="UpperEll"/>
<!-- Modifiers -->
<module name="ModifierOrder"/>
<module name="RedundantModifier"/>
<!-- Naming conventions -->
<module name="CatchParameterName">
<property name="format" value="^(e\d*|t\d*|ex\d*|err\d*)$"/>
</module>
<module name="ClassTypeParameterName"/>
<module name="InterfaceTypeParameterName"/>
<module name="LocalFinalVariableName"/>
<module name="LocalVariableName"/>
<module name="MemberName"/>
<module name="MethodName">
<property name="applyToProtected" value="false"/>
<property name="applyToPackage" value="false"/>
<property name="applyToPrivate" value="false"/>
</module>
<module name="MethodTypeParameterName"/>
<module name="PackageName"/>
<module name="ParameterName"/>
<module name="StaticVariableName"/>
<module name="TypeName"/>
<!-- Whitespace -->
<module name="EmptyForInitializerPad"/>
<module name="EmptyForIteratorPad"/>
<module name="GenericWhitespace"/>
<module name="MethodParamPad"/>
<module name="NoLineWrap"/>
<module name="NoWhitespaceAfter">
<property name="tokens" value="AT, INC, DEC, UNARY_MINUS, UNARY_PLUS, BNOT, LNOT, DOT, ARRAY_DECLARATOR, INDEX_OP, METHOD_REF"/>
</module>
<module name="NoWhitespaceBefore">
<property name="allowLineBreaks" value="true"/>
<property name="tokens" value="COMMA, SEMI, POST_INC, POST_DEC, DOT, ELLIPSIS, METHOD_REF"/>
</module>
<module name="OperatorWrap">
<property name="option" value="eol"/>
</module>
<module name="ParenPad"/>
<module name="SeparatorWrap">
<property name="option" value="eol"/>
<property name="tokens" value="COMMA"/>
</module>
<module name="SeparatorWrap">
<property name="option" value="nl"/>
<property name="tokens" value="DOT"/>
</module>
<module name="TypecastParenPad">
<property name="option" value="nospace"/>
</module>
<module name="WhitespaceAfter">
<property name="tokens" value="SEMI"/>
</module>
</module>
</module>

View File

@ -29,7 +29,6 @@ import static org.junit.Assert.fail;
import java.util.ArrayList;
import java.util.List;
import com.apple.foundationdb.tuple.ByteArrayUtil;
import org.junit.Test;
/**

View File

@ -25,9 +25,12 @@ import java.util.concurrent.Executor;
/**
* The {@code Cluster} represents a connection to a physical set of cooperating machines
* running FoundationDB. A {@code Cluster} is opened with a reference to a cluster file.
* running FoundationDB. A {@code Cluster} is opened with a reference to a cluster file.<br>
* <br>
* <b>Note:</b> {@code Cluster} objects must be {@link #close closed} when no longer in use
* in order to free any associated resources.
*/
public class Cluster extends DefaultDisposableImpl implements Disposable {
public class Cluster extends NativeObjectWrapper {
private ClusterOptions options;
private final Executor executor;
@ -52,12 +55,19 @@ public class Cluster extends DefaultDisposableImpl implements Disposable {
*
* @return a set of cluster-specific options affecting this {@code Cluster}
*/
public ClusterOptions options() { return options; }
public ClusterOptions options() {
return options;
}
@Override
protected void finalize() throws Throwable {
dispose();
super.finalize();
try {
checkUnclosed("Cluster");
close();
}
finally {
super.finalize();
}
}
/**
@ -88,7 +98,7 @@ public class Cluster extends DefaultDisposableImpl implements Disposable {
}
@Override
protected void disposeInternal(long cPtr) {
protected void closeInternal(long cPtr) {
Cluster_dispose(cPtr);
}

View File

@ -35,10 +35,12 @@ import java.util.function.Function;
* in the {@link TransactionContext} interface. When used on a {@code Database} these
* methods will call {@code Transaction#commit()} after user code has been
* executed. These methods will not return successfully until {@code commit()} has
* returned successfully.
*
* returned successfully.<br>
* <br>
* <b>Note:</b> {@code Database} objects must be {@link #close closed} when no longer
* in use in order to free any associated resources.
*/
public interface Database extends Disposable, TransactionContext {
public interface Database extends AutoCloseable, TransactionContext {
/**
* Creates a {@link Transaction} that operates on this {@code Database}.<br>
* <br>
@ -114,7 +116,7 @@ public interface Database extends Disposable, TransactionContext {
*/
@Override
default <T> CompletableFuture<T> readAsync(
Function<? super ReadTransaction, CompletableFuture<T>> retryable) {
Function<? super ReadTransaction, ? extends CompletableFuture<T>> retryable) {
return readAsync(retryable, getExecutor());
}
@ -130,7 +132,7 @@ public interface Database extends Disposable, TransactionContext {
* @see #readAsync(Function)
*/
<T> CompletableFuture<T> readAsync(
Function<? super ReadTransaction, CompletableFuture<T>> retryable, Executor e);
Function<? super ReadTransaction, ? extends CompletableFuture<T>> retryable, Executor e);
/**
* Runs a transactional function against this {@code Database} with retry logic.
@ -192,7 +194,7 @@ public interface Database extends Disposable, TransactionContext {
*/
@Override
default <T> CompletableFuture<T> runAsync(
Function<? super Transaction, CompletableFuture<T>> retryable) {
Function<? super Transaction, ? extends CompletableFuture<T>> retryable) {
return runAsync(retryable, getExecutor());
}
@ -208,5 +210,13 @@ public interface Database extends Disposable, TransactionContext {
* @see #run(Function)
*/
<T> CompletableFuture<T> runAsync(
Function<? super Transaction, CompletableFuture<T>> retryable, Executor e);
Function<? super Transaction, ? extends CompletableFuture<T>> retryable, Executor e);
/**
* Close the {@code Database} object and release any associated resources. This must be called at
* least once after the {@code Database} object is no longer in use. This can be called multiple
* times, but care should be taken that it is not in use in another thread at the time of the call.
*/
@Override
void close();
}

View File

@ -1,33 +0,0 @@
/*
* Disposable.java
*
* 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.
*/
package com.apple.foundationdb;
/**
* A FoundationDB object with native resources that can be freed. It is not mandatory to call
* {@link Disposable#dispose()} most of the time, as disposal will happen at finalization.
*/
public interface Disposable {
/**
* Dispose of the object. This can be called multiple times, but care should be
* taken that an object is not in use in another thread at the time of the call.
*/
void dispose();
}

View File

@ -64,7 +64,7 @@ public class FDB {
static class DaemonThreadFactory implements ThreadFactory {
private final ThreadFactory factory;
public DaemonThreadFactory(ThreadFactory factory) {
DaemonThreadFactory(ThreadFactory factory) {
this.factory = factory;
}
@ -81,7 +81,8 @@ public class FDB {
final int apiVersion;
private volatile boolean netStarted = false;
private volatile boolean netStopped = false;
final private Semaphore netRunning = new Semaphore(1);
volatile boolean warnOnUnclosed = true;
private final Semaphore netRunning = new Semaphore(1);
private final NetworkOptions options;
static {
@ -102,21 +103,8 @@ public class FDB {
private FDB(int apiVersion) {
this.apiVersion = apiVersion;
options = new NetworkOptions(new OptionConsumer() {
@Override
public void setOption(int code, byte[] parameter) {
Network_setOption(code, parameter);
}
});
Runtime.getRuntime().addShutdownHook(new Thread(
new Runnable(){
@Override
public void run() {
FDB.this.stopNetwork();
}
}
));
options = new NetworkOptions(this::Network_setOption);
Runtime.getRuntime().addShutdownHook(new Thread(this::stopNetwork));
}
/**
@ -128,7 +116,9 @@ public class FDB {
*
* @return a set of options affecting this instance of the FoundationDB API
*/
public NetworkOptions options() { return options; }
public NetworkOptions options() {
return options;
}
/**
* Select the version for the client API. An exception will be thrown if the
@ -147,7 +137,7 @@ public class FDB {
*
* @return the FoundationDB API object
*/
public synchronized static FDB selectAPIVersion(final int version) throws FDBException {
public static synchronized FDB selectAPIVersion(final int version) throws FDBException {
if(singleton != null) {
if(version != singleton.apiVersion) {
throw new IllegalArgumentException(
@ -155,12 +145,34 @@ public class FDB {
}
return singleton;
}
if(version < 500)
throw new IllegalArgumentException("API version not supported (minimum 500)");
if(version < 510)
throw new IllegalArgumentException("API version not supported (minimum 510)");
if(version > 510)
throw new IllegalArgumentException("API version not supported (maximum 510)");
Select_API_version(version);
return singleton = new FDB(version);
FDB fdb = new FDB(version);
return singleton = fdb;
}
/**
* Enables or disables the stderr warning that is printed whenever an object with FoundationDB
* native resources is garbage collected without being closed. By default, this feature is enabled.
*
* @param warnOnUnclosed Whether the warning should be printed for unclosed objects
*/
public void setUnclosedWarning(boolean warnOnUnclosed) {
this.warnOnUnclosed = warnOnUnclosed;
}
// Singleton is initialized to null and only set once by a call to selectAPIVersion
static FDB getInstance() {
if(singleton != null) {
return singleton;
}
throw new IllegalStateException("API version has not been selected");
}
/**
@ -277,7 +289,10 @@ public class FDB {
f = new FutureCluster(Cluster_create(clusterFilePath), e);
}
Cluster c = f.join();
return c.openDatabase(e);
Database db = c.openDatabase(e);
c.close();
return db;
}
/**
@ -321,38 +336,37 @@ public class FDB {
Network_setup();
netStarted = true;
e.execute(new Runnable() {
@Override
public void run() {
boolean acquired = false;
try {
while(!acquired) {
try {
// make attempt to avoid a needless deadlock
synchronized (FDB.this) {
if(netStopped) {
return;
}
}
netRunning.acquire();
acquired = true;
} catch(InterruptedException e) {}
}
e.execute(() -> {
boolean acquired = false;
try {
while(!acquired) {
try {
Network_run();
} catch (Throwable t) {
System.err.println("Unhandled error in FoundationDB network thread: " + t.getMessage());
// eat this error. we have nowhere to send it.
}
} finally {
if(acquired) {
netRunning.release();
}
synchronized (FDB.this) {
netStopped = true;
// make attempt to avoid a needless deadlock
synchronized (FDB.this) {
if(netStopped) {
return;
}
}
netRunning.acquire();
acquired = true;
} catch(InterruptedException err) {
// Swallow thread interruption
}
}
try {
Network_run();
} catch (Throwable t) {
System.err.println("Unhandled error in FoundationDB network thread: " + t.getMessage());
// eat this error. we have nowhere to send it.
}
} finally {
if(acquired) {
netRunning.release();
}
synchronized (FDB.this) {
netStopped = true;
}
}
});
}
@ -388,7 +402,10 @@ public class FDB {
// that we will never again be able to call runNetwork()
netRunning.acquire();
return;
} catch (InterruptedException e) {}
} catch (InterruptedException e) {
// If the thread is interrupted while trying to acquire
// the semaphore, we just want to try again.
}
}
}

View File

@ -28,7 +28,7 @@ import java.util.function.Function;
import com.apple.foundationdb.async.AsyncUtil;
class FDBDatabase extends DefaultDisposableImpl implements Database, Disposable, OptionConsumer {
class FDBDatabase extends NativeObjectWrapper implements Database, OptionConsumer {
private DatabaseOptions options;
private final Executor executor;
@ -57,7 +57,7 @@ class FDBDatabase extends DefaultDisposableImpl implements Database, Disposable,
}
}
} finally {
t.dispose();
t.close();
}
}
@ -67,52 +67,63 @@ class FDBDatabase extends DefaultDisposableImpl implements Database, Disposable,
}
@Override
public <T> CompletableFuture<T> runAsync(final Function<? super Transaction, CompletableFuture<T>> retryable, Executor e) {
public <T> CompletableFuture<T> runAsync(final Function<? super Transaction, ? extends CompletableFuture<T>> retryable, Executor e) {
final AtomicReference<Transaction> trRef = new AtomicReference<>(createTransaction(e));
final AtomicReference<T> returnValue = new AtomicReference<>();
return AsyncUtil.whileTrue(() -> {
CompletableFuture<T> process = AsyncUtil.applySafely(retryable, trRef.get());
return process.thenComposeAsync(returnVal ->
return AsyncUtil.composeHandleAsync(process.thenComposeAsync(returnVal ->
trRef.get().commit().thenApply(o -> {
returnValue.set(returnVal);
return false;
})
, e).handleAsync((value, t) -> {
if(t == null)
return CompletableFuture.completedFuture(value);
if(!(t instanceof RuntimeException))
throw new CompletionException(t);
return trRef.get().onError(t).thenApply(newTr -> {
trRef.set(newTr);
return true;
});
}, e).thenCompose(x -> x);
}, e).thenApply(o -> {
trRef.get().dispose();
return returnValue.get();
});
}), e),
(value, t) -> {
if(t == null)
return CompletableFuture.completedFuture(value);
if(!(t instanceof RuntimeException))
throw new CompletionException(t);
return trRef.get().onError(t).thenApply(newTr -> {
trRef.set(newTr);
return true;
});
}, e);
}, e)
.thenApply(o -> returnValue.get())
.whenComplete((v, t) -> trRef.get().close());
}
@Override
public <T> CompletableFuture<T> readAsync(
Function<? super ReadTransaction, CompletableFuture<T>> retryable, Executor e) {
Function<? super ReadTransaction, ? extends CompletableFuture<T>> retryable, Executor e) {
return this.runAsync(retryable, e);
}
@Override
protected void finalize() throws Throwable {
dispose();
super.finalize();
try {
checkUnclosed("Database");
close();
}
finally {
super.finalize();
}
}
@Override
public Transaction createTransaction(Executor e) {
pointerReadLock.lock();
Transaction tr = null;
try {
Transaction tr = new FDBTransaction(Database_createTransaction(getPtr()), this, e);
tr = new FDBTransaction(Database_createTransaction(getPtr()), this, e);
tr.options().setUsedDuringCommitProtectionDisable();
return tr;
} catch(RuntimeException err) {
if(tr != null) {
tr.close();
}
throw err;
} finally {
pointerReadLock.unlock();
}
@ -134,7 +145,7 @@ class FDBDatabase extends DefaultDisposableImpl implements Database, Disposable,
}
@Override
protected void disposeInternal(long cPtr) {
protected void closeInternal(long cPtr) {
Database_dispose(cPtr);
}

View File

@ -26,10 +26,11 @@ import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.function.Function;
import com.apple.foundationdb.async.*;
import com.apple.foundationdb.async.AsyncIterable;
import com.apple.foundationdb.async.AsyncUtil;
import com.apple.foundationdb.tuple.ByteArrayUtil;
class FDBTransaction extends DefaultDisposableImpl implements Disposable, Transaction, OptionConsumer {
class FDBTransaction extends NativeObjectWrapper implements Transaction, OptionConsumer {
private final Database database;
private final Executor executor;
private final TransactionOptions options;
@ -60,7 +61,7 @@ class FDBTransaction extends DefaultDisposableImpl implements Disposable, Transa
@Override
public AsyncIterable<KeyValue> getRange(KeySelector begin, KeySelector end,
int limit, boolean reverse, StreamingMode mode) {
return RangeQuery.start(FDBTransaction.this, true, begin, end, limit, reverse, mode);
return new RangeQuery(FDBTransaction.this, true, begin, end, limit, reverse, mode);
}
@Override
public AsyncIterable<KeyValue> getRange(KeySelector begin, KeySelector end,
@ -137,7 +138,7 @@ class FDBTransaction extends DefaultDisposableImpl implements Disposable, Transa
@Override
public <T> CompletableFuture<T> readAsync(
Function<? super ReadTransaction, CompletableFuture<T>> retryable) {
Function<? super ReadTransaction, ? extends CompletableFuture<T>> retryable) {
return AsyncUtil.applySafely(retryable, this);
}
@ -183,7 +184,7 @@ class FDBTransaction extends DefaultDisposableImpl implements Disposable, Transa
public CompletableFuture<Long> getReadVersion() {
pointerReadLock.lock();
try {
return new FutureVersion( Transaction_getReadVersion(getPtr()), executor);
return new FutureVersion(Transaction_getReadVersion(getPtr()), executor);
} finally {
pointerReadLock.unlock();
}
@ -200,7 +201,7 @@ class FDBTransaction extends DefaultDisposableImpl implements Disposable, Transa
private CompletableFuture<byte[]> get_internal(byte[] key, boolean isSnapshot) {
pointerReadLock.lock();
try {
return new FutureResult( Transaction_get(getPtr(), key, isSnapshot), executor);
return new FutureResult(Transaction_get(getPtr(), key, isSnapshot), executor);
} finally {
pointerReadLock.unlock();
}
@ -217,7 +218,7 @@ class FDBTransaction extends DefaultDisposableImpl implements Disposable, Transa
private CompletableFuture<byte[]> getKey_internal(KeySelector selector, boolean isSnapshot) {
pointerReadLock.lock();
try {
return new FutureKey( Transaction_getKey(getPtr(),
return new FutureKey(Transaction_getKey(getPtr(),
selector.getKey(), selector.orEqual(), selector.getOffset(), isSnapshot), executor);
} finally {
pointerReadLock.unlock();
@ -230,7 +231,7 @@ class FDBTransaction extends DefaultDisposableImpl implements Disposable, Transa
@Override
public AsyncIterable<KeyValue> getRange(KeySelector begin, KeySelector end,
int limit, boolean reverse, StreamingMode mode) {
return RangeQuery.start(this, false, begin, end, limit, reverse, mode);
return new RangeQuery(this, false, begin, end, limit, reverse, mode);
}
@Override
public AsyncIterable<KeyValue> getRange(KeySelector begin, KeySelector end,
@ -300,6 +301,7 @@ class FDBTransaction extends DefaultDisposableImpl implements Disposable, Transa
return database;
}
// Users of this function must close the returned FutureResults when finished
protected FutureResults getRange_internal(
KeySelector begin, KeySelector end,
int rowLimit, int targetBytes, int streamingMode,
@ -356,7 +358,7 @@ class FDBTransaction extends DefaultDisposableImpl implements Disposable, Transa
@Override
public <T> CompletableFuture<T> runAsync(
Function<? super Transaction, CompletableFuture<T>> retryable) {
Function<? super Transaction, ? extends CompletableFuture<T>> retryable) {
return AsyncUtil.applySafely(retryable, this);
}
@ -367,7 +369,7 @@ class FDBTransaction extends DefaultDisposableImpl implements Disposable, Transa
@Override
public <T> CompletableFuture<T> readAsync(
Function<? super ReadTransaction, CompletableFuture<T>> retryable) {
Function<? super ReadTransaction, ? extends CompletableFuture<T>> retryable) {
return AsyncUtil.applySafely(retryable, this);
}
@ -495,13 +497,13 @@ class FDBTransaction extends DefaultDisposableImpl implements Disposable, Transa
return f.thenApply(v -> tr)
.whenComplete((v, t) -> {
if(t != null) {
tr.dispose();
tr.close();
}
});
} finally {
pointerReadLock.unlock();
if(!transactionOwner) {
dispose();
close();
}
}
}
@ -527,10 +529,20 @@ class FDBTransaction extends DefaultDisposableImpl implements Disposable, Transa
// Must hold pointerReadLock when calling
private FDBTransaction transfer() {
FDBTransaction tr = new FDBTransaction(getPtr(), database, executor);
tr.options().setUsedDuringCommitProtectionDisable();
transactionOwner = false;
return tr;
FDBTransaction tr = null;
try {
tr = new FDBTransaction(getPtr(), database, executor);
tr.options().setUsedDuringCommitProtectionDisable();
transactionOwner = false;
return tr;
}
catch(RuntimeException err) {
if(tr != null) {
tr.close();
}
throw err;
}
}
@Override
@ -545,11 +557,17 @@ class FDBTransaction extends DefaultDisposableImpl implements Disposable, Transa
@Override
protected void finalize() throws Throwable {
dispose();
try {
checkUnclosed("Transaction");
close();
}
finally {
super.finalize();
}
}
@Override
protected void disposeInternal(long cPtr) {
protected void closeInternal(long cPtr) {
if(transactionOwner) {
Transaction_dispose(cPtr);
}

View File

@ -32,7 +32,7 @@ class FutureCluster extends NativeFuture<Cluster> {
}
@Override
public Cluster getIfDone_internal() throws FDBException {
protected Cluster getIfDone_internal(long cPtr) throws FDBException {
return new Cluster(FutureCluster_get(cPtr), executor);
}

View File

@ -32,7 +32,7 @@ class FutureDatabase extends NativeFuture<Database> {
}
@Override
public Database getIfDone_internal() throws FDBException {
protected Database getIfDone_internal(long cPtr) throws FDBException {
return new FDBDatabase(FutureDatabase_get(cPtr), executor);
}

View File

@ -29,7 +29,7 @@ class FutureKey extends NativeFuture<byte[]> {
}
@Override
public byte[] getIfDone_internal() throws FDBException {
protected byte[] getIfDone_internal(long cPtr) throws FDBException {
return FutureKey_get(cPtr);
}

View File

@ -29,7 +29,7 @@ class FutureResult extends NativeFuture<byte[]> {
}
@Override
public byte[] getIfDone_internal() throws FDBException {
protected byte[] getIfDone_internal(long cPtr) throws FDBException {
return FutureResult_get(cPtr);
}

View File

@ -30,11 +30,11 @@ class FutureResults extends NativeFuture<RangeResultInfo> {
@Override
protected void postMarshal() {
// We can't dispose because this class actually marshals on-demand
// We can't close because this class actually marshals on-demand
}
@Override
public RangeResultInfo getIfDone_internal() throws FDBException {
protected RangeResultInfo getIfDone_internal(long cPtr) throws FDBException {
FDBException err = Future_getError(cPtr);
if(!err.isSuccess()) {
@ -45,11 +45,23 @@ class FutureResults extends NativeFuture<RangeResultInfo> {
}
public RangeResultSummary getSummary() {
return FutureResults_getSummary(cPtr);
try {
pointerReadLock.lock();
return FutureResults_getSummary(getPtr());
}
finally {
pointerReadLock.unlock();
}
}
public RangeResult getResults() {
return FutureResults_get(cPtr);
try {
pointerReadLock.lock();
return FutureResults_get(getPtr());
}
finally {
pointerReadLock.unlock();
}
}
private native RangeResultSummary FutureResults_getSummary(long ptr) throws FDBException;

View File

@ -29,7 +29,7 @@ class FutureStrings extends NativeFuture<String[]> {
}
@Override
public String[] getIfDone_internal() throws FDBException {
protected String[] getIfDone_internal(long cPtr) throws FDBException {
return FutureStrings_get(cPtr);
}

View File

@ -29,7 +29,7 @@ class FutureVersion extends NativeFuture<Long> {
}
@Override
Long getIfDone_internal() throws FDBException {
protected Long getIfDone_internal(long cPtr) throws FDBException {
return FutureVersion_get(cPtr);
}

View File

@ -29,7 +29,7 @@ class FutureVoid extends NativeFuture<Void> {
}
@Override
public Void getIfDone_internal() throws FDBException {
protected Void getIfDone_internal(long cPtr) throws FDBException {
// With "future-cleanup" we get rid of FutureVoid_get and replace instead
// with a get on the error and throw if the error is not success.
FDBException err = Future_getError(cPtr);

View File

@ -179,4 +179,6 @@ class JNIUtil {
return OS.OSX;
throw new IllegalStateException("Unknown or unsupported OS: " + osname);
}
private JNIUtil() {}
}

View File

@ -71,8 +71,8 @@ public class KeySelector {
*
* @return a newly created {@code KeySelector}
*/
public static KeySelector lastLessThan( byte[] key ) {
return new KeySelector( key, false, 0 );
public static KeySelector lastLessThan(byte[] key) {
return new KeySelector(key, false, 0);
}
/**
@ -82,8 +82,8 @@ public class KeySelector {
*
* @return a newly created {@code KeySelector}
*/
public static KeySelector lastLessOrEqual( byte[] key ) {
return new KeySelector( key, true, 0 );
public static KeySelector lastLessOrEqual(byte[] key) {
return new KeySelector(key, true, 0);
}
/**
@ -93,8 +93,8 @@ public class KeySelector {
*
* @return a newly created {@code KeySelector}
*/
public static KeySelector firstGreaterThan( byte[] key ) {
return new KeySelector( key, true, +1 );
public static KeySelector firstGreaterThan(byte[] key) {
return new KeySelector(key, true, +1);
}
/**
@ -104,8 +104,8 @@ public class KeySelector {
*
* @return a newly created {@code KeySelector}
*/
public static KeySelector firstGreaterOrEqual( byte[] key ) {
return new KeySelector( key, false, +1 );
public static KeySelector firstGreaterOrEqual(byte[] key) {
return new KeySelector(key, false, +1);
}
/**
@ -126,8 +126,8 @@ public class KeySelector {
*
* @return a newly created {@code KeySelector} that is offset by a number of keys.
*/
public KeySelector add( int offset ) {
return new KeySelector( getKey(), orEqual(), getOffset() + offset );
public KeySelector add(int offset) {
return new KeySelector(getKey(), orEqual(), getOffset() + offset);
}
/**

View File

@ -22,14 +22,15 @@ package com.apple.foundationdb;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.Executor;
import java.util.function.BiFunction;
import com.apple.foundationdb.async.AsyncIterable;
import com.apple.foundationdb.async.AsyncIterator;
import com.apple.foundationdb.async.AsyncUtil;
import com.apple.foundationdb.async.CloseableAsyncIterator;
import com.apple.foundationdb.tuple.ByteArrayUtil;
/**
@ -41,7 +42,7 @@ import com.apple.foundationdb.tuple.ByteArrayUtil;
*/
public class LocalityUtil {
/**
* Returns a {@code AsyncIterable} of keys {@code k} such that
* Returns a {@code CloseableAsyncIterator} of keys {@code k} such that
* {@code begin <= k < end} and {@code k} is located at the start of a
* contiguous range stored on a single server.<br>
*<br>
@ -54,12 +55,12 @@ public class LocalityUtil {
*
* @return an sequence of keys denoting the start of single-server ranges
*/
public static AsyncIterable<byte[]> getBoundaryKeys(Database db, byte[] begin, byte[] end) {
public static CloseableAsyncIterator<byte[]> getBoundaryKeys(Database db, byte[] begin, byte[] end) {
return getBoundaryKeys_internal(db.createTransaction(), begin, end);
}
/**
* Returns a {@code AsyncIterable} of keys {@code k} such that
* Returns a {@code CloseableAsyncIterator} of keys {@code k} such that
* {@code begin <= k < end} and {@code k} is located at the start of a
* contiguous range stored on a single server.<br>
*<br>
@ -80,13 +81,13 @@ public class LocalityUtil {
*
* @return an sequence of keys denoting the start of single-server ranges
*/
public static AsyncIterable<byte[]> getBoundaryKeys(Transaction tr, byte[] begin, byte[] end) {
Transaction local = tr.getDatabase().createTransaction();
public static CloseableAsyncIterator<byte[]> getBoundaryKeys(Transaction tr, byte[] begin, byte[] end) {
Transaction local = tr.getDatabase().createTransaction(tr.getExecutor());
CompletableFuture<Long> readVersion = tr.getReadVersion();
if(readVersion.isDone() && !readVersion.isCompletedExceptionally()) {
local.setReadVersion(readVersion.getNow(null));
}
return new BoundaryIterable(local, begin, end);
return new BoundaryIterator(local, begin, end);
}
/**
@ -111,128 +112,129 @@ public class LocalityUtil {
return ((FDBTransaction)tr).getAddressesForKey(key);
}
private static AsyncIterable<byte[]> getBoundaryKeys_internal(Transaction tr, byte[] begin, byte[] end) {
return new BoundaryIterable(tr, begin, end);
private static CloseableAsyncIterator<byte[]> getBoundaryKeys_internal(Transaction tr, byte[] begin, byte[] end) {
return new BoundaryIterator(tr, begin, end);
}
static class BoundaryIterable implements AsyncIterable<byte[]> {
final Transaction tr;
final byte[] begin;
static class BoundaryIterator implements CloseableAsyncIterator<byte[]> {
Transaction tr;
byte[] begin;
byte[] lastBegin;
final byte[] end;
final AsyncIterable<KeyValue> firstGet;
public BoundaryIterable(Transaction tr, byte[] begin, byte[] end) {
AsyncIterator<KeyValue> block;
private CompletableFuture<Boolean> nextFuture;
private boolean closed;
BoundaryIterator(Transaction tr, byte[] begin, byte[] end) {
this.tr = tr;
this.begin = Arrays.copyOf(begin, begin.length);
this.end = Arrays.copyOf(end, end.length);
lastBegin = begin;
tr.options().setReadSystemKeys();
tr.options().setLockAware();
firstGet = tr.getRange(keyServersForKey(begin), keyServersForKey(end));
block = firstGet.iterator();
nextFuture = AsyncUtil.composeHandleAsync(block.onHasNext(), handler, tr.getExecutor());
closed = false;
}
@Override
public AsyncIterator<byte[]> iterator() {
return new BoundaryIterator();
public CompletableFuture<Boolean> onHasNext() {
return nextFuture;
}
@Override
public CompletableFuture<List<byte[]>> asList() {
return AsyncUtil.collect(this, tr.getExecutor());
}
@Override
public boolean hasNext() {
return nextFuture.join();
}
class BoundaryIterator implements AsyncIterator<byte[]> {
AsyncIterator<KeyValue> block = BoundaryIterable.this.firstGet.iterator();
Transaction tr = BoundaryIterable.this.tr;
byte[] begin = BoundaryIterable.this.begin;
byte[] lastBegin = begin;
private CompletableFuture<Boolean> nextFuture;
public BoundaryIterator() {
nextFuture = block.onHasNext().handleAsync(handler, tr.getExecutor()).thenCompose(x -> x);
CompletableFuture<Boolean> restartGet() {
if(ByteArrayUtil.compareUnsigned(begin, end) >= 0) {
return AsyncUtil.READY_FALSE;
}
lastBegin = begin;
tr.options().setReadSystemKeys();
block = tr.getRange(
keyServersForKey(begin),
keyServersForKey(end)).iterator();
nextFuture = AsyncUtil.composeHandleAsync(block.onHasNext(), handler, tr.getExecutor());
return nextFuture;
}
BiFunction<Boolean, Throwable, CompletableFuture<Boolean>> handler = new BiFunction<Boolean, Throwable, CompletableFuture<Boolean>>() {
@Override
public CompletableFuture<Boolean> onHasNext() {
return nextFuture;
}
@Override
public boolean hasNext() {
return nextFuture.join();
}
CompletableFuture<Boolean> restartGet() {
if(ByteArrayUtil.compareUnsigned(begin, end) >= 0) {
return CompletableFuture.completedFuture(false);
public CompletableFuture<Boolean> apply(Boolean b, Throwable o) {
if(b != null) {
return CompletableFuture.completedFuture(b);
}
lastBegin = begin;
tr.options().setReadSystemKeys();
block = tr.getRange(
keyServersForKey(begin),
keyServersForKey(end)).iterator();
nextFuture = block.onHasNext().handleAsync(handler, tr.getExecutor()).thenCompose(x -> x);
return nextFuture;
}
BiFunction<Boolean, Throwable, CompletableFuture<Boolean>> handler = new BiFunction<Boolean, Throwable, CompletableFuture<Boolean>>() {
@Override
public CompletableFuture<Boolean> apply(Boolean b, Throwable o) {
if(b != null) {
return CompletableFuture.completedFuture(b);
}
if(o instanceof FDBException) {
FDBException err = (FDBException) o;
if(err.getCode() == 1007 && !Arrays.equals(begin, lastBegin)) {
BoundaryIterator.this.tr.dispose();
BoundaryIterator.this.tr =
BoundaryIterator.this.tr.getDatabase().createTransaction();
return restartGet();
}
}
if(!(o instanceof RuntimeException))
throw new CompletionException(o);
CompletableFuture<Transaction> onError = BoundaryIterator.this.tr.onError((RuntimeException) o);
return onError.thenComposeAsync(tr -> {
BoundaryIterator.this.tr = tr;
if(o instanceof FDBException) {
FDBException err = (FDBException) o;
if(err.getCode() == 1007 && !Arrays.equals(begin, lastBegin)) {
Executor executor = BoundaryIterator.this.tr.getExecutor();
BoundaryIterator.this.tr.close();
BoundaryIterator.this.tr = BoundaryIterator.this.tr.getDatabase().createTransaction(executor);
return restartGet();
}, tr.getExecutor());
}
}
};
@Override
public byte[] next() {
if(!nextFuture.isDone()) {
throw new IllegalStateException("Call to next without hasNext()=true");
if(!(o instanceof RuntimeException))
throw new CompletionException(o);
CompletableFuture<Transaction> onError = BoundaryIterator.this.tr.onError(o);
return onError.thenComposeAsync(tr -> {
BoundaryIterator.this.tr = tr;
return restartGet();
}, tr.getExecutor());
}
};
@Override
public byte[] next() {
if(!nextFuture.isDone()) {
throw new IllegalStateException("Call to next without hasNext()=true");
}
KeyValue o = block.next();
byte[] key = o.getKey();
byte[] suffix = Arrays.copyOfRange(key, 13, key.length);
BoundaryIterator.this.begin = ByteArrayUtil.join(suffix, new byte[] { (byte)0 });
nextFuture = AsyncUtil.composeHandleAsync(block.onHasNext(), handler, tr.getExecutor());
return suffix;
}
@Override
public void remove() {
throw new UnsupportedOperationException("Boundary keys are read-only");
}
@Override
public void close() {
BoundaryIterator.this.tr.close();
closed = true;
}
@Override
protected void finalize() throws Throwable {
try {
if(FDB.getInstance().warnOnUnclosed && !closed) {
System.err.println("CloseableAsyncIterator not closed (getBoundaryKeys)");
}
if(!closed) {
close();
}
KeyValue o = block.next();
byte[] key = o.getKey();
byte[] suffix = Arrays.copyOfRange(key, 13, key.length);
BoundaryIterator.this.begin = ByteArrayUtil.join(suffix, new byte[] { (byte)0 });
nextFuture = block.onHasNext().handleAsync(handler, tr.getExecutor()).thenCompose(x -> x);
return suffix;
}
@Override
public void remove() {
throw new UnsupportedOperationException("Boundary keys are read-only");
}
@Override
public void cancel() {
// TODO Auto-generated method stub
}
@Override
public void dispose() {
BoundaryIterator.this.tr.dispose();
finally {
super.finalize();
}
}
}
private static Charset ASCII = Charset.forName("US-ASCII");
private static final Charset ASCII = Charset.forName("US-ASCII");
static byte[] keyServersForKey(byte[] key) {
return ByteArrayUtil.join(new byte[] { (byte)255 },
"/keyServers/".getBytes(ASCII),

View File

@ -22,9 +22,14 @@ package com.apple.foundationdb;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
abstract class NativeFuture<T> extends CompletableFuture<T> {
protected final long cPtr;
abstract class NativeFuture<T> extends CompletableFuture<T> implements AutoCloseable {
private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
protected final Lock pointerReadLock = rwl.readLock();
private long cPtr;
protected NativeFuture(long cPtr) {
this.cPtr = cPtr;
@ -36,44 +41,93 @@ abstract class NativeFuture<T> extends CompletableFuture<T> {
// constructor of this class because a quickly completing future can
// lead to a race where the marshalWhenDone tries to run on an
// unconstructed subclass.
//
// Since this must be called from a constructor, we assume that close
// cannot be called concurrently.
protected void registerMarshalCallback(Executor executor) {
Future_registerCallback(cPtr, () -> executor.execute(this::marshalWhenDone));
if(cPtr != 0) {
Future_registerCallback(cPtr, () -> executor.execute(this::marshalWhenDone));
}
}
private void marshalWhenDone() {
try {
T val = getIfDone_internal();
postMarshal();
complete(val);
} catch(FDBException t) {
assert(t.getCode() != 2015); // future_not_set not possible
if(t.getCode() != 1102) { // future_released
completeExceptionally(t);
T val = null;
boolean shouldComplete = false;
try {
pointerReadLock.lock();
if(cPtr != 0) {
val = getIfDone_internal(cPtr);
shouldComplete = true;
}
}
finally {
pointerReadLock.unlock();
}
if(shouldComplete) {
complete(val);
}
} catch(FDBException t) {
assert(t.getCode() != 1102 && t.getCode() != 2015); // future_released, future_not_set not possible
completeExceptionally(t);
} catch(Throwable t) {
completeExceptionally(t);
} finally {
postMarshal();
}
}
protected void postMarshal() {
dispose();
close();
}
abstract T getIfDone_internal() throws FDBException;
protected abstract T getIfDone_internal(long cPtr) throws FDBException;
public void dispose() {
Future_releaseMemory(cPtr);
@Override
public void close() {
long ptr = 0;
rwl.writeLock().lock();
if(cPtr != 0) {
ptr = cPtr;
cPtr = 0;
}
rwl.writeLock().unlock();
if(ptr != 0) {
Future_dispose(ptr);
if(!isDone()) {
completeExceptionally(new IllegalStateException("Future has been closed"));
}
}
}
@Override
protected void finalize() throws Throwable {
Future_dispose(cPtr);
public boolean cancel(boolean mayInterruptIfRunning) {
boolean result = super.cancel(mayInterruptIfRunning);
try {
rwl.readLock().lock();
if(cPtr != 0) {
Future_cancel(cPtr);
}
return result;
}
finally {
rwl.readLock().unlock();
}
}
@Override
public T join() {
Future_blockUntilReady(cPtr);
return super.join();
protected long getPtr() {
// we must have a read lock for this function to make sense, however it
// does not make sense to take the lock here, since the code that uses
// the result must inherently have the read lock itself.
assert(rwl.getReadHoldCount() > 0);
if(cPtr == 0)
throw new IllegalStateException("Cannot access closed object");
return cPtr;
}
private native void Future_registerCallback(long cPtr, Runnable callback);

View File

@ -1,5 +1,5 @@
/*
* DefaultDisposableImpl.java
* NativeObjectWrapper.java
*
* This source file is part of the FoundationDB open source project
*
@ -23,60 +23,69 @@ package com.apple.foundationdb;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
abstract class DefaultDisposableImpl implements Disposable {
abstract class NativeObjectWrapper implements AutoCloseable {
private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
protected final Lock pointerReadLock = rwl.readLock();
private boolean disposed = false;
private boolean closed = false;
private long cPtr;
public DefaultDisposableImpl() {
}
public DefaultDisposableImpl(long cPtr) {
NativeObjectWrapper(long cPtr) {
this.cPtr = cPtr;
if(this.cPtr == 0)
this.disposed = true;
this.closed = true;
}
public boolean isDisposed() {
public boolean isClosed() {
// we must have a read lock for this function to make sense, however it
// does not make sense to take the lock here, since the code that uses
// the result must inherently have the read lock itself.
assert( rwl.getReadHoldCount() > 0 );
assert(rwl.getReadHoldCount() > 0);
return disposed;
return closed;
}
public void checkUnclosed(String context) {
try {
if(FDB.getInstance().warnOnUnclosed && !closed) {
System.err.println(context + " not closed");
}
}
catch(Exception e) {
// Eat this error. This is called from the finalizer,
// so there isn't much we can do.
}
}
@Override
public void dispose() {
public void close() {
rwl.writeLock().lock();
long ptr = 0;
try {
if(disposed)
if(closed)
return;
ptr = cPtr;
this.cPtr = 0;
disposed = true;
closed = true;
} finally {
rwl.writeLock().unlock();
}
disposeInternal(ptr);
closeInternal(ptr);
}
protected long getPtr() {
// we must have a read lock for this function to make sense, however it
// does not make sense to take the lock here, since the code that uses
// the result must inherently have the read lock itself.
assert( rwl.getReadHoldCount() > 0 );
assert(rwl.getReadHoldCount() > 0);
if(this.disposed)
throw new IllegalStateException("Cannot access disposed object");
if(this.closed)
throw new IllegalStateException("Cannot access closed object");
return this.cPtr;
}
protected abstract void disposeInternal(long cPtr);
protected abstract void closeInternal(long cPtr);
}

View File

@ -25,10 +25,10 @@ import java.nio.ByteOrder;
import java.nio.charset.Charset;
abstract class OptionsSet {
private final static Charset CHARSET_UTF8 = Charset.forName("UTF-8");
private static final Charset CHARSET_UTF8 = Charset.forName("UTF-8");
OptionConsumer consumer;
public OptionsSet(OptionConsumer provider) {
OptionsSet(OptionConsumer provider) {
this.consumer = provider;
}
@ -39,7 +39,9 @@ abstract class OptionsSet {
*
* @return target of option set calls
*/
public OptionConsumer getOptionConsumer() { return consumer; }
public OptionConsumer getOptionConsumer() {
return consumer;
}
protected void setOption(int code) {
consumer.setOption(code, null);
@ -57,6 +59,6 @@ abstract class OptionsSet {
ByteBuffer b = ByteBuffer.allocate(8);
b.order(ByteOrder.LITTLE_ENDIAN);
b.putLong(param);
consumer.setOption(code, b.array() );
consumer.setOption(code, b.array());
}
}

View File

@ -64,7 +64,7 @@ public class Range {
public static Range startsWith(byte[] prefix) {
if(prefix == null)
throw new NullPointerException("prefix cannot be null");
return new Range( prefix, ByteArrayUtil.strinc(prefix) );
return new Range(prefix, ByteArrayUtil.strinc(prefix));
}
/**
@ -113,7 +113,7 @@ public class Range {
*/
@Override
public String toString() {
return "Range(" + (begin == null ? "null" : "\"" + ByteArrayUtil.printable(begin) + "\"")
+ ", " + (end == null ? "null" : "\"" + ByteArrayUtil.printable(end) + "\"") + ")";
return "Range(" + (begin == null ? "null" : "\"" + ByteArrayUtil.printable(begin) + "\"") +
", " + (end == null ? "null" : "\"" + ByteArrayUtil.printable(end) + "\"") + ")";
}
}

View File

@ -25,7 +25,6 @@ import java.util.NoSuchElementException;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiConsumer;
import java.util.function.Function;
import com.apple.foundationdb.async.AsyncIterable;
import com.apple.foundationdb.async.AsyncIterator;
@ -53,12 +52,10 @@ class RangeQuery implements AsyncIterable<KeyValue>, Iterable<KeyValue> {
private final int rowLimit;
private final boolean reverse;
private final StreamingMode streamingMode;
private final FutureResults firstChunk;
private RangeQuery(FDBTransaction transaction, boolean isSnapshot,
RangeQuery(FDBTransaction transaction, boolean isSnapshot,
KeySelector begin, KeySelector end, int rowLimit,
boolean reverse, StreamingMode streamingMode,
FutureResults firstChunk) {
boolean reverse, StreamingMode streamingMode) {
this.tr = transaction;
this.begin = begin;
this.end = end;
@ -66,17 +63,6 @@ class RangeQuery implements AsyncIterable<KeyValue>, Iterable<KeyValue> {
this.rowLimit = rowLimit;
this.reverse = reverse;
this.streamingMode = streamingMode;
this.firstChunk = firstChunk;
}
static RangeQuery start(FDBTransaction transaction, boolean isSnapshot,
KeySelector begin, KeySelector end, int rowLimit,
boolean reverse, StreamingMode streamingMode) {
// start the first fetch...
FutureResults firstChunk = transaction.getRange_internal(begin, end,
rowLimit, 0, streamingMode.code(), 1, isSnapshot, reverse);
return new RangeQuery(transaction, isSnapshot, begin, end, rowLimit, reverse, streamingMode, firstChunk);
}
/**
@ -95,20 +81,16 @@ class RangeQuery implements AsyncIterable<KeyValue>, Iterable<KeyValue> {
// if the streaming mode is EXACT, try and grab things as one chunk
if(mode == StreamingMode.EXACT) {
CompletableFuture<RangeResultInfo> range = tr.getRange_internal(
FutureResults range = tr.getRange_internal(
this.begin, this.end, this.rowLimit, 0, StreamingMode.EXACT.code(),
1, this.snapshot, this.reverse);
return range.thenApply(new Function<RangeResultInfo, List<KeyValue>>() {
@Override
public List<KeyValue> apply(RangeResultInfo o) {
return o.get().values;
}
});
return range.thenApply(result -> result.get().values)
.whenComplete((result, e) -> range.close());
}
// If the streaming mode is not EXACT, simply collect the results of an iteration into a list
return AsyncUtil.collect(
new RangeQuery(tr, snapshot, begin, end, rowLimit, reverse, mode, firstChunk), tr.getExecutor());
new RangeQuery(tr, snapshot, begin, end, rowLimit, reverse, mode), tr.getExecutor());
}
/**
@ -130,16 +112,16 @@ class RangeQuery implements AsyncIterable<KeyValue>, Iterable<KeyValue> {
// There is the chance for parallelism in the two "chunks" for fetched data
private RangeResult chunk = null;
private RangeResult nextChunk = null;
private boolean fetchOutstanding = true;
private boolean fetchOutstanding = false;
private byte[] prevKey = null;
private int index = 0;
// The first request is made in the constructor for the parent Iterable, so start at 1
private int iteration = 1;
private int iteration = 0;
private KeySelector begin;
private KeySelector end;
private int rowsRemaining;
private FutureResults fetchingChunk;
private CompletableFuture<Boolean> nextFuture;
private boolean isCancelled = false;
@ -151,19 +133,7 @@ class RangeQuery implements AsyncIterable<KeyValue>, Iterable<KeyValue> {
this.reverse = reverse;
this.streamingMode = streamingMode;
// Register for completion, etc. on the first chunk. Some of the fields in
// this class were initialized with the knowledge that this fetch is active
// at creation time. This set normally happens in startNextFetch, but
// the first fetch has already been configured and started.
CompletableFuture<Boolean> promise = new CompletableFuture<Boolean>();
nextFuture = promise;
// FIXME: should we propagate cancellation into the first chuck fetch?
// This would invalidate the whole iterable, not just the iterator
//promise.onCancelledCancel(firstChunk);
// FIXME: I have no idea if this will just get garbage collected away, etc.
firstChunk.whenComplete(new FetchComplete(firstChunk, promise));
startNextFetch();
}
private synchronized boolean mainChunkIsTheLast() {
@ -174,54 +144,61 @@ class RangeQuery implements AsyncIterable<KeyValue>, Iterable<KeyValue> {
final FutureResults fetchingChunk;
final CompletableFuture<Boolean> promise;
public FetchComplete(FutureResults fetch, CompletableFuture<Boolean> promise) {
FetchComplete(FutureResults fetch, CompletableFuture<Boolean> promise) {
this.fetchingChunk = fetch;
this.promise = promise;
}
@Override
public void accept(RangeResultInfo data, Throwable error) {
final RangeResultSummary summary;
try {
final RangeResultSummary summary;
if(error != null) {
promise.completeExceptionally(error);
if(error instanceof Error) {
throw (Error)error;
if(error != null) {
promise.completeExceptionally(error);
if(error instanceof Error) {
throw (Error) error;
}
return;
}
return;
}
summary = data.getSummary();
if(summary.lastKey == null) {
promise.complete(Boolean.FALSE);
return;
}
synchronized(AsyncRangeIterator.this) {
fetchOutstanding = false;
// adjust the total number of rows we should ever fetch
rowsRemaining -= summary.keyCount;
// set up the next fetch
if (reverse) {
end = KeySelector.firstGreaterOrEqual(summary.lastKey);
} else {
begin = KeySelector.firstGreaterThan(summary.lastKey);
summary = data.getSummary();
if(summary.lastKey == null) {
promise.complete(Boolean.FALSE);
return;
}
// If this is the first fetch or the main chunk is exhausted
if(chunk == null || index == chunk.values.size()) {
nextChunk = null;
chunk = data.get();
index = 0;
} else {
nextChunk = data.get();
}
}
synchronized(AsyncRangeIterator.this) {
fetchOutstanding = false;
promise.complete(Boolean.TRUE);
// adjust the total number of rows we should ever fetch
rowsRemaining -= summary.keyCount;
// set up the next fetch
if(reverse) {
end = KeySelector.firstGreaterOrEqual(summary.lastKey);
}
else {
begin = KeySelector.firstGreaterThan(summary.lastKey);
}
// If this is the first fetch or the main chunk is exhausted
if(chunk == null || index == chunk.values.size()) {
nextChunk = null;
chunk = data.get();
index = 0;
}
else {
nextChunk = data.get();
}
}
promise.complete(Boolean.TRUE);
}
finally {
fetchingChunk.close();
}
}
}
@ -231,24 +208,18 @@ class RangeQuery implements AsyncIterable<KeyValue>, Iterable<KeyValue> {
if(isCancelled)
return;
if(mainChunkIsTheLast())
if(chunk != null && mainChunkIsTheLast())
return;
fetchOutstanding = true;
nextChunk = null;
FutureResults fetchingChunk = tr.getRange_internal(begin, end,
fetchingChunk = tr.getRange_internal(begin, end,
rowsLimited ? rowsRemaining : 0, 0, streamingMode.code(),
++iteration, snapshot, reverse);
CompletableFuture<Boolean> promise = new CompletableFuture<Boolean>();
nextFuture = promise;
// FIXME: BOOOOOOOOOO! Maybe we don't need this?
// promise.onCancelledCancel(fetchingChunk);
// TODO: again, I have no idea if this will get out-of-scope collected right away
fetchingChunk.whenComplete(new FetchComplete(fetchingChunk, promise));
nextFuture = new CompletableFuture<>();
fetchingChunk.whenComplete(new FetchComplete(fetchingChunk, nextFuture));
}
@Override
@ -263,14 +234,14 @@ class RangeQuery implements AsyncIterable<KeyValue>, Iterable<KeyValue> {
// We have a chunk and are still working though it
if(index < chunk.values.size()) {
return CompletableFuture.completedFuture(true);
return AsyncUtil.READY_TRUE;
}
// If we are at the end of the current chunk there is either:
// - no more data -or-
// - we are already fetching the next block
return mainChunkIsTheLast() ?
CompletableFuture.completedFuture(false) :
AsyncUtil.READY_FALSE :
nextFuture;
}
@ -340,11 +311,7 @@ class RangeQuery implements AsyncIterable<KeyValue>, Iterable<KeyValue> {
public synchronized void cancel() {
isCancelled = true;
nextFuture.cancel(true);
}
@Override
public void dispose() {
cancel();
fetchingChunk.cancel(true);
}
}
}

View File

@ -21,7 +21,6 @@
package com.apple.foundationdb;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.function.Function;
@ -62,7 +61,7 @@ public interface ReadTransactionContext {
* to {@code retryable}
*/
<T> CompletableFuture<T> readAsync(
Function<? super ReadTransaction, CompletableFuture<T>> retryable);
Function<? super ReadTransaction, ? extends CompletableFuture<T>> retryable);
/**
* Retrieves the {@link Executor} used by this {@code TransactionContext} when running

View File

@ -22,6 +22,7 @@ package com.apple.foundationdb;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
import com.apple.foundationdb.tuple.Tuple;
/**
@ -68,9 +69,12 @@ import com.apple.foundationdb.tuple.Tuple;
* <br>
* <b>Note:</b> Java transactions automatically set the {@link TransactionOptions#setUsedDuringCommitProtectionDisable}
* option. This is because the Java bindings disallow use of {@code Transaction} objects after {@link #onError}
* is called.
* is called.<br>
* <br>
* <b>Note:</b> {@code Transaction} objects must be {@link #close closed} when no longer
* in use in order to free any associated resources.
*/
public interface Transaction extends Disposable, ReadTransaction, TransactionContext {
public interface Transaction extends AutoCloseable, ReadTransaction, TransactionContext {
/**
* Return special-purpose, read-only view of the database. Reads done through this interface are known as "snapshot reads".
@ -364,6 +368,13 @@ public interface Transaction extends Disposable, ReadTransaction, TransactionCon
*/
@Override
<T> CompletableFuture<T> runAsync(
Function<? super Transaction, CompletableFuture<T>> retryable);
Function<? super Transaction, ? extends CompletableFuture<T>> retryable);
/**
* Close the {@code Transaction} object and release any associated resources. This must be called at
* least once after the {@code Transaction} object is no longer in use. This can be called multiple
* times, but care should be taken that it is not in use in another thread at the time of the call.
*/
@Override
void close();
}

View File

@ -59,5 +59,5 @@ public interface TransactionContext extends ReadTransactionContext {
* @return a {@code CompletableFuture} that will be set to the value returned by the last call
* to {@code retryable}
*/
<T> CompletableFuture<T> runAsync(Function<? super Transaction, CompletableFuture<T>> retryable);
<T> CompletableFuture<T> runAsync(Function<? super Transaction, ? extends CompletableFuture<T>> retryable);
}

View File

@ -24,8 +24,6 @@ import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.concurrent.CompletableFuture;
import com.apple.foundationdb.Disposable;
/**
* A version of {@code Iterator} that allows for non-blocking iteration over elements.
* Calls to {@link #next()} will not block if {@link #onHasNext()} has been called
@ -34,10 +32,10 @@ import com.apple.foundationdb.Disposable;
*
* @param <T> the type of object yielded by {@code next()}
*/
public interface AsyncIterator<T> extends Iterator<T>, Disposable {
public interface AsyncIterator<T> extends Iterator<T> {
/**
* Returns a asynchronous signal for the presence of more elements in the sequence.
* Once the future returned by {@link #onHasNext()} is ready, the next call to
* Once the future returned by {@code onHasNext()} is ready, the next call to
* {@link #next} will not block.
*
* @return a {@code CompletableFuture} that will be set to {@code true} if {@code next()}
@ -78,11 +76,4 @@ public interface AsyncIterator<T> extends Iterator<T>, Disposable {
* Cancels any outstanding asynchronous work associated with this {@code AsyncIterator}.
*/
void cancel();
/**
* Cancel this {@code AsyncIterable} and dispose of associated resources. Equivalent
* to calling {@link AsyncIterator#cancel()}.
*/
@Override
void dispose();
}

View File

@ -29,6 +29,7 @@ import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
@ -36,6 +37,26 @@ import java.util.function.Supplier;
* Provided utilities for using and manipulating {@link CompletableFuture}s.
*/
public class AsyncUtil {
/**
* A completed future of type {@link Void}. In particular, it is completed to {@code null},
* but that shouldn't really matter for the {@link Void} type. This can be used instead
* of creating a new future if one wants to signal that some asynchronous task has
* already been completed.
*/
public static final CompletableFuture<Void> DONE = CompletableFuture.completedFuture(null);
/**
* A completed future of type {@link Boolean} that is set to {@code true}. This can be
* used instead of creating a new future if one wants to signal that some task has
* already been completed with a {@code true} result.
*/
public static final CompletableFuture<Boolean> READY_TRUE = CompletableFuture.completedFuture(Boolean.TRUE);
/**
* A completed future of type {@link Boolean} that is set to {@code false}. This can be
* used instead of creating a new future if one wants to signal that some task has
* already been completed with a {@code false} result.
*/
public static final CompletableFuture<Boolean> READY_FALSE = CompletableFuture.completedFuture(Boolean.FALSE);
/**
* Run {@code Function} {@code func}, returning all caught exceptions as a
* {@code CompletableFuture} in an error state.
@ -46,7 +67,7 @@ public class AsyncUtil {
* @return the output of {@code func}, or a {@code CompletableFuture} carrying any exception
* caught in the process.
*/
public static <I,O> CompletableFuture<O> applySafely( Function<I, CompletableFuture<O>> func, I value ) {
public static <I,O> CompletableFuture<O> applySafely(Function<I, ? extends CompletableFuture<O>> func, I value) {
try {
return func.apply(value);
} catch (RuntimeException e) {
@ -56,6 +77,87 @@ public class AsyncUtil {
}
}
/**
* Run the {@code consumer} on each element of the iterable in order. The future will
* complete with either the first error encountered by either the iterable itself
* or by the consumer provided or with {@code null} if the future completes
* successfully. Items are processed in order from the iterable, and each item
* will be processed only after the item before it has finished processing.
*
* @param iterable the source of data over from which to consume
* @param consumer operation to apply to each item
* @param <V> type of the items returned by the iterable
*
* @return a future that is ready once the asynchronous operation completes
*/
public static <V> CompletableFuture<Void> forEach(final AsyncIterable<V> iterable, final Consumer<? super V> consumer) {
return forEachRemaining(iterable.iterator(), consumer);
}
/**
* Run the {@code consumer} on each element of the iterable in order. The future will
* complete with either the first error encountered by either the iterable itself
* or by the consumer provided or with {@code null} if the future completes
* successfully. Items are processed in order from the iterable, and each item
* will be processed only after the item before it has finished processing. Asynchronous
* tasks needed to complete this operation are scheduled on the provided executor.
*
* @param iterable the source of data over from which to consume
* @param consumer operation to apply to each item
* @param executor executor on which to schedule asynchronous tasks
* @param <V> type of the items returned by the iterable
*
* @return a future that is ready once the asynchronous operation completes
*/
public static <V> CompletableFuture<Void> forEach(final AsyncIterable<V> iterable, final Consumer<? super V> consumer, final Executor executor) {
return forEachRemaining(iterable.iterator(), consumer, executor);
}
/**
* Run the {@code consumer} on each element remaining in the iterator in order. The future will
* complete with either the first error encountered by either the iterator itself
* or by the consumer provided or with {@code null} if the future completes
* successfully. Items are processed in order from the iterator, and each item
* will be processed only after the item before it has finished processing.
*
* @param iterator the source of data over from which to consume
* @param consumer operation to apply to each item
* @param <V> type of the items returned by the iterator
*
* @return a future that is ready once the asynchronous operation completes
*/
public static <V> CompletableFuture<Void> forEachRemaining(final AsyncIterator<V> iterator, final Consumer<? super V> consumer) {
return forEachRemaining(iterator, consumer, DEFAULT_EXECUTOR);
}
/**
* Run the {@code consumer} on each element remaining if the iterator in order. The future will
* complete with either the first error encountered by either the iterator itself
* or by the consumer provided or with {@code null} if the future completes
* successfully. Items are processed in order from the iterator, and each item
* will be processed only after the item before it has finished processing. Asynchronous
* tasks needed to complete this operation are scheduled on the provided executor.
*
* @param iterator the source of data over from which to consume
* @param consumer operation to apply to each item
* @param executor executor on which to schedule asynchronous tasks
* @param <V> type of the items returned by the iterator
*
* @return a future that is ready once the asynchronous operation completes
*/
public static <V> CompletableFuture<Void> forEachRemaining(final AsyncIterator<V> iterator, final Consumer<? super V> consumer, final Executor executor) {
return iterator.onHasNext().thenComposeAsync(hasAny -> {
if (hasAny) {
return whileTrue(() -> {
consumer.accept(iterator.next());
return iterator.onHasNext();
}, executor);
} else {
return DONE;
}
}, executor);
}
/**
* Iterates over a set of items and returns the result as a list.
*
@ -68,6 +170,18 @@ public class AsyncUtil {
return collect(iterable, DEFAULT_EXECUTOR);
}
/**
* Iterates over a set of items and returns the remaining results as a list.
*
* @param iterator the source of data over which to iterate. This function will exhaust the iterator.
*
* @return a {@code CompletableFuture} which will be set to the amalgamation of results
* from iteration.
*/
public static <V> CompletableFuture<List<V>> collectRemaining(final AsyncIterator<V> iterator) {
return collectRemaining(iterator, DEFAULT_EXECUTOR);
}
/**
* Iterates over a set of items and returns the result as a list.
*
@ -78,29 +192,21 @@ public class AsyncUtil {
* from iteration.
*/
public static <V> CompletableFuture<List<V>> collect(final AsyncIterable<V> iterable, final Executor executor) {
final AsyncIterator<V> it = iterable.iterator();
final List<V> accumulator = new LinkedList<V>();
return collectRemaining(iterable.iterator(), executor);
}
// The condition of the while loop is simply "onHasNext()" returning true
Supplier<CompletableFuture<Boolean>> condition = new Supplier<CompletableFuture<Boolean>>() {
@Override
public CompletableFuture<Boolean> get() {
return it.onHasNext().thenApply(new Function<Boolean, Boolean>() {
@Override
public Boolean apply(Boolean o) {
if(o) {
accumulator.add(it.next());
}
return o;
}
});
}
};
CompletableFuture<Void> complete = whileTrue(condition, executor);
CompletableFuture<List<V>> result = tag(complete, accumulator);
return result;
/**
* Iterates over a set of items and returns the remaining results as a list.
*
* @param iterator the source of data over which to iterate. This function will exhaust the iterator.
* @param executor the {@link Executor} to use for asynchronous operations
*
* @return a {@code CompletableFuture} which will be set to the amalgamation of results
* from iteration.
*/
public static <V> CompletableFuture<List<V>> collectRemaining(final AsyncIterator<V> iterator, final Executor executor) {
final List<V> accumulator = new LinkedList<>();
return tag(forEachRemaining(iterator, accumulator::add, executor), accumulator);
}
/**
@ -116,52 +222,94 @@ public class AsyncUtil {
return new AsyncIterable<T>() {
@Override
public AsyncIterator<T> iterator() {
final AsyncIterator<V> it = iterable.iterator();
return new AsyncIterator<T>() {
@Override
public void remove() {
it.remove();
}
@Override
public CompletableFuture<Boolean> onHasNext() {
return it.onHasNext();
}
@Override
public boolean hasNext() {
return it.hasNext();
}
@Override
public T next() {
return func.apply(it.next());
}
@Override
public void cancel() {
it.cancel();
}
@Override
public void dispose() {
it.dispose();
}
};
return mapIterator(iterable.iterator(), func);
}
@Override
public CompletableFuture<List<T>> asList() {
return iterable.asList().thenApply(new Function<List<V>, List<T>>() {
@Override
public List<T> apply(List<V> o) {
ArrayList<T> out = new ArrayList<T>(o.size());
for(V in : o)
out.add(func.apply(in));
return out;
}
});
final List<T> accumulator = new LinkedList<>();
return tag(AsyncUtil.forEach(iterable, value -> accumulator.add(func.apply(value))), accumulator);
}
};
}
/**
* Map an {@code AsyncIterator} into an {@code AsyncIterator} of another type or with
* each element modified in some fashion.
*
* @param iterator input
* @param func mapping function applied to each element
* @return a new iterator with each element mapped to a different value
*/
public static <V, T> AsyncIterator<T> mapIterator(final AsyncIterator<V> iterator,
final Function<V, T> func) {
return new AsyncIterator<T>() {
@Override
public void remove() {
iterator.remove();
}
@Override
public CompletableFuture<Boolean> onHasNext() {
return iterator.onHasNext();
}
@Override
public boolean hasNext() {
return iterator.hasNext();
}
@Override
public T next() {
return func.apply(iterator.next());
}
@Override
public void cancel() {
iterator.cancel();
}
};
}
/**
* Map a {@code CloseableAsyncIterator} into a {@code CloseableAsyncIterator} of another type or with
* each element modified in some fashion.
*
* @param iterator input
* @param func mapping function applied to each element
* @return a new iterator with each element mapped to a different value
*/
public static <V, T> CloseableAsyncIterator<T> mapIterator(final CloseableAsyncIterator<V> iterator,
final Function<V, T> func) {
return new CloseableAsyncIterator<T>() {
@Override
public void remove() {
iterator.remove();
}
@Override
public CompletableFuture<Boolean> onHasNext() {
return iterator.onHasNext();
}
@Override
public boolean hasNext() {
return iterator.hasNext();
}
@Override
public T next() {
return func.apply(iterator.next());
}
@Override
public void cancel() {
iterator.cancel();
}
@Override
public void close() {
iterator.close();
}
};
}
@ -222,7 +370,7 @@ public class AsyncUtil {
*
* @param body the asynchronous operation over which to loop
*
* @return a {@code PartialFuture} which will be set at completion of the loop.
* @return a {@link CompletableFuture} which will be set at completion of the loop.
* @deprecated Since version 5.1.0. Use the version of {@link #whileTrue(Supplier) whileTrue} that takes a
* {@link Supplier} instead.
*/
@ -237,7 +385,7 @@ public class AsyncUtil {
* @param body the asynchronous operation over which to loop
* @param executor the {@link Executor} to use for asynchronous operations
*
* @return a {@code PartialFuture} which will be set at completion of the loop.
* @return a {@link CompletableFuture} which will be set at completion of the loop.
* @deprecated Since version 5.1.0. Use the version of {@link #whileTrue(Supplier, Executor) whileTrue} that takes a
* {@link Supplier} instead.
*/
@ -251,7 +399,7 @@ public class AsyncUtil {
*
* @param body the asynchronous operation over which to loop
*
* @return a {@code PartialFuture} which will be set at completion of the loop.
* @return a {@link CompletableFuture} which will be set at completion of the loop.
*/
public static CompletableFuture<Void> whileTrue(Supplier<CompletableFuture<Boolean>> body) {
return whileTrue(body, DEFAULT_EXECUTOR);
@ -263,7 +411,7 @@ public class AsyncUtil {
* @param body the asynchronous operation over which to loop
* @param executor the {@link Executor} to use for asynchronous operations
*
* @return a {@code PartialFuture} which will be set at completion of the loop.
* @return a {@link CompletableFuture} which will be set at completion of the loop.
*/
public static CompletableFuture<Void> whileTrue(Supplier<CompletableFuture<Boolean>> body, Executor executor) {
return new LoopPartial(body, executor).run();
@ -293,8 +441,7 @@ public class AsyncUtil {
* @return a new {@link CompletableFuture} that is set when {@code task} is ready.
*/
public static <V> CompletableFuture<Void> whenReady(CompletableFuture<V> task) {
return task.thenApply(o -> (Void)null)
.exceptionally(o -> null);
return task.handle((v, t) -> null);
}
public static <V> CompletableFuture<V> composeExceptionally(CompletableFuture<V> task, Function<Throwable, CompletableFuture<V>> fn) {
@ -305,7 +452,68 @@ public class AsyncUtil {
} else {
return task;
}
});
});
}
/**
* Compose a handler bi-function to the result of a future. Unlike the
* {@link CompletableFuture#handle(BiFunction) CompletableFuture.handle()}
* function, which requires that the handler return a regular value, this
* method requires that the handler return a {@link CompletableFuture}.
* The returned future will then be ready with the result of the
* handler's future (or an error if that future completes exceptionally).
*
* @param future future to compose the handler onto
* @param handler handler bi-function to compose onto the passed future
* @param <V> return type of original future
* @param <T> return type of final future
*
* @return future with same completion properties as the future returned by the handler
*/
public static <V, T> CompletableFuture<T> composeHandle(CompletableFuture<V> future, BiFunction<V,Throwable,? extends CompletableFuture<T>> handler) {
return future.handle(handler).thenCompose(Function.identity());
}
/**
* Compose a handler bi-function to the result of a future. Unlike the
* {@link CompletableFuture#handle(BiFunction) CompletableFuture.handle()}
* function, which requires that the handler return a regular value, this
* method requires that the handler return a {@link CompletableFuture}.
* The returned future will then be ready with the result of the
* handler's future (or an error if that future completes exceptionally).
* The handler will execute on the {@link com.apple.foundationdb.FDB#DEFAULT_EXECUTOR default executor}
* used for asychronous tasks.
*
* @param future future to compose the handler onto
* @param handler handler bi-function to compose onto the passed future
* @param <V> return type of original future
* @param <T> return type of final future
*
* @return future with same completion properties as the future returned by the handler
*/
public static <V, T> CompletableFuture<T> composeHandleAsync(CompletableFuture<V> future, BiFunction<V,Throwable,? extends CompletableFuture<T>> handler) {
return composeHandleAsync(future, handler, DEFAULT_EXECUTOR);
}
/**
* Compose a handler bi-function to the result of a future. Unlike the
* {@link CompletableFuture#handle(BiFunction) CompletableFuture.handle()}
* function, which requires that the handler return a regular value, this
* method requires that the handler return a {@link CompletableFuture}.
* The returned future will then be ready with the result of the
* handler's future (or an error if that future completes excpetionally).
* The handler will execute on the passed {@link Executor}.
*
* @param future future to compose the handler onto
* @param handler handler bi-function to compose onto the passed future
* @param executor executor on which to execute the handler function
* @param <V> return type of original future
* @param <T> return type of final future
*
* @return future with same completion properties as the future returned by the handler
*/
public static <V, T> CompletableFuture<T> composeHandleAsync(CompletableFuture<V> future, BiFunction<V,Throwable,? extends CompletableFuture<T>> handler, Executor executor) {
return future.handleAsync(handler, executor).thenCompose(Function.identity());
}
/**
@ -318,7 +526,7 @@ public class AsyncUtil {
*/
public static <V> CompletableFuture<List<V>> getAll(final Collection<CompletableFuture<V>> tasks) {
return whenAll(tasks).thenApply(unused -> {
List<V> result = new ArrayList<>();
List<V> result = new ArrayList<>(tasks.size());
for(CompletableFuture<V> f : tasks) {
assert(f.isDone());
result.add(f.getNow(null));
@ -341,33 +549,31 @@ public class AsyncUtil {
}
/**
* Return a {@code CompletableFuture} that will be set when any of the {@code PartialFuture}
* Return a {@code CompletableFuture} that will be set when any of the {@link CompletableFuture}
* inputs are done. A {@code CompletableFuture} is done both on success and failure.
*
* @param input the list of {@code PartialFuture}s to monitor. This list
* @param input the list of {@link CompletableFuture}s to monitor. This list
* <b>must not</b> be modified during the execution of this call.
*
* @return a signal that will be set when any of the {@code CompletableFuture}s are done
*/
public static <V> CompletableFuture<Void> whenAny(final Collection<? extends CompletableFuture<V>> input) {
@SuppressWarnings("unchecked")
CompletableFuture<V>[] array = (CompletableFuture<V>[]) input.toArray(new CompletableFuture<?>[input.size()]);
CompletableFuture<?>[] array = input.toArray(new CompletableFuture<?>[input.size()]);
CompletableFuture<Object> anyOf = CompletableFuture.anyOf(array);
return success(anyOf);
}
/**
* Return a {@code CompletableFuture} that will be set when all the {@code PartialFuture}
* Return a {@code CompletableFuture} that will be set when all the {@link CompletableFuture}
* inputs are done. A {@code CompletableFuture} is done both on success and failure.
*
* @param input the list of {@code PartialFuture}s to monitor. This list
* @param input the list of {@link CompletableFuture}s to monitor. This list
* <b>must not</b> be modified during the execution of this call.
*
* @return a signal that will be set when all of the {@code CompletableFuture}s are done
*/
public static <V> CompletableFuture<Void> whenAll(final Collection<? extends CompletableFuture<V>> input) {
@SuppressWarnings("unchecked")
CompletableFuture<V>[] array = (CompletableFuture<V>[]) input.toArray(new CompletableFuture<?>[input.size()]);
CompletableFuture<?>[] array = input.toArray(new CompletableFuture<?>[input.size()]);
return CompletableFuture.allOf(array);
}

View File

@ -33,5 +33,5 @@ public interface Cancellable {
* is not an error to call this method on an operation that has already completed or
* already been cancelled. This method will not block or throw non-fatal exceptions.
*/
public abstract void cancel();
void cancel();
}

View File

@ -30,5 +30,5 @@ public interface CloneableException {
*
* @return a newly created {@code Exception}.
*/
public Exception retargetClone();
Exception retargetClone();
}

View File

@ -0,0 +1,46 @@
/*
* CloseableAsyncIterator.java
*
* 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.
*/
package com.apple.foundationdb.async;
/**
* A version of {@link AsyncIterator} that must be closed once no longer in use in order to free
* any associated resources.
*
* @param <T> the type of object yielded by {@code next()}
*/
public interface CloseableAsyncIterator<T> extends AutoCloseable, AsyncIterator<T> {
/**
* Cancels any outstanding asynchronous work, closes the iterator, and frees any associated
* resources. This must be called at least once after the object is no longer in use. This
* can be called multiple times, but care should be taken that an object is not in use
* in another thread at the time of the call.
*/
@Override
void close();
/**
* Alias for {@link #close}.
*/
@Override
default void cancel() {
close();
}
}

View File

@ -54,21 +54,21 @@ class DirectoryPartition extends DirectorySubspace {
this.parentDirectoryLayer = parentDirectoryLayer;
}
/**
* Raises an exception because DirectoryPartition cannot be used as a Subspace.
*
* @throws UnsupportedOperationException
*/
/**
* Raises an exception because DirectoryPartition cannot be used as a Subspace.
*
* @throws UnsupportedOperationException
*/
@Override
public Subspace get(Object o) {
throw new UnsupportedOperationException("Cannot open subspace in the root of a directory partition.");
}
/**
* Raises an exception because DirectoryPartition cannot be used as a Subspace.
*
* @throws UnsupportedOperationException
*/
/**
* Raises an exception because DirectoryPartition cannot be used as a Subspace.
*
* @throws UnsupportedOperationException
*/
@Override
public Subspace get(Tuple name) {
throw new UnsupportedOperationException("Cannot open subspace in the root of a directory partition.");
@ -84,61 +84,61 @@ class DirectoryPartition extends DirectorySubspace {
throw new UnsupportedOperationException("Cannot get key for the root of a directory partition.");
}
/**
* Raises an exception because DirectoryPartition cannot be used as a Subspace.
*
* @throws UnsupportedOperationException
*/
/**
* Raises an exception because DirectoryPartition cannot be used as a Subspace.
*
* @throws UnsupportedOperationException
*/
@Override
public byte[] pack() {
throw new UnsupportedOperationException("Cannot pack keys using the root of a directory partition.");
}
/**
* Raises an exception because DirectoryPartition cannot be used as a Subspace.
*
* @throws UnsupportedOperationException
*/
/**
* Raises an exception because DirectoryPartition cannot be used as a Subspace.
*
* @throws UnsupportedOperationException
*/
@Override
public byte[] pack(Object o) {
throw new UnsupportedOperationException("Cannot pack keys using the root of a directory partition.");
}
/**
* Raises an exception because DirectoryPartition cannot be used as a Subspace.
*
* @throws UnsupportedOperationException
*/
/**
* Raises an exception because DirectoryPartition cannot be used as a Subspace.
*
* @throws UnsupportedOperationException
*/
@Override
public byte[] pack(Tuple tuple) {
throw new UnsupportedOperationException("Cannot pack keys using the root of a directory partition.");
}
/**
* Raises an exception because DirectoryPartition cannot be used as a Subspace.
*
* @throws UnsupportedOperationException
*/
/**
* Raises an exception because DirectoryPartition cannot be used as a Subspace.
*
* @throws UnsupportedOperationException
*/
@Override
public Tuple unpack(byte[] key) {
throw new UnsupportedOperationException("Cannot unpack keys using the root of a directory partition.");
}
/**
* Raises an exception because DirectoryPartition cannot be used as a Subspace.
*
* @throws UnsupportedOperationException
*/
/**
* Raises an exception because DirectoryPartition cannot be used as a Subspace.
*
* @throws UnsupportedOperationException
*/
@Override
public Range range() {
throw new UnsupportedOperationException("Cannot get range for the root of a directory partition.");
}
/**
* Raises an exception because DirectoryPartition cannot be used as a Subspace.
*
* @throws UnsupportedOperationException
*/
/**
* Raises an exception because DirectoryPartition cannot be used as a Subspace.
*
* @throws UnsupportedOperationException
*/
@Override
public Range range(Tuple tuple) {
throw new UnsupportedOperationException("Cannot get range for the root of a directory partition.");
@ -154,11 +154,11 @@ class DirectoryPartition extends DirectorySubspace {
throw new UnsupportedOperationException("Cannot check whether a key belongs to the root of a directory partition.");
}
/**
* Raises an exception because DirectoryPartition cannot be used as a Subspace.
*
* @throws UnsupportedOperationException
*/
/**
* Raises an exception because DirectoryPartition cannot be used as a Subspace.
*
* @throws UnsupportedOperationException
*/
@Override
public Subspace subspace(Tuple tuple) {
throw new UnsupportedOperationException("Cannot open subspace in the root of a directory partition.");
@ -180,18 +180,33 @@ class DirectoryPartition extends DirectorySubspace {
* @param rhs the {@code} Object to test for equality
* @return true if this is equal to {@code rhs}
*/
@Override
public boolean equals(Object rhs) {
if(this == rhs) {
return true;
}
if(rhs == null || getClass() != rhs.getClass()) {
return false;
}
@Override
public boolean equals(Object rhs) {
if(this == rhs) {
return true;
}
if(rhs == null || getClass() != rhs.getClass()) {
return false;
}
DirectoryPartition other = (DirectoryPartition)rhs;
return (getPath() == other.getPath() || getPath() == other.getPath()) &&
DirectoryPartition other = (DirectoryPartition)rhs;
return (getPath() == other.getPath() || getPath().equals(other.getPath())) &&
parentDirectoryLayer.equals(other.parentDirectoryLayer) &&
super.equals(rhs);
}
}
/**
* Computes a hash code compatible with this class's {@link #equals(Object) equals()}
* method. In particular, it computes a hash that is based off of the
* hash of the parent {@link DirectoryLayer} and this partition's
* path, layer, and subspace prefix.
*
* @return a hash compatible with this class's {@code equals()} method
*/
@Override
public int hashCode() {
// The path, layer, and subspace prefix information comes from the super
// class's hash code method.
return parentDirectoryLayer.hashCode() ^ (super.hashCode() * 3209);
}
}

View File

@ -45,28 +45,28 @@ import com.apple.foundationdb.subspace.Subspace;
* </p>
*/
public class DirectorySubspace extends Subspace implements Directory {
private final List<String> path;
private final byte[] layer;
private final DirectoryLayer directoryLayer;
private final List<String> path;
private final byte[] layer;
private final DirectoryLayer directoryLayer;
DirectorySubspace(List<String> path, byte[] prefix, DirectoryLayer directoryLayer) {
this(path, prefix, directoryLayer, EMPTY_BYTES);
}
DirectorySubspace(List<String> path, byte[] prefix, DirectoryLayer directoryLayer) {
this(path, prefix, directoryLayer, EMPTY_BYTES);
}
DirectorySubspace(List<String> path, byte[] prefix, DirectoryLayer directoryLayer, byte[] layer) {
super(prefix);
this.path = path;
this.layer = layer;
this.directoryLayer = directoryLayer;
}
DirectorySubspace(List<String> path, byte[] prefix, DirectoryLayer directoryLayer, byte[] layer) {
super(prefix);
this.path = path;
this.layer = layer;
this.directoryLayer = directoryLayer;
}
/**
* @return a printable representation of this {@code DirectorySubspace}
*/
@Override
public String toString() {
return getClass().getSimpleName() + '(' + DirectoryUtil.pathStr(path) + ", " + printable(getKey()) + ')';
}
@Override
public String toString() {
return getClass().getSimpleName() + '(' + DirectoryUtil.pathStr(path) + ", " + printable(getKey()) + ')';
}
/**
* Returns whether this {@code DirectorySubspace} is equal to {@code rhs}.
@ -76,91 +76,103 @@ public class DirectorySubspace extends Subspace implements Directory {
* @param rhs the {@code} Object to test for equality
* @return true if this is equal to {@code rhs}
*/
@Override
public boolean equals(Object rhs) {
if(this == rhs) {
return true;
}
if(rhs == null || getClass() != rhs.getClass()) {
return false;
}
DirectorySubspace other = (DirectorySubspace)rhs;
return (path == other.path || path.equals(other.path)) &&
Arrays.equals(layer, other.layer) &&
@Override
public boolean equals(Object rhs) {
if(this == rhs) {
return true;
}
if(rhs == null || getClass() != rhs.getClass()) {
return false;
}
DirectorySubspace other = (DirectorySubspace)rhs;
return (path == other.path || path.equals(other.path)) &&
Arrays.equals(layer, other.layer) &&
directoryLayer.equals(other.directoryLayer) &&
super.equals(rhs);
}
}
@Override
public List<String> getPath() {
return Collections.unmodifiableList(path);
}
/**
* Computes a hash code compatible with the {@link #equals(Object) equals()} method of
* this class. In particular, it will produce a hash code that is based off of the hashes
* of its path, its layer, and its subspace prefix.
*
* @return a hash compatible with this class's {@code equals()} method
*/
@Override
public int hashCode() {
return path.hashCode() ^ (Arrays.hashCode(layer) * 1153) ^ (directoryLayer.hashCode() * 929) ^ (super.hashCode() * 419);
}
@Override
public byte[] getLayer() {
return Arrays.copyOf(layer, layer.length);
}
@Override
public List<String> getPath() {
return Collections.unmodifiableList(path);
}
@Override
public byte[] getLayer() {
return Arrays.copyOf(layer, layer.length);
}
@Override
public DirectoryLayer getDirectoryLayer() {
return directoryLayer;
}
@Override
public CompletableFuture<DirectorySubspace> createOrOpen(TransactionContext tcx, List<String> subpath, byte[] otherLayer) {
return directoryLayer.createOrOpen(tcx, getPartitionSubpath(subpath), otherLayer);
}
@Override
public CompletableFuture<DirectorySubspace> createOrOpen(TransactionContext tcx, List<String> subpath, byte[] otherLayer) {
return directoryLayer.createOrOpen(tcx, getPartitionSubpath(subpath), otherLayer);
}
@Override
public CompletableFuture<DirectorySubspace> open(ReadTransactionContext tcx, List<String> subpath, byte[] otherLayer) {
return directoryLayer.open(tcx, getPartitionSubpath(subpath), otherLayer);
}
@Override
public CompletableFuture<DirectorySubspace> open(ReadTransactionContext tcx, List<String> subpath, byte[] otherLayer) {
return directoryLayer.open(tcx, getPartitionSubpath(subpath), otherLayer);
}
@Override
public CompletableFuture<DirectorySubspace> create(TransactionContext tcx, List<String> subpath, byte[] otherLayer, byte[] prefix) {
return directoryLayer.create(tcx, getPartitionSubpath(subpath), otherLayer, prefix);
}
@Override
public CompletableFuture<DirectorySubspace> create(TransactionContext tcx, List<String> subpath, byte[] otherLayer, byte[] prefix) {
return directoryLayer.create(tcx, getPartitionSubpath(subpath), otherLayer, prefix);
}
@Override
public CompletableFuture<List<String>> list(ReadTransactionContext tcx, List<String> subpath) {
return directoryLayer.list(tcx, getPartitionSubpath(subpath));
}
@Override
public CompletableFuture<List<String>> list(ReadTransactionContext tcx, List<String> subpath) {
return directoryLayer.list(tcx, getPartitionSubpath(subpath));
}
@Override
public CompletableFuture<DirectorySubspace> move(TransactionContext tcx, List<String> oldSubpath, List<String> newSubpath) {
return directoryLayer.move(tcx, getPartitionSubpath(oldSubpath), getPartitionSubpath(newSubpath));
}
@Override
public CompletableFuture<DirectorySubspace> move(TransactionContext tcx, List<String> oldSubpath, List<String> newSubpath) {
return directoryLayer.move(tcx, getPartitionSubpath(oldSubpath), getPartitionSubpath(newSubpath));
}
@Override
public CompletableFuture<DirectorySubspace> moveTo(TransactionContext tcx, List<String> newAbsolutePath) {
@Override
public CompletableFuture<DirectorySubspace> moveTo(TransactionContext tcx, List<String> newAbsolutePath) {
DirectoryLayer dir = getLayerForPath(EMPTY_PATH);
int partitionLen = dir.getPath().size();
List<String> partitionPath = newAbsolutePath.subList(0, Math.min(newAbsolutePath.size(), partitionLen));
if(!partitionPath.equals(dir.getPath()))
throw new DirectoryMoveException("Cannot move between partitions", path, newAbsolutePath);
return dir.move(tcx,
return dir.move(tcx,
getPartitionSubpath(EMPTY_PATH, dir),
newAbsolutePath.subList(partitionLen, newAbsolutePath.size()));
}
}
@Override
public CompletableFuture<Void> remove(TransactionContext tcx, List<String> subpath) {
@Override
public CompletableFuture<Void> remove(TransactionContext tcx, List<String> subpath) {
DirectoryLayer dir = getLayerForPath(subpath);
return dir.remove(tcx, getPartitionSubpath(subpath, dir));
}
return dir.remove(tcx, getPartitionSubpath(subpath, dir));
}
@Override
public CompletableFuture<Boolean> removeIfExists(TransactionContext tcx, List<String> subpath) {
@Override
public CompletableFuture<Boolean> removeIfExists(TransactionContext tcx, List<String> subpath) {
DirectoryLayer dir = getLayerForPath(subpath);
return dir.removeIfExists(tcx, getPartitionSubpath(subpath, dir));
}
return dir.removeIfExists(tcx, getPartitionSubpath(subpath, dir));
}
@Override
public CompletableFuture<Boolean> exists(ReadTransactionContext tcx, List<String> subpath) {
@Override
public CompletableFuture<Boolean> exists(ReadTransactionContext tcx, List<String> subpath) {
DirectoryLayer dir = getLayerForPath(subpath);
return dir.exists(tcx, getPartitionSubpath(subpath, dir));
}
return dir.exists(tcx, getPartitionSubpath(subpath, dir));
}
private List<String> getPartitionSubpath(List<String> path) {
return getPartitionSubpath(path, directoryLayer);

View File

@ -42,4 +42,6 @@ class DirectoryUtil {
sb.append(')');
return sb.toString();
}
private DirectoryUtil() {}
}

View File

@ -20,10 +20,10 @@
package com.apple.foundationdb.directory;
import com.apple.foundationdb.tuple.ByteArrayUtil;
import java.util.List;
import com.apple.foundationdb.tuple.ByteArrayUtil;
/**
* A {@link DirectoryException} that is thrown when a directory is opened with an incompatible layer.
*/

View File

@ -92,4 +92,6 @@ public class PathUtil {
return new LinkedList<String>(path.subList(0, path.size() - 1));
}
private PathUtil() {}
}

View File

@ -45,8 +45,7 @@ import com.apple.foundationdb.tuple.Versionstamp;
* As a best practice, API clients should use at least one subspace for application data.
* </p>
*/
public class Subspace
{
public class Subspace {
static final Tuple EMPTY_TUPLE = Tuple.from();
static final byte[] EMPTY_BYTES = new byte[0];
@ -112,7 +111,7 @@ public class Subspace
return false;
}
Subspace other = (Subspace)rhs;
return Arrays.equals(rawPrefix, other.rawPrefix) ;
return Arrays.equals(rawPrefix, other.rawPrefix);
}
/**

View File

@ -59,29 +59,29 @@ public class ByteArrayUtil {
if(interlude == null)
interlude = new byte[0];
int element_totals = 0;
int elementTotals = 0;
int interludeSize = interlude.length;
for(byte[] e : parts) {
element_totals += e.length;
elementTotals += e.length;
}
byte[] dest = new byte[(interludeSize * (partCount - 1)) + element_totals];
byte[] dest = new byte[(interludeSize * (partCount - 1)) + elementTotals];
//System.out.println(" interlude -> " + ArrayUtils.printable(interlude));
int start_byte = 0;
int startByte = 0;
int index = 0;
for(byte[] part : parts) {
//System.out.println(" section -> " + ArrayUtils.printable(parts.get(i)));
int length = part.length;
if(length > 0) {
System.arraycopy(part, 0, dest, start_byte, length);
start_byte += length;
System.arraycopy(part, 0, dest, startByte, length);
startByte += length;
}
if(index < partCount - 1 && interludeSize > 0) {
// If this is not the last element, append the interlude
System.arraycopy(interlude, 0, dest, start_byte, interludeSize);
start_byte += interludeSize;
System.arraycopy(interlude, 0, dest, startByte, interludeSize);
startByte += interludeSize;
}
index++;
}
@ -97,7 +97,7 @@ public class ByteArrayUtil {
*
* @return a newly created concatenation of the input
*/
public static byte[] join(byte[] ... parts) {
public static byte[] join(byte[]... parts) {
return join(null, Arrays.asList(parts));
}

View File

@ -21,11 +21,8 @@
package com.apple.foundationdb.tuple;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
@ -852,12 +849,12 @@ public class Tuple implements Comparable<Tuple>, Iterable<Object> {
*
* @return a newly created {@code Tuple}
*/
public static Tuple from(Object ... items) {
public static Tuple from(Object... items) {
return fromList(Arrays.asList(items));
}
static void main(String[] args) {
for( int i : new int[] {10, 100, 1000, 10000, 100000, 1000000} ) {
for(int i : new int[] {10, 100, 1000, 10000, 100000, 1000000}) {
createTuple(i);
}

View File

@ -421,10 +421,10 @@ class TupleUtil {
// Convert to long if in range -- otherwise, leave as BigInteger.
if (val.compareTo(BigInteger.valueOf(Long.MIN_VALUE))<0||
val.compareTo(BigInteger.valueOf(Long.MAX_VALUE))>0) {
// This can occur if the thing can be represented with 8 bytes but not
// the right sign information.
return new DecodeResult(end, val);
val.compareTo(BigInteger.valueOf(Long.MAX_VALUE))>0) {
// This can occur if the thing can be represented with 8 bytes but not
// the right sign information.
return new DecodeResult(end, val);
}
return new DecodeResult(end, val.longValue());
}
@ -610,18 +610,18 @@ class TupleUtil {
public static void main(String[] args) {
try {
byte[] bytes = pack(Collections.singletonList(4), null );
assert 4 == (Integer)(decode( bytes, 0, bytes.length ).o);
byte[] bytes = pack(Collections.singletonList(4), null);
assert 4 == (Integer)(decode(bytes, 0, bytes.length).o);
} catch (Exception e) {
e.printStackTrace();
System.out.println("Error " + e.getMessage());
}
try {
byte[] bytes = pack( Collections.singletonList("\u021Aest \u0218tring"), null );
String string = (String)(decode( bytes, 0, bytes.length ).o);
byte[] bytes = pack(Collections.singletonList("\u021Aest \u0218tring"), null);
String string = (String)(decode(bytes, 0, bytes.length).o);
System.out.println("contents -> " + string);
assert "\u021Aest \u0218tring" == string;
assert "\u021Aest \u0218tring".equals(string);
} catch (Exception e) {
e.printStackTrace();
System.out.println("Error " + e.getMessage());

View File

@ -20,12 +20,12 @@
package com.apple.foundationdb.test;
import com.apple.foundationdb.Database;
import com.apple.foundationdb.FDB;
import java.nio.charset.Charset;
import java.util.Random;
import com.apple.foundationdb.Database;
import com.apple.foundationdb.FDB;
public abstract class AbstractTester {
public static final int API_VERSION = 510;
protected static final int NUM_RUNS = 25;

View File

@ -22,13 +22,11 @@ package com.apple.foundationdb.test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
import com.apple.foundationdb.Range;
import com.apple.foundationdb.Transaction;
import com.apple.foundationdb.async.AsyncUtil;
import com.apple.foundationdb.directory.Directory;
import com.apple.foundationdb.directory.DirectoryLayer;
@ -37,11 +35,11 @@ import com.apple.foundationdb.subspace.Subspace;
import com.apple.foundationdb.tuple.Tuple;
class AsyncDirectoryExtension {
List<Object> dirList = new ArrayList<Object>();
List<Object> dirList = new ArrayList<>();
int dirIndex = 0;
int errorIndex = 0;
public AsyncDirectoryExtension() {
AsyncDirectoryExtension() {
dirList.add(DirectoryLayer.getDefault());
}
@ -54,13 +52,9 @@ class AsyncDirectoryExtension {
}
CompletableFuture<Void> processInstruction(final Instruction inst) {
return executeInstruction(inst)
.exceptionally(new Function<Throwable, Void>() {
@Override
public Void apply(Throwable e) {
DirectoryUtil.pushError(inst, e, dirList);
return null;
}
return executeInstruction(inst).exceptionally(e -> {
DirectoryUtil.pushError(inst, e, dirList);
return null;
});
}
@ -69,372 +63,184 @@ class AsyncDirectoryExtension {
if(op == DirectoryOperation.DIRECTORY_CREATE_SUBSPACE) {
return DirectoryUtil.popTuple(inst)
.thenComposeAsync(new Function<Tuple, CompletableFuture<Void>>() {
@Override
public CompletableFuture<Void> apply(final Tuple prefix) {
return inst.popParam()
.thenApplyAsync(new Function<Object, Void>() {
@Override
public Void apply(Object rawPrefix) {
dirList.add(new Subspace(prefix, (byte[])rawPrefix));
return null;
}
});
}
});
.thenComposeAsync(prefix -> inst.popParam()
.thenAccept(rawPrefix -> dirList.add(new Subspace(prefix, (byte[])rawPrefix))));
}
else if(op == DirectoryOperation.DIRECTORY_CREATE_LAYER) {
return inst.popParams(3)
.thenApplyAsync(new Function<List<Object>, Void>() {
@Override
public Void apply(List<Object> params) {
Subspace nodeSubspace = (Subspace)dirList.get(StackUtils.getInt(params.get(0)));
Subspace contentSubspace = (Subspace)dirList.get(StackUtils.getInt(params.get(1)));
boolean allowManualPrefixes = StackUtils.getInt(params.get(2)) == 1;
return inst.popParams(3).thenAcceptAsync(params -> {
Subspace nodeSubspace = (Subspace)dirList.get(StackUtils.getInt(params.get(0)));
Subspace contentSubspace = (Subspace)dirList.get(StackUtils.getInt(params.get(1)));
boolean allowManualPrefixes = StackUtils.getInt(params.get(2)) == 1;
if(nodeSubspace == null || contentSubspace == null)
dirList.add(null);
else
dirList.add(new DirectoryLayer(nodeSubspace, contentSubspace, allowManualPrefixes));
return null;
}
if(nodeSubspace == null || contentSubspace == null)
dirList.add(null);
else
dirList.add(new DirectoryLayer(nodeSubspace, contentSubspace, allowManualPrefixes));
});
}
else if(op == DirectoryOperation.DIRECTORY_CHANGE) {
return inst.popParam()
.thenApplyAsync(new Function<Object, Void>() {
@Override
public Void apply(Object index) {
dirIndex = StackUtils.getInt(index);
if(dirList.get(dirIndex) == null)
dirIndex = errorIndex;
return null;
}
return inst.popParam().thenAcceptAsync(index -> {
dirIndex = StackUtils.getInt(index);
if(dirList.get(dirIndex) == null)
dirIndex = errorIndex;
});
}
else if(op == DirectoryOperation.DIRECTORY_SET_ERROR_INDEX) {
return inst.popParam()
.thenApplyAsync(new Function<Object, Void>() {
@Override
public Void apply(Object index) {
errorIndex = StackUtils.getInt(index);
return null;
}
});
return inst.popParam().thenAcceptAsync(index -> errorIndex = StackUtils.getInt(index));
}
else if(op == DirectoryOperation.DIRECTORY_CREATE_OR_OPEN || op == DirectoryOperation.DIRECTORY_OPEN) {
return DirectoryUtil.popPath(inst)
.thenComposeAsync(new Function<List<String>, CompletableFuture<Void>>() {
@Override
public CompletableFuture<Void> apply(final List<String> path) {
return inst.popParam()
.thenComposeAsync(new Function<Object, CompletableFuture<Void>>() {
@Override
public CompletableFuture<Void> apply(Object layer) {
CompletableFuture<DirectorySubspace> dir;
if(layer == null) {
if(op == DirectoryOperation.DIRECTORY_CREATE_OR_OPEN)
dir = directory().createOrOpen(inst.tcx, path);
else
dir = directory().open(inst.readTcx, path);
}
else {
if(op == DirectoryOperation.DIRECTORY_CREATE_OR_OPEN)
dir = directory().createOrOpen(inst.tcx, path, (byte[])layer);
else
dir = directory().open(inst.readTcx, path, (byte[])layer);
}
return dir.thenApplyAsync(new Function<DirectorySubspace, Void>() {
@Override
public Void apply(DirectorySubspace dirSubspace) {
dirList.add(dirSubspace);
return null;
}
});
}
});
.thenComposeAsync(path -> inst.popParam().thenComposeAsync(layer -> {
CompletableFuture<DirectorySubspace> dir;
if(layer == null) {
if(op == DirectoryOperation.DIRECTORY_CREATE_OR_OPEN)
dir = directory().createOrOpen(inst.tcx, path);
else
dir = directory().open(inst.readTcx, path);
}
});
else {
if(op == DirectoryOperation.DIRECTORY_CREATE_OR_OPEN)
dir = directory().createOrOpen(inst.tcx, path, (byte[])layer);
else
dir = directory().open(inst.readTcx, path, (byte[])layer);
}
return dir.thenAccept(dirList::add);
}));
}
else if(op == DirectoryOperation.DIRECTORY_CREATE) {
return DirectoryUtil.popPath(inst)
.thenComposeAsync(new Function<List<String>, CompletableFuture<Void>>() {
@Override
public CompletableFuture<Void> apply(final List<String> path) {
return inst.popParams(2)
.thenComposeAsync(new Function<List<Object>, CompletableFuture<Void>>() {
@Override
public CompletableFuture<Void> apply(List<Object> params) {
byte[] layer = (byte[])params.get(0);
byte[] prefix = (byte[])params.get(1);
return DirectoryUtil.popPath(inst).thenComposeAsync(path -> inst.popParams(2).thenComposeAsync(params -> {
byte[] layer = (byte[])params.get(0);
byte[] prefix = (byte[])params.get(1);
CompletableFuture<DirectorySubspace> dir;
if(layer == null && prefix == null)
dir = directory().create(inst.tcx, path);
else if(prefix == null)
dir = directory().create(inst.tcx, path, layer);
else {
if(layer == null)
layer = new byte[0];
CompletableFuture<DirectorySubspace> dir;
if(layer == null && prefix == null)
dir = directory().create(inst.tcx, path);
else if(prefix == null)
dir = directory().create(inst.tcx, path, layer);
else {
if(layer == null)
layer = new byte[0];
dir = directory().create(inst.tcx, path, layer, prefix);
}
return dir.thenApplyAsync(new Function<DirectorySubspace, Void>() {
@Override
public Void apply(DirectorySubspace dirSubspace) {
dirList.add(dirSubspace);
return null;
}
});
}
});
dir = directory().create(inst.tcx, path, layer, prefix);
}
});
return dir.thenAccept(dirList::add);
}));
}
else if(op == DirectoryOperation.DIRECTORY_MOVE) {
return DirectoryUtil.popPaths(inst, 2)
.thenComposeAsync(new Function<List<List<String>>, CompletableFuture<DirectorySubspace>>() {
@Override
public CompletableFuture<DirectorySubspace> apply(List<List<String>> paths) {
return directory().move(inst.tcx, paths.get(0), paths.get(1));
}
})
.thenApplyAsync(new Function<DirectorySubspace, Void>() {
@Override
public Void apply(DirectorySubspace dirSubspace) {
dirList.add(dirSubspace);
return null;
}
});
.thenComposeAsync(paths -> directory().move(inst.tcx, paths.get(0), paths.get(1)))
.thenAccept(dirList::add);
}
else if(op == DirectoryOperation.DIRECTORY_MOVE_TO) {
return DirectoryUtil.popPath(inst)
.thenComposeAsync(new Function<List<String>, CompletableFuture<DirectorySubspace>>() {
@Override
public CompletableFuture<DirectorySubspace> apply(List<String> newAbsolutePath) {
return directory().moveTo(inst.tcx, newAbsolutePath);
}
})
.thenApplyAsync(new Function<DirectorySubspace, Void>() {
@Override
public Void apply(DirectorySubspace dirSubspace) {
dirList.add(dirSubspace);
return null;
}
});
.thenComposeAsync(newAbsolutePath -> directory().moveTo(inst.tcx, newAbsolutePath))
.thenAccept(dirList::add);
}
else if(op == DirectoryOperation.DIRECTORY_REMOVE) {
return inst.popParam()
.thenComposeAsync(new Function<Object, CompletableFuture<List<List<String>>>>() {
@Override
public CompletableFuture<List<List<String>>> apply(Object count) {
return DirectoryUtil.popPaths(inst, StackUtils.getInt(count));
}
})
.thenComposeAsync(new Function<List<List<String>>, CompletableFuture<Void>>() {
@Override
public CompletableFuture<Void> apply(List<List<String>> path) {
if(path.size() == 0)
return directory().remove(inst.tcx);
else
return directory().remove(inst.tcx, path.get(0));
}
.thenComposeAsync(count -> DirectoryUtil.popPaths(inst, StackUtils.getInt(count)))
.thenComposeAsync(path -> {
if(path.size() == 0)
return directory().remove(inst.tcx);
else
return directory().remove(inst.tcx, path.get(0));
});
}
else if(op == DirectoryOperation.DIRECTORY_REMOVE_IF_EXISTS) {
return inst.popParam()
.thenComposeAsync(new Function<Object, CompletableFuture<List<List<String>>>>() {
@Override
public CompletableFuture<List<List<String>>> apply(Object count) {
return DirectoryUtil.popPaths(inst, StackUtils.getInt(count));
}
})
.thenComposeAsync(new Function<List<List<String>>, CompletableFuture<Void>>() {
@Override
public CompletableFuture<Void> apply(List<List<String>> path) {
if(path.size() == 0)
return AsyncUtil.success(directory().removeIfExists(inst.tcx));
else
return AsyncUtil.success(directory().removeIfExists(inst.tcx, path.get(0)));
}
.thenComposeAsync(count -> DirectoryUtil.popPaths(inst, StackUtils.getInt(count)))
.thenComposeAsync(path -> {
if(path.size() == 0)
return AsyncUtil.success(directory().removeIfExists(inst.tcx));
else
return AsyncUtil.success(directory().removeIfExists(inst.tcx, path.get(0)));
});
}
else if(op == DirectoryOperation.DIRECTORY_LIST) {
return inst.popParam()
.thenComposeAsync(new Function<Object, CompletableFuture<List<List<String>>>>() {
@Override
public CompletableFuture<List<List<String>>> apply(Object count) {
return DirectoryUtil.popPaths(inst, StackUtils.getInt(count));
}
.thenComposeAsync(count -> DirectoryUtil.popPaths(inst, StackUtils.getInt(count)))
.thenComposeAsync(path -> {
if(path.size() == 0)
return directory().list(inst.readTcx);
else
return directory().list(inst.readTcx, path.get(0));
})
.thenComposeAsync(new Function<List<List<String>>, CompletableFuture<List<String>>>() {
@Override
public CompletableFuture<List<String>> apply(List<List<String>> path) {
if(path.size() == 0)
return directory().list(inst.readTcx);
else
return directory().list(inst.readTcx, path.get(0));
}
})
.thenApplyAsync(new Function<List<String>, Void>() {
@Override
public Void apply(List<String> children) {
inst.push(Tuple.fromItems(children).pack());
return null;
}
});
.thenAccept(children -> inst.push(Tuple.fromItems(children).pack()));
}
else if(op == DirectoryOperation.DIRECTORY_EXISTS) {
return inst.popParam()
.thenComposeAsync(new Function<Object, CompletableFuture<List<List<String>>>>() {
@Override
public CompletableFuture<List<List<String>>> apply(Object count) {
return DirectoryUtil.popPaths(inst, StackUtils.getInt(count));
}
.thenComposeAsync(count -> DirectoryUtil.popPaths(inst, StackUtils.getInt(count)))
.thenComposeAsync(path -> {
if(path.size() == 0)
return directory().exists(inst.readTcx);
else
return directory().exists(inst.readTcx, path.get(0));
})
.thenComposeAsync(new Function<List<List<String>>, CompletableFuture<Boolean>>() {
@Override
public CompletableFuture<Boolean> apply(List<List<String>> path) {
if(path.size() == 0)
return directory().exists(inst.readTcx);
else
return directory().exists(inst.readTcx, path.get(0));
}
})
.thenApplyAsync(new Function<Boolean, Void>() {
@Override
public Void apply(Boolean exists){
inst.push(exists ? 1 : 0);
return null;
}
});
.thenAccept(exists -> inst.push(exists ? 1 : 0));
}
else if(op == DirectoryOperation.DIRECTORY_PACK_KEY) {
return DirectoryUtil.popTuple(inst)
.thenApplyAsync(new Function<Tuple, Void>() {
@Override
public Void apply(Tuple keyTuple) {
inst.push(subspace().pack(keyTuple));
return null;
}
});
return DirectoryUtil.popTuple(inst).thenAccept(keyTuple -> inst.push(subspace().pack(keyTuple)));
}
else if(op == DirectoryOperation.DIRECTORY_UNPACK_KEY) {
return inst.popParam()
.thenApplyAsync(new Function<Object, Void>() {
@Override
public Void apply(Object key) {
Tuple tup = subspace().unpack((byte[])key);
for(Object o : tup)
inst.push(o);
return null;
}
return inst.popParam().thenAcceptAsync(key -> {
Tuple tup = subspace().unpack((byte[])key);
for(Object o : tup)
inst.push(o);
});
}
else if(op == DirectoryOperation.DIRECTORY_RANGE) {
return DirectoryUtil.popTuple(inst)
.thenApplyAsync(new Function<Tuple, Void>() {
@Override
public Void apply(Tuple tup) {
Range range = subspace().range(tup);
inst.push(range.begin);
inst.push(range.end);
return null;
}
return DirectoryUtil.popTuple(inst).thenAcceptAsync(tup -> {
Range range = subspace().range(tup);
inst.push(range.begin);
inst.push(range.end);
});
}
else if(op == DirectoryOperation.DIRECTORY_CONTAINS) {
return inst.popParam()
.thenApplyAsync(new Function<Object, Void>() {
@Override
public Void apply(Object key) {
inst.push(subspace().contains((byte[])key) ? 1 : 0);
return null;
}
});
return inst.popParam().thenAccept(key -> inst.push(subspace().contains((byte[])key) ? 1 : 0));
}
else if(op == DirectoryOperation.DIRECTORY_OPEN_SUBSPACE) {
return DirectoryUtil.popTuple(inst)
.thenApplyAsync(new Function<Tuple, Void>() {
@Override
public Void apply(Tuple prefix) {
dirList.add(subspace().subspace(prefix));
return null;
}
});
return DirectoryUtil.popTuple(inst).thenAcceptAsync(prefix -> dirList.add(subspace().subspace(prefix)));
}
else if(op == DirectoryOperation.DIRECTORY_LOG_SUBSPACE) {
return inst.popParam()
.thenComposeAsync(new Function<Object, CompletableFuture<Void>>() {
@Override
public CompletableFuture<Void> apply(final Object prefix) {
return inst.tcx.runAsync(new Function<Transaction, CompletableFuture<Void>>() {
@Override
public CompletableFuture<Void> apply(Transaction tr) {
tr.set(Tuple.from(dirIndex).pack((byte[])prefix), subspace().getKey());
return CompletableFuture.completedFuture(null);
}
});
}
});
return inst.popParam().thenComposeAsync(prefix ->
inst.tcx.runAsync(tr -> {
tr.set(Tuple.from(dirIndex).pack((byte[])prefix), subspace().getKey());
return AsyncUtil.DONE;
})
);
}
else if(op == DirectoryOperation.DIRECTORY_LOG_DIRECTORY) {
return inst.popParam()
.thenComposeAsync(new Function<Object, CompletableFuture<Void>>() {
@Override
public CompletableFuture<Void> apply(Object prefix) {
final Subspace logSubspace = new Subspace(new Tuple().add(dirIndex), (byte[])prefix);
return inst.tcx.runAsync(new Function<Transaction, CompletableFuture<Void>>() {
@Override
public CompletableFuture<Void> apply(final Transaction tr) {
return directory().exists(tr)
.thenComposeAsync(new Function<Boolean, CompletableFuture<List<String>>>() {
@Override
public CompletableFuture<List<String>> apply(Boolean exists) {
tr.set(logSubspace.pack("path"), Tuple.fromItems(directory().getPath()).pack());
tr.set(logSubspace.pack("layer"), new Tuple().add(directory().getLayer()).pack());
tr.set(logSubspace.pack("exists"), new Tuple().add(exists ? 1 : 0).pack());
if(exists)
return directory().list(tr);
else
return CompletableFuture.completedFuture(new ArrayList<String>());
}
})
.thenApplyAsync(new Function<List<String>, Void>() {
@Override
public Void apply(List<String> children) {
tr.set(logSubspace.pack("children"), Tuple.fromItems(children).pack());
return null;
}
});
}
});
}
return inst.popParam().thenComposeAsync(prefix -> {
final Subspace logSubspace = new Subspace(new Tuple().add(dirIndex), (byte[])prefix);
return inst.tcx.runAsync(tr -> directory().exists(tr)
.thenComposeAsync(exists -> {
tr.set(logSubspace.pack("path"), Tuple.fromItems(directory().getPath()).pack());
tr.set(logSubspace.pack("layer"), new Tuple().add(directory().getLayer()).pack());
tr.set(logSubspace.pack("exists"), new Tuple().add(exists ? 1 : 0).pack());
if(exists)
return directory().list(tr);
else
return CompletableFuture.completedFuture(Collections.emptyList());
})
.thenAcceptAsync(children -> tr.set(logSubspace.pack("children"), Tuple.fromItems(children).pack()))
);
});
}
else if(op == DirectoryOperation.DIRECTORY_STRIP_PREFIX) {
return inst.popParam()
.thenApplyAsync(new Function<Object, Void>() {
@Override
public Void apply(Object param) {
byte[] str = (byte[])param;
byte[] rawPrefix = subspace().getKey();
return inst.popParam().thenAcceptAsync(param -> {
byte[] str = (byte[])param;
byte[] rawPrefix = subspace().getKey();
if(str.length < rawPrefix.length)
if(str.length < rawPrefix.length)
throw new RuntimeException("String does not start with raw prefix");
for(int i = 0; i < rawPrefix.length; ++i)
if(str[i] != rawPrefix[i])
throw new RuntimeException("String does not start with raw prefix");
for(int i = 0; i < rawPrefix.length; ++i)
if(str[i] != rawPrefix[i])
throw new RuntimeException("String does not start with raw prefix");
inst.push(Arrays.copyOfRange(str, rawPrefix.length, str.length));
return null;
}
inst.push(Arrays.copyOfRange(str, rawPrefix.length, str.length));
});
}
else {

View File

@ -1,28 +0,0 @@
/*
* AsyncPerformanceTester.java
*
* 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.
*/
package com.apple.foundationdb.test;
public class AsyncPerformanceTester {
public static void main(String[] args) {
System.out.println("Running Java async performance test on Java version " + System.getProperty("java.version"));
}
}

View File

@ -44,78 +44,59 @@ public class BlockingBenchmark {
Transaction tr = database.createTransaction();
tr.setReadVersion(100000);
final Function<Long,Long> identity = new Function<Long, Long>() {
@Override
public Long apply(Long o) {
return o;
}
};
System.out.println("readVersion().join():");
runTests(tr, new Function<CompletableFuture<Long>, Void>() {
@Override
public Void apply(CompletableFuture<Long> o) {
try {
o.join();
} catch(Exception e) { }
return null;
runTests(tr, o -> {
try {
o.join();
} catch(Exception e) {
// Ignore
}
return null;
});
System.out.println("readVersion().get():");
runTests(tr, new Function<CompletableFuture<Long>, Void>() {
@Override
public Void apply(CompletableFuture<Long> o) {
try {
o.get();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch(Exception e) { }
return null;
runTests(tr, o -> {
try {
o.get();
} catch(Exception e) {
// Ignore
}
return null;
});
System.out.println("readVersion().thenApplyAsync(identity).get():");
runTests(tr, new Function<CompletableFuture<Long>, Void>() {
@Override
public Void apply(CompletableFuture<Long> o) {
try {
o.thenApplyAsync(identity).get();
} catch(Exception e) { }
return null;
runTests(tr, o -> {
try {
o.thenApplyAsync(Function.identity(), FDB.DEFAULT_EXECUTOR).get();
} catch(Exception e) {
// Ignore
}
return null;
});
System.out.println("readVersion().thenApplyAsync^10(identity).get():");
runTests(tr, new Function<CompletableFuture<Long>, Void>() {
@Override
public Void apply(CompletableFuture<Long> o) {
for(int i=0; i<10; i++)
o = o.thenApplyAsync(identity);
try {
o.get();
} catch(Exception e) { }
return null;
runTests(tr, o -> {
for(int i=0; i<10; i++)
o = o.thenApplyAsync(Function.identity(), FDB.DEFAULT_EXECUTOR);
try {
o.get();
} catch(Exception e) {
// Ignore
}
return null;
});
System.out.println("readVersion().get^100():");
runTests(tr, new Function<CompletableFuture<Long>, Void>() {
@Override
public Void apply(CompletableFuture<Long> o) {
for(int i=0; i<100; i++) {
try {
o.get();
} catch(Exception e) { }
runTests(tr, o -> {
for(int i=0; i<100; i++) {
try {
o.get();
} catch(Exception e) {
// Ignore
}
return null;
}
return null;
});
}
@ -124,7 +105,7 @@ public class BlockingBenchmark {
for(int r=0; r<4; r++) {
long start = System.currentTimeMillis();
for(int i = 0; i < REPS; i++) {
blockMethod.apply( tr.getReadVersion() );
blockMethod.apply(tr.getReadVersion());
}
long taken = System.currentTimeMillis() - start;
@ -145,4 +126,6 @@ public class BlockingBenchmark {
System.out.println(" " + REPS + " done in " + taken + "ms -> " + (REPS / (taken)) + " KHz");
}
}
private BlockingBenchmark() {}
}

View File

@ -23,14 +23,11 @@ package com.apple.foundationdb.test;
import java.nio.charset.Charset;
import java.security.SecureRandom;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import com.apple.foundationdb.Database;
import com.apple.foundationdb.FDB;
import com.apple.foundationdb.Transaction;
public class ConcurrentGetSetGet {
public static final Charset UTF8 = Charset.forName("UTF-8");
@ -55,25 +52,22 @@ public class ConcurrentGetSetGet {
new ConcurrentGetSetGet().apply(database);
}
public void apply(Database d) {
new Thread(new Runnable() {
@Override
public void run() {
int loops = 0;
try {
Thread.sleep(5000);
System.out.println("Loop " + loops++ + ":");
System.out.println(" attempts: " + attemptCount.get());
System.out.println(" gets complete: " + getCompleteCount.get());
System.out.println(" errors: " + errorCount.get());
System.out.println(" sem: " + semaphore);
System.out.println();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
public void apply(Database db) {
new Thread(() -> {
int loops = 0;
try {
Thread.sleep(5000);
System.out.println("Loop " + loops++ + ":");
System.out.println(" attempts: " + attemptCount.get());
System.out.println(" gets complete: " + getCompleteCount.get());
System.out.println(" errors: " + errorCount.get());
System.out.println(" sem: " + semaphore);
System.out.println();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}).start();
final Random random = new SecureRandom();
try {
@ -86,50 +80,37 @@ public class ConcurrentGetSetGet {
System.out.println("Waited " + wait + "ms");
}
current = System.currentTimeMillis();
d.runAsync(new Function<Transaction, CompletableFuture<Void>>() {
@Override
public CompletableFuture<Void> apply(final Transaction r) {
attemptCount.addAndGet(1);
final String key = "test:" + random.nextInt();
return r.get($(key)).thenComposeAsync(new Function<byte[], CompletableFuture<Void>>() {
@Override
public CompletableFuture<Void> apply(byte[] o) {
r.set($(key), $("value"));
return r.get($(key)).thenApplyAsync(new Function<byte[], Void>() {
@Override
public Void apply(byte[] o) {
getCompleteCount.addAndGet(1);
semaphore.release();
return null;
}
});
}
}).exceptionally(new Function<Throwable, Void>() {
@Override
public Void apply(Throwable o) {
errorCount.addAndGet(1);
System.err.println("Fail (" + o.getMessage() + ")");
semaphore.release();
return null;
}
});
}
db.runAsync(tr -> {
attemptCount.addAndGet(1);
final String key = "test:" + random.nextInt();
return tr.get($(key)).thenComposeAsync(ignore -> {
tr.set($(key), $("value"));
return tr.get($(key)).thenRunAsync(() -> {
getCompleteCount.addAndGet(1);
semaphore.release();
}, FDB.DEFAULT_EXECUTOR);
}, FDB.DEFAULT_EXECUTOR).exceptionally(t -> {
errorCount.addAndGet(1);
System.err.println("Fail (" + t.getMessage() + ")");
semaphore.release();
return null;
});
});
}
semaphore.acquire(CONCURRENCY);
long diff = System.currentTimeMillis() - start;
System.out.println("time taken (ms): " + diff);
System.out.println("tr/sec:" + COUNT * 1000l / diff);
System.out.println("tr/sec:" + COUNT * 1000L / diff);
System.out.println("attempts: " + attemptCount.get());
System.out.println("gets complete: " + getCompleteCount.get());
System.out.println("errors: " + errorCount.get());
System.out.println();
// Can be enabled in Database.java
//System.out.println("db success: " + d.commitSuccessCount.get());
//System.out.println("db errors: " + d.commitErrorCount.get());
//System.out.println("db success: " + db.commitSuccessCount.get());
//System.out.println("db errors: " + db.commitErrorCount.get());
System.exit(0);
} catch (Throwable throwable) {
throwable.printStackTrace();
} catch (Throwable t) {
t.printStackTrace();
System.exit(1);
}
}

View File

@ -20,15 +20,17 @@
package com.apple.foundationdb.test;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
import java.util.function.Function;
import com.apple.foundationdb.Database;
import com.apple.foundationdb.FDB;
import com.apple.foundationdb.FDBException;
import com.apple.foundationdb.KeySelector;
import com.apple.foundationdb.Range;
@ -37,17 +39,18 @@ import com.apple.foundationdb.Transaction;
import com.apple.foundationdb.tuple.ByteArrayUtil;
import com.apple.foundationdb.tuple.Tuple;
abstract class Context implements Runnable {
abstract class Context implements Runnable, AutoCloseable {
final Stack stack = new Stack();
final Database db;
final String preStr;
int instructionIndex = 0;
String trName;
KeySelector nextKey, endKey;
Long lastVersion = null;
List<Thread> children = new LinkedList<Thread>();
static ConcurrentHashMap<String, Transaction> transactionMap = new ConcurrentHashMap<>();
private String trName;
private List<Thread> children = new LinkedList<>();
private static Map<String, Transaction> transactionMap = new HashMap<>();
private static Map<Transaction, AtomicInteger> transactionRefCounts = new HashMap<>();
Context(Database db, byte[] prefix) {
this.db = db;
@ -83,43 +86,69 @@ abstract class Context implements Runnable {
}
}
public Transaction getCurrentTransaction() {
return Context.transactionMap.get(this.trName);
public static synchronized void addTransactionReference(Transaction tr) {
transactionRefCounts.computeIfAbsent(tr, x -> new AtomicInteger(0)).incrementAndGet();
}
public void updateCurrentTransaction(Transaction tr) {
Context.transactionMap.put(this.trName, tr);
}
public boolean updateCurrentTransaction(Transaction oldTr, Transaction newTr) {
return Context.transactionMap.replace(this.trName, oldTr, newTr);
}
public Transaction newTransaction() {
Transaction tr = db.createTransaction();
Context.transactionMap.put(this.trName, tr);
private static synchronized Transaction getTransaction(String trName) {
Transaction tr = transactionMap.get(trName);
addTransactionReference(tr);
return tr;
}
public Transaction newTransaction(Transaction oldTr) {
Transaction newTr = db.createTransaction();
boolean replaced = Context.transactionMap.replace(this.trName, oldTr, newTr);
if(replaced) {
return newTr;
}
else {
newTr.cancel();
return Context.transactionMap.get(this.trName);
public Transaction getCurrentTransaction() {
return getTransaction(trName);
}
public static synchronized void releaseTransaction(Transaction tr) {
if(tr != null) {
AtomicInteger count = transactionRefCounts.get(tr);
if(count.decrementAndGet() == 0) {
assert !transactionMap.containsValue(tr);
transactionRefCounts.remove(tr);
tr.close();
}
}
}
public void switchTransaction(byte[] trName) {
this.trName = ByteArrayUtil.printable(trName);
Transaction tr = db.createTransaction();
Transaction previousTr = Context.transactionMap.putIfAbsent(this.trName, tr);
if(previousTr != null) {
tr.cancel();
private static synchronized void updateTransaction(String trName, Transaction tr) {
releaseTransaction(transactionMap.put(trName, tr));
addTransactionReference(tr);
}
private static synchronized boolean updateTransaction(String trName, Transaction oldTr, Transaction newTr) {
if(transactionMap.replace(trName, oldTr, newTr)) {
addTransactionReference(newTr);
releaseTransaction(oldTr);
return true;
}
return false;
}
public void updateCurrentTransaction(Transaction tr) {
updateTransaction(trName, tr);
}
public boolean updateCurrentTransaction(Transaction oldTr, Transaction newTr) {
return updateTransaction(trName, oldTr, newTr);
}
public void newTransaction() {
Transaction tr = db.createTransaction();
updateCurrentTransaction(tr);
}
public void newTransaction(Transaction oldTr) {
Transaction newTr = db.createTransaction();
if(!updateCurrentTransaction(oldTr, newTr)) {
newTr.close();
}
}
public void switchTransaction(byte[] rawTrName) {
trName = ByteArrayUtil.printable(rawTrName);
newTransaction(null);
}
abstract void executeOperations() throws Throwable;
@ -140,36 +169,32 @@ abstract class Context implements Runnable {
throw new IllegalArgumentException("Invalid code: " + code);
}
void popParams(int num, final List<Object> params, final CompletableFuture<Void> done) {
private void popParams(int num, final List<Object> params, final CompletableFuture<Void> done) {
while(num-- > 0) {
Object item = stack.pop().value;
if(item instanceof CompletableFuture) {
@SuppressWarnings("unchecked")
final CompletableFuture<Object> future = (CompletableFuture<Object>)item;
final CompletableFuture<?> future = (CompletableFuture<?>)item;
final int nextNum = num;
future.whenCompleteAsync(new BiConsumer<Object, Throwable>() {
@Override
public void accept(Object o, Throwable t) {
if(t != null) {
Throwable root = StackUtils.getRootFDBException(t);
if(root instanceof FDBException) {
params.add(StackUtils.getErrorBytes((FDBException)root));
popParams(nextNum, params, done);
}
else {
done.completeExceptionally(t);
}
}
else {
if(o == null)
params.add("RESULT_NOT_PRESENT".getBytes());
else
params.add(o);
future.whenCompleteAsync((o, t) -> {
if(t != null) {
FDBException root = StackUtils.getRootFDBException(t);
if(root != null) {
params.add(StackUtils.getErrorBytes(root));
popParams(nextNum, params, done);
}
else {
done.completeExceptionally(t);
}
}
});
else {
if(o == null)
params.add("RESULT_NOT_PRESENT".getBytes());
else
params.add(o);
popParams(nextNum, params, done);
}
}, FDB.DEFAULT_EXECUTOR);
return;
}
@ -181,15 +206,17 @@ abstract class Context implements Runnable {
}
CompletableFuture<List<Object>> popParams(int num) {
final List<Object> params = new LinkedList<Object>();
CompletableFuture<Void> done = new CompletableFuture<Void>();
final List<Object> params = new ArrayList<>(num);
CompletableFuture<Void> done = new CompletableFuture<>();
popParams(num, params, done);
return done.thenApplyAsync(new Function<Void, List<Object>>() {
@Override
public List<Object> apply(Void n) {
return params;
}
});
return done.thenApply(x -> params);
}
@Override
public void close() {
for(Transaction tr : transactionMap.values()) {
tr.close();
}
}
}

View File

@ -25,54 +25,59 @@ import java.util.Collections;
import java.util.List;
import java.util.Random;
public class ContinuousSample <T extends Number & Comparable<T>> {
public ContinuousSample( int sampleSize ) {
public class ContinuousSample<T extends Number & Comparable<T>> {
public ContinuousSample(int sampleSize) {
this.sampleSize = sampleSize;
this.samples = new ArrayList<T>(sampleSize);
this.samples = new ArrayList<>(sampleSize);
this.populationSize = 0;
this.sorted = true;
}
public ContinuousSample<T> addSample(T sample) {
if(populationSize == 0)
_min = _max = sample;
min = max = sample;
populationSize++;
sorted = false;
if( populationSize <= sampleSize ) {
samples.add( sample );
} else if( random.nextDouble() < ( (double)sampleSize / populationSize ) ) {
if(populationSize <= sampleSize) {
samples.add(sample);
} else if(random.nextDouble() < ((double)sampleSize / populationSize)) {
samples.add(random.nextInt(sampleSize), sample);
}
_max = sample.compareTo(_max) > 0 ? sample : _max;
_min = sample.compareTo(_min) < 0 ? sample : _min;
max = sample.compareTo(max) > 0 ? sample : max;
min = sample.compareTo(min) < 0 ? sample : min;
return this;
}
public double mean() {
if (samples.size() == 0) return 0;
double sum = 0;
for( int c = 0; c < samples.size(); c++ ) {
for(int c = 0; c < samples.size(); c++) {
sum += samples.get(c).doubleValue();
}
return sum / samples.size();
}
public T median() {
return percentile( 0.5 );
return percentile(0.5);
}
public T percentile( double percentile ) {
if( samples.size() == 0 || percentile < 0.0 || percentile > 1.0 )
public T percentile(double percentile) {
if(samples.size() == 0 || percentile < 0.0 || percentile > 1.0)
return null;
sort();
int idx = (int)Math.floor( ( samples.size() - 1 ) * percentile );
int idx = (int)Math.floor((samples.size() - 1) * percentile);
return samples.get(idx);
}
public T min() { return _min; }
public T max() { return _max; }
public T min() {
return min;
}
public T max() {
return max;
}
@Override
public String toString() {
@ -85,10 +90,10 @@ public class ContinuousSample <T extends Number & Comparable<T>> {
private long populationSize;
private boolean sorted;
private List<T> samples;
private T _min, _max;
private T min, max;
private void sort() {
if( !sorted && samples.size() > 1 )
if(!sorted && samples.size() > 1)
Collections.sort(samples);
sorted = true;
}

View File

@ -38,7 +38,7 @@ class DirectoryExtension {
int dirIndex = 0;
int errorIndex = 0;
public DirectoryExtension() {
DirectoryExtension() {
dirList.add(DirectoryLayer.getDefault());
}

View File

@ -24,12 +24,9 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import com.apple.foundationdb.Cluster;
import com.apple.foundationdb.Database;
import com.apple.foundationdb.FDB;
import com.apple.foundationdb.Transaction;
import com.apple.foundationdb.TransactionContext;
import com.apple.foundationdb.directory.DirectoryLayer;
import com.apple.foundationdb.directory.DirectorySubspace;
@ -52,33 +49,30 @@ public class DirectoryTest {
final DirectoryLayer dir = new DirectoryLayer();
try {
db.run(new Function<Transaction, Void>() {
@Override
public Void apply(Transaction tr) {
List<String> path = new ArrayList<>();
path.add("foo");
DirectorySubspace foo = dir.create(tr, path).join();//, "partition".getBytes("UTF-8")).get();
System.out.println(foo.getPath());
path.add("bar");
DirectorySubspace bar = dir.create(tr, path).join();//, "partition".getBytes("UTF-8")).get();
System.out.println(foo.getPath());
path.add("baz");
DirectorySubspace baz = dir.create(tr, path).join();
System.out.println(foo.getPath());
System.out.println("Created foo: " + foo.exists(tr).join());
System.out.println("Created bar: " + bar.exists(tr).join());
System.out.println("Created baz: " + baz.exists(tr).join());
db.run(tr -> {
List<String> path = new ArrayList<>();
path.add("foo");
DirectorySubspace foo = dir.create(tr, path).join(); //, "partition".getBytes("UTF-8")).get();
System.out.println(foo.getPath());
path.add("bar");
DirectorySubspace bar = dir.create(tr, path).join(); //, "partition".getBytes("UTF-8")).get();
System.out.println(foo.getPath());
path.add("baz");
DirectorySubspace baz = dir.create(tr, path).join();
System.out.println(foo.getPath());
System.out.println("Created foo: " + foo.exists(tr).join());
System.out.println("Created bar: " + bar.exists(tr).join());
System.out.println("Created baz: " + baz.exists(tr).join());
DirectorySubspace bat = baz.moveTo(tr, Arrays.asList("foo", "bar", "bat")).join();
DirectorySubspace bat = baz.moveTo(tr, Arrays.asList("foo", "bar", "bat")).join();
System.out.println("Moved baz to bat: " + bat.exists(tr).join());
System.out.println("Moved baz to bat: " + bat.exists(tr).join());
foo.removeIfExists(tr).join();
foo.removeIfExists(tr).join();
System.out.println("Removed foo: " + foo.exists(tr).join());
System.out.println("Removed foo: " + foo.exists(tr).join());
return null;
}
return null;
});
} catch (Throwable e) {
e.printStackTrace();
@ -86,4 +80,6 @@ public class DirectoryTest {
System.exit(0);
}
private DirectoryTest() {}
}

View File

@ -23,9 +23,8 @@ package com.apple.foundationdb.test;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
import java.util.function.Supplier;
import com.apple.foundationdb.FDB;
import com.apple.foundationdb.async.AsyncUtil;
import com.apple.foundationdb.tuple.Tuple;
@ -41,34 +40,18 @@ class DirectoryUtil {
}
CompletableFuture<List<Tuple>> pop() {
return AsyncUtil.whileTrue(new Supplier<CompletableFuture<Boolean>>() {
@Override
public CompletableFuture<Boolean> get() {
if(num-- == 0) {
return CompletableFuture.completedFuture(false);
}
return inst.popParam()
.thenComposeAsync(new Function<Object, CompletableFuture<List<Object>>>() {
@Override
public CompletableFuture<List<Object>> apply(Object count) {
return inst.popParams(StackUtils.getInt(count));
}
})
.thenApplyAsync(new Function<List<Object>, Boolean>() {
@Override
public Boolean apply(List<Object> elements) {
return AsyncUtil.whileTrue(() -> {
if(num-- == 0) {
return AsyncUtil.READY_FALSE;
}
return inst.popParam()
.thenComposeAsync(count -> inst.popParams(StackUtils.getInt(count)), FDB.DEFAULT_EXECUTOR)
.thenApplyAsync(elements -> {
tuples.add(Tuple.fromItems(elements));
return num > 0;
}
});
}
}, FDB.DEFAULT_EXECUTOR);
})
.thenApplyAsync(new Function<Void, List<Tuple>>() {
@Override
public List<Tuple> apply(Void ignore) {
return tuples;
}
});
.thenApply(ignore -> tuples);
}
}
@ -77,42 +60,26 @@ class DirectoryUtil {
}
static CompletableFuture<Tuple> popTuple(Instruction inst) {
return popTuples(inst, 1)
.thenApplyAsync(new Function<List<Tuple>, Tuple>() {
@Override
public Tuple apply(List<Tuple> tuples) {
return tuples.get(0);
}
});
return popTuples(inst, 1).thenApply(tuples -> tuples.get(0));
}
static CompletableFuture<List<List<String>>> popPaths(Instruction inst, int num) {
return popTuples(inst, num)
.thenApplyAsync(new Function<List<Tuple>, List<List<String>>>() {
@Override
public List<List<String>> apply(List<Tuple> tuples) {
List<List<String>> paths = new ArrayList<List<String>>();
for(Tuple t : tuples) {
List<String> path = new ArrayList<String>();
for(int i = 0; i < t.size(); ++i)
path.add(t.getString(i));
return popTuples(inst, num).thenApplyAsync(tuples -> {
List<List<String>> paths = new ArrayList<>(tuples.size());
for(Tuple t : tuples) {
List<String> path = new ArrayList<>(t.size());
for(int i = 0; i < t.size(); ++i)
path.add(t.getString(i));
paths.add(path);
}
return paths;
paths.add(path);
}
});
return paths;
}, FDB.DEFAULT_EXECUTOR);
}
static CompletableFuture<List<String>> popPath(Instruction inst) {
return popPaths(inst, 1)
.thenApplyAsync(new Function<List<List<String>>, List<String>>() {
@Override
public List<String> apply(List<List<String>> paths) {
return paths.get(0);
}
});
return popPaths(inst, 1).thenApply(paths -> paths.get(0));
}
static void pushError(Instruction inst, Throwable t, List<Object> dirList) {
@ -123,4 +90,6 @@ class DirectoryUtil {
if(op.createsDirectory)
dirList.add(null);
}
private DirectoryUtil() {}
}

View File

@ -29,27 +29,23 @@ import com.apple.foundationdb.Transaction;
import com.apple.foundationdb.tuple.Tuple;
public class Example {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FDB fdb = FDB.selectAPIVersion(510);
Database db = fdb.open();
public static void main(String[] args) throws ExecutionException, InterruptedException {
FDB fdb = FDB.selectAPIVersion(510);
Database db = fdb.open();
// Run an operation on the database
db.run(new Function<Transaction, Void>() {
@Override
public Void apply(Transaction tr) {
tr.set(Tuple.from("hello").pack(), Tuple.from("world").pack());
return null;
}
});
// Run an operation on the database
db.run((Function<Transaction, Void>) tr -> {
tr.set(Tuple.from("hello").pack(), Tuple.from("world").pack());
return null;
});
// Get the value of 'hello' from the database
String hello = db.run(new Function<Transaction, String>() {
@Override
public String apply(Transaction tr) {
byte[] result = tr.get(Tuple.from("hello").pack()).join();
return Tuple.fromBytes(result).getString(0);
}
});
System.out.println("Hello " + hello);
}
// Get the value of 'hello' from the database
String hello = db.run(tr -> {
byte[] result = tr.get(Tuple.from("hello").pack()).join();
return Tuple.fromBytes(result).getString(0);
});
System.out.println("Hello " + hello);
}
private Example() {}
}

View File

@ -21,7 +21,7 @@
package com.apple.foundationdb.test;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
import java.util.List;
import com.apple.foundationdb.ReadTransaction;
import com.apple.foundationdb.ReadTransactionContext;
@ -29,64 +29,71 @@ import com.apple.foundationdb.Transaction;
import com.apple.foundationdb.TransactionContext;
import com.apple.foundationdb.tuple.Tuple;
import java.util.List;
class Instruction extends Stack {
private final static String SUFFIX_SNAPSHOT = "_SNAPSHOT";
private final static String SUFFIX_DATABASE = "_DATABASE";
private static final String SUFFIX_SNAPSHOT = "_SNAPSHOT";
private static final String SUFFIX_DATABASE = "_DATABASE";
String op;
Tuple tokens;
Context context;
boolean isDatabase;
boolean isSnapshot;
Transaction tr;
ReadTransaction readTr;
TransactionContext tcx;
ReadTransactionContext readTcx;
final String op;
final Tuple tokens;
final Context context;
final boolean isDatabase;
final boolean isSnapshot;
final Transaction tr;
final ReadTransaction readTr;
final TransactionContext tcx;
final ReadTransactionContext readTcx;
public Instruction(Context context, Tuple tokens) {
Instruction(Context context, Tuple tokens) {
this.context = context;
this.tokens = tokens;
op = tokens.getString(0);
isDatabase = op.endsWith(SUFFIX_DATABASE);
isSnapshot = op.endsWith(SUFFIX_SNAPSHOT);
String fullOp = tokens.getString(0);
isDatabase = fullOp.endsWith(SUFFIX_DATABASE);
isSnapshot = fullOp.endsWith(SUFFIX_SNAPSHOT);
if(isDatabase) {
this.tr = context.db.createTransaction();
readTr = this.tr;
op = op.substring(0, op.length() - SUFFIX_DATABASE.length());
tr = null;
readTr = null;
op = fullOp.substring(0, fullOp.length() - SUFFIX_DATABASE.length());
}
else if(isSnapshot) {
this.tr = context.getCurrentTransaction();
readTr = this.tr.snapshot();
op = op.substring(0, op.length() - SUFFIX_SNAPSHOT.length());
tr = context.getCurrentTransaction();
readTr = tr.snapshot();
op = fullOp.substring(0, fullOp.length() - SUFFIX_SNAPSHOT.length());
}
else {
this.tr = context.getCurrentTransaction();
readTr = this.tr;
tr = context.getCurrentTransaction();
readTr = tr;
op = fullOp;
}
tcx = isDatabase ? context.db : this.tr;
readTcx = isDatabase ? context.db : this.readTr;
tcx = isDatabase ? context.db : tr;
readTcx = isDatabase ? context.db : readTr;
}
void setTransaction(Transaction tr) {
this.tr = tr;
if(isSnapshot) {
readTr = this.tr.snapshot();
}
else {
readTr = tr;
}
void setTransaction(Transaction newTr) {
if(!isDatabase) {
context.updateCurrentTransaction(tr);
context.updateCurrentTransaction(newTr);
}
}
void setTransaction(Transaction oldTr, Transaction newTr) {
if(!isDatabase) {
context.updateCurrentTransaction(oldTr, newTr);
}
}
void releaseTransaction() {
Context.releaseTransaction(tr);
}
void push(Object o) {
if(o instanceof CompletableFuture && tr != null) {
CompletableFuture<?> future = (CompletableFuture<?>)o;
Context.addTransactionReference(tr);
future.whenComplete((x, t) -> Context.releaseTransaction(tr));
}
context.stack.push(context.instructionIndex, o);
}
@ -120,10 +127,6 @@ class Instruction extends Stack {
CompletableFuture<Object> popParam() {
return popParams(1)
.thenApplyAsync(new Function<List<Object>, Object>() {
public Object apply(List<Object> params) {
return params.get(0);
}
});
.thenApplyAsync((params) -> params.get(0));
}
}

View File

@ -21,13 +21,11 @@
package com.apple.foundationdb.test;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import com.apple.foundationdb.Cluster;
import com.apple.foundationdb.Database;
import com.apple.foundationdb.FDB;
import com.apple.foundationdb.KeyValue;
import com.apple.foundationdb.Transaction;
import com.apple.foundationdb.TransactionContext;
public class IterableTest {
@ -49,14 +47,11 @@ public class IterableTest {
long start = System.currentTimeMillis();
final AtomicInteger lastcount = new AtomicInteger(0);
try {
db.run(new Function<Transaction, Void>() {
@Override
public Void apply(Transaction tr) {
for(KeyValue e : tr.getRange("vcount".getBytes(), "zz".getBytes())) {
System.out.println("K: " + new String(e.getKey()) + ", V: " + new String(e.getValue()));
}
return null;
db.run(tr -> {
for(KeyValue e : tr.getRange("vcount".getBytes(), "zz".getBytes())) {
System.out.println("K: " + new String(e.getKey()) + ", V: " + new String(e.getValue()));
}
return null;
});
} catch (Throwable e) {
e.printStackTrace();
@ -71,4 +66,6 @@ public class IterableTest {
System.exit(0);
}
private IterableTest() {}
}

View File

@ -27,7 +27,7 @@ import com.apple.foundationdb.Database;
import com.apple.foundationdb.FDB;
import com.apple.foundationdb.LocalityUtil;
import com.apple.foundationdb.Transaction;
import com.apple.foundationdb.async.AsyncIterable;
import com.apple.foundationdb.async.CloseableAsyncIterator;
import com.apple.foundationdb.async.AsyncUtil;
import com.apple.foundationdb.tuple.ByteArrayUtil;
@ -36,23 +36,28 @@ public class LocalityTests {
public static void main(String[] args) {
FDB fdb = FDB.selectAPIVersion(510);
Database database = fdb.open(args[0]);
{
Transaction tr = database.createTransaction();
String[] keyAddresses = LocalityUtil.getAddressesForKey(tr, "a".getBytes()).join();
for(String s : keyAddresses) {
System.out.println(" @ " + s);
}
Transaction tr = database.createTransaction();
String[] keyAddresses = LocalityUtil.getAddressesForKey(tr, "a".getBytes()).join();
for(String s : keyAddresses) {
System.out.println(" @ " + s);
}
long start = System.currentTimeMillis();
AsyncIterable<byte[]> keys = LocalityUtil.getBoundaryKeys(database, new byte[0], new byte[] { (byte)255 } );
CompletableFuture<List<byte[]>> collection = AsyncUtil.collect(keys);
CloseableAsyncIterator<byte[]> keys = LocalityUtil.getBoundaryKeys(database, new byte[0], new byte[] { (byte)255 });
CompletableFuture<List<byte[]>> collection = AsyncUtil.collectRemaining(keys);
List<byte[]> list = collection.join();
System.out.println("Took " + (System.currentTimeMillis() - start) + "ms to get " +
list.size() + " items");
keys.close();
int i = 0;
for(byte[] key : collection.join()) {
System.out.println(i++ + ": " + ByteArrayUtil.printable(key));
}
}
private LocalityTests() {}
}

View File

@ -25,8 +25,6 @@ import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
import java.util.function.Function;
import com.apple.foundationdb.Database;
import com.apple.foundationdb.FDB;
@ -92,34 +90,26 @@ public class ParallelRandomScan {
final long launch = System.nanoTime();
final AsyncIterator<KeyValue> it = range.iterator();
final CompletableFuture<KeyValue> f = it.onHasNext().thenApplyAsync(
new Function<Boolean, KeyValue>() {
@Override
public KeyValue apply(Boolean o) {
if(!o) {
return null;
}
return it.next();
}
}
);
f.whenCompleteAsync(new BiConsumer<KeyValue, Throwable>() {
@Override
public void accept(KeyValue kv, Throwable t) {
if(kv != null) {
readsCompleted.incrementAndGet();
long timeTaken = System.nanoTime() - launch;
synchronized(latencies) {
latencies.addSample(timeTaken);
}
}
else if(t != null) {
errors.incrementAndGet();
}
coordinator.release();
final CompletableFuture<KeyValue> f = it.onHasNext().thenApplyAsync(hasFirst -> {
if(!hasFirst) {
return null;
}
});
return it.next();
}, FDB.DEFAULT_EXECUTOR);
f.whenCompleteAsync((kv, t) -> {
if(kv != null) {
readsCompleted.incrementAndGet();
long timeTaken = System.nanoTime() - launch;
synchronized(latencies) {
latencies.addSample(timeTaken);
}
}
else if(t != null) {
errors.incrementAndGet();
}
coordinator.release();
}, FDB.DEFAULT_EXECUTOR);
}
// Block for ALL tasks to end!
@ -133,4 +123,6 @@ public class ParallelRandomScan {
System.out.println(String.format(" Mean: %.2f, Median: %d, 98%%: %d",
latencies.mean(), latencies.median(), latencies.percentile(0.98)));
}
private ParallelRandomScan() {}
}

View File

@ -20,13 +20,6 @@
package com.apple.foundationdb.test;
import com.apple.foundationdb.Database;
import com.apple.foundationdb.KeySelector;
import com.apple.foundationdb.Transaction;
import com.apple.foundationdb.TransactionContext;
import com.apple.foundationdb.async.AsyncUtil;
import com.apple.foundationdb.tuple.ByteArrayUtil;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@ -40,6 +33,13 @@ import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import com.apple.foundationdb.Database;
import com.apple.foundationdb.KeySelector;
import com.apple.foundationdb.Transaction;
import com.apple.foundationdb.TransactionContext;
import com.apple.foundationdb.async.AsyncUtil;
import com.apple.foundationdb.tuple.ByteArrayUtil;
public class PerformanceTester extends AbstractTester {
private final int keyCount;
private final int keySize;

View File

@ -20,14 +20,17 @@
package com.apple.foundationdb.test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
import com.apple.foundationdb.Database;
import com.apple.foundationdb.Transaction;
import com.apple.foundationdb.tuple.ByteArrayUtil;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
public class RYWBenchmark extends AbstractTester {
private int keyCount;
@ -156,7 +159,7 @@ public class RYWBenchmark extends AbstractTester {
public Double getRangeBasic(Transaction tr, int count) {
long start = System.nanoTime();
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();

View File

@ -55,22 +55,19 @@ public class RangeTest {
Database db = fdb.open();
try {
db.run(new Function<Transaction, Void>() {
@Override
public Void apply(Transaction tr) {
long version = tr.getReadVersion().join();
System.out.println("DB version: " + version);
tr.get("apple1".getBytes()).join();
tr.set("apple1".getBytes(), "crunchy1".getBytes());
tr.set("apple2".getBytes(), "crunchy2".getBytes());
tr.set("apple3".getBytes(), "crunchy3".getBytes());
tr.set("apple4".getBytes(), "crunchy4".getBytes());
tr.set("apple5".getBytes(), "crunchy5".getBytes());
tr.set("apple6".getBytes(), "crunchy6".getBytes());
System.out.println("Attempting to commit apple/crunchy pairs...");
db.run((Function<Transaction, Void>) tr -> {
long version = tr.getReadVersion().join();
System.out.println("DB version: " + version);
tr.get("apple1".getBytes()).join();
tr.set("apple1".getBytes(), "crunchy1".getBytes());
tr.set("apple2".getBytes(), "crunchy2".getBytes());
tr.set("apple3".getBytes(), "crunchy3".getBytes());
tr.set("apple4".getBytes(), "crunchy4".getBytes());
tr.set("apple5".getBytes(), "crunchy5".getBytes());
tr.set("apple6".getBytes(), "crunchy6".getBytes());
System.out.println("Attempting to commit apple/crunchy pairs...");
return null;
}
return null;
});
} catch (Throwable e){
e.printStackTrace();
@ -114,8 +111,8 @@ public class RangeTest {
e.printStackTrace();
return;
}
//db.dispose();
//cluster.dispose();
//db.close();
//cluster.close();
tr = db.createTransaction();
checkRange(tr);
@ -154,8 +151,8 @@ public class RangeTest {
System.out.println("range comparisons okay");
}
db.dispose();
//cluster.dispose();
db.close();
//cluster.close();
//fdb.stopNetwork();
System.out.println("Done with test program");
}
@ -167,7 +164,7 @@ public class RangeTest {
System.out.println("Value is " +
(val != null ? new String(val) : "not present"));
AsyncIterable<KeyValue> entryList = tr.getRange(
AsyncIterable<KeyValue> entryList = tr.getRange(
KeySelector.firstGreaterOrEqual("apple".getBytes()),
KeySelector.firstGreaterOrEqual("banana".getBytes()),4);
List<KeyValue> entries = entryList.asList().join();
@ -187,4 +184,6 @@ public class RangeTest {
}
}
private RangeTest() {}
}

View File

@ -94,4 +94,5 @@ public class SerialInsertion {
}
}
private SerialInsertion() {}
}

View File

@ -133,4 +133,6 @@ public class SerialIteration {
}
return counter;
}
private SerialIteration() {}
}

View File

@ -21,12 +21,10 @@
package com.apple.foundationdb.test;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import com.apple.foundationdb.Cluster;
import com.apple.foundationdb.Database;
import com.apple.foundationdb.FDB;
import com.apple.foundationdb.Transaction;
import com.apple.foundationdb.TransactionContext;
public class SerialTest {
@ -56,17 +54,14 @@ public class SerialTest {
final AtomicInteger lastcount = new AtomicInteger(0);
for(int i = 0; i < reps; i++) {
try {
db.run(new Function<Transaction, Void>() {
@Override
public Void apply(Transaction tr) {
byte[] val = tr.get("count".getBytes()).join();
//System.out.println("Got value");
int count = Integer.parseInt(new String(val));
tr.set("count".getBytes(), Integer.toString(count + 1).getBytes());
lastcount.set(count);
db.run(tr -> {
byte[] val = tr.get("count".getBytes()).join();
//System.out.println("Got value");
int count = Integer.parseInt(new String(val));
tr.set("count".getBytes(), Integer.toString(count + 1).getBytes());
lastcount.set(count);
return null;
}
return null;
});
} catch (Throwable e) {
e.printStackTrace();
@ -83,4 +78,5 @@ public class SerialTest {
System.exit(0);
}
private SerialTest() {}
}

View File

@ -23,7 +23,7 @@ package com.apple.foundationdb.test;
class StackEntry {
int idx;
Object value;
public StackEntry(int idx, Object value) {
StackEntry(int idx, Object value) {
this.idx = idx;
this.value = value;
}

View File

@ -21,10 +21,15 @@
package com.apple.foundationdb.test;
import java.math.BigInteger;
import java.util.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.function.Function;
@ -37,10 +42,11 @@ import com.apple.foundationdb.KeyValue;
import com.apple.foundationdb.LocalityUtil;
import com.apple.foundationdb.MutationType;
import com.apple.foundationdb.Range;
import com.apple.foundationdb.ReadTransaction;
import com.apple.foundationdb.StreamingMode;
import com.apple.foundationdb.Transaction;
import com.apple.foundationdb.async.AsyncIterable;
import com.apple.foundationdb.async.AsyncUtil;
import com.apple.foundationdb.async.CloseableAsyncIterator;
import com.apple.foundationdb.tuple.ByteArrayUtil;
import com.apple.foundationdb.tuple.Tuple;
@ -109,7 +115,7 @@ public class StackTester {
}
else if(op == StackOperation.WAIT_EMPTY) {
List<Object> params = inst.popParams(1).join();
inst.context.db.run(new WaitEmpty((byte [])params.get(0)));
inst.context.db.run(new WaitEmpty((byte[])params.get(0)));
inst.push("WAITED_FOR_EMPTY".getBytes());
}
else if(op == StackOperation.START_THREAD) {
@ -128,62 +134,40 @@ public class StackTester {
final List<Object> params = inst.popParams(2).join();
//System.out.println(inst.context.preStr + " - " + "Setting '" + ArrayUtils.printable((byte[]) params.get(0)) +
// "' to '" + ArrayUtils.printable((byte[]) params.get(1)) + "'");
executeMutation(inst,
new Function<Transaction, Void>() {
@Override
public Void apply(Transaction tr) {
tr.set((byte[])params.get(0), (byte[])params.get(1));
return null;
}
});
executeMutation(inst, tr -> {
tr.set((byte[])params.get(0), (byte[])params.get(1));
return null;
});
}
else if(op == StackOperation.CLEAR) {
final List<Object> params = inst.popParams(1).join();
//System.out.println(inst.context.preStr + " - " + "Clearing: '" + ByteArrayUtil.printable((byte[]) params.get(0)) + "'");
executeMutation(inst,
new Function<Transaction, Void>() {
@Override
public Void apply(Transaction tr) {
tr.clear((byte[])params.get(0));
return null;
}
}
);
executeMutation(inst, tr -> {
tr.clear((byte[])params.get(0));
return null;
});
}
else if(op == StackOperation.CLEAR_RANGE) {
final List<Object> params = inst.popParams(2).join();
executeMutation(inst,
new Function<Transaction, Void>() {
@Override
public Void apply(Transaction tr) {
tr.clear((byte[])params.get(0), (byte[])params.get(1));
return null;
}
});
executeMutation(inst, tr -> {
tr.clear((byte[])params.get(0), (byte[])params.get(1));
return null;
});
}
else if(op == StackOperation.CLEAR_RANGE_STARTS_WITH) {
final List<Object> params = inst.popParams(1).join();
executeMutation(inst,
new Function<Transaction, Void>() {
@Override
public Void apply(Transaction tr) {
tr.clear(Range.startsWith((byte[])params.get(0)));
return null;
}
});
executeMutation(inst, tr -> {
tr.clear(Range.startsWith((byte[])params.get(0)));
return null;
});
}
else if(op == StackOperation.ATOMIC_OP) {
final List<Object> params = inst.popParams(3).join();
final MutationType optype = MutationType.valueOf((String)params.get(0));
executeMutation(inst,
new Function<Transaction, Void>() {
@Override
public Void apply(Transaction tr) {
tr.mutate(optype, (byte[])params.get(1), (byte[])params.get(2));
return null;
}
}
);
executeMutation(inst, tr -> {
tr.mutate(optype, (byte[])params.get(1), (byte[])params.get(2));
return null;
});
}
else if(op == StackOperation.COMMIT) {
inst.push(inst.tr.commit());
@ -507,6 +491,8 @@ public class StackTester {
directoryExtension.processInstruction(inst);
else
processInstruction(inst);
inst.releaseTransaction();
}
@Override
@ -515,8 +501,10 @@ public class StackTester {
while(true) {
Transaction t = db.createTransaction();
List<KeyValue> keyValues = t.getRange(begin, endKey/*, 1000*/).asList().join();
if(keyValues.size() == 0)
t.close();
if(keyValues.size() == 0) {
break;
}
//System.out.println(" * Got " + keyValues.size() + " instructions");
for(KeyValue next : keyValues) {
@ -525,6 +513,7 @@ public class StackTester {
instructionIndex++;
}
}
//System.out.println(" * Completed " + instructionIndex + " instructions");
}
}
@ -669,22 +658,27 @@ public class StackTester {
tr.options().setTimeout(60*1000);
tr.options().setReadSystemKeys();
tr.getReadVersion().join();
AsyncIterable<byte[]> boundaryKeys = LocalityUtil.getBoundaryKeys(
CloseableAsyncIterator<byte[]> boundaryKeys = LocalityUtil.getBoundaryKeys(
tr, new byte[0], new byte[]{(byte) 255, (byte) 255});
List<byte[]> keys = boundaryKeys.asList().join();
for(int i = 0; i < keys.size() - 1; i++) {
byte[] start = keys.get(i);
byte[] end = tr.getKey(KeySelector.lastLessThan(keys.get(i + 1))).join();
List<String> startAddresses = Arrays.asList(LocalityUtil.getAddressesForKey(tr, start).join());
List<String> endAddresses = Arrays.asList(LocalityUtil.getAddressesForKey(tr, end).join());
for(String a : startAddresses) {
if(!endAddresses.contains(a)) {
throw new RuntimeException("Locality not internally consistent.");
try {
List<byte[]> keys = AsyncUtil.collectRemaining(boundaryKeys).join();
for(int i = 0; i < keys.size() - 1; i++) {
byte[] start = keys.get(i);
byte[] end = tr.getKey(KeySelector.lastLessThan(keys.get(i + 1))).join();
List<String> startAddresses = Arrays.asList(LocalityUtil.getAddressesForKey(tr, start).join());
List<String> endAddresses = Arrays.asList(LocalityUtil.getAddressesForKey(tr, end).join());
for(String a : startAddresses) {
if(!endAddresses.contains(a)) {
throw new RuntimeException("Locality not internally consistent.");
}
}
}
}
return null;
return null;
}
finally {
boundaryKeys.close();
}
});
}
@ -709,6 +703,10 @@ public class StackTester {
//System.out.println("Starting test...");
c.run();
//System.out.println("Done with test.");
db.close();
System.gc();
}
private StackTester() {}
}

View File

@ -27,7 +27,6 @@ import java.util.concurrent.CompletionException;
import com.apple.foundationdb.FDBException;
import com.apple.foundationdb.KeySelector;
import com.apple.foundationdb.async.AsyncUtil;
import com.apple.foundationdb.tuple.Tuple;
public class StackUtils {
@ -127,4 +126,6 @@ public class StackUtils {
return (t instanceof FDBException) ? (FDBException)t : null;
}
private StackUtils() {}
}

View File

@ -20,13 +20,12 @@
package com.apple.foundationdb.test;
import java.util.ArrayList;
import java.util.List;
import com.apple.foundationdb.subspace.Subspace;
import com.apple.foundationdb.tuple.Tuple;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class TesterArgs {
private String outputDirectory;
private boolean multiversionApi;
@ -45,15 +44,15 @@ public class TesterArgs {
}
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"
+ "\n"
+ "Arguments:\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"
+ " --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"
+ " --tests-to-run TEST [TEST ...] List of test names to run.\n"
+ " -h/--help Print this help message and then quit.\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" +
"Arguments:\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" +
" --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" +
" --tests-to-run TEST [TEST ...] List of test names to run.\n" +
" -h/--help Print this help message and then quit.\n";
System.out.print(usage);
}

View File

@ -20,12 +20,9 @@
package com.apple.foundationdb.test;
import java.util.function.Function;
import com.apple.foundationdb.Cluster;
import com.apple.foundationdb.Database;
import com.apple.foundationdb.FDB;
import com.apple.foundationdb.Transaction;
import com.apple.foundationdb.TransactionContext;
import com.apple.foundationdb.tuple.Tuple;
@ -47,19 +44,16 @@ public class TupleTest {
System.out.println("Running tests...");
long start = System.currentTimeMillis();
try {
db.run(new Function<Transaction, Void>() {
@Override
public Void apply(Transaction tr) {
Tuple t = new Tuple();
t.add(100230045000L);
t.add("Hello!");
t.add("foo".getBytes());
db.run(tr -> {
Tuple t = new Tuple();
t.add(100230045000L);
t.add("Hello!");
t.add("foo".getBytes());
/*for(Map.Entry<byte[], byte[]> e : tr.getRange("vcount".getBytes(), "zz".getBytes())) {
System.out.println("K: " + new String(e.getKey()) + ", V: " + new String(e.getValue()));
}*/
return null;
}
/*for(Map.Entry<byte[], byte[]> e : tr.getRange("vcount".getBytes(), "zz".getBytes())) {
System.out.println("K: " + new String(e.getKey()) + ", V: " + new String(e.getValue()));
}*/
return null;
});
} catch (Throwable e) {
e.printStackTrace();
@ -73,4 +67,6 @@ public class TupleTest {
System.exit(0);
}
private TupleTest() {}
}

View File

@ -20,6 +20,8 @@
package com.apple.foundationdb.test;
import java.util.concurrent.CompletableFuture;
import com.apple.foundationdb.Database;
import com.apple.foundationdb.FDB;
import com.apple.foundationdb.MutationType;
@ -28,36 +30,36 @@ import com.apple.foundationdb.subspace.Subspace;
import com.apple.foundationdb.tuple.Tuple;
import com.apple.foundationdb.tuple.Versionstamp;
import java.util.concurrent.CompletableFuture;
public class VersionstampSmokeTest {
public static void main(String[] args) {
FDB fdb = FDB.selectAPIVersion(510);
Database db = fdb.open();
db.run(tr -> {
tr.clear(Tuple.from("prefix").range());
return null;
tr.clear(Tuple.from("prefix").range());
return null;
});
CompletableFuture<byte[]> trVersionFuture = db.run((Transaction tr) -> {
// The incomplete Versionstamp will have tr's version information when committed.
Tuple t = Tuple.from("prefix", Versionstamp.incomplete());
tr.mutate(MutationType.SET_VERSIONSTAMPED_KEY, t.packWithVersionstamp(), new byte[0]);
return tr.getVersionstamp();
// The incomplete Versionstamp will have tr's version information when committed.
Tuple t = Tuple.from("prefix", Versionstamp.incomplete());
tr.mutate(MutationType.SET_VERSIONSTAMPED_KEY, t.packWithVersionstamp(), new byte[0]);
return tr.getVersionstamp();
});
byte[] trVersion = trVersionFuture.join();
Versionstamp v = db.run((Transaction tr) -> {
Subspace subspace = new Subspace(Tuple.from("prefix"));
byte[] serialized = tr.getRange(subspace.range(), 1).iterator().next().getKey();
Tuple t = subspace.unpack(serialized);
return t.getVersionstamp(0);
Subspace subspace = new Subspace(Tuple.from("prefix"));
byte[] serialized = tr.getRange(subspace.range(), 1).iterator().next().getKey();
Tuple t = subspace.unpack(serialized);
return t.getVersionstamp(0);
});
System.out.println(v);
System.out.println(Versionstamp.complete(trVersion));
assert v.equals(Versionstamp.complete(trVersion));
}
private VersionstampSmokeTest() {}
}

View File

@ -75,20 +75,17 @@ public class WatchTest {
a.incrementAndGet();
}
};
Runnable get = new Runnable() {
@Override
public void run() {
try {
System.err.println("`f' get()...");
f.join();
System.err.println("`f' changed");
} catch(FDBException e) {
System.err.println("`f' watch error -> " + e.getMessage());
if(e.getCode() != 1101)
throw e;
} finally {
a.incrementAndGet();
}
Runnable get = () -> {
try {
System.err.println("`f' get()...");
f.join();
System.err.println("`f' changed");
} catch(FDBException e12) {
System.err.println("`f' watch error -> " + e12.getMessage());
if(e12.getCode() != 1101)
throw e12;
} finally {
a.incrementAndGet();
}
};
if(r.nextBoolean()) {
@ -109,8 +106,10 @@ public class WatchTest {
}
//if(i % 1000 == 0) {
System.out.println("Done with " + i);
System.out.println("Done with " + i);
//}
}
}
private WatchTest() {}
}

View File

@ -20,10 +20,11 @@
package com.apple.foundationdb.test;
import com.apple.foundationdb.async.AsyncUtil;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicInteger;
import com.apple.foundationdb.async.AsyncUtil;
public class WhileTrueTest {
public static void main(String[] args) {
// This should cause memory issues using the old implementation but not the new one.
@ -32,4 +33,6 @@ public class WhileTrueTest {
AsyncUtil.whileTrue(() -> CompletableFuture.completedFuture(count.decrementAndGet()).thenApplyAsync(c -> c > 0)).join();
System.out.println("Final value: " + count.get());
}
private WhileTrueTest() {}
}

View File

@ -277,7 +277,10 @@ public class FDB {
f = new FutureCluster(Cluster_create(clusterFilePath), e);
}
Cluster c = f.get();
return c.openDatabase(e);
Database db = c.openDatabase(e);
c.dispose();
return db;
}
/**

View File

@ -121,7 +121,7 @@ class FDBDatabase extends DefaultDisposableImpl implements Database, Disposable,
public <T> Future<T> runAsync(final Function<? super Transaction, Future<T>> retryable, Executor e) {
final AtomicReference<Transaction> trRef = new AtomicReference<Transaction>(createTransaction(e));
final AtomicReference<T> returnValue = new AtomicReference<T>();
return AsyncUtil.whileTrue(new Function<Void, Future<Boolean>>() {
Future<T> result = AsyncUtil.whileTrue(new Function<Void, Future<Boolean>>() {
@Override
public Future<Boolean> apply(Void v) {
Future<T> process = AsyncUtil.applySafely(retryable, trRef.get());
@ -150,13 +150,21 @@ class FDBDatabase extends DefaultDisposableImpl implements Database, Disposable,
}
});
}
}).map(new Function<Void, T>(){
}).map(new Function<Void, T>() {
@Override
public T apply(Void o) {
trRef.get().dispose();
return returnValue.get();
}
});
result.onReady(new Runnable() {
@Override
public void run() {
trRef.get().dispose();
}
});
return result;
}
@Override
@ -180,7 +188,7 @@ class FDBDatabase extends DefaultDisposableImpl implements Database, Disposable,
public <T> PartialFuture<T> runAsync(final PartialFunction<? super Transaction, ? extends PartialFuture<T>> retryable, Executor e) {
final AtomicReference<Transaction> trRef = new AtomicReference<Transaction>(createTransaction());
final AtomicReference<T> returnValue = new AtomicReference<T>();
return AsyncUtil.whileTrue(new Function<Void, PartialFuture<Boolean>>() {
PartialFuture<T> result = AsyncUtil.whileTrue(new Function<Void, PartialFuture<Boolean>>() {
@Override
public PartialFuture<Boolean> apply(Void v) {
PartialFuture<T> process = AsyncUtil.applySafely(retryable, trRef.get());
@ -209,13 +217,21 @@ class FDBDatabase extends DefaultDisposableImpl implements Database, Disposable,
}
});
}
}).map(new Function<Void, T>(){
}).map(new Function<Void, T>() {
@Override
public T apply(Void o) {
trRef.get().dispose();
return returnValue.get();
}
});
result.onReady(new Runnable() {
@Override
public void run() {
trRef.get().dispose();
}
});
return result;
}
@Override
@ -244,10 +260,17 @@ class FDBDatabase extends DefaultDisposableImpl implements Database, Disposable,
@Override
public Transaction createTransaction(Executor e) {
pointerReadLock.lock();
Transaction tr = null;
try {
Transaction tr = new FDBTransaction(Database_createTransaction(getPtr()), this, e);
tr = new FDBTransaction(Database_createTransaction(getPtr()), this, e);
tr.options().setUsedDuringCommitProtectionDisable();
return tr;
} catch(RuntimeException err) {
if(tr != null) {
tr.dispose();
}
throw err;
} finally {
pointerReadLock.unlock();
}
@ -276,4 +299,4 @@ class FDBDatabase extends DefaultDisposableImpl implements Database, Disposable,
private native long Database_createTransaction(long cPtr);
private native void Database_dispose(long cPtr);
private native void Database_setOption(long cPtr, int code, byte[] value) throws FDBException;
}
}

View File

@ -579,10 +579,20 @@ class FDBTransaction extends DefaultDisposableImpl implements Disposable, Transa
// Must hold pointerReadLock when calling
private FDBTransaction transfer() {
FDBTransaction tr = new FDBTransaction(getPtr(), database, executor);
tr.options().setUsedDuringCommitProtectionDisable();
transactionOwner = false;
return tr;
FDBTransaction tr = null;
try {
tr = new FDBTransaction(getPtr(), database, executor);
tr.options().setUsedDuringCommitProtectionDisable();
transactionOwner = false;
return tr;
}
catch(RuntimeException err) {
if(tr != null) {
tr.dispose();
}
throw err;
}
}
@Override

View File

@ -165,6 +165,7 @@ public class LocalityUtil {
}
lastBegin = begin;
tr.options().setReadSystemKeys();
block.dispose();
block = tr.getRange(
keyServersForKey(begin),
keyServersForKey(end)).iterator();
@ -179,8 +180,7 @@ public class LocalityUtil {
FDBException err = (FDBException) o;
if(err.getCode() == 1007 && !Arrays.equals(begin, lastBegin)) {
BoundaryIterator.this.tr.dispose();
BoundaryIterator.this.tr =
BoundaryIterator.this.tr.getDatabase().createTransaction();
BoundaryIterator.this.tr = BoundaryIterator.this.tr.getDatabase().createTransaction();
return restartGet();
}
}
@ -222,6 +222,7 @@ public class LocalityUtil {
@Override
public void dispose() {
BoundaryIterator.this.tr.dispose();
block.dispose();
}
}
}

View File

@ -99,6 +99,13 @@ public class AsyncUtil {
Future<Void> complete = whileTrue(condition);
Future<List<V>> result = tag(complete, accumulator);
result.onReady(new Runnable() {
@Override
public void run() {
it.dispose();
}
});
return result;
}

View File

@ -854,7 +854,7 @@ public class DirectoryLayer implements Directory
tr.clear(Range.startsWith(nodeSubspace.unpack(node.getKey()).getBytes(0)));
tr.clear(node.range());
return AsyncUtil.whileTrue(new Function<Void, Future<Boolean>>() {
Future<Void> result = AsyncUtil.whileTrue(new Function<Void, Future<Boolean>>() {
@Override
public Future<Boolean> apply(Void ignore) {
Future<Void> subdirRemoveFuture;
@ -872,6 +872,15 @@ public class DirectoryLayer implements Directory
});
}
});
result.onReady(new Runnable() {
@Override
public void run() {
rangeItr.dispose();
}
});
return result;
}
private Future<Boolean> isPrefixFree(final ReadTransaction tr, final byte[] prefix) {
@ -888,14 +897,23 @@ public class DirectoryLayer implements Directory
if(node != null)
return new ReadyFuture<Boolean>(false);
AsyncIterator<KeyValue> it = tr.getRange(nodeSubspace.pack(prefix), nodeSubspace.pack(ByteArrayUtil.strinc(prefix)), 1).iterator();
return it.onHasNext()
final AsyncIterator<KeyValue> it = tr.getRange(nodeSubspace.pack(prefix), nodeSubspace.pack(ByteArrayUtil.strinc(prefix)), 1).iterator();
Future<Boolean> result = it.onHasNext()
.map(new Function<Boolean, Boolean>() {
@Override
public Boolean apply(Boolean hasNext) {
return !hasNext;
}
});
result.onReady(new Runnable() {
@Override
public void run() {
it.dispose();
}
});
return result;
}
});
}
@ -1286,7 +1304,7 @@ public class DirectoryLayer implements Directory
@Override
public Future<Boolean> apply(Void ignore) {
final AsyncIterator<KeyValue> rangeItr = tr.snapshot().getRange(allocator.counters.range(), 1, true).iterator();
return rangeItr.onHasNext()
Future<Boolean> result = rangeItr.onHasNext()
.map(new Function<Boolean, Void>() {
@Override
public Void apply(Boolean hasNext) {
@ -1310,6 +1328,15 @@ public class DirectoryLayer implements Directory
return choosePrefix(tr, allocator); // false exits the loop (i.e. we have a valid prefix)
}
});
result.onReady(new Runnable() {
@Override
public void run() {
rangeItr.dispose();
}
});
return result;
}
})
.map(new Function<Void, byte[]>() {

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suppressions PUBLIC
"-//Puppy Crawl//DTD Suppressions 1.1//EN"
"http://checkstyle.sourceforge.net/dtds/suppressions_1_1.dtd">
<suppressions>
<!-- These files are auto generated. Ignore those files for style checks. -->
<suppress files=".+Options\.java" checks=".*"/>
<suppress files=".+ConflictRangeType\.java" checks=".*"/>
<suppress files=".+FDBException\.java" checks=".*"/>
<suppress files=".+MutationType\.java" checks=".*"/>
<suppress files=".+StreamingMode\.java" checks=".*"/>
</suppressions>