package org.reveno.atp.examples.compensating_mutable_model;
import org.reveno.atp.api.Configuration;
import org.reveno.atp.api.Reveno;
import org.reveno.atp.core.Engine;
import org.reveno.atp.utils.MapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Examples {
protected static final Logger LOG = LoggerFactory.getLogger(Examples.class);
public static void main(String[] args) throws Exception {
Reveno reveno = new Engine(args[0]);
reveno.config().mutableModel();
reveno.config().mutableModelFailover(Configuration.MutableModelFailover.COMPENSATING_ACTIONS);
reveno.domain().transaction("createAccount",
(t,c) -> c.repo().store(t.id(), new Account(t.id(), t.longArg("balance"))))
.uniqueIdFor(Account.class)
.conditionalCommand((cmd, c) -> cmd.longArg("balance") >= 0L).command();
// we use same AddToBalance class as command and tx action for simplicity here
// as we don't need any special logic in commands rather than just call tx action
reveno.domain().command(AddToBalance.class, (c, ctx) -> ctx.executeTxAction(c));
reveno.domain().transactionWithCompensatingAction(AddToBalance.class, (t, ctx) -> {
Account account = ctx.repo().get(Account.class, t.account);
ctx.data().put("exists", true);
account.add(t.amount);
// then some exception happens eventually
if (t.amount == 100500)
throw new RuntimeException("Amount is zero!");
}, (t, ctx) -> {
// we shouldn't decrement balance if we are not sure that it actually used to happen
if (ctx.data().containsKey("exists")) {
ctx.repo().get(Account.class, t.account).add(-t.amount);
}
});
reveno.domain().viewMapper(Account.class, AccountView.class, (a, b, c) -> new AccountView(b.balance));
reveno.startup();
long acc = reveno.executeSync("createAccount", MapUtils.map("balance", 1000l));
// this is normal execution which won't lead to any issue
reveno.executeCommand(new AddToBalance(acc, 5)).get();
long goodIncrementBalance = reveno.query().find(AccountView.class, acc).balance;
LOG.info("Balance after fine command: {}", goodIncrementBalance);
// try wrong account ID, in which case exception will be thrown by Tx Action,
// but there should be no compensating action decrement of balance
reveno.executeCommand(new AddToBalance(acc + 1, 5)).get();
long wrongCommandBalance = reveno.query().find(AccountView.class, acc).balance;
LOG.info("Balance after wrong command: {}", wrongCommandBalance);
// this will lead to compensating action to be executed, decrementing balance back to its value
// so final balance will be 1005, not 101505
reveno.executeCommand(new AddToBalance(acc, 100500)).get();
wrongCommandBalance = reveno.query().find(AccountView.class, acc).balance;
LOG.info("Balance after wrong command: {}", wrongCommandBalance);
reveno.shutdown();
}
public static class Account {
public final long id;
public long balance;
public Account(long id, long balance) {
this.id = id;
this.balance = balance;
}
public void add(long amount) {
this.balance += amount;
}
}
public static class AccountView {
public final long balance;
public AccountView(long balance) {
this.balance = balance;
}
}
public static class AddToBalance {
public final long account;
public final long amount;
public AddToBalance(long account, long amount) {
this.account = account;
this.amount = amount;
}
}
}