mirror of
https://github.com/netbirdio/netbird.git
synced 2026-08-27 18:11:29 +02:00
Allocation read a per-cluster set of taken labels, picked one, and wrote it later. That had three defects: the set was per-cluster, which is wrong once labels must be unique across a shared zone; the read and the write were not atomic; and on pool exhaustion it appended the first four characters of the account ID with no retry and no uniqueness check -- and those four characters are constant for accounts created within roughly the same hour, so two such accounts could be handed the same label. Allocation now picks a label and inserts it inside a transaction, retrying with a fresh label when the database rejects a duplicate, and failing loudly when the attempt budget is exhausted. A fresh transaction per attempt is required rather than incidental: on PostgreSQL a failed statement poisons the enclosing transaction, so a single transaction wrapping the loop would fail every attempt after the first. Because the settings primary key is the account ID, a concurrent bootstrap for the same account fails on the primary key rather than the subdomain index. That is indistinguishable from a label collision by message, so the loop re-reads by account before retrying and returns the winner's row -- the same answer the sequential path gives.