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:
commit
24c6439456
|
@ -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),
|
||||
}
|
||||
|
|
|
@ -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>
|
|
@ -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;
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -179,4 +179,6 @@ class JNIUtil {
|
|||
return OS.OSX;
|
||||
throw new IllegalStateException("Unknown or unsupported OS: " + osname);
|
||||
}
|
||||
|
||||
private JNIUtil() {}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) + "\"") + ")";
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -30,5 +30,5 @@ public interface CloneableException {
|
|||
*
|
||||
* @return a newly created {@code Exception}.
|
||||
*/
|
||||
public Exception retargetClone();
|
||||
Exception retargetClone();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -42,4 +42,6 @@ class DirectoryUtil {
|
|||
sb.append(')');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private DirectoryUtil() {}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -92,4 +92,6 @@ public class PathUtil {
|
|||
|
||||
return new LinkedList<String>(path.subList(0, path.size() - 1));
|
||||
}
|
||||
|
||||
private PathUtil() {}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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"));
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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() {}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ class DirectoryExtension {
|
|||
int dirIndex = 0;
|
||||
int errorIndex = 0;
|
||||
|
||||
public DirectoryExtension() {
|
||||
DirectoryExtension() {
|
||||
dirList.add(DirectoryLayer.getDefault());
|
||||
}
|
||||
|
||||
|
|
|
@ -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() {}
|
||||
}
|
||||
|
|
|
@ -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() {}
|
||||
}
|
||||
|
|
|
@ -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() {}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() {}
|
||||
}
|
||||
|
|
|
@ -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() {}
|
||||
}
|
||||
|
|
|
@ -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() {}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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() {}
|
||||
}
|
||||
|
|
|
@ -94,4 +94,5 @@ public class SerialInsertion {
|
|||
}
|
||||
}
|
||||
|
||||
private SerialInsertion() {}
|
||||
}
|
||||
|
|
|
@ -133,4 +133,6 @@ public class SerialIteration {
|
|||
}
|
||||
return counter;
|
||||
}
|
||||
|
||||
private SerialIteration() {}
|
||||
}
|
||||
|
|
|
@ -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() {}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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() {}
|
||||
}
|
||||
|
||||
|
|
|
@ -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() {}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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() {}
|
||||
}
|
||||
|
|
|
@ -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() {}
|
||||
}
|
||||
|
|
|
@ -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() {}
|
||||
}
|
||||
|
|
|
@ -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() {}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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[]>() {
|
||||
|
|
|
@ -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>
|
Loading…
Reference in New Issue