// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements.  See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You 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.
= Performing Transactions

This Alpha release introduces the key-value API that provides an interface for starting and completing transactions.

=== Synchronous Transactions

Use the `igniteTransactions` class to create a transaction object, and the `commit` method to send it.

[tabs]
--
tab:Java[]
[source,java]
----
Transaction tx = igniteTransactions.begin();
tx.commit()
----
--

You can also perform a rollback with the `rollback` command:

[tabs]
--
tab:Java[]
[source,java]
----
Transaction tx = igniteTransactions.begin();
tx.rollback()
----
--


Here is the example of a transaction that transfers money from one account to another, and handles a possible overdraft:

--
[source,java]
----
Transaction tx = igniteTransactions.begin();

try {
    Tuple row1 = accounts.get(tx, Tuple.create().set("accountId", 1));
    if (row1.doubleValue("balance") - amount < 0) {
        tx.rollback();
        return false;
    }
    Tuple row2 = accounts.get(tx, Tuple.create().set("accountId", 2);
    accounts.upsert(tx, Tuple.create().set("accountId", 1).set("balance", row1.doubleValue("balance") - amount));
    accounts.upsert(tx, Tuple.create().set("accountId", 2).set("balance", row2.doubleValue("balance") + amount));
    tx.commit();
} catch (Throwable t) {
    tx.rollback();
}
----
--

== Asynchronous Transactions

You can also perform transactions asynchronously.

[tabs]
--
tab:Java[]
[source,java]
----
protected Table accounts;
protected Table customers;

accounts.recordView().upsert(null, makeValue(1, BALANCE_1));
accounts.recordView().upsert(null, makeValue(2, BALANCE_2));
igniteTransactions.beginAsync()
    .thenCompose(tx -> accounts.recordView().getAsync(tx, makeKey(1))
        .thenCombine(accounts.recordView().getAsync(tx, makeKey(2)), (v1, v2) -> new Pair<>(v1, v2))
        .thenCompose(pair -> allOf(
            accounts.recordView().upsertAsync(
                tx, makeValue(1, pair.getFirst().doubleValue("balance") - DELTA)),
            accounts.recordView().upsertAsync(
                tx, makeValue(2, pair.getSecond().doubleValue("balance") + DELTA))
        )
        .thenApply(ignored -> tx)
        )
    ).thenCompose(Transaction::commitAsync).join();
----
--


== Implicit Transaction Management

Apache Ignite 3 also provides implicit transaction managemet for dealing with simpler transactions by using the `runInTransaction` class. When using it, the following will be done automatically:

- The transaction is started and substituted to the closure.
- The transaction is committed if no exceptions were thrown during the closure.
- The transaction will be retried in case of recoverable error. Closure must be purely functional - not causing side effects.

Here is the example of a transaction that transfers money from one account to another, and handles a possible overdraft:

[tabs]
--
tab:Java[]
[source,java]
----
igniteTransactions.runInTransaction(tx -> {
    CompletableFuture<Tuple> fut1 = view.getAsync(tx, Tuple.create().set("accountId", 1));
    CompletableFuture<Tuple> fut2 = view.getAsync(tx, Tuple.create().set("accountId", 2)); // Read second balance concurrently
    if (fut1.join().doubleValue("balance") - amount < 0) {
        tx.rollback();
        return;
    }

    view.upsertAsync(tx, Tuple.create().set("accountId", 1).set("balance", fut1.join().doubleValue("balance") - amount));
    view.upsertAsync(tx, Tuple.create().set("accountId", 2).set("balance", fut2.join().doubleValue("balance") + amount);
});
----
--