Synced from monorepo
Synced from monorepo Changes: - Workspace server: surface preview-proxy metrics through the hub metric pump - Shell: reclaim a session’s retained state in one entry - Shell: reclaim a session’s resident state in one entry - Pager: withhold key event types from Alacritty builds that double keys - Tools: cancel a session’s subagents when it closes - Pager: keep the whole plan in scrollback and separate reasoning from output in minimal mode - Pager: probe terminal version over DA2 and include it with feedback - SuperGrok Plus: identity, CLI, and analytics tier surfaces - Shell: inherit the session process scope into subagents - Pager: build @-file-search matcher lazily on first use - Tools: fix description and output contradictions in tool definitions - Workspace: degrade @-file-search instead of aborting on thread exhaustion - Tools: reap a session’s LSP servers when it closes - Tools: fix contradictions and defects in tool descriptions, schemas, and harness pools - MCP: reap stdio MCP children on session close - Shell: reuse spawn-time skill discovery for session telemetry - Tools: stop leaking shell-wrapper positional params into sourced scripts (fixes activate_conda under persistent/static shell) - Shell: self-heal corrupt session-search SQLite cache - Workspace: cap workspace-server tokio workers on many-core hosts - Shell: reap a session’s child processes when it closes - Crash handler: capture SIGABRT so panic-aborts leave crash reports - CLI chat proxy: team-scoped Grok Code managed-config admin routes - MCP: add CLI enable/disable for MCP servers - Shell: cap tokio worker threads for startup thread demand - Workspace: harden git_commit and add git_sync_base operation - Circuit breaker: add feature-gated gRPC retry policy Source-Revision: 2a818575225183d8ca915f5632a09b8067b5156a
This commit is contained in:
parent
02d9359435
commit
5da6962e4a
192 changed files with 10337 additions and 3421 deletions
|
|
@ -28,6 +28,7 @@ mod jemalloc_malloc_conf {
|
|||
use anyhow::Result;
|
||||
use std::env;
|
||||
use std::net::SocketAddr;
|
||||
use std::num::NonZeroUsize;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use xai_grok_pager::app::{
|
||||
AgentCmd, Command, HeadlessArgs, LeaderMgmtArgs, LeaderMgmtCommand, LeaderMode,
|
||||
|
|
@ -1522,6 +1523,90 @@ fn flag_dashboard_at_startup_if_requested(args: &mut PagerArgs) -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
const RUNTIME_SHUTDOWN_GRACE: std::time::Duration = std::time::Duration::from_secs(2);
|
||||
const GROK_WORKER_THREADS_ENV: &str = "GROK_WORKER_THREADS";
|
||||
/// tokio defaults to one worker per logical CPU. On a host with hundreds of
|
||||
/// CPUs that can exhaust a cgroup thread budget at startup and abort under
|
||||
/// `panic = "abort"`. A terminal UI is I/O-bound, so cap at 8.
|
||||
const DEFAULT_MAX_WORKER_THREADS: NonZeroUsize = NonZeroUsize::new(8).unwrap();
|
||||
/// How `GROK_WORKER_THREADS` resolved.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
enum WorkerCount {
|
||||
Accepted(NonZeroUsize),
|
||||
Clamped {
|
||||
requested: i128,
|
||||
used: NonZeroUsize,
|
||||
cores: NonZeroUsize,
|
||||
},
|
||||
Ignored {
|
||||
value: String,
|
||||
used: NonZeroUsize,
|
||||
},
|
||||
}
|
||||
impl WorkerCount {
|
||||
fn used(&self) -> NonZeroUsize {
|
||||
match self {
|
||||
Self::Accepted(used) | Self::Clamped { used, .. } | Self::Ignored { used, .. } => *used,
|
||||
}
|
||||
}
|
||||
fn notice(&self) -> Option<String> {
|
||||
match self {
|
||||
Self::Accepted(_) => None,
|
||||
Self::Clamped {
|
||||
requested,
|
||||
used,
|
||||
cores,
|
||||
} => Some(format!(
|
||||
"grok: clamped {GROK_WORKER_THREADS_ENV}={requested} to {used} (valid range is 1..={cores})"
|
||||
)),
|
||||
Self::Ignored { value, .. } => Some(format!(
|
||||
"grok: ignoring {GROK_WORKER_THREADS_ENV}={value:?} (not a valid integer)"
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
fn cli_worker_threads() -> NonZeroUsize {
|
||||
let cores = std::thread::available_parallelism().unwrap_or(NonZeroUsize::MIN);
|
||||
let resolved = match std::env::var(GROK_WORKER_THREADS_ENV) {
|
||||
Ok(value) => worker_threads_from(Some(&value), cores),
|
||||
Err(std::env::VarError::NotPresent) => worker_threads_from(None, cores),
|
||||
Err(std::env::VarError::NotUnicode(value)) => WorkerCount::Ignored {
|
||||
value: value.to_string_lossy().into_owned(),
|
||||
used: default_worker_threads(cores),
|
||||
},
|
||||
};
|
||||
if let Some(notice) = resolved.notice() {
|
||||
eprintln!("{notice}");
|
||||
}
|
||||
resolved.used()
|
||||
}
|
||||
fn worker_threads_from(env_override: Option<&str>, cores: NonZeroUsize) -> WorkerCount {
|
||||
match env_override {
|
||||
Some(value) => resolve_worker_override(value, cores),
|
||||
None => WorkerCount::Accepted(default_worker_threads(cores)),
|
||||
}
|
||||
}
|
||||
fn default_worker_threads(cores: NonZeroUsize) -> NonZeroUsize {
|
||||
cores.min(DEFAULT_MAX_WORKER_THREADS)
|
||||
}
|
||||
fn resolve_worker_override(value: &str, cores: NonZeroUsize) -> WorkerCount {
|
||||
let Ok(requested) = value.trim().parse::<i128>() else {
|
||||
return WorkerCount::Ignored {
|
||||
value: value.to_owned(),
|
||||
used: default_worker_threads(cores),
|
||||
};
|
||||
};
|
||||
let clamped = requested.clamp(1, cores.get() as i128) as usize;
|
||||
let used = NonZeroUsize::new(clamped).expect("clamp floor of 1 guarantees non-zero");
|
||||
if requested == used.get() as i128 {
|
||||
WorkerCount::Accepted(used)
|
||||
} else {
|
||||
WorkerCount::Clamped {
|
||||
requested,
|
||||
used,
|
||||
cores,
|
||||
}
|
||||
}
|
||||
}
|
||||
/// A plain runtime drop blocks forever on an uncancellable in-flight blocking
|
||||
/// task; `shutdown_timeout` abandons it after `grace` so exit can't hang.
|
||||
fn run_and_shutdown<F: std::future::Future>(
|
||||
|
|
@ -1755,10 +1840,15 @@ fn main() {
|
|||
"Found crashed sessions from a previous run"
|
||||
);
|
||||
}
|
||||
let workers = cli_worker_threads();
|
||||
let runtime = tokio::runtime::Builder::new_multi_thread()
|
||||
.worker_threads(workers.get())
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap_or_else(|e| panic!("failed to start tokio runtime: {e}"));
|
||||
.unwrap_or_else(|e| {
|
||||
eprintln!("grok: failed to start tokio runtime with {workers} workers: {e}");
|
||||
shutdown_and_flush_telemetry(1);
|
||||
});
|
||||
let result = run_and_shutdown(runtime, async_main(args), RUNTIME_SHUTDOWN_GRACE);
|
||||
xai_grok_telemetry::debug_log::flush();
|
||||
if let Err(e) = result {
|
||||
|
|
@ -2358,6 +2448,86 @@ async fn signal_leaders_to_relaunch(installed_version: &str) {
|
|||
mod tests {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn default_caps_the_core_count() {
|
||||
let nz = |n| NonZeroUsize::new(n).unwrap();
|
||||
assert_eq!(default_worker_threads(nz(360)), DEFAULT_MAX_WORKER_THREADS);
|
||||
assert_eq!(default_worker_threads(nz(4)), nz(4));
|
||||
}
|
||||
#[test]
|
||||
fn worker_threads_from_selects_default_or_override() {
|
||||
let nz = |n| NonZeroUsize::new(n).unwrap();
|
||||
let cores = nz(360);
|
||||
assert_eq!(
|
||||
worker_threads_from(None, cores),
|
||||
WorkerCount::Accepted(default_worker_threads(cores))
|
||||
);
|
||||
assert_eq!(
|
||||
worker_threads_from(Some("16"), cores),
|
||||
WorkerCount::Accepted(nz(16))
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn override_in_range_is_used_without_a_notice() {
|
||||
let nz = |n| NonZeroUsize::new(n).unwrap();
|
||||
let cores = nz(360);
|
||||
assert_eq!(
|
||||
resolve_worker_override("16", cores),
|
||||
WorkerCount::Accepted(nz(16))
|
||||
);
|
||||
assert_eq!(resolve_worker_override("16", cores).notice(), None);
|
||||
assert_eq!(resolve_worker_override(" 8 ", cores).used().get(), 8);
|
||||
assert_eq!(
|
||||
resolve_worker_override("360", cores),
|
||||
WorkerCount::Accepted(cores)
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn override_out_of_range_is_clamped_with_a_notice() {
|
||||
let nz = |n| NonZeroUsize::new(n).unwrap();
|
||||
let cores = nz(360);
|
||||
assert_eq!(
|
||||
resolve_worker_override("100000", cores),
|
||||
WorkerCount::Clamped {
|
||||
requested: 100000,
|
||||
used: cores,
|
||||
cores
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
resolve_worker_override("0", cores),
|
||||
WorkerCount::Clamped {
|
||||
requested: 0,
|
||||
used: nz(1),
|
||||
cores
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
resolve_worker_override("-1", cores),
|
||||
WorkerCount::Clamped {
|
||||
requested: -1,
|
||||
used: nz(1),
|
||||
cores
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
resolve_worker_override("100000", cores).notice().unwrap(),
|
||||
"grok: clamped GROK_WORKER_THREADS=100000 to 360 (valid range is 1..=360)"
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn override_unparseable_is_ignored_with_a_notice() {
|
||||
let cores = NonZeroUsize::new(360).unwrap();
|
||||
for value in ["abc", "", "99999999999999999999999999999999999999999"] {
|
||||
let ignored = resolve_worker_override(value, cores);
|
||||
assert!(matches!(ignored, WorkerCount::Ignored { .. }), "{value}");
|
||||
assert_eq!(ignored.used(), default_worker_threads(cores), "{value}");
|
||||
}
|
||||
assert_eq!(
|
||||
resolve_worker_override("abc", cores).notice().unwrap(),
|
||||
"grok: ignoring GROK_WORKER_THREADS=\"abc\" (not a valid integer)"
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn version_output_writer_preserves_channel_aware_contract() {
|
||||
for (label, expected_suffix) in [
|
||||
(" [alpha]", " [alpha]\n"),
|
||||
|
|
|
|||
Loading…
Reference in a new issue