fix(runtime): harden standalone cold starts

This commit is contained in:
xixu-me committed 2026-03-30 22:19:27 +08:00
1 parent 0ebdaa421c
commit ac349eae97
10 files changed
+39 -8

No files matched your search

+1 -1
View File
@@ -2,7 +2,7 @@
"name": "xread",
"scripts": {
"lint": "eslint --ext .js,.ts .",
"test:ci": "node --test tests/open-source-build.test.cjs tests/integrity-check.test.cjs tests/prepare-licensed-assets.test.cjs tests/github-automation.test.cjs",
"test:ci": "node --test tests/open-source-build.test.cjs tests/integrity-check.test.cjs tests/prepare-licensed-assets.test.cjs tests/github-automation.test.cjs tests/container-runtime-regressions.test.cjs",
"prepare:licensed-assets": "node ./scripts/prepare-licensed-assets.cjs",
"build": "node ./integrity-check.cjs && tsc -p .",
"build:watch": "tsc -p . -w",
+2 -1
View File
@@ -29,6 +29,7 @@ import { CurlControl } from '../services/curl';
import { LmControl } from '../services/lm';
import { tryDecodeURIComponent } from '../utils/misc';
import { CFBrowserRendering } from '../services/cf-browser-rendering';
import { STANDALONE_BOOT_TIMEOUT_MS } from '../services/boot-timeouts';
import { GlobalLogger } from '../services/logger';
import { RateLimitControl, RateLimitDesc } from '../shared/services/rate-limit';
@@ -178,7 +179,7 @@ export class CrawlerHost extends RPCHost {
}
override async init() {
await this.dependencyReady();
await this.dependencyReady(STANDALONE_BOOT_TIMEOUT_MS);
if (this.puppeteerControl.effectiveUA) {
this.curlControl.impersonateChrome(this.puppeteerControl.effectiveUA);
+2 -1
View File
@@ -29,6 +29,7 @@ import { SerperSearchQueryParams } from '../shared/3rd-party/serper-search';
import { WebSearchEntry } from '../services/serp/compat';
import { SecretExposer } from '../shared/services/secrets';
import { StandaloneSearchFallbackService } from '../services/serp/standalone-fallback';
import { STANDALONE_BOOT_TIMEOUT_MS } from '../services/boot-timeouts';
interface FormattedPage extends RealFormattedPage {
favicon?: string;
@@ -97,7 +98,7 @@ export class SearcherHost extends RPCHost {
}
override async init() {
await this.dependencyReady();
await this.dependencyReady(STANDALONE_BOOT_TIMEOUT_MS);
this.emit('ready');
}
+2 -1
View File
@@ -26,6 +26,7 @@ import { LRUCache } from 'lru-cache';
import { API_CALL_STATUS } from '../shared/db/api-roll';
import { SecretExposer } from '../shared/services/secrets';
import { StandaloneSearchFallbackService } from '../services/serp/standalone-fallback';
import { STANDALONE_BOOT_TIMEOUT_MS } from '../services/boot-timeouts';
type RateLimitCache = {
blockedUntil?: Date;
@@ -115,7 +116,7 @@ export class SerpHost extends RPCHost {
}
override async init() {
await this.dependencyReady();
await this.dependencyReady(STANDALONE_BOOT_TIMEOUT_MS);
this.emit('ready');
}
+2
View File
@@ -0,0 +1,2 @@
export const STANDALONE_BOOT_TIMEOUT_MS = 90_000;
export const SERP_BOOT_TIMEOUT_MS = 90_000;
+2 -1
View File
@@ -13,6 +13,7 @@ import { ServiceBadApproachError, ServiceBadAttemptError } from '../errors';
import { parseJSONText } from 'civkit/vectorize';
import { retryWith } from 'civkit/decorators';
import { ProxyProviderService } from '../../shared/services/proxy-provider';
import { SERP_BOOT_TIMEOUT_MS } from '../boot-timeouts';
@singleton()
export class GoogleSERP extends AsyncService {
@@ -31,7 +32,7 @@ export class GoogleSERP extends AsyncService {
}
override async init() {
await this.dependencyReady();
await this.dependencyReady(SERP_BOOT_TIMEOUT_MS);
this.emit('ready');
}
+3 -1
View File
@@ -19,6 +19,7 @@ import { GlobalLogger } from '../services/logger';
import { AsyncLocalContext } from '../services/async-context';
import finalizer, { Finalizer } from '../services/finalizer';
import koaCompress from 'koa-compress';
import { STANDALONE_BOOT_TIMEOUT_MS } from '../services/boot-timeouts';
@singleton()
export class CrawlStandAloneServer extends KoaServer {
@@ -51,6 +52,7 @@ export class CrawlStandAloneServer extends KoaServer {
override async init() {
await this.walkForAssets();
await this.dependencyReady(STANDALONE_BOOT_TIMEOUT_MS);
await super.init();
}
@@ -166,4 +168,4 @@ if (process.env.NODE_ENV?.includes('dry-run')) {
instance.serviceReady().then(() => finalizer.terminate());
} else {
instance.serviceReady().then((s) => s.h2c().listen(parseInt(process.env.PORT || '') || 3000));
}
}
+2 -1
View File
@@ -19,6 +19,7 @@ import { GlobalLogger } from '../services/logger';
import { AsyncLocalContext } from '../services/async-context';
import finalizer, { Finalizer } from '../services/finalizer';
import koaCompress from 'koa-compress';
import { STANDALONE_BOOT_TIMEOUT_MS } from '../services/boot-timeouts';
@singleton()
export class SearchStandAloneServer extends KoaServer {
@@ -51,7 +52,7 @@ export class SearchStandAloneServer extends KoaServer {
override async init() {
await this.walkForAssets();
await this.dependencyReady();
await this.dependencyReady(STANDALONE_BOOT_TIMEOUT_MS);
for (const [k, v] of this.registry.conf.entries()) {
if (v.tags?.includes('crawl')) {
+2 -1
View File
@@ -20,6 +20,7 @@ import finalizer, { Finalizer } from '../services/finalizer';
import { SerpHost } from '../api/serp';
import koaCompress from 'koa-compress';
import { getAuditionMiddleware } from '../shared/utils/audition';
import { STANDALONE_BOOT_TIMEOUT_MS } from '../services/boot-timeouts';
@singleton()
export class SERPStandAloneServer extends KoaServer {
@@ -52,7 +53,7 @@ export class SERPStandAloneServer extends KoaServer {
override async init() {
await this.walkForAssets();
await this.dependencyReady();
await this.dependencyReady(STANDALONE_BOOT_TIMEOUT_MS);
for (const [k, v] of this.registry.conf.entries()) {
if (v.tags?.includes('crawl')) {
@@ -74,3 +74,24 @@ test('standalone URL validation can allow Docker DNS rewrite ranges behind an ex
assert.match(miscService, /198\.18/);
assert.match(miscService, /fd00::/i);
});
test('standalone services use extended boot timeouts for cold starts', () => {
const bootTimeouts = readProjectFile('src/services/boot-timeouts.ts');
const crawlHost = readProjectFile('src/api/crawler.ts');
const searcherHost = readProjectFile('src/api/searcher.ts');
const serpHost = readProjectFile('src/api/serp.ts');
const crawlServer = readProjectFile('src/stand-alone/crawl.ts');
const searchServer = readProjectFile('src/stand-alone/search.ts');
const serpServer = readProjectFile('src/stand-alone/serp.ts');
const googleSerp = readProjectFile('src/services/serp/google.ts');
assert.match(bootTimeouts, /STANDALONE_BOOT_TIMEOUT_MS = 90_000/);
assert.match(bootTimeouts, /SERP_BOOT_TIMEOUT_MS = 90_000/);
assert.match(crawlHost, /dependencyReady\(STANDALONE_BOOT_TIMEOUT_MS\)/);
assert.match(searcherHost, /dependencyReady\(STANDALONE_BOOT_TIMEOUT_MS\)/);
assert.match(serpHost, /dependencyReady\(STANDALONE_BOOT_TIMEOUT_MS\)/);
assert.match(crawlServer, /dependencyReady\(STANDALONE_BOOT_TIMEOUT_MS\)/);
assert.match(searchServer, /dependencyReady\(STANDALONE_BOOT_TIMEOUT_MS\)/);
assert.match(serpServer, /dependencyReady\(STANDALONE_BOOT_TIMEOUT_MS\)/);
assert.match(googleSerp, /dependencyReady\(SERP_BOOT_TIMEOUT_MS\)/);
});