/*
 * Database.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;

import com.apple.foundationdb.async.Function;
import com.apple.foundationdb.async.Future;
import com.apple.foundationdb.async.PartialFunction;
import com.apple.foundationdb.async.PartialFuture;

import java.util.concurrent.Executor;

/**
 * A mutable, lexicographically ordered mapping from binary keys to binary values.
 *  A {@code Database} is stored on a FoundationDB {@link Cluster}.
 *  {@link Transaction}s are used to manipulate data within a single
 *  {@code Database} -- multiple, concurrent
 *  {@code Transaction}s on a {@code Database} enforce <b>ACID</b> properties.<br>
 * <br>
 * The simplest correct programs using FoundationDB will make use of the methods defined
 *  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.
 *
 */
public interface Database extends Disposable, TransactionContext {
	/**
	 * Creates a {@link Transaction} that operates on this {@code Database}.<br>
	 * <br>
	 * 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.
	 *
	 * @return a newly created {@code Transaction} that reads from and writes to this {@code Database}.
	 */
	public Transaction createTransaction();

	/**
	 * Creates a {@link Transaction} that operates on this {@code Database} with the given {@link Executor}
	 * for asynchronous callbacks.
	 *
	 * @param e the {@link Executor} to use when executing asynchronous callbacks for the database
	 * @return a newly created {@code Transaction} that reads from and writes to this {@code Database}.
	 */
	public Transaction createTransaction(Executor e);

	/**
	 * Returns a set of options that can be set on a {@code Database}
	 *
	 * @return a set of database-specific options affecting this {@code Database}
	 */
	public DatabaseOptions options();

	/**
	 * Runs a read-only transactional function against this {@code Database} with retry logic.
	 *  {@link Function#apply(Object) apply(ReadTransaction)} will be called on the
	 *  supplied {@link Function} until a non-retryable
	 *  {@link FDBException} (or any {@code Throwable} other than an {@code FDBException})
	 *  is thrown. This call is blocking -- this
	 *  method will not return until the {@code Function} has been called and completed without error.<br>
	 *
	 * @param retryable the block of logic to execute in a {@link Transaction} against
	 *  this database
	 */
	@Override
	public <T> T read(Function<? super ReadTransaction, T> retryable);

	/**
	 * Runs a read-only transactional function against this {@code Database} with retry logic. Use
	 *  this formulation of {@link #read(Function)} if one wants to set a custom {@link Executor}
	 *  for the transaction when run.
	 *
	 * @param retryable the block of logic to execute in a {@link Transaction} against
	 *  this database
	 * @param e the {@link Executor} to use for asynchronous callbacks
	 * 
	 * @see #read(Function)
	 */
	public <T> T read(Function<? super ReadTransaction, T> retryable, Executor e);

	/**
	 * Runs a read-only transactional function against this {@code Database} with retry logic. Use
	 *  this formulation of {@link #read(Function)} if the user code being executed
	 *  throws a checked exception.
	 *
	 * @param retryable the block of logic to execute in a {@link ReadTransaction} against
	 *  this database
	 *
	 * @throws Exception if non-transient database errors are encountered or user code throws any other error
	 *
	 * @see #read(Function)
	 */
	@Override
	public abstract <T> T read(PartialFunction<? super ReadTransaction, T> retryable) throws Exception;

	/**
	 * Runs a read-only transactional function against this {@code Database} with retry logic. Use
	 *  this formulation of {@link #read(Function)} if the user code being executed
	 *  throws a checked exception and one wants to set a custom {@link Executor} for the
	 *  transaction when run.
	 *
	 * @param retryable the block of logic to execute in a {@link ReadTransaction} against
	 *  this database
	 * @param e the {@link Executor} to use for asynchronous callbacks
	 *
	 * @throws Exception if non-transient database errors are encountered or user code throws any other error
	 *
	 * @see #read(Function)
	 */
	public abstract <T> T read(PartialFunction<? super ReadTransaction, T> retryable, Executor e) throws Exception;

