Give each team or tenant its own concurrency cap and a fair share of workers, pull jobs round robin across tenants rather than first in first out, and set quotas on expensive scan types. A single global queue lets one team that pushes constantly delay everyone else.
The noisy neighbour problem
On a shared scanning platform, the queue is a shared resource. A team with a monorepo and a busy merge bot can enqueue hundreds of jobs an hour. In a plain first in first out queue, other teams' jobs sit behind them. Those teams see slow scans and blame the platform.
Fairness is a design choice, not a default. You have to build it.
Techniques
| Technique | How it works | Trade-off |
|---|---|---|
| Per-tenant concurrency cap | No tenant runs more than N jobs at once | Idle capacity if others are quiet, unless the cap is soft |
| Fair queuing | Workers take the next job from each tenant in turn | More complex queue structure |
| Weighted shares | Tenants with bigger needs get a larger share | Weights need governance |
| Per-scan-type quotas | Limit heavy scans, such as full image rescans | Teams may need an exception path |
| Burst credits | Allow short bursts above the cap, then throttle | Needs tuning |
A good starting point is fair queuing plus a soft cap: when capacity is free, a tenant can exceed its share, but the moment others have work waiting, the cap applies again.
Steps to put it in place
- Tag every job with a tenant or team identifier at enqueue time.
- Keep a logical sub-queue per tenant inside each priority lane.
- Have workers choose the next tenant by rotation, then take that tenant's oldest job.
- Apply a concurrency cap per tenant, and make the soft limit configurable.
- Report queue wait time per tenant, so you can see unfairness rather than guess.
- Tell teams their limits and how to request more, so a cap is not a surprise.
Reduce the load, not just ration it
Fairness policies share a fixed pot. Cutting waste enlarges it. Dedupe repeat jobs for the same target, cancel superseded pull request scans, use caching of inventories by digest, and spread scheduled scans with jitter. A noisy team is often noisy because its pipeline triggers duplicate scans on every push, and fixing that helps them as well.
Watch out for
Starvation of tiny tenants by a strict cap on large ones is rare, but starvation of the background lane by interactive work is common. Reserve a share for it.
Security issues: tenant isolation also applies to results and caches. A shared cache keyed by content digest is fine for inventories, but access to results must stay per tenant.
Try it yourself
Click through it at your own pace, or open it full screen. Notice how findings from many projects appear in one view, which is the shared platform scenario.
Safeguard supports many teams and projects in one platform with policies per team. You can try the free scan at https://gold.safeguard.sh/scan.
Quick check
Is a hard cap per team enough?
It protects others but wastes capacity when the platform is quiet. A soft cap that tightens under contention works better.
How do I know fairness is working?
Track queue wait time by tenant and compare the spread. If one tenant's waits are far worse than others, the policy needs adjusting.
Last reviewed: 19 October 2026.