|
| 1 | +--- |
| 2 | +icon: material/numeric |
| 3 | +--- |
| 4 | + |
| 5 | +# Sharded sequences |
| 6 | + |
| 7 | +!!! note "Unique IDs" |
| 8 | + Sharded sequences require a bit more configuration to get working. If you're looking |
| 9 | + for an easy way to generate cross-shard unique 64-bit integers, consider [Unique IDs](unique-ids.md). |
| 10 | + |
| 11 | +!!! note "Experimental feature" |
| 12 | + This feature is new and experimental. Please report any issues you may run into and test it |
| 13 | + before deploying to production. |
| 14 | + |
| 15 | +Sharded sequences are a way to generate monotonically increasing, globally unique 64-bit integers, without large gaps between numbers |
| 16 | +or using a timestamp-based approach that produces very large numbers. |
| 17 | + |
| 18 | +They can be used for producing cross-shard unique primary keys in [sharded](query-routing.md#sharding-configuration) tables, directly inside the database. |
| 19 | + |
| 20 | +## How it works |
| 21 | + |
| 22 | +Sharded sequences combine two Postgres primitives: |
| 23 | + |
| 24 | +1. A normal sequence (created with `CREATE SEQUENCE`) |
| 25 | +2. A hashing function, `satisfies_hash_partition`, used for number selection |
| 26 | + |
| 27 | +The two are called inside a PL/pgSQL function that fetches numbers from a sequence until `satisfies_hash_partition` returns `true`, for the total number of shards in the cluster and the shard number it's being executed on: |
| 28 | + |
| 29 | +```postgresql |
| 30 | +DO $$ |
| 31 | +BEGIN |
| 32 | + LOOP |
| 33 | + SELECT nextval('normal_seq'::regclass) INTO val; |
| 34 | +
|
| 35 | + IF satisfies_hash_partition(/* ... */, val) THEN |
| 36 | + RETURN val; |
| 37 | + END IF; |
| 38 | + END LOOP; |
| 39 | +END $$; |
| 40 | +``` |
| 41 | + |
| 42 | +Since fetching values from a sequence is very quick, we are able to find the correct number without introducing significant latency to row creation. The Postgres hash function is also good at producing uniform outputs, so all shards will have similar, small gaps between generated numbers. |
| 43 | + |
| 44 | +### Configuration |
| 45 | + |
| 46 | +Sharded sequences can only be used to generate primary keys for _sharded_ tables. [Omnisharded](omnishards.md) tables cannot use database sequences since they aren't guaranteed to produce the same number on all shards. |
| 47 | + |
| 48 | +To make sure this constraint is enforced, PgDog can inject [unique IDs](unique-ids.md) into omnisharded-targeted `INSERT` queries only: |
| 49 | + |
| 50 | +=== "pgdog.toml" |
| 51 | + ```toml |
| 52 | + [rewrite] |
| 53 | + primary_key = "rewrite_omni" |
| 54 | + ``` |
| 55 | +=== "Helm chart" |
| 56 | + ```yaml |
| 57 | + rewrite: |
| 58 | + primaryKey: rewrite_omni |
| 59 | + ``` |
| 60 | + |
| 61 | +This configuration setting is required to use sharded sequences, so make sure to set it before proceeding. |
| 62 | + |
| 63 | +### Installation |
| 64 | + |
| 65 | +To install and use sharded sequences, configure [rewrites](#configuration) to target omnisharded tables only, add all the shards to [`pgdog.toml`](../../configuration/pgdog.toml/databases.md) `[[databases]]` section, and run the following [admin database](../../administration/index.md) command: |
| 66 | + |
| 67 | +=== "Admin database" |
| 68 | + ``` |
| 69 | + SETUP SCHEMA; |
| 70 | + ``` |
| 71 | +=== "CLI" |
| 72 | + Since PgDog is also a CLI application, you can run the same command as follows: |
| 73 | + |
| 74 | + ``` |
| 75 | + $ pgdog setup --database <name> |
| 76 | + ``` |
| 77 | + |
| 78 | + | Option | Description | |
| 79 | + |-|-| |
| 80 | + | `database` | Database `name` in `pgdog.toml`. | |
| 81 | + |
| 82 | +This command will perform the following steps: |
| 83 | + |
| 84 | +1. Install the [schema manager](schema_management/index.md) into all database shards along with the necessary PL/pgSQL functions |
| 85 | +2. Find all tables that contain `BIGINT PRIMARY KEY` columns (incl. `BIGSERIAL`) and change their default values to call the sharded sequence function |
| 86 | + |
| 87 | +Once done, all subsequent `INSERT` statements that don't specify the primary key will automatically use the sharded sequence for their respective tables, for example: |
| 88 | + |
| 89 | +=== "Queries" |
| 90 | + ```postgresql |
| 91 | + -- Using DEFAULT explicitly. |
| 92 | + INSERT INTO users |
| 93 | + (id, email, tenant_id) |
| 94 | + VALUES |
| 95 | + (DEFAULT, 'admin@example.com', 5) RETURNING id; |
| 96 | + |
| 97 | + -- Omitting the primary key. |
| 98 | + INSERT INTO users |
| 99 | + (email, tenant_id) |
| 100 | + VALUES |
| 101 | + ('user@example.com', 5) RETURNING id; |
| 102 | + ``` |
| 103 | +=== "Output" |
| 104 | + ``` |
| 105 | + id |
| 106 | + ---- |
| 107 | + 1 |
| 108 | + (1 row) |
| 109 | + |
| 110 | + id |
| 111 | + ---- |
| 112 | + 5 |
| 113 | + (1 row) |
| 114 | + ``` |
| 115 | + |
| 116 | +The returned `id` will be globally unique and monotonically increasing. |
| 117 | + |
| 118 | +### Migrations |
| 119 | + |
| 120 | +The schema manager will only install the sharded sequence in tables currently present in the database. When adding new tables or primary keys, make sure to execute the following PL/pgSQL function |
| 121 | +as well: |
| 122 | + |
| 123 | +```postgresql |
| 124 | +SELECT pgdog.install_sharded_sequence('schema_name', 'table_name', 'column_name'); |
| 125 | +``` |
| 126 | + |
| 127 | +| Argument | Description | |
| 128 | +|-|-| |
| 129 | +| Schema name | The name of the schema where the table is being created. This is commonly the `public` schema, but can be any other as well. | |
| 130 | +| Table name | The name of the new or existing table with the primary key. | |
| 131 | +| Column name | The name of the primary key column. | |
| 132 | + |
| 133 | +##### Example |
| 134 | + |
| 135 | +The entire migration can be executed inside the same transaction: |
| 136 | + |
| 137 | +```postgresql |
| 138 | +BEGIN; |
| 139 | +
|
| 140 | +CREATE TABLE public.users ( |
| 141 | + id BIGINT PRIMARY KEY, |
| 142 | + email VARCHAR NOT NULL, |
| 143 | + created_at TIMESTAMPTZ |
| 144 | +); |
| 145 | +
|
| 146 | +SELECT pgdog.install_sharded_sequence('public', 'users', 'id'); |
| 147 | +
|
| 148 | +COMMIT; |
| 149 | +``` |
0 commit comments