From 17746d805f2f7ec213a83b906991b0a177cddfe8 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 22 Jul 2026 07:05:55 +0200 Subject: [PATCH] reviewer: limit max concurrency semaphore The reviewer code works with two semaphores: - semaphore, which reflects what's there at concurrency; - llm_semaphore, which is initialized to have 3 times the value of concurrency. This is based on an estimation that an active patch review consumes ~3 LLM slots. Such model works when using cloud-based models, but when the models run locally, this must be aligned with the maximum queue size of the local engine. Add an extra optional parameter to allow adjusting such limit. Signed-off-by: Mauro Carvalho Chehab --- docs/configuration.md | 1 + src/reviewer.rs | 10 ++++++++-- src/settings.rs | 2 ++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 37fc97f90..bc9dd4aae 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -159,6 +159,7 @@ Optional array of additional git remotes to track. | Key | Type | Default | Description | |-----|------|---------|-------------| | `concurrency` | integer | -- | Number of concurrent reviews. | +| `max_concurrency` | integer | -- | Max number of concurrent review queue. | | `worktree_dir` | string | -- | Directory for git worktrees used during review. | | `timeout_seconds` | integer | `3600` | Maximum time per review (seconds). | | `max_retries` | integer | `3` | Retry count on transient failures. | diff --git a/src/reviewer.rs b/src/reviewer.rs index 383e71711..d4b9928f4 100644 --- a/src/reviewer.rs +++ b/src/reviewer.rs @@ -96,7 +96,8 @@ impl Reviewer { /// * `db` - The database connection. /// * `settings` - Application settings. pub async fn new(db: Arc, settings: Settings) -> Self { - let concurrency = settings.review.concurrency; + let mut concurrency = settings.review.concurrency; + let max_concurrency = settings.review.max_concurrency; let repo_path = PathBuf::from(&settings.git.repository_path); let baseline_registry = @@ -129,12 +130,17 @@ impl Reviewer { // On average, an active patch review consumes ~3 LLM slots over its execution lifetime. // Thus, the global LLM request semaphore is scaled to (concurrency * 3) to fully // saturate LLM capacity while gating local processes/worktrees strictly to `concurrency`. - let llm_concurrency = if concurrency < 2 { + let mut llm_concurrency = if concurrency < 2 { 1 } else { std::cmp::max(1, concurrency * 3) }; + if max_concurrency.is_some() { + concurrency = concurrency.min(max_concurrency.unwrap_or(usize::MAX)); + llm_concurrency = llm_concurrency.min(max_concurrency.unwrap_or(usize::MAX)); + } + Self { db, settings, diff --git a/src/settings.rs b/src/settings.rs index ff3e79cdc..e9c7f1a17 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -367,6 +367,7 @@ pub struct GitSettings { #[allow(unused)] pub struct ReviewSettings { pub concurrency: usize, + pub max_concurrency: Option, pub worktree_dir: String, #[serde(default = "default_review_timeout")] pub timeout_seconds: u64, @@ -469,6 +470,7 @@ fn default_forge() -> ForgeSettings { #[derive(Debug, Deserialize, Clone)] pub struct LocalReviewReviewSettings { pub concurrency: Option, + pub max_concurrency: Option, } #[derive(Debug, Deserialize, Clone)]