Make maximum security update scheduler concurrency configurable
<!--
Implementation issues are used break-up a large piece of work into small, discrete tasks that can
move independently through the build workflow steps. They're typically used to populate a Feature
Epic. Once created, an implementation issue is usually refined in order to populate and review the
implementation plan and weight.
Example workflow: https://about.gitlab.com/handbook/engineering/development/threat-management/planning/diagram.html#plan
-->
## Why are we doing this work
<!--
A brief explanation of the why, not the what or how. Assume the reader doesn't know the
background and won't have time to dig-up information from comment threads.
-->
Protection against resource exhaustion, and fine configuration for self-managed instances.
## Relevant links
<!--
Information that the developer might need to refer to when implementing the issue.
- [Design Issue](https://gitlab.com/gitlab-org/gitlab/-/issues/<id>)
- [Design 1](https://gitlab.com/gitlab-org/gitlab/-/issues/<id>/designs/<image>.png)
- [Design 2](https://gitlab.com/gitlab-org/gitlab/-/issues/<id>/designs/<image>.png)
- [Similar implementation](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/<id>)
-->
n/a
## Implementation plan
### 1. Add the application setting
Create the column definition file:
```yaml
# config/application_setting_columns/dependency_management_scheduler_worker_max_concurrency.yml
---
api_type:
attr: dependency_management_scheduler_worker_max_concurrency
clusterwide: false
column: dependency_management_scheduler_worker_max_concurrency
db_type: integer
default: '30'
description:
encrypted: false
gitlab_com_different_than_default: false
jihu: false
not_null: true
```
Add the matching `application_settings` migration. Validate `numericality: { only_integer: true, greater_than_or_equal_to: 0 }` (0 effectively pauses the worker — useful kill switch).
### 2. Wire the worker
Update [`ee/app/workers/dependency_management/security_update/scheduler_worker.rb`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/app/workers/dependency_management/security_update/scheduler_worker.rb):
```ruby
module DependencyManagement
module SecurityUpdate
class SchedulerWorker
include Gitlab::EventStore::Subscriber
data_consistency :delayed
feature_category :dependency_management
urgency :low
deduplicate :until_executed
idempotent!
MAX_SCHEDULER_WORKER_LIMIT = 200
concurrency_limit -> { [Gitlab::CurrentSettings.dependency_management_scheduler_worker_max_concurrency, MAX_SCHEDULER_WORKER_LIMIT].min }
defer_on_database_health_signal :gitlab_sec, [:sbom_occurrences], 1.minute
def handle_event(event)
# … unchanged
end
end
end
end
```
No service-layer changes. The middleware does the heavy lifting.
### 3. Tests
* Unit spec for the worker asserting `get_concurrency_limit` returns the value set on `ApplicationSetting` (mirror existing patterns in `spec/workers/concerns/worker_attributes_spec.rb`).
* `ApplicationSetting` spec for the new column default + validation.
* Shared concurrency-limit shared examples already cover middleware behavior; no need to re-test the framework.
### 4. Observability
The existing `Gitlab::Metrics::Samplers::ConcurrencyLimitSampler` automatically picks up any worker with a `concurrency_limit`, so we get Prometheus metrics for queue size and concurrent worker count for free.
### 5. Docs
Add a one-liner under the auto-remediation admin docs noting the new setting, the default, and that `0` pauses scheduling.
## Verification steps
Verify that we don't see stability changes as we roll this out across more projects.
issue