Java tutorial
// Copyright 2015 The Vanadium Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package io.v.v23.syncbase; import com.google.common.util.concurrent.AsyncFunction; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.SettableFuture; import io.v.v23.VFutures; import io.v.v23.context.VContext; import io.v.v23.services.syncbase.BatchOptions; import io.v.v23.services.syncbase.ConcurrentBatchException; import javax.annotation.CheckReturnValue; /** * Various utility methods for the batch operations. */ public class Batch { /** * Runs the given batch operation, managing retries and * {@link BatchDatabase#commit commit()}/{@link BatchDatabase#abort abort()}s. * <p> * The returned future is guaranteed to be executed on an {@link java.util.concurrent.Executor} * specified in {@code context} (see {@link io.v.v23.V#withExecutor}). * <p> * The returned future will fail with {@link java.util.concurrent.CancellationException} if * {@code context} gets canceled. * * @param context Vanadium context * @param db database on which the batch operation is to be performed * @param opts batch configuration * @param op batch operation */ @CheckReturnValue public static ListenableFuture<Void> runInBatch(VContext context, Database db, BatchOptions opts, BatchOperation op) { return VFutures.withUserLandChecks(context, Futures.transform(Futures.immediateFuture(false), getRetryFn(context, db, opts, op, 0))); } private static AsyncFunction<Boolean, Void> getRetryFn(final VContext ctx, final Database db, final BatchOptions opts, final BatchOperation op, final int round) { return new AsyncFunction<Boolean, Void>() { @Override public ListenableFuture<Void> apply(Boolean success) throws Exception { if (success) { return Futures.immediateFuture(null); } if (round >= 3) { throw new ConcurrentBatchException(ctx); } return Futures.transform(tryBatch(ctx, db, opts, op), getRetryFn(ctx, db, opts, op, round + 1)); } }; } @CheckReturnValue private static ListenableFuture<Boolean> tryBatch(final VContext ctx, final Database db, final BatchOptions opts, final BatchOperation op) { final SettableFuture<Boolean> ret = SettableFuture.create(); Futures.addCallback(db.beginBatch(ctx, opts), new FutureCallback<BatchDatabase>() { @Override public void onFailure(Throwable t) { ret.setException(t); } @Override public void onSuccess(final BatchDatabase batch) { Futures.addCallback(op.run(batch), new FutureCallback<Void>() { @Override public void onFailure(final Throwable t) { Futures.addCallback(batch.abort(ctx), new FutureCallback<Void>() { @Override public void onSuccess(Void result) { ret.setException(t); } @Override public void onFailure(Throwable newT) { ret.setException(t); } }); } @Override public void onSuccess(Void result) { Futures.addCallback(opts.getReadOnly() ? batch.abort(ctx) : batch.commit(ctx), new FutureCallback<Void>() { @Override public void onSuccess(Void result) { ret.set(true); // success } @Override public void onFailure(Throwable t) { if (t instanceof ConcurrentBatchException) { // retry ret.set(false); } else { ret.setException(t); } } }); } }); } }); return ret; } /** * Interface for a batch operation that is executed as part of {@link #runInBatch runInBatch()}. */ public interface BatchOperation { /** * Performs the batch operation. * * @param db batch database on which the operation is performed */ @CheckReturnValue ListenableFuture<Void> run(BatchDatabase db); } }