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'.

  1. 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 1

Index 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.

  1. 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).

  2. The new is_first_shard check (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 1

Index 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

Edited by Shabini Rajadas

Merge request reports

Loading
Loading