Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: move two-phase transfers out of reference and into guide #1638

Merged
merged 2 commits into from Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/design/consistency.md
Expand Up @@ -74,7 +74,7 @@ What specific guarantees does TigerBeetle provide to applications?

- Transfers are immutable — once created, they are never modified.
- There is at most one `Transfer` with a particular `id`.
- A [pending transfer](../reference/transfers.md#pending-transfer) resolves at most once.
- A [pending transfer](./two-phase-transfers.md#reserve-funds-pending-transfer) resolves at most once.
- Transfer [timeouts](../reference/transfers.md#timeout) are deterministic, driven
by the cluster's timestamp.

Expand Down
111 changes: 57 additions & 54 deletions docs/design/two-phase-transfers.md
@@ -1,69 +1,72 @@
# Two-phase transfers
# Two-Phase Transfers

A two-phase transfer moves funds in stages:

1. Reserve funds ([pending](#reserve-funds-pending-transfer))
2. Resolve funds ([post](#post-pending-transfer), [void](#void-pending-transfer), or [timeout](#timeout))

The name "two-phase transfer" is a reference to the [two-phase commit
protocol for distributed
transactions](https://en.wikipedia.org/wiki/Two-phase_commit_protocol).

Single-phase transfers post funds to accounts immediately when they
are created. That is, they increase the
[`debits_posted`](../reference/accounts.md#debits_posted) and
[`credits_posted`](../reference/accounts.md#credits_posted) fields on
respective accounts.
## Reserve Funds (Pending Transfer)

A pending transfer, denoted by [`flags.pending`](../reference/transfers.md#flagspending),
reserves its `amount` in the debit/credit accounts'
[`debits_pending`](../reference/accounts.md#debits_pending)/[`credits_pending`](../reference/accounts.md#credits_pending)
fields, respectively. Pending transfers leave the
`debits_posted`/`credits_posted` unmodified.

## Resolve Funds

Pending transfers can be posted, voided, or they may time out.

In contrast to single-phase transfers, a two-phase transfer moves
funds in stages:
### Post-Pending Transfer

1. First, the pending transfer reserves funds. While reserved, they
cannot be used by either the payer or payee. Only the
[`debits_pending`](../reference/accounts.md#debits_pending) and
[`credits_pending`](../reference/accounts.md#credits_pending) fields are
increased on the relevant accounts at this point.
A post-pending transfer, denoted by [`flags.post_pending_transfer`](../reference/transfers.md#flagspost_pending_transfer),
causes a pending transfer to "post", transferring some or all of the pending transfer's reserved amount to its destination.

2. Later, the application creates another transfer — with either of
the following flags:
* [`post_pending_transfer`](../reference/transfers.md#flagspost_pending_transfer): Move all (or part) of the reserved funds to the pending transfer's destination.
* [`void_pending_transfer`](../reference/transfers.md#flagsvoid_pending_transfer): Revert all of the reserved funds to the original account.
* If the posted [`amount`](../reference/transfers.md#amount) is 0, the full pending transfer's amount is
posted.
* If the posted [`amount`](../reference/transfers.md#amount) is nonzero, then only this amount is posted,
and the remainder is restored to its original accounts. It must be
less than or equal to the pending transfer's amount.

## Resolving a two-phase transfer
Additionally, when `flags.post_pending_transfer` is set:

When the pending transfer is resolved (posted or voided), the
[`debits_pending`](../reference/accounts.md#debits_pending) and
[`credits_pending`](../reference/accounts.md#credits_pending) fields
on the respective accounts are decreased by the
[`amount`](../reference/transfers.md#amount) of the **pending** transfer.
* [`pending_id`](../reference/transfers.md#pending_id) must reference a [pending transfer](#reserve-funds-pending-transfer).
* [`flags.void_pending_transfer`](../reference/transfers.md#flagsvoid_pending_transfer) must not be set.

### Post
The following fields may either be zero or they must match the
value of the pending transfer's field:

When the pending transfer is posted,
[`debits_posted`](../reference/accounts.md#debits_posted) and
[`credits_posted`](../reference/accounts.md#credits_posted) fields on
the respective accounts are increased by the **posting** transfer's
[`amount`](../reference/transfers.md#amount) (which cannot exceed the
pending amount, but need not equal the pending amount either).
* [`debit_account_id`](../reference/transfers.md#debit_account_id)
* [`credit_account_id`](../reference/transfers.md#credit_account_id)
* [`ledger`](../reference/transfers.md#ledger)
* [`code`](../reference/transfers.md#code)

#### Post partial pending `amount`
### Void-Pending Transfer

Although an initial [`amount`](../reference/transfers.md#amount) is
reserved when a pending transfer is created, you can set the
[`amount`](../reference/transfers.md#amount) field to that amount *or*
smaller than that initial amount when posting a pending transfer.
A void-pending transfer, denoted by [`flags.void_pending_transfer`](../reference/transfers.md#flagsvoid_pending_transfer),
restores the pending amount its original accounts.
Additionally, when this field is set:

In the event that you post less than the amount you initially
reserved, the rest of the amount not posted reverts back to the
original account.
* [`pending_id`](../reference/transfers.md#pending_id) must reference a [pending transfer](#reserve-funds-pending-transfer).
* [`flags.post_pending_transfer`](../reference/transfers.md#flagspost_pending_transfer) must not be set.

### Void
The following fields may either be zero or they must match the
value of the pending transfer's field:

When the pending transfer is voided,
[`debits_posted`](../reference/accounts.md#debits_posted) and
[`credits_posted`](../reference/accounts.md#credits_posted) are not
modified.
* [`debit_account_id`](../reference/transfers.md#debit_account_id)
* [`credit_account_id`](../reference/transfers.md#credit_account_id)
* [`ledger`](../reference/transfers.md#ledger)
* [`code`](../reference/transfers.md#code)

### Timeout

If a pending transfer is created with a [timeout](../reference/transfers.md#timeout) (which is optional),
then if it has not been posted or voided by the time the timeout
expires, the full amount will be voided.
A pending transfer may optionally be created with a
[timeout](../reference/transfers.md#timeout). If the timeout interval passes
before the transfer is either posted or voided, the full amount will be voided.

### Errors

Expand All @@ -75,7 +78,7 @@ Attempting to resolve a pending transfer more than once will return the applicab
- [`pending_transfer_already_voided`](../reference/operations/create_transfers.md#pending_transfer_already_voided)
- [`pending_transfer_expired`](../reference/operations/create_transfers.md#pending_transfer_expired)

## Interaction with account invariants
## Interaction with Account Invariants

The pending transfer's amount is reserved in a way that the second
step in a two-phase transfer will never cause the accounts' configured
Expand All @@ -85,7 +88,7 @@ or
[`debits_must_not_exceed_credits`](../reference/accounts.md#flagsdebits_must_not_exceed_credits))
to be broken, whether the second step is a post or void.

### Pessimistic pending transfers
### Pessimistic Pending Transfers

If an account with
[`debits_must_not_exceed_credits`](../reference/accounts.md#flagsdebits_must_not_exceed_credits)
Expand All @@ -94,7 +97,7 @@ transfer is started causing the account to have `debits_pending = 50`,
the *pending* transfer will fail. It will not wait to get to *posted*
status to fail.

## All transfers are immutable
## All Transfers Are Immutable

To reiterate, completing a two-phase transfer (by either marking it
void or posted) does not involve modifying the pending
Expand All @@ -121,7 +124,7 @@ The following examples show the state of two accounts in three steps:
2. After a pending transfer
3. And after the pending transfer is posted or voided

### Post full pending amount
### Post Full Pending Amount

| Account `A` | | Account `B` | | Transfers | | | |
|------------:|-----------:|------------:|-----------:|:---------------------|:----------------------|-----------:|:------------------------|
Expand All @@ -131,7 +134,7 @@ The following examples show the state of two accounts in three steps:
| 123 + `w` | `x` | 123 + `y` | `z` | `A` | `B` | 123 | `pending` |
| `w` | 123 + `x` | `y` | 123 + `z` | `A` | `B` | 123 | `post_pending_transfer` |

### Post partial pending amount
### Post Partial Pending Amount

| Account `A` | | Account `B` | | Transfers | | | |
|------------:|-----------:|------------:|-----------:|:---------------------|:----------------------|-----------:|:------------------------|
Expand All @@ -141,7 +144,7 @@ The following examples show the state of two accounts in three steps:
| 123 + `w` | `x` | 123 + `y` | `z` | `A` | `B` | 123 | `pending` |
| `w` | 100 + `x` | `y` | 100 + `z` | `A` | `B` | 100 | `post_pending_transfer` |

### Void pending transfer
### Void Pending Transfer

| Account `A` | | Account `B` | | Transfers | | | |
|------------:|-----------:|------------:|-----------:|:---------------------|:----------------------|-----------:|:------------------------|
Expand All @@ -151,7 +154,7 @@ The following examples show the state of two accounts in three steps:
| 123 + `w` | `x` | 123 + `y` | `z` | `A` | `B` | 123 | `pending` |
| `w` | `x` | `y` | `z` | `A` | `B` | 123 | `void_pending_transfer` |

## Client documentation
## Client Documentation

emschwartz marked this conversation as resolved.
Show resolved Hide resolved
Read more about how two-phase transfers work with each client.

Expand All @@ -160,7 +163,7 @@ Read more about how two-phase transfers work with each client.
* [Java](/src/clients/java/README.md#two-phase-transfers)
* [Node](/src/clients/node/README.md#two-phase-transfers)

## Client samples
## Client Samples

Or take a look at how it works with real code.

Expand Down
90 changes: 8 additions & 82 deletions docs/reference/transfers.md
Expand Up @@ -16,6 +16,10 @@ Transfers *cannot be modified* after creation.

## Modes

Transfers can either be Single-Phase, where they are executed immediately, or Two-Phase, where
they are first put in a Pending state and then either Posted or Voided. For more details on the
latter, see the [Two-Phase Transfer guide](../design/two-phase-transfers.md).

Fields used by each mode of transfer:

| Field | Single-Phase | Pending | Post-Pending | Void-Pending |
Expand Down Expand Up @@ -45,81 +49,6 @@ fields that TigerBeetle is responsible for. For example, you will set the
[`timestamp`](#timestamp) field to `0` when creating a transfer and then
TigerBeetle will set the field to the timestamp when the transfer was executed.

### Single-phase transfer

Single-phase transfers post funds to accounts immediately when they are created.

### Two-phase transfer

A pending transfer followed by a post-pending transfer, void-pending transfer, or a timeout is
called a "two-phase transfer". Unlike a single-phase transfer, a two-phase transfer moves funds in
stages:

1. Reserve funds
2. Resolve funds (post, void, or timeout)

Attempting to resolve a pending transfer more than once will return the applicable error result:
- [`pending_transfer_already_posted`](../reference/operations/create_transfers.md#pending_transfer_already_posted)
- [`pending_transfer_already_voided`](../reference/operations/create_transfers.md#pending_transfer_already_voided)
- [`pending_transfer_expired`](../reference/operations/create_transfers.md#pending_transfer_expired)

#### Pending transfer

A pending transfer, denoted by [`flags.pending`](#flagspending),
reserves its `amount` in the debit/credit accounts'
[`debits_pending`](./accounts.md#debits_pending)/[`credits_pending`](./accounts.md#credits_pending)
fields respectively, leaving `debits_posted`/`credits_posted` unmodified.

#### Post-pending transfer

A post-pending transfer, denoted by [`flags.post_pending_transfer`](#flagspost_pending_transfer),
causes the corresponding pending transfer (referenced by [`pending_id`](#pending_id)) to "post",
transferring some or all of the pending transfer's reserved amount to its destination, and restoring
(voiding) the remainder (if any) to its origin accounts.

* If the posted `amount` is 0, the full pending transfer's amount is
posted.
* If the posted `amount` is nonzero, then only this amount is posted,
and the remainder is restored to its original accounts. It must be
less than or equal to the pending transfer's amount.

Additionally, when `flags.post_pending_transfer` is set:

* `pending_id` must reference a [pending transfer](#pending-transfer).
* `flags.void_pending_transfer` must not be set.

And the following fields may either be zero, otherwise must match the
value of the pending transfer's field:

* `debit_account_id`
* `credit_account_id`
* `ledger`
* `code`

#### Void-pending transfer

A void-pending transfer, denoted by [`flags.void_pending_transfer`](#flagsvoid_pending_transfer),
causes the pending transfer (referenced by [`pending_id`](#pending_id)) to void. The pending amount
is restored to its original accounts.
Additionally, when this field is set:

* `pending_id` must reference a [pending transfer](#pending-transfer).
* `flags.post_pending_transfer` must not be set.

And the following fields may either be zero, otherwise must match the
value of the pending transfer's field:

* `debit_account_id`
* `credit_account_id`
* `amount`
* `ledger`
* `code`

#### Read more

See the [Two-phase transfers](../design/two-phase-transfers.md) guide
for details, examples, and sample code.

## Fields

### `id`
Expand Down Expand Up @@ -208,10 +137,7 @@ If this transfer will post or void a pending transfer, `pending_id`
references that pending transfer. If this is not a post or void
transfer, it must be zero.

See also:
* [Pending Transfer](#pending-transfer)
* [Post-Pending Transfer](#post-pending-transfer)
* [Void-Pending Transfer](#void-pending-transfer)
See the section on [Two-Phase Transfers](../design/two-phase-transfers.md) for more information on how the `pending_id` is used.

Constraints:

Expand Down Expand Up @@ -377,15 +303,15 @@ fields.

#### `flags.pending`

Mark the transfer as a [pending transfer](#pending-transfer).
Mark the transfer as a [pending transfer](../design/two-phase-transfers.md#reserve-funds-pending-transfer).

#### `flags.post_pending_transfer`

Mark the transfer as a [post-pending transfer](#post-pending-transfer).
Mark the transfer as a [post-pending transfer](../design/two-phase-transfers.md#post-pending-transfer).

#### `flags.void_pending_transfer`

Mark the transfer as a [void-pending transfer](#void-pending-transfer).
Mark the transfer as a [void-pending transfer](../design/two-phase-transfers.md#void-pending-transfer).

#### `flags.balancing_debit`

Expand Down