Evidence ledger
One row per claim in There is no neutral identifier: who published it, what grade it carries, when it was written, when the link was last checked, and the quote or figure it rests on. Nothing in the guide is cited from memory, so anything not in this table is not in the guide.
Topic: how production systems mint row and object identifiers, and how the choice fails (exhaustion, duplicate issuance, hotspots, leakage). Research date: 2026-09-27. All URLs fetched from this session's environment on 2026-09-27.
Network note: this session's egress policy allows github.com, gitlab.com, raw.githubusercontent.com and little else. Engineering-blog, paper and talk hosts (signalvnoise.com, instagram-engineering.com, github.blog, krebsonsecurity.com, postgresql.org, usenix.org, arxiv.org, youtube.com and others) are blocked, so this guide is built from repository artefacts: issues, merge requests, commits, specs, runbooks and the RFC working group's own repository. Accounts that exist only on blocked hosts are represented through the danluu/post-mortems collection entry that indexes them, and are labelled as such.
| # | Org | Title | Tier | Published | Checked | URL | Claim I take from it | Supporting quote or figure |
|---|---|---|---|---|---|---|---|---|
| 1 | GitLab | Tracking primary key integer overflow risk for ci_builds.id (#215017) |
adr | 2020-04-20 | 2026-09-27 | https://gitlab.com/gitlab-org/gitlab/-/issues/215017 | GitLab noticed int4 exhaustion coming on its largest CI table and forecast the date | "Primary key is an integer-typed sequence with results in a maximum number of 2,147,483,647 records"; as of 2021-04-10 max id 1.168 billion, "54.42%" of capacity; overflow forecast "between September 2021 (at a worst case scenario using an exponential forecast) and May 2022 (following a polynomial model)" |
| 2 | GitLab | Add migration helpers for converting int columns to bigint (!49778) | source | 2020-12-11 (merged) | 2026-09-27 | https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49778 | The online int→bigint conversion is a dual-write: new column, sync trigger, batched backfill, no downtime | Helper creates a bigint column with NOT NULL DEFAULT 0, "setup a trigger to keep the two columns in sync", schedules background jobs to backfill; "DOWNTIME = false" |
| 3 | GitLab | Cleanup migration failure for p_ci_builds bigint conversion (#468671) | source | 2024-06 | 2026-09-27 | https://gitlab.com/gitlab-org/gitlab/-/work_items/468671 | The conversion programme begun in 2020 was still producing upgrade failures in 2024 | "PG::DependentObjectsStillExist: ERROR" raised by cleanup_bigint_conversions_for_p_ci_builds in a 2k-reference upgrade test |
| 4 | GitLab | The WebHook recent_failures counter may overflow (#330817) |
postmortem | 2021-05-12 | 2026-09-27 | https://gitlab.com/gitlab-org/gitlab/-/issues/330817 | A production incident on GitLab.com from a smallint counter incremented without bound | "32768 is out of range for ActiveModel::Type::Integer with limit 2 bytes" from WebHookWorkers; "hook.update!(recent_failures: hook.recent_failures + 1)"; incident tracked at gl-infra/production#4589 |
| 5 | GitLab | PG::UniqueViolation on index_issues_on_project_id_and_iid (#229614) | postmortem | 2020-07-16 | 2026-09-27 | https://gitlab.com/gitlab-org/gitlab/-/issues/229614 | Application-level ID allocation (per-project iid) collides under concurrent import |
"duplicate key value violates unique constraint 'index_issues_on_project_id_and_iid'"; race during long-running imports, "restore is somehow finding duplicates"; production Sentry exception referenced |
| 6 | GitLab | Runbook: Patroni log analysis, duplicate key errors | source | checked 2026-09 (living doc) | 2026-09-27 | https://gitlab.com/gitlab-com/runbooks/-/blob/master/docs/patroni/log_analysis.md | Operators see duplicate-key storms when a sequence is reset or lags a failover | duplicate key "can [be] because of a sequence (implemented for serial columns) has been modified (i.e. has been RESET)"; remediation setval() above max, or ON CONFLICT |
| 7 | Nextcloud | Some columns in the database are missing a conversion to big int (#13704) | source | 2019-01-20 | 2026-09-27 | https://github.com/nextcloud/server/issues/13704 | Widening ships as opt-in because the rewrite is too slow to run in an upgrade | "Due to the fact that changing column types on big tables could take some time they were not changed automatically"; occ db:convert-filecache-bigint "needs to be made while the instance is offline"; columns include filecache.fileid, storages.numeric_id |
| 8 | Nextcloud | Admin manual: bigint identifiers | vendor | living doc | 2026-09-27 | https://raw.githubusercontent.com/nextcloud/documentation/master/admin_manual/configuration_database/bigint_identifiers.rst | The official cost statement for widening a live table | "Changing columns in large tables can take several hours or even days, depending on the number of files in the Nextcloud instance"; advises shutting down the web server or maintenance mode |
| 9 | Nextcloud | fileid numeric value out of range (files_antivirus #697) | postmortem | 2026-08-26 | 2026-09-27 | https://github.com/nextcloud/files_antivirus/issues/697 | An operator hit int exhaustion in production via a foreign column that lagged the primary's widening | "SQLSTATE[22003]: Numeric value out of range: 1264 Out of range value for column 'fileid' at row 1"; oc_files_antivirus.fileid was int(10) unsigned while oc_filecache.fileid is BIGINT |
| 10 | danluu/post-mortems (collection) | Basecamp, GitHub, Strava exhaustion entries | postmortem | entries 2014–2021 | 2026-09-27 | https://raw.githubusercontent.com/danluu/post-mortems/master/README.md | Three named exhaustion outages: Basecamp Nov 2018 read-only; GitHub May 2021 scoped-tokens FK at max INT32, 9h48m of failures; Strava 2014-07-29 signed-int PK, uploads failed | "In November 2018 a database hit the integer limit, leaving the service in read-only mode" (Basecamp); "A foreign key on the scoped-tokens table hit max INT32, causing high failure rates for Actions and Pages ... for 9h48m" (GitHub); "Hit the signed integer limit on a primary key, causing uploads to fail" (Strava). Primary write-ups sit on hosts this session cannot reach; claims are taken from the collection's entries. |
| 11 | snowflake (snowflake-2010 branch) README | source | 2010 | 2026-09-27 | https://github.com/twitter-archive/snowflake/tree/snowflake-2010 | The requirements and the clock posture of the archetypal node-local generator | "There is no sequential id generation facility in Cassandra, nor should there be"; minimum 10k ids/sec/process, <2ms; k-sorted within ~"1 second" bound; "If your clock is running fast and NTP tells it to repeat a few milliseconds, snowflake will refuse to generate ids until a time that is after the last time we generated an id" | |
| 12 | Sony | sonyflake README | source | 2015 (repo) | 2026-09-27 | https://github.com/sony/sonyflake | The bit budget is a three-way trade; Sony re-cut Snowflake's fields | "39 bits for time in units of 10 msec, 8 bits for a sequence number, 16 bits for a machine id"; lifetime "174 years" vs Snowflake's 69; works on "2^16" machines vs 2^10; at most "2^8 IDs per 10 msec" per instance |
| 13 | Segment (Twilio) | ksuid README | source | 2017 | 2026-09-27 | https://github.com/segmentio/ksuid | 160-bit k-sortable IDs: pay 96 extra bits to drop worker coordination | 20 bytes: 32-bit big-endian timestamp + "128 bits of pseudorandom data", "64 times larger than the 122 bits" of UUIDv4; epoch "adjusted to May 13th, 2014, providing over 100 years of life"; base62 text is 27 chars and lexicographically sortable |
| 14 | ULID project | ulid/spec README | source | 2016 | 2026-09-27 | https://github.com/ulid/spec | The monotonicity contract and its stated failure mode | UUIDv4 "provides no other information than randomness which can cause fragmentation in many data structures"; 48-bit ms timestamp "valid until 10889 AD" + 80 random bits; same-millisecond generation increments "the random component ... by 1 bit in the least significant bit position (with carrying)"; on 2^80 overflow "the generation will fail" |
| 15 | Mastodon | Non-serial ("snowflake") IDs (PR #4801) | adr | merged 2017-10-04 | 2026-09-27 | https://github.com/mastodon/mastodon/pull/4801 | A recorded argument for abandoning sequential IDs, on privacy grounds, with dissent | aschmitz: "My motivation is purely to hide the total number of entries in each table"; nightpool: "This seems like a huge change for no perceivable benefit to >90% of instances"; layout: top 6 bytes ms-resolution time, bottom 2 bytes sequence; IDs returned as strings to protect JavaScript clients from 64-bit precision loss |
| 16 | IETF uuidrev WG | rfc4122bis working repository | adr | published as RFC 9562, 2024-05 | 2026-09-27 | https://github.com/ietf-wg-uuidrev/rfc4122bis | The UUID revision was developed in public, on GitHub, and obsoletes RFC 4122 | "RFC 9562 - Universally Unique IDentifiers (UUIDs)", May 2024, "Obsoletes: RFC 4122"; repo is "the working area for the IETF UUIDREV Working Group Internet-Draft" |
| 17 | IETF uuidrev WG | draft-ietf-uuidrev-rfc4122bis (full text) | adr | 2024 | 2026-09-27 | https://raw.githubusercontent.com/ietf-wg-uuidrev/rfc4122bis/main/draft-ietf-uuidrev-rfc4122bis.md | UUIDv7 exists because random UUIDs thrash database indexes; the spec claims an order-of-magnitude locality gap | "Time ordered monotonic UUIDs benefit from greater database index locality"; "the real-world differences in this approach of index locality vs random data inserts can be one order of magnitude or more" |
| 18 | IETF uuidrev WG | same document, monotonicity methods | adr | 2024 | 2026-09-27 | https://raw.githubusercontent.com/ietf-wg-uuidrev/rfc4122bis/main/draft-ietf-uuidrev-rfc4122bis.md | The spec standardises three ways to keep same-millisecond IDs ordered | Method 1 "fixed bit-length counter ... MUST be positioned immediately after the embedded timestamp"; Method 2 monotonic random "incremented in the least significant position"; Method 3 sub-millisecond precision "substitute for up to 12 random bits immediately following the timestamp" |
| 19 | IETF uuidrev WG | same document, security and background | adr | 2024 | 2026-09-27 | https://raw.githubusercontent.com/ietf-wg-uuidrev/rfc4122bis/main/draft-ietf-uuidrev-rfc4122bis.md | IDs are not credentials, and the RFC exists because industry forked first | "Implementations SHOULD NOT assume that UUIDs are hard to guess. For example, they MUST NOT be used as security capabilities"; UUIDv1 timestamp epoch "uncommon and difficult to represent"; MAC addresses "SHOULD NOT be used within a UUID"; background catalogues ~16 non-standard schemes (ULID, KSUID, Snowflake, Sonyflake, ObjectID, ...) built "to solve the problem of creating a better time-based, sortable unique identifier for use as a database key" |
| 20 | PostgreSQL | commit 78c5e141: Add UUID version 7 generation function | source | 2024-12-11 | 2026-09-27 | https://github.com/postgres/postgres/commit/78c5e141e9c139fc2ff36a220334e4aa25e1b0eb | Postgres implements RFC 9562 method 3 and survives backwards clocks inside one backend | uuidv7() per RFC 9562; "the 12-bit sub-millisecond timestamp fraction is stored immediately after the timestamp, in the space referred to as 'rand_a'", guaranteeing monotonic generation "within the same backend even when the system clock goes backward" |
| 21 | 2ndQuadrant / Tomas Vondra | tvondra/sequential-uuids README | source | 2018 | 2026-09-27 | https://github.com/tvondra/sequential-uuids | Why random UUIDs thrash a B-tree, and a wrap-around design that limits the damage | "Regular random UUIDs are distributed uniformly over the whole range of possible values. This results in poor locality when inserting data into indexes - all index leaf pages are equally likely to be hit, forcing the whole index into memory"; generators wrap (256-value blocks / 60s intervals over 65,536 blocks) |
| 22 | Rails | PR #26266: Change default primary keys to BIGINT | source | merged 2016-12 | 2026-09-27 | https://github.com/rails/rails/pull/26266 | The framework-level fix for exhaustion: change the default before apps need it | Opens with "Friends don't let friends use INT as a primary key"; int→bigint default for PostgreSQL and MySQL in Rails 5.1; compatibility layer keeps pre-5.1 migrations producing integer |
| 23 | Rails | PR #24962: bigserial pk by default (closed unmerged) | source | closed 2016-12-08 | 2026-09-27 | https://github.com/rails/rails/pull/24962 | The rejected first attempt, and why: same migration, different schema | tenderlove: "Running the same migration on Rails 4 and Rails 5 will produce a different database schema"; rafaelfranca: a 5.0 schema loaded on 5.1 would "generate a different result"; closed "Fixed by #26266" |
| 24 | CockroachDB | Issue #78049: use hash-sharded indexes by default | adr | 2022-03-17 | 2026-09-27 | https://github.com/cockroachdb/cockroach/issues/78049 | In a range-sharded store the ordered key is the problem, not the solution | bdarnell: "Workloads involving sequential keys can have major performance problems on CockroachDB. Hash-sharded indexes alleviate this problem, but because they're not the default ... many users who would benefit from them do not know to use them"; cost: "hash-sharding an index does reduce scan performance (proportional to the number of buckets)" |
| 25 | stateless-me | uuidv47 README | source | 2025 | 2026-09-27 | https://github.com/stateless-me/uuidv47 | The locality/leakage tension is real enough that people encrypt the timestamp | "store sortable UUIDv7 in your database while emitting a UUIDv4-looking façade at your API boundary"; "XOR-masks only the UUIDv7 timestamp field with a keyed SipHash-2-4 stream" |
| 26 | CockroachDB | Issue #39340: improve UX for hash-sharded indexes | source | 2019-08-05 | 2026-09-27 | https://github.com/cockroachdb/cockroach/issues/39340 | The workload behind the hotspot problem, and why the fix had to be cheap syntax | an "INDEX (ts)" schema "would have a hot spot"; the manual computed-column workaround "is starting to get heavy"; proposal: "we shoulder the burden of hashing and installing a check constraint behind a new syntax" |
Tier mix
postmortem 4 (rows 4, 5, 9, 10) · source 14 · adr 7 · vendor 1. No papers and no talks: the hosts that carry them (usenix.org, arxiv.org, youtube.com) are outside this session's network policy; the absence is stated in the guide.
Organisations represented
GitLab, Nextcloud, Twitter, Sony, Segment/Twilio, ULID project, Mastodon, IETF uuidrev WG, PostgreSQL, 2ndQuadrant, Rails core, CockroachDB, stateless-me, plus Basecamp, GitHub and Strava via the postmortem collection: 16.