perf(skills): sync registry entries concurrently

Use bounded ordered concurrency for /skills sync so registry downloads no longer serialize one skill at a time while preserving deterministic outcome order.

Harvested from PR #3139.

Co-authored-by: Hmbown <101357273+Hmbown@users.noreply.github.com>

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
This commit is contained in:
Hunter B
2026-06-12 02:41:18 -07:00
parent d50dfd4827
commit b1c94b3f02
3 changed files with 13 additions and 6 deletions
+3
View File
@@ -61,6 +61,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
loaded only after the workspace is trusted in user-owned config, matching the
project-local MCP trust model while preserving the documented shell-command
hook contract.
- **Skill registry sync latency (#3139).** `/skills sync` now syncs registry
entries with bounded ordered concurrency, so network latency no longer stacks
one skill at a time while output order stays deterministic.
- **SiliconFlow China provider config (#2893/#2895).** `siliconflow-CN`
now reads its own `[providers.siliconflow_cn]` / `[providers.siliconflow-CN]`
table and falls back to `[providers.siliconflow]` only for unset
+3
View File
@@ -61,6 +61,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
loaded only after the workspace is trusted in user-owned config, matching the
project-local MCP trust model while preserving the documented shell-command
hook contract.
- **Skill registry sync latency (#3139).** `/skills sync` now syncs registry
entries with bounded ordered concurrency, so network latency no longer stacks
one skill at a time while output order stays deterministic.
- **SiliconFlow China provider config (#2893/#2895).** `siliconflow-CN`
now reads its own `[providers.siliconflow_cn]` / `[providers.siliconflow-CN]`
table and falls back to `[providers.siliconflow]` only for unset
+7 -6
View File
@@ -39,6 +39,7 @@ use std::path::{Component, Path, PathBuf};
use anyhow::{Context, Result, bail};
use flate2::read::GzDecoder;
use futures_util::stream::{self, StreamExt};
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use thiserror::Error;
@@ -69,6 +70,7 @@ pub const DEFAULT_REGISTRY_URL: &str =
/// Default per-skill size cap (5 MiB). Honored at unpack time so a malicious
/// gzip bomb can't blow up RAM.
pub const DEFAULT_MAX_SIZE_BYTES: u64 = 5 * 1024 * 1024;
const SYNC_REGISTRY_CONCURRENCY: usize = 8;
/// File written under each installed skill so [`update`] / [`uninstall`] can
/// recover the original [`InstallSource`] without re-parsing user input.
@@ -587,12 +589,11 @@ pub async fn sync_registry(
}
};
let mut outcomes = Vec::new();
for (name, entry) in &doc.skills {
let outcome = sync_one_skill(name, entry, network, cache_dir, max_size).await;
outcomes.push(outcome);
}
let outcomes = stream::iter(doc.skills.iter())
.map(|(name, entry)| sync_one_skill(name, entry, network, cache_dir, max_size))
.buffered(SYNC_REGISTRY_CONCURRENCY)
.collect()
.await;
Ok(SyncResult::Done { outcomes })
}