# Configure Consistency

Source: https://fragment.dev/guides/configure-consistency

[Documentation index](https://fragment.dev/llms.txt)

<a id="configure-consistency"></a>

To ensure correctness at scale, Fragment's consistency mode lets you build guardrails without sacrificing performance.

[Visual: configureConsistency. View on the HTML page.](https://fragment.dev/guides/configure-consistency)

You can configure consistency within your Schema to make granular tradeoffs between throughput and consistency.

<a id="ledger-configuration"></a>

## Ledger Configuration

You can configure the consistency of the Ledger Entries list query in your Ledger. To do this, set `consistencyConfig` at the top level of your Schema:
- Use `entries: eventual` for Ledgers that require high throughput but can tolerate a stale entry list
- Use `entries: strong` for Ledgers that have lower throughput but require strong consistency, such as those powering reconcilation dashboards

```json title="Configuring consistency"
{
  "consistencyConfig": {
    "entries": "strong"
  },
  "chartOfAccounts": [...]
}
```

By default, all Ledgers use `eventual` consistency.

<a id="ledger-account-configuration"></a>

## Ledger Account Configuration

You can configure the consistency of balances, as well as the Ledger Lines list query, in your Ledger Account.

To configure an account's balance consistency, set `consistencyConfig.totalBalanceUpdates` within a Ledger Account's definition. `totalBalanceUpdates` controls the consistency with which the `selfTotal`, `childTotal` and total `balance` components of the account are updated. When `totalBalanceUpdates` is set to `strong`, any entry posted to the account or any of its children will update the account's balance immediately.

Options:
- Use `totalBalanceUpdates: eventual` for Ledger Accounts that require high throughput but can tolerate stale balances, such as those used for reporting
- Use `totalBalanceUpdates: strong` for Ledger Accounts that have lower throughput but require strong consistency, such as those used to authorize transactions

Similarly, to configure the consistency of an account's lines, set `consistencyConfig.lines`:
- Use `lines: eventual` for Ledger Accounts that require high throughput but can tolerate a stale line list
- Use `lines: strong` for Ledger Accounts that have lower throughput but require strong consistency, such as those powering transaction histories displayed to end users

```json title="Configuring consistency"
{
  "accounts": [
    {
      "key": "user-balance",
      "template": true,
      "type": "asset",
      "consistencyConfig": {
        "totalBalanceUpdates": "strong",
        "lines": "eventual"
      }
    }
  ]
}
```

By default, all Ledger Accounts use `eventual` for both properties.

For low-throughput applications, setting all Ledger Accounts as `strong` may make implementation easier. To do this, set `defaultConsistencyConfig` on `chartOfAccounts`:

```json title="Configuring default consistency"
{
  "chartOfAccounts": {
    "defaultConsistencyConfig": {
      "totalBalanceUpdates": "strong",
      "lines": "strong"
    },
    "accounts": [...]
  }
}
```

Strongly consistent Ledger Accounts generally won't have children, but in all cases child Ledger Accounts inherit the parent's `consistencyConfig` setting.

<a id="balance-queries"></a>

## Balance queries

To query a strongly consistent `balance`, set `consistencyMode` to `strong` when querying the Ledger Account:

```graphql title="Setting consistency on balance reads"
query GetBalances(
  $ledgerAccount: LedgerAccountMatchInput!
) {
  ledgerAccount(ledgerAccount: $ledgerAccount) {
    balance(consistencyMode: strong)
    balances(consistencyMode: strong) {
      nodes {
        amount
        currency {
          code
        }
      }
    }
  }
}
```

By default, balance queries on all Ledger Accounts are eventually consistent.

Restrictions:
- Querying a strongly consistent balance on an eventually consistent Ledger Account throws a GraphQL error
- You cannot combine `at` with `consistencyMode` to query strongly consistent historical balances

<a id="entry-conditions"></a>

## Entry conditions

Entry conditions are rules defined in your Schema to manage concurrency and enforce correctness within your Ledger.

Conditions are evaluated when a Ledger Entry is posted. If a condition is not met, the Ledger Entry is not posted and the mutation returns a `BadRequestError` with code `conditional_request_failed`.

Use `precondition` when your application reads a balance and needs to guarantee that it hasn't changed before posting the Ledger Entry.
```json title="Using precondition"
{
  "type": "pay-employee",
  "lines": [...],
  "conditions": [
    {
      "account": {
        "path": "bank-account"
      },
      "precondition": {
        "totalBalance": {
          "eq": "{{current_balance}}"
        }
      }
    }
  ]
}
```

Use `postcondition` to guarantee that a write never puts a Ledger Account's balance in an undesirable state.
```json title="Using postcondition"
{
  "type": "pay-employee",
  "lines": [...],
  "conditions": [
    {
      "account": {
        "path": "bank-account"
      },
      "postcondition": {
        "totalBalance": {
          "gte": "0"
        }
      }
    }
  ]
}
```

Restrictions:
-  Entry conditions using `totalBalance` apply to the account's total balance, which includes both the account's own lines and its children's lines.
-  Ledger Accounts referenced in Entry conditions must have `consistencyConfig.totalBalanceUpdates` set to `strong`
-  Linked Ledger Accounts do not support Entry conditions

<a id="repeated-conditions"></a>

## Repeated conditions

When using [repeated](https://fragment.dev/markdown/guides/post-ledger-entries.md#repeated-lines) Lines, conditions can be repeated using the same `repeated` key. This lets you enforce a condition on a variable set of accounts:

```json title="Ledger Entry with repeated lines and conditions"
{
  "type": "batch_transfer",
  "lines": [
    {
      "key": "from",
      "account": { "path": "liabilities/users:{{from_user}}/available" },
      "amount": "-{{amount}}",
      "repeated": { "key": "transfers" }
    },
    {
      "key": "to",
      "account": { "path": "liabilities/users:{{to_user}}/available" },
      "amount": "{{amount}}",
      "repeated": { "key": "transfers" }
    }
  ],
  "conditions": [
    {
      "account": { "path": "liabilities/users:{{from_user}}/available" },
      "postcondition": { "totalBalance": { "gte": "0" } },
      "currency": { "code": "USD" },
      "repeated": { "key": "transfers" }
    }
  ]
}
```

Each element in the `transfers` array generates both the transfer lines and its own balance condition.

<a id="consistent-groups"></a>

## Consistent Groups
You can configure an account to have a strongly-consistent ownBalance for specific group keys.
To do this, use the `consistencyConfig.groups` configuration in the Schema.

```json title="Ledger Entry Group balance consistency"
{
  "accounts": [
    {
      "key": "user-balance",
      "template": true,
      "type": "asset",
      "consistencyConfig": {
        "groups": [
          {
            "key": "invoice_id",
            "ownBalanceUpdates": "strong"
          }
        ]
      }
    }
  ]
}
```

See [Group Ledger Entries](https://fragment.dev/markdown/guides/group-ledger-entries.md) for more information about how to use Ledger Entry Groups.

<a id="query-consistent-groups"></a>

## Query Consistent Groups

To query a strongly consistent Ledger Entry Group balance, set `consistencyMode` to `strong` or `use_account` when querying the group's balances.
```graphql title="Setting consistency on group ownBalance reads"
query ListLedgerEntryGroupBalances(
  $ledger: LedgerMatchInput!
  $groupKey: SafeString!
  $groupValue: SafeString!
  $consistencyMode: ReadBalanceConsistencyMode
) {
  ledgerEntryGroup(ledgerEntryGroup: {
    ledger: $ledger,
    key: $groupKey,
    value: $groupValue,
  }) {
    balances {
      nodes {
        account {
          path
        }
        ownBalance(consistencyMode: $consistencyMode)
      }
    }
  }
}
```

By default, Ledger Entry Group balances are eventually consistent. Like other consistency options, enabling strong consistency will reduce the maximum throughput of an account.

Restrictions:
- Ledger Entry Group balances cannot be used for Entry conditions.
- Querying a strongly consistent Ledger Entry Group balance without configuring strong group consistency for that Ledger Account throws a GraphQL error.

<a id="legacy-ownbalance-consistency"></a>

## Legacy ownBalance Consistency

`ownBalanceUpdates` is the legacy way of setting account level consistency. It only controls the consistency of the account's own balance (excluding children). `childTotal` and `balance` default to `eventual` on Schemas using `ownBalanceUpdates`. A Ledger Account cannot have both `totalBalanceUpdates` and `ownBalanceUpdates` set.

Schemas using `ownBalanceUpdates` can only use `ownBalance` conditions. These conditions only apply to the account's own balance and require `consistencyConfig.ownBalanceUpdates` set to `strong`.

Any examples or documentation on this page that use `totalBalanceUpdates` or `totalBalance` conditions can be adapted for a legacy `ownBalanceUpdates` Schema by replacing:
- `totalBalanceUpdates` with `ownBalanceUpdates` in your consistency config
- `totalBalance` with `ownBalance` in your entry conditions
