Buckets are a way to isolate different ledgers within the same database server. This is useful when you want to separate data for different clients, or different applications, or different environments.
Understanding PostgreSQL Schemas
At a technical level, a bucket in Formance is directly mapped to a PostgreSQL schema. In PostgreSQL, a schema is a namespace that groups together database objects (tables, views, indexes, functions):
- Without schemas: All tables would live in one big global namespace, which could quickly become messy
- With schemas: You can organize and separate data logically, avoid name collisions, and apply different permissions
Two tables with the same name can exist in different schemas, as they live under different namespaces.
When you create a ledger, Formance stores its data inside a PostgreSQL schema (the bucket). This has several implications:
| Aspect | Description |
|---|
| Isolation | Each bucket/schema provides clean separation of ledger data. Two ledgers in different buckets won’t share tables, reducing risk of accidental data mixing. |
| Shared storage within a bucket | Multiple ledgers inside the same bucket share the same underlying tables, which can be efficient but means their data is less isolated. |
| Scaling strategy | By using multiple buckets, you can spread data across different schemas to avoid a single schema growing too large and becoming a performance bottleneck. |
System Schema
Formance uses a special internal schema called _system to track metadata about your ledgers and buckets.
If you ever delete a bucket manually in PostgreSQL (via DROP SCHEMA), you must also remove the corresponding entry in the _system schema. Skipping this step can leave your ledger registry in an inconsistent state.
Practical Considerations
- Data Isolation: Buckets are a good fit if you need strong separation (e.g., per-tenant ledgers in a multi-tenant system)
- Performance Management: For high-volume workloads, spreading ledgers across multiple buckets can reduce contention and keep queries fast
- Simplicity: If isolation isn’t critical, sticking with the
_default bucket keeps things straightforward
Creating a bucket
Buckets are automatically created when you create a new ledger. By default, if the bucket is not specified, the ledger is created in the _default bucket.
Using fctl, you can specify the bucket when creating a new ledger:
fctl ledger create <ledger-name> --bucket <bucket-name>
Alternatively, to create a ledger on a specific bucket, use the command:
curl -X POST http://localhost:3068/v2/testing -d '{"bucket": "bucket0"}'
curl http://localhost:3068/v2/testing | jq
{
"data": {
"bucket": "bucket0",
"metadata": {},
"features": {
"ACCOUNT_METADATA_HISTORY": "SYNC",
"HASH_LOGS": "SYNC",
"MOVES_HISTORY": "ON",
"MOVES_HISTORY_POST_COMMIT_EFFECTIVE_VOLUMES": "SYNC",
"TRANSACTION_METADATA_HISTORY": "SYNC"
},
"id": 2,
"name": "testing",
"addedAt": "2024-10-03T08:27:11.540373Z"
}
}
Features
Each usage of the ledger service is different. Some usage involves high write throughput, while others involve high read throughput, custom aggregation, etc.
So, each ledger can be configured with a set of features. By default, when creating a ledger, all features are enabled.
To create a ledger with specific features, use the command:
curl -X POST http://localhost:3068/v2/testing2 -d '{"features": {"HASH_LOGS": "DISABLED"}}'
curl http://localhost:3068/v2/testing2 | jq
{
"data": {
"bucket": "_default",
"metadata": {},
"features": {
"ACCOUNT_METADATA_HISTORY": "SYNC",
"HASH_LOGS": "DISABLED",
"MOVES_HISTORY": "ON",
"MOVES_HISTORY_POST_COMMIT_EFFECTIVE_VOLUMES": "SYNC",
"TRANSACTION_METADATA_HISTORY": "SYNC"
},
"id": 3,
"name": "testing2",
"addedAt": "2024-10-03T08:40:40.545233Z"
}
}
When overriding features, all not specified features will receive the default configuration.
Current set of features is not stable, some can be added or removed.
Current set of features
| Name | Default value | Possible configuration | Description |
|---|
| ACCOUNT_METADATA_HISTORY | SYNC | SYNC | DISABLED | Historize metadata changes on accounts |
| TRANSACTION_METADATA_HISTORY | SYNC | SYNC | DISABLED | Historize metadata changes on transactions |
| HASH_LOGS | SYNC | SYNC | DISABLED | Hash logs |
| MOVES_HISTORY | ON | ON | OFF | Historize funds movements by account |
| MOVES_HISTORY_POST_COMMIT_EFFECTIVE_VOLUMES | SYNC | SYNC | DISABLED | Compute and maintains post commit effective volumes |