io.v.v23.syncbase.Batch.java Source code

Java tutorial

Introduction

Here is the source code for io.v.v23.syncbase.Batch.java

Source

// 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);
    }

}