Add sharded write path for namespace compute minutes
What does this MR do and why?
Add sharded write path for namespace compute minutes. MR 3 of this draft MR !224699 (closed)
Distribute NamespaceMonthlyUsage writes across up to MAX_NUMBER_OF_SHARDS rows per namespace per month, chosen by a CRC32 hash of the build id, to reduce write-lock contention on ci_namespace_monthly_usages, where every build previously contended for a single row.
The sharded write in UpdateProjectAndNamespaceUsageService is gated behind the fix_minute_contention feature flag (gitlab_com_derisk, default-off) and checked with the :group actor since the write path must stay deletion-safe. Read consumers already aggregate across shards, so behaviour is unchanged while only one shard exists.
Related to #490968
References
EXPLAIN links:
scope :for_shard, ->(shard_number) { where(shard_number: shard_number) }
On its own that's not a runnable query, so the explain actually composed in the new find_or_create_current_shard (current_month.for_namespace(…).for_shard(…).take), using namespace 9970 (gitlab-org) and date = '2026-08-01'.
- Shard lookup — hit (https://console.postgres.ai/gitlab/gitlab-production-ci/sessions/54200/commands/156739)
SELECT "ci_namespace_monthly_usages".* FROM "ci_namespace_monthly_usages"
WHERE "date" = '2026-08-01' AND "namespace_id" = 9970 AND "shard_number" = 1 LIMIT 1Index Scan on idx_ci_namespace_monthly_usages_namespace_id_date_shard_number, 0.028 ms, 8 shared buffers all hit, rows=1. The existing unique index covers the new predicate exactly — no new index required.
-
Shard lookup — miss (https://console.postgres.ai/gitlab/gitlab-production-ci/sessions/54200/commands/156741) — same query with shard_number = 2: 0.065 ms, 7 buffers, rows=0. Confirms the not-yet-created-shard path is equally cheap (this is the common case on first write to each of the 10 shards).
-
The new
is_first_shardcheck (https://console.postgres.ai/gitlab/gitlab-production-ci/sessions/54200/commands/156738)
SELECT 1 AS one FROM "ci_namespace_monthly_usages"
WHERE "date" = '2026-08-01' AND "namespace_id" = 9970 LIMIT 1Index Only Scan on the same index, rows=1 — but 9.57 ms, 17 buffers read cold, Heap Fetches: 14. Warm it's sub-ms; the number is cold-cache I/O on the thin clone, not a plan problem. Worth knowing it only runs on the create path, not per-build.
Screenshots or screen recordings
| Before | After |
|---|---|
How to set up and validate locally
MR acceptance checklist
Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.
Related to #490968