	/**
	 * Runs a read-only transactional function against this {@code Database} with retry logic.
	 *  {@link Function#apply(Object) apply(ReadTransaction)} will be called on the
	 *  supplied {@link Function} until a non-retryable
	 *  {@link FDBException} (or any {@code Throwable} other than an {@code FDBException})
	 *  is thrown. This call is non-blocking -- this
	 *  method will return immediately and with a {@link Future} that will be
	 *  set when the {@code Function} has been called and completed without error.<br>
	 * <br>
	 * Any errors encountered executing {@code retryable}, or received from the
	 *  database, will be set on the returned {@code Future}.
	 *
	 * @param retryable the block of logic to execute in a {@link ReadTransaction} against
	 *  this database
	 */
	@Override
	public abstract <T> Future<T> readAsync(
			Function<? super ReadTransaction, Future<T>> retryable);

	/**
	 * Runs a read-only transactional function against this {@code Database} with retry logic.
	 *  Use this version of {@link #readAsync(Function)} if one wants to set a custom
	 *  {@link Executor} for the transaction when run.
	 *
	 * @param retryable the block of logic to execute in a {@link ReadTransaction} against
	 *  this database
	 * @param e the {@link Executor} to use for asynchronous callbacks
	 * 
	 * @see #readAsync(Function)
	 */
	public abstract <T> Future<T> readAsync(
			Function<? super ReadTransaction, Future<T>> retryable, Executor e);

	/**
	 * Runs a read-only transactional function against this {@code Database} with retry logic. Use
	 *  this formulation of the non-blocking {@link #readAsync(Function)} if the user code being executed
	 *  throws a checked exception.
	 *
	 * @param retryable the block of logic to execute in a {@link ReadTransaction} against
	 *  this database
	 *
	 * @see #read(Function)
	 */
	@Override
	public abstract <T> PartialFuture<T> readAsync(
			PartialFunction<? super ReadTransaction, ? extends PartialFuture<T>> retryable);

	/**
	 * Runs a read-only transactional function against this {@code Database} with retry logic. Use
	 *  this formulation of the non-blocking {@link #readAsync(Function)} if the user code being executed
	 *  throws a checked exception and one wants to set a custom {@link Executor} for the
	 *  transaction when run.
	 *
	 * @param retryable the block of logic to execute in a {@link ReadTransaction} against
	 *  this database
	 * @param e the {@link Executor} to use for asynchronous callbacks
	 *
	 * @see #read(Function)
	 */
	public abstract <T> PartialFuture<T> readAsync(
			PartialFunction<? super ReadTransaction, ? extends PartialFuture<T>> retryable, Executor e);


	/**
	 * Runs a transactional function against this {@code Database} with retry logic.
	 *  {@link Function#apply(Object) apply(Transaction)} will be called on the
	 *  supplied {@link Function} until a non-retryable
	 *  {@link FDBException} (or any {@code Throwable} other than an {@code FDBException})
	 *  is thrown or {@link Transaction#commit() commit()},
	 *  when called after {@code apply()}, returns success. This call is blocking -- this
	 *  method will not return until {@code commit()} has been called and returned success.<br>
	 * <br>
	 * As with other client/server databases, in some failure scenarios a client may
	 *  be unable to determine whether a transaction succeeded. In these cases, your
	 *  transaction may be executed twice. For more information about how to reason
	 *  about these situations see
	 * <a href="/documentation/developer-guide.html#transactions-with-unknown-results"
	 *   target="_blank">the FounationDB Developer Guide</a>
	 *
	 * @param retryable the block of logic to execute in a {@link Transaction} against
	 *  this database
	 */
	@Override
	public <T> T run(Function<? super Transaction, T> retryable);

	/**
	 * Runs a transactional function against this {@code Database} with retry logic.
	 *  Use this formulation of {@link #run(Function)} if the user code will not throw
	 *  a checked exception but one would like to set a custom {@link Executor}
	 *  for the transaction when run.
	 *
	 * @param retryable the block of logic to execute in a {@link Transaction} against
	 *  this database
	 * @param e the {@link Executor} to use for asynchronous callbacks
	 */
	public <T> T run(Function<? super Transaction, T> retryable, Executor e);

