diff --git a/bindings/java/src-completable/main/com/apple/foundationdb/Database.java b/bindings/java/src-completable/main/com/apple/foundationdb/Database.java index 03eaa52196..386cc936a7 100644 --- a/bindings/java/src-completable/main/com/apple/foundationdb/Database.java +++ b/bindings/java/src-completable/main/com/apple/foundationdb/Database.java @@ -35,8 +35,10 @@ 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.
+ *
+ * Note: {@code Database} objects must be disposed when no longer in use in order + * to free associated native memory. */ public interface Database extends Disposable, TransactionContext { /** @@ -44,10 +46,7 @@ public interface Database extends Disposable, TransactionContext { *
* Note: Java transactions automatically set the {@link TransactionOptions#setUsedDuringCommitProtectionDisable} * option. This is because the Java bindings disallow use of {@code Transaction} objects after - * {@link Transaction#onError} is called.
- *
- * Note: {@code Database} objects must be disposed when no longer in use in order - * to free associated native memory. + * {@link Transaction#onError} is called. * * @return a newly created {@code Transaction} that reads from and writes to this {@code Database}. */ diff --git a/bindings/java/src-completable/main/com/apple/foundationdb/FDBTransaction.java b/bindings/java/src-completable/main/com/apple/foundationdb/FDBTransaction.java index 1576eade78..94599e8284 100644 --- a/bindings/java/src-completable/main/com/apple/foundationdb/FDBTransaction.java +++ b/bindings/java/src-completable/main/com/apple/foundationdb/FDBTransaction.java @@ -300,6 +300,7 @@ class FDBTransaction extends DefaultDisposableImpl implements Disposable, Transa return database; } + // Users of this function must dispose of the returned FutureResults when finished protected FutureResults getRange_internal( KeySelector begin, KeySelector end, int rowLimit, int targetBytes, int streamingMode, diff --git a/bindings/java/src-completable/test/com/apple/foundationdb/test/AsyncStackTester.java b/bindings/java/src-completable/test/com/apple/foundationdb/test/AsyncStackTester.java index 3ae2f247e7..9d7dd84a4c 100644 --- a/bindings/java/src-completable/test/com/apple/foundationdb/test/AsyncStackTester.java +++ b/bindings/java/src-completable/test/com/apple/foundationdb/test/AsyncStackTester.java @@ -696,37 +696,6 @@ public class AsyncStackTester { return AsyncUtil.DONE; }); } - private static CompletableFuture logStack(final Instruction inst, final byte[] prefix, int i) { - //System.out.println("Logging stack at " + i); - while(inst.size() > 0) { - StackEntry e = inst.pop(); - byte[] pk = Tuple.from(i, e.idx).pack(prefix); - byte[] pv = Tuple.from(StackUtils.serializeFuture(e.value)).pack(); - inst.tr.set(pk, pv.length < 40000 ? pv : Arrays.copyOfRange(pv, 0, 40000)); - i--; - if(i % 100 == 0) { - final int saved = i; - return inst.tr.commit().thenComposeAsync(new Function>() { - @Override - public CompletableFuture apply(Void o) { - inst.releaseTransaction(); - inst.context.newTransaction(); - inst.tr = inst.context.getCurrentTransaction(); - return logStack(inst, prefix, saved); - } - }); - } - } - return inst.tr.commit().thenApplyAsync(new Function() { - @Override - public Void apply(Void a) { - inst.releaseTransaction(); - inst.context.newTransaction(); - inst.tr = inst.context.getCurrentTransaction(); - return null; - } - }); - } private static CompletableFuture pushRange(Instruction inst, CompletableFuture> range) { return pushRange(inst, range, null); diff --git a/bindings/java/src-completable/test/com/apple/foundationdb/test/Context.java b/bindings/java/src-completable/test/com/apple/foundationdb/test/Context.java index 6626075053..29d316b47a 100644 --- a/bindings/java/src-completable/test/com/apple/foundationdb/test/Context.java +++ b/bindings/java/src-completable/test/com/apple/foundationdb/test/Context.java @@ -84,31 +84,39 @@ abstract class Context implements Runnable { } } - public synchronized Transaction getCurrentTransaction() { - Transaction tr = Context.transactionMap.get(this.trName); - Context.transactionRefCounts.get(tr).incrementAndGet(); + public static synchronized void addTransactionReference(Transaction tr) { + transactionRefCounts.computeIfAbsent(tr, x -> new AtomicInteger(0)).incrementAndGet(); + } + + private static synchronized Transaction getTransaction(String trName) { + Transaction tr = transactionMap.get(trName); + addTransactionReference(tr); return tr; } - public synchronized void releaseTransaction(Transaction tr) { + public Transaction getCurrentTransaction() { + return getTransaction(trName); + } + + public static synchronized void releaseTransaction(Transaction tr) { if(tr != null) { - AtomicInteger count = Context.transactionRefCounts.get(tr); + AtomicInteger count = transactionRefCounts.get(tr); if(count.decrementAndGet() == 0) { - Context.transactionRefCounts.remove(tr); + assert !transactionMap.containsValue(tr); + transactionRefCounts.remove(tr); tr.dispose(); } } } - public synchronized void updateCurrentTransaction(Transaction tr) { - Context.transactionRefCounts.computeIfAbsent(tr, x -> new AtomicInteger(1)); - releaseTransaction(Context.transactionMap.put(this.trName, tr)); + private static synchronized void updateTransaction(String trName, Transaction tr) { + releaseTransaction(transactionMap.put(trName, tr)); + addTransactionReference(tr); } - public synchronized boolean updateCurrentTransaction(Transaction oldTr, Transaction newTr) { - if(Context.transactionMap.replace(this.trName, oldTr, newTr)) { - AtomicInteger count = Context.transactionRefCounts.computeIfAbsent(newTr, x -> new AtomicInteger(0)); - count.incrementAndGet(); + private static synchronized boolean updateTransaction(String trName, Transaction oldTr, Transaction newTr) { + if(transactionMap.replace(trName, oldTr, newTr)) { + addTransactionReference(newTr); releaseTransaction(oldTr); return true; } @@ -116,6 +124,14 @@ abstract class Context implements Runnable { 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); @@ -128,10 +144,9 @@ abstract class Context implements Runnable { } } - public synchronized void switchTransaction(byte[] trName) { - this.trName = ByteArrayUtil.printable(trName); - Transaction tr = Context.transactionMap.computeIfAbsent(this.trName, x -> db.createTransaction()); - Context.transactionRefCounts.computeIfAbsent(tr, x -> new AtomicInteger(1)); + public void switchTransaction(byte[] rawTrName) { + trName = ByteArrayUtil.printable(rawTrName); + newTransaction(null); } abstract void executeOperations() throws Throwable; diff --git a/bindings/java/src-completable/test/com/apple/foundationdb/test/Instruction.java b/bindings/java/src-completable/test/com/apple/foundationdb/test/Instruction.java index 21921fe503..82792f041b 100644 --- a/bindings/java/src-completable/test/com/apple/foundationdb/test/Instruction.java +++ b/bindings/java/src-completable/test/com/apple/foundationdb/test/Instruction.java @@ -34,77 +34,66 @@ class Instruction extends Stack { private final static String SUFFIX_SNAPSHOT = "_SNAPSHOT"; private final static 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) { 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 = null; + tr = null; readTr = null; - op = op.substring(0, op.length() - SUFFIX_DATABASE.length()); + 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) { + void setTransaction(Transaction newTr) { if(!isDatabase) { - context.releaseTransaction(this.tr); - context.updateCurrentTransaction(tr); - - this.tr = context.getCurrentTransaction(); - if(isSnapshot) { - readTr = this.tr.snapshot(); - } - else { - readTr = tr; - } + context.updateCurrentTransaction(newTr); } } void setTransaction(Transaction oldTr, Transaction newTr) { if(!isDatabase) { context.updateCurrentTransaction(oldTr, newTr); - - this.tr = context.getCurrentTransaction(); - if(isSnapshot) { - readTr = this.tr.snapshot(); - } - else { - readTr = tr; - } } } void releaseTransaction() { - context.releaseTransaction(this.tr); + 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); }