






















@@ -10,6 +10,7 @@ const DEFAULT_PARALLELISM = 8;
1010const DEFAULT_TAIL_PARALLELISM = 8;
1111const DEFAULT_FAILURE_TAIL_LINES = 80;
1212const DEFAULT_LANE_TIMEOUT_MS = 120 * 60 * 1000;
13+const DEFAULT_LANE_START_STAGGER_MS = 2_000;
13141415const lanes = [
1516["live-models", "OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:live-models"],
@@ -68,13 +69,30 @@ function parsePositiveInt(raw, fallback, label) {
6869return parsed;
6970}
707172+function parseNonNegativeInt(raw, fallback, label) {
73+if (!raw) {
74+return fallback;
75+}
76+const parsed = Number(raw);
77+if (!Number.isInteger(parsed) || parsed < 0) {
78+throw new Error(`${label} must be a non-negative integer. Got: ${JSON.stringify(raw)}`);
79+}
80+return parsed;
81+}
82+7183function parseBool(raw, fallback) {
7284if (raw === undefined || raw === "") {
7385return fallback;
7486}
7587return !/^(?:0|false|no)$/i.test(raw);
7688}
778990+function sleep(ms) {
91+return new Promise((resolve) => {
92+setTimeout(resolve, ms);
93+});
94+}
95+7896function utcStampForPath() {
7997return new Date().toISOString().replaceAll("-", "").replaceAll(":", "").replace(/\..*$/, "Z");
8098}
@@ -209,13 +227,34 @@ async function runLane(lane, baseEnv, logDir, timeoutMs) {
209227async function runLanePool(poolLanes, baseEnv, logDir, parallelism, options) {
210228const failures = [];
211229let nextIndex = 0;
230+let lastLaneStartAt = 0;
231+let laneStartQueue = Promise.resolve();
232+233+async function waitForLaneStartSlot() {
234+if (options.startStaggerMs <= 0) {
235+return;
236+}
237+const previous = laneStartQueue;
238+let releaseQueue;
239+laneStartQueue = new Promise((resolve) => {
240+releaseQueue = resolve;
241+});
242+await previous;
243+const waitMs = Math.max(0, lastLaneStartAt + options.startStaggerMs - Date.now());
244+if (waitMs > 0) {
245+await sleep(waitMs);
246+}
247+lastLaneStartAt = Date.now();
248+releaseQueue();
249+}
212250213251async function worker() {
214252while (nextIndex < poolLanes.length) {
215253if (options.failFast && failures.length > 0) {
216254return;
217255}
218256const lane = poolLanes[nextIndex++];
257+await waitForLaneStartSlot();
219258const result = await runLane(lane, baseEnv, logDir, options.timeoutMs);
220259if (result.status !== 0) {
221260failures.push(result);
@@ -285,6 +324,11 @@ async function main() {
285324DEFAULT_LANE_TIMEOUT_MS,
286325"OPENCLAW_DOCKER_ALL_LANE_TIMEOUT_MS",
287326);
327+const laneStartStaggerMs = parseNonNegativeInt(
328+process.env.OPENCLAW_DOCKER_ALL_START_STAGGER_MS,
329+DEFAULT_LANE_START_STAGGER_MS,
330+"OPENCLAW_DOCKER_ALL_START_STAGGER_MS",
331+);
288332const failFast = parseBool(process.env.OPENCLAW_DOCKER_ALL_FAIL_FAST, true);
289333const runId = process.env.OPENCLAW_DOCKER_ALL_RUN_ID || utcStampForPath();
290334const logDir = path.resolve(
@@ -304,6 +348,7 @@ async function main() {
304348console.log(`==> Parallelism: ${parallelism}`);
305349console.log(`==> Tail parallelism: ${tailParallelism}`);
306350console.log(`==> Lane timeout: ${laneTimeoutMs}ms`);
351+console.log(`==> Lane start stagger: ${laneStartStaggerMs}ms`);
307352console.log(`==> Fail fast: ${failFast ? "yes" : "no"}`);
308353console.log(`==> Live-test bundled plugin deps: ${baseEnv.OPENCLAW_DOCKER_BUILD_EXTENSIONS}`);
309354@@ -314,7 +359,7 @@ async function main() {
314359baseEnv,
315360);
316361317-const options = { failFast, timeoutMs: laneTimeoutMs };
362+const options = { failFast, startStaggerMs: laneStartStaggerMs, timeoutMs: laneTimeoutMs };
318363const failures = await runLanePool(lanes, baseEnv, logDir, parallelism, options);
319364if (failFast && failures.length > 0) {
320365await printFailureSummary(failures, tailLines);
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。