	/**
	 * Runs a transactional function against this {@code Database} with retry logic. Use
	 *  this formulation of {@link #run(Function)} if the user code being executed
	 *  throws a checked exception.
	 *
	 * @param retryable the block of logic to execute in a {@link Transaction} against
	 *  this database
	 *
	 * @throws Exception if non-transient database errors are encountered or user code throws any other error
	 *
	 * @see #run(Function)
	 */
	@Override
	public abstract <T> T run(PartialFunction<? super Transaction, T> retryable) throws Exception;

	/**
	 * Runs a transactional function against this {@code Database} with retry logic. Use
	 *  this formulation of {@link #run(Function)} if the user code being executed
	 *  throws a checked exception and one would like to set a custom executor for the
	 *  transaction when run.
	 *
	 * @param retryable the block of logic to execute in a {@link Transaction} against
	 *  this database
	 * @param e the {@link Executor} to use for asynchronous callbacks
	 *
	 * @throws Exception if non-transient database errors are encountered or user code throws any other error
	 *
	 * @see #run(Function)
	 */
	public abstract <T> T run(PartialFunction<? super Transaction, T> retryable, Executor e) throws Exception;

	/**
	 * Runs a transactional function against this {@code Database} with retry logic.
	 *  {@link Function#apply(Object) apply(Transaction)} will be called on the
	 *  supplied {@link Function} until a non-retryable
	 *  {@link FDBException} (or any {@code Throwable} other than an {@code FDBException})
	 *  is thrown or {@link Transaction#commit() commit()},
	 *  when called after {@code apply()}, returns success. This call is non-blocking -- this
	 *  method will return immediately and with a {@link Future} that will be
	 *  set when {@code commit()} has been called and returned success.<br>
	 * <br>
	 * As with other client/server databases, in some failure scenarios a client may
	 *  be unable to determine whether a transaction succeeded. In these cases, your
	 *  transaction may be executed twice. For more information about how to reason
	 *  about these situations see
	 * <a href="/documentation/developer-guide.html#transactions-with-unknown-results"
	 *   target="_blank">the FounationDB Developer Guide</a><br>
	 * <br>
	 * Any errors encountered executing {@code retryable}, or received from the
	 *  database, will be set on the returned {@code Future}.
	 *
	 * @param retryable the block of logic to execute in a {@link Transaction} against
	 *  this database
	 */
	@Override
	public abstract <T> Future<T> runAsync(
			Function<? super Transaction, Future<T>> retryable);

	/**
	 * Runs a transactional function against this {@code Database} with retry logic. Use
	 *  this formulation of the non-blocking {@link #runAsync(Function)} if one wants
	 *  to set a custom {@link Executor} for the transaction when run.
	 *
	 * @param retryable the block of logic to execute in a {@link Transaction} against
	 *  this database
	 * @param e the {@link Executor} to use for asynchronous callbacks
	 *
	 * @see #run(Function)
	 */
	public abstract <T> Future<T> runAsync(
			Function<? super Transaction, Future<T>> retryable, Executor e);

	/**
	 * Runs a transactional function against this {@code Database} with retry logic. Use
	 *  this formulation of the non-blocking {@link #runAsync(Function)} if the user code being executed
	 *  throws a checked exception.
	 *
	 * @param retryable the block of logic to execute in a {@link Transaction} against
	 *  this database
	 *
	 * @see #run(Function)
	 */
	@Override
	public abstract <T> PartialFuture<T> runAsync(
			PartialFunction<? super Transaction, ? extends PartialFuture<T>> retryable);

	/**
	 * Runs a transactional function against this {@code Database} with retry logic. Use
	 *  this formulation of the non-blocking {@link #runAsync(Function)} if the user code being executed
	 *  throws a checked exception and one wants to set a custom {@link Executor} for the transaction
	 *  when run.
	 *
	 * @param retryable the block of logic to execute in a {@link Transaction} against
	 *  this database
	 * @param e the {@link Executor} to use for asynchronous callbacks
	 *
	 * @see #run(Function)
	 */
	public abstract <T> PartialFuture<T> runAsync(
			PartialFunction<? super Transaction, ? extends PartialFuture<T>> retryable, Executor e);

}
