Slow execution of Sidekiq job Ci::SyncReportsToReportApprovalRulesWorker
<!--Triage of infradev Issues is desired to occur asynchronously. For maximum efficiency, please ensure the following, so that your infradev issues can gain maximum traction. https://about.gitlab.com/handbook/engineering/workflow/#a-guide-to-creating-effective-infradev-issues--> ## Summary We have a regular recurring issue on a dedicated tenant `still_silver_kangaroo` where the execution time of the high urgency cpu-bound Sidekiq job `Ci::SyncReportsToReportApprovalRulesWorker` spikes and delays the whole high urgency cpu-bound queue, impacting the wider instance. There is no infrastructure saturation at these times and it looks like an application side performance issue. Example log from a slow job: <details> <summary>Sanitised log extract</summary> ``` { "correlation_id": "01KH7EW2JPP0YMVRYGA7MPK5M4", "meta.caller_id": "Security::SyncProjectPolicyWorker", "severity": "INFO", "queue": "urgent_cpu_bound", "@timestamp": "2026-02-11T23:05:42.040193372Z", "duration_s": 216.172466, "queue_duration_s": 0.007047, "scheduling_latency_s": 0.007125, "db_duration_s": 35.921989, "db_primary_duration_s": 35.92, "db_replica_duration_s": 0, "db_primary_txn_duration_s": 0, "db_replica_txn_duration_s": 0, "db_primary_txn_max_duration_s": 0, "db_replica_txn_max_duration_s": 0, "db_main_duration_s": 29.346, "db_ci_duration_s": 6.574, "db_main_replica_duration_s": 0, "db_ci_replica_duration_s": 0, "db_main_txn_duration_s": 0.027, "db_ci_txn_duration_s": 0, "db_main_replica_txn_duration_s": 0, "db_ci_replica_txn_duration_s": 0, "db_main_txn_max_duration_s": 0.023, "db_ci_txn_max_duration_s": 0, "db_main_replica_txn_max_duration_s": 0, "db_ci_replica_txn_max_duration_s": 0, "redis_duration_s": 1.233852, "redis_cache_duration_s": 0.32225, "redis_queues_duration_s": 0.004107, "redis_queues_metadata_duration_s": 0.184265, "redis_shared_state_duration_s": 0.72323, "external_http_duration_s": 8.20343656919431, "exclusive_lock_wait_duration_s": 0, "exclusive_lock_hold_duration_s": 0, "redis_calls": 91, "redis_cache_calls": 37, "redis_queues_calls": 13, "redis_queues_metadata_calls": 10, "redis_shared_state_calls": 31, "redis_read_bytes": 378835, "redis_write_bytes": 12089, "redis_cache_read_bytes": 378547, "redis_cache_write_bytes": 2294, "redis_queues_read_bytes": 132, "redis_queues_write_bytes": 6665, "redis_queues_metadata_read_bytes": 14, "redis_queues_metadata_write_bytes": 1657, "redis_shared_state_read_bytes": 142, "redis_shared_state_write_bytes": 1473, "job_size_bytes": 8, "mem_bytes": 703123898, "mem_total_bytes": 2673575498, "cpu_s": 47.416435, "external_http_count": 32 } ``` </details> ## Impact 1. Exceeds the threshold 10s execution time for [high urgency jobs](https://docs.gitlab.com/development/sidekiq/worker_attributes/) - these thresholds are defined in-app. 2. This leads to a backup of the whole high urgency cpu-bound queue and delays of all high urgency cpu-bound jobs on the instance of up to 10 minutes. The acceptable threshold is 10s for all other jobs in this queue also. So this is quite a significant delay. 3. This happens roughly 3 times per week. ## Recommendation We can provide more detailed logs for Dedicated, or even temporary log access to application developers to further investigation. Recommendation to fix is to do one of: 1. Reduce the execution time at these peaks. 2. Break down the slower jobs into multiple smaller jobs. 3. Move the slower jobs to the low urgency queues instead of high-urgency, allowing slower execution time. ## Optimization Plan: `Ci::SyncReportsToReportApprovalRulesWorker` --- **1. Move to low urgency queue** *(Highest impact — addresses the stated incident)* Changed `urgency :high` → `urgency :low`. Job no longer competes in the `urgent_cpu_bound` queue, directly eliminating the queue backup impact. **2. Added `defer_on_database_health_signal`** Defers the job 1 minute when the `approval_merge_request_rules` table is under load, preventing cascading DB pressure during peaks. **3. Rewrote `opened_merge_requests_with_head_sha`** - **Before:** `all_merge_requests.opened.select { |mr| mr.diff_head_pipeline?(self) }` — loaded all MRs into Ruby and filtered in-process. Source of `mem_bytes: 670MB` in the log. - **After:** DB-side `EXISTS` subquery — only fetches matching MRs. Eliminates the memory spike and reduces `db_duration_s`. **4. Memoized `target_branch_report` in `UpdateLicenseApprovalsService`** - **Before:** Fetched the license scan report from object storage once per approval rule per MR. - **After:** `strong_memoize_with(:target_branch_report_by_pipeline, pipeline.id)` caches per pipeline. Addresses most of `external_http_count: 32` and `external_http_duration_s: 8.2s` in the log. **5. Changed `worker_resource_boundary :cpu` → `:unknown`** *(Routing correctness)* The work is primarily DB I/O bound, not CPU-bound. Routes the job to the correct Sidekiq process pool. ### MR link https://gitlab.com/gitlab-org/gitlab/-/merge_requests/231984/diffs
issue