Add Namespaces::StatePropagationWorker
What does this MR do and why?
Implements Namespaces::StatePropagationWorker, the idempotent Sidekiq worker that performs propagation-based state inheritance through the namespace hierarchy (Organization > Group > Subgroup > Project), as described in ADR 003.
The worker:
- Finds the pending outbox record (
Namespaces::StatePropagation) bynamespace_id+target_state. - Computes
overwritable_statesfromsource_state/target_stateviaNamespaces::Stateful::StatePrecedence. When empty, the outbox record is dropped without a status write. - Marks it
processingand setsstarted_at. - Drives
Gitlab::Database::Namespaces::StatePropagationIteratorin batches of 500, applying a conditional bulk updateSET state = target_state WHERE id IN (ids) AND state IN (overwritable_states). - Deletes the outbox record on completion. On error the record is left in place (
processing) for the CRON worker to recover.
Worker attributes
idempotent!+deduplicate :until_executed, including_scheduled: truedata_consistency :sticky,urgency :low,concurrency_limit -> { 200 }- Registered in
app/workers/all_queues.yml
Database review
Two new queries are introduced.
1. Outbox lookup (find_pending_propagation)
EXPLAIN
SELECT namespace_state_propagations.*
FROM namespace_state_propagations
WHERE namespace_state_propagations.status = 0
AND namespace_state_propagations.namespace_id = 1
AND namespace_state_propagations.target_state = 1
LIMIT 1;Expected index: idx_namespace_state_propagations_unique_pending_processing (unique partial on (namespace_id, target_state) WHERE status IN (0, 1)).
https://console.postgres.ai/gitlab/projects/gitlab-production-main/sessions/53857/commands/156182
namespace_state_propagations is empty on production for now, so this plan only confirms index selection. Real-scale cost and timing numbers will be available once the table is populated.
2. Conditional bulk state update (apply_state)
Scoped to a batch of at most 500 descendant IDs produced by the iterator:
EXPLAIN
UPDATE "namespaces"
SET "state" = 1
WHERE "namespaces"."id" IN (9970, 9971, 9972, 9973, 9974)
AND "namespaces"."state" IN (0);Expected index: namespaces_pkey for the batched id IN (...).
https://console.postgres.ai/gitlab/projects/gitlab-production-main/sessions/53857/commands/156183
References
Closes #599298 Parent epic: &21922
How to set up and validate locally
bundle exec rspec spec/workers/namespaces/state_propagation_worker_spec.rb