Skip to content

Commit 3318291

Browse files
drichardsonclaude
andcommitted
Add PREK_MAX_CONCURRENCY environment variable
Allow users to cap the maximum number of concurrent hooks via the PREK_MAX_CONCURRENCY environment variable. The value is clamped between 1 and the available CPU core count. This is useful when ulimit -n is low and concurrent hook execution can exhaust file descriptors. Unlike ulimit, an environment variable can be set in CI configs, .env files, and wrapper scripts without requiring shell builtins. Closes #1696 Assisted by AI Co-Authored-By: Claude <noreply@anthropic.com>
1 parent f7b0c00 commit 3318291

File tree

3 files changed

+59
-5
lines changed

3 files changed

+59
-5
lines changed

crates/prek-consts/src/env_vars.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ impl EnvVars {
2222
pub const PREK_SKIP: &'static str = "PREK_SKIP";
2323
pub const PREK_ALLOW_NO_CONFIG: &'static str = "PREK_ALLOW_NO_CONFIG";
2424
pub const PREK_NO_CONCURRENCY: &'static str = "PREK_NO_CONCURRENCY";
25+
pub const PREK_MAX_CONCURRENCY: &'static str = "PREK_MAX_CONCURRENCY";
2526
pub const PREK_NO_FAST_PATH: &'static str = "PREK_NO_FAST_PATH";
2627
pub const PREK_UV_SOURCE: &'static str = "PREK_UV_SOURCE";
2728
pub const PREK_NATIVE_TLS: &'static str = "PREK_NATIVE_TLS";

crates/prek/src/run.rs

Lines changed: 56 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,35 @@ pub(crate) static USE_COLOR: LazyLock<bool> =
1919
ColorChoice::Auto => unreachable!(),
2020
});
2121

22+
fn resolve_max_concurrency(cpu: usize, max_concurrency: Option<&str>) -> usize {
23+
if let Some(v) = max_concurrency {
24+
match v.parse::<usize>() {
25+
Ok(cap) => return cap.max(1),
26+
Err(_) => {
27+
tracing::warn!(
28+
"Invalid value for {}: {v:?}, using default ({cpu})",
29+
EnvVars::PREK_MAX_CONCURRENCY,
30+
);
31+
}
32+
}
33+
}
34+
35+
cpu
36+
}
37+
2238
pub(crate) static CONCURRENCY: LazyLock<usize> = LazyLock::new(|| {
2339
if EnvVars::is_set(EnvVars::PREK_NO_CONCURRENCY) {
24-
1
25-
} else {
26-
std::thread::available_parallelism()
27-
.map(std::num::NonZero::get)
28-
.unwrap_or(1)
40+
return 1;
2941
}
42+
43+
let cpu = std::thread::available_parallelism()
44+
.map(std::num::NonZero::get)
45+
.unwrap_or(1);
46+
47+
resolve_max_concurrency(
48+
cpu,
49+
EnvVars::var(EnvVars::PREK_MAX_CONCURRENCY).ok().as_deref(),
50+
)
3051
});
3152

3253
fn target_concurrency(serial: bool) -> usize {
@@ -352,6 +373,36 @@ mod tests {
352373
assert_eq!(total_files, 100);
353374
}
354375

376+
#[test]
377+
fn test_resolve_max_concurrency_none() {
378+
assert_eq!(resolve_max_concurrency(16, None), 16);
379+
}
380+
381+
#[test]
382+
fn test_resolve_max_concurrency_valid() {
383+
assert_eq!(resolve_max_concurrency(16, Some("4")), 4);
384+
}
385+
386+
#[test]
387+
fn test_resolve_max_concurrency_above_cpu() {
388+
assert_eq!(resolve_max_concurrency(8, Some("32")), 32);
389+
}
390+
391+
#[test]
392+
fn test_resolve_max_concurrency_zero_floors_to_one() {
393+
assert_eq!(resolve_max_concurrency(16, Some("0")), 1);
394+
}
395+
396+
#[test]
397+
fn test_resolve_max_concurrency_invalid_falls_back() {
398+
assert_eq!(resolve_max_concurrency(16, Some("abc")), 16);
399+
}
400+
401+
#[test]
402+
fn test_resolve_max_concurrency_empty_falls_back() {
403+
assert_eq!(resolve_max_concurrency(16, Some("")), 16);
404+
}
405+
355406
#[test]
356407
fn test_partitions_respects_cli_length_limit() {
357408
// Create files that will exceed CLI length limit

docs/configuration.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1332,6 +1332,8 @@ prek supports the following environment variables:
13321332

13331333
- `PREK_NO_CONCURRENCY` — Disable parallelism for installs and runs (If set, force concurrency to 1).
13341334

1335+
- `PREK_MAX_CONCURRENCY` — Set the maximum number of concurrent hooks (minimum 1). Defaults to the number of CPU cores when unset. Ignored when `PREK_NO_CONCURRENCY` is set. If you encounter "Too many open files" errors, lowering this value or raising the file descriptor limit with `ulimit -n` can help.
1336+
13351337
- `PREK_NO_FAST_PATH` — Disable Rust-native built-in hooks; always use the original hook implementation. See [Built-in Fast Hooks](builtin.md) for details.
13361338

13371339
- `PREK_UV_SOURCE` — Control how uv (Python package installer) is installed. Options:

0 commit comments

Comments
 (0)