Transactions
A transaction
method gets exclusive, atomic access to state in order
to update it (similar to a writer
). But unlike a writer
, a
transaction
can also call other writer
and transaction
methods. Those reads and writes all occur as a (distributed) ACID
transaction.
A transaction
method gets passed a context of type TransactionContext
.
A TransactionContext
can be used to make calls to reader
's, writer
's,
and other transaction
methods (although currently nested calls to a
transaction
method may only read/write mutually exclusive state.)
Here's an example of a transaction
method called Transfer
on our
Bank
state that deposits money in one account and withdraws it from
another:
- Python
- TypeScript
from bank.v1.bank_rbt import Bank
class BankServicer(Bank.Servicer):
async def Transfer(
self,
context: TransactionContext,
request: TransferRequest,
) -> TransferResponse:
from_account = Account.ref(request.from_account_id)
to_account = Account.ref(request.to_account_id)
await from_account.Withdraw(context, amount=request.amount)
await to_account.Deposit(context, amount=request.amount)
return TransferResponse()
- Object literal
- Class
import { Account } from "../../api/bank/v1/account_rbt.js";
const accountServicer = Account.servicer({
transfer: async (
context: TransactionContext,
state: Bank.State,
request: Bank.TransferRequest
): Promise<[Bank.State, Bank.PartialTransferResponse]> => {
const fromAccount = Account.ref(request.fromAccountId);
const toAccount = Account.ref(request.toAccountId);
await fromAccount.withdraw(context, { amount: request.amount });
await toAccount.deposit(context, { amount: request.amount });
return [state, {}];
},
});
import { Account } from "../../api/bank/v1/account_rbt.js";
class AccountServicer extends Account.Servicer {
async transfer(
context: TransactionContext,
request: Bank.TransferRequest
): Promise<Bank.PartialTransferResponse> {
const fromAccount = Account.ref(request.fromAccountId);
const toAccount = Account.ref(request.toAccountId);
await fromAccount.withdraw(context, { amount: request.amount });
await toAccount.deposit(context, { amount: request.amount });
return {};
}
}
Again, like writers
, state
can be modified directly in a
transaction
method, for example here is an example from the SignUp
method for Bank
that stores the account IDs:
- Python
- TypeScript
# Transactions like writers can alter state directly.
self.state.account_ids.append(account.state_id)
- Object literal
- Class
// Transactions like writers can alter state directly.
state.accountIds.push(newAccountId);
// Transactions like writers can alter state directly.
this.state.accountIds.push(newAccountId);
In addition to returning a response and updating state, a
transaction
can also schedule async tasks, which
are atomically started (or enqueued) if and only if the transaction
completes successfully.