Trae/solo agent 1 wl ux n#468
Conversation
- Add requestTimeout: 300_000 (5min) to handle large backup imports - Add keepAliveTimeout: 65_000 for connection management - Fixes 30s timeout issue on /api/settings/backup/import endpoint Co-authored-by: monkeycode-ai <monkeycode-ai@chaitin.com>
- 新增 batchQueryAll 分页查询工具函数,每次最多加载10条记录 - 新增 batchInsertHelper 分批插入工具函数,每批最多插入10条记录 - 重构 collectCurrentRuntimeStateSnapshot() 使用分页加载 proxyLogs 和 checkinLogs - 重构 importAccountsSection() 将所有表的插入改为批量执行 - 解决大数据量导入时内存暴涨导致的502问题 Co-authored-by: monkeycode-ai <monkeycode-ai@chaitin.com>
Co-authored-by: traeagent <traeagent@users.noreply.github.com>
Co-authored-by: traeagent <traeagent@users.noreply.github.com>
Co-authored-by: traeagent <traeagent@users.noreply.github.com>
Co-authored-by: traeagent <traeagent@users.noreply.github.com>
feat: 优化超时处理并提升备份服务性能
Co-authored-by: traeagent <traeagent@users.noreply.github.com>
Co-authored-by: traeagent <traeagent@users.noreply.github.com>
Co-authored-by: traeagent <traeagent@users.noreply.github.com>
Co-authored-by: traeagent <traeagent@users.noreply.github.com>
Co-authored-by: traeagent <traeagent@users.noreply.github.com>
Co-authored-by: traeagent <traeagent@users.noreply.github.com>
Co-authored-by: traeagent <traeagent@users.noreply.github.com>
📝 WalkthroughWalkthroughComprehensive optimization initiative addressing timeout configuration across all system layers, implementing database batch operation utilities for large-scale data handling, refactoring backup import logic to improve error handling and data integrity, and enhancing model availability probe with site-level concurrency control and dynamic prompt selection. Includes extensive specifications, test plans, and new test infrastructure. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
⚔️ Resolve merge conflicts
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 15
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
src/server/services/backupService.ts (3)
1651-1711:⚠️ Potential issue | 🔴 CriticalThe smart sync still keys child rows by imported numeric IDs.
Sites are matched by identity key, but every child table here still persists the backup's raw
siteId/accountId/tokenId/routeIdand decides update-vs-insert from raw IDs. If the backup was created on another database, the same numeric IDs can refer to different entities or not exist at all. That can overwrite unrelated rows, point children at the wrong parents, or trip FK/unique constraints as soon as IDs diverge. This path needs imported-identity → persisted-ID remapping before inserting or updating children.Also applies to: 1714-1882
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/server/services/backupService.ts` around lines 1651 - 1711, The code updates/inserts sites by identity (buildSiteIdentityKey) but then continues to persist child rows using the backup's raw numeric IDs (e.g., section.siteApiEndpoints.map(... siteId: row.siteId ...)), which can point to wrong parents; fix by building an importedId→persistedId map as you process each site (when checking existingSite and after tx.insert or tx.update of schema.sites capture the actual persisted id), store mapping keyed by the backup site.id, then before calling batchInsertHelper (and for other child blocks dealing with accountId/tokenId/routeId) replace row.siteId (and other parent ids) with the mapped persisted id (or resolve to a newly inserted id) and use that mapped id for updates/inserts and FK checks so children reference the correct local records.
1476-1498:⚠️ Potential issue | 🟠 MajorThis coercion now swallows legacy account backups before the converter runs.
detectAccountsSection()callscoerceAccountsSection(data.accounts)beforebuildAllApiHubV2AccountsSection(). After this change, any nested object with a non-emptyaccountsarray is treated as a nativeAccountsBackupSection, even when those rows are still legacysite_url/account_infoshapes. That bypasses the legacy converter and feeds malformed rows intoimportAccountsSection().🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/server/services/backupService.ts` around lines 1476 - 1498, The coercion in coerceAccountsSection() is too permissive and is treating legacy nested objects with a non-empty accounts array as a v2 AccountsBackupSection, which bypasses the legacy converter; update the coercion logic (used by detectAccountsSection(), coerceAccountsSection(), and before buildAllApiHubV2AccountsSection()/importAccountsSection()) to perform a strict structural check for v2 account rows (e.g., require v2-specific fields) and explicitly reject or return undefined for objects that contain legacy-only keys like site_url or account_info so the legacy converter (buildAllApiHubV2AccountsSection()) still runs instead of swallowing legacy data.
1931-2116:⚠️ Potential issue | 🔴 CriticalThese runtime tables are appended back into the live DB instead of being replaced or merged.
runtimeStateis captured from the current database, thenmodelAvailability,tokenModelAvailability,siteAnnouncements,downstreamApiKeys,proxyLogs, andcheckinLogsare inserted again without clearing or upserting existing rows. On any import into a populated DB, the log inserts reuse existingids and will hit PK conflicts; the other tables either duplicate stale state or leave rows that the backup removed. The long-running tests hide this because they start from an empty database.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/server/services/backupService.ts` around lines 1931 - 2116, The runtime tables (runtimeState.modelAvailability, tokenAvailability, siteAnnouncements, downstreamApiKeys, proxyLogs, checkinLogs) are being blindly inserted back into the live DB causing duplicates and PK conflicts; update the backup import to either (a) remove existing runtime rows for the affected scope then insert, or (b) perform upserts/ON CONFLICT merges so existing rows are replaced/merged. Concretely, in the transaction blocks that write to schema.modelAvailability, schema.tokenModelAvailability, schema.siteAnnouncements, schema.downstreamApiKeys, schema.proxyLogs and schema.checkinLogs, replace the plain batchInsertHelper/tx.insert calls with logic that either deletes matching rows first (using importedIndexes lookups to limit scope) or uses an upsert API (onConflict/merge) to handle primary-key conflicts and to ensure manual entries (isManual flag) are preserved/overwritten as intended; also ensure proxyLogs preserves unique id handling (do not reuse ids into an occupied DB) by generating new ids or using upsert semantics.
🧹 Nitpick comments (2)
src/server/services/runtimeModelProbe.ts (1)
81-105: Prune per-site prompt state.
sitePromptUsageis process-global and never evicted, so a long-lived worker keeps oneSetper site forever. A small TTL/LRU cache, or a rolling index scoped to the current probe window, would avoid unbounded growth.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/server/services/runtimeModelProbe.ts` around lines 81 - 105, The sitePromptUsage Map in this file (used by getRandomPrompt and populated with selected prompts from PROBE_PROMPTS) grows unbounded because entries are never evicted; replace it with a bounded cache or TTL/LRU (or implement a rolling index per-site keyed by siteId) to prune old site entries: update references to sitePromptUsage and getRandomPrompt to use the new cache API (evict least-recently-used or expire entries after the probe window) and ensure you still track per-site used prompts set semantics and reset behavior when all prompts are exhausted.src/server/routes/api/settings.ts (1)
1839-1861: Extract the backup-import job body out of the route.Lines 1852-1859 make this handler own the queued workflow plus its side effects (
applyImportedSettingToRuntimeandreloadBackupWebdavScheduler). Please move that logic into a service/helper and keep the route limited to request parsing and HTTP response mapping.As per coding guidelines, "Route files in
src/server/routes/**are adapters, not owners. They may register Fastify endpoints, parse request context, and delegate but must not own protocol conversion, retry policy, stream lifecycle, billing, or persistence."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/server/routes/api/settings.ts` around lines 1839 - 1861, The route currently embeds the background job body and side effects (calling importBackup, applyImportedSettingToRuntime, and reloadBackupWebdavScheduler) inside the startBackgroundTask callback; extract that logic into a dedicated service function (e.g., performBackupImport or createBackupImportJob) that accepts the parsed backupData and returns the import result, moves the loop that calls applyImportedSettingToRuntime and the conditional reloadBackupWebdavScheduler call into that service, and export/import that helper into the route; then change the startBackgroundTask call in the route to pass the service function (or a thin wrapper that just invokes it) so the route only handles request parsing and delegates the queued work to the new service while preserving the task metadata (type/title/dedupeKey/notifyOnFailure/successMessage/failureMessage) and return value behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@CODE_WIKI.md`:
- Around line 35-46: Replace all file:///workspace URI links in CODE_WIKI.md
with repository-relative paths so they work on GitHub and in published docs;
update the entries referencing index.ts, config.ts, db/, middleware/, routes/,
services/, transformers/, proxy-core/ and the src/web files (main.tsx, App.tsx)
to use ./src/server/index.ts, ./src/server/config.ts, ./src/server/... and
./src/web/main.tsx, ./src/web/App.tsx (or the appropriate relative path)
everywhere instead of file:///workspace/...; ensure markdown link syntax remains
valid and run a quick search/replace for the file:///workspace prefix to catch
all occurrences.
In `@docs/configuration.md`:
- Line 388: The Docker image reference is inconsistent: "image:
metapi/metapi:latest" appears here while CODE_WIKI.md uses
"1467078763/metapi:latest"; update the occurrence of "image:
metapi/metapi:latest" to the canonical "1467078763/metapi:latest" (or vice versa
if your canonical choice is the other string) so both documents use the exact
same image tag; ensure you change the exact token "image: metapi/metapi:latest"
to match the canonical image reference and run a quick grep to verify no other
mismatches remain.
In `@src/server/db/index.ts`:
- Around line 65-68: The DB pool is using long acquisition timeouts and an
unbounded queue (connectTimeout, connectionTimeoutMillis set to 300000 and
queueLimit: 0), which lets requests pile up during DB pressure; tighten those
acquisition timeouts, set a bounded queueLimit (e.g., non-zero small value), and
add query-level timeouts instead of relying on acquisition timeouts—either by
setting Postgres session statement_timeout or MySQL per-connection query timeout
and/or enforcing timeouts at the call sites where queries are executed.
In `@src/server/db/longRunningOperations.test.ts`:
- Around line 20-35: The test's simulated workload inside
simulateLongRunningOperation (the for-loops inserting into test_long_running and
the intermittent setTimeout delays) doesn't respect the requested seconds
argument, so change simulateLongRunningOperation to measure a real deadline:
record the start time, perform the batched inserts as currently implemented but
after the loop (or between batches) compute remaining = seconds - elapsed and
await a single sleep for Math.max(0, remaining * 1000) before returning; ensure
any per-batch short sleeps remain but do not replace the final wait so the
function reliably runs for at least the requested duration.
- Around line 119-144: These tests call simulateLongRunningOperation (and then
db.execute) for 10–60+ seconds but lack Vitest timeouts, so add explicit
timeouts: either call vi.setTimeout(70000) at the top of this test file/suite or
set per-test timeouts for the three tests (the "should handle operations lasting
more than 60 seconds without timeout", "should handle multiple long-running
operations sequentially", and "should maintain database connection after long
operations") so each allows at least 70_000 ms; ensure the timeout change
surrounds tests that call simulateLongRunningOperation and db.execute so they
won't be aborted.
In `@src/server/routes/api/settings.ts`:
- Around line 1839-1869: The current use of a static dedupeKey ('backup-import')
in startBackgroundTask causes different uploads to reuse an in-flight import
(startBackgroundTask, dedupeKey 'backup-import'), losing the new backupData;
change the logic to either compute a fingerprint of backupData and include it in
the dedupeKey (e.g., 'backup-import:'+fingerprint) or detect when
startBackgroundTask returns reused=true and compare the payload fingerprint to
the running task: if they differ, reject the request with HTTP 409 via
reply.code(409). Ensure the response still returns jobId/task.id when
legitimately reused by identical payloads and only proceed to call
importBackup/reloadBackupWebdavScheduler when a new task is created.
In `@src/server/services/backupService.longRunning.test.ts`:
- Around line 1-2: The test imports join from the wrong module; replace the
import of join from 'node:fs' with an import from 'node:path' so the symbol used
in this file (join) is imported from the correct module (change the import
statement that currently destructures mkdtempSync, rmSync, join to instead
import mkdtempSync and rmSync from 'node:fs' and import join from 'node:path' or
update the combined import to reference 'node:path' for join).
In `@src/server/services/backupService.ts`:
- Around line 1514-1540: coercePreferencesSection currently returns { settings:
[] } for any record even when settings is missing; update it so it only returns
a PreferencesBackupSection when a valid settings payload exists: (1) check that
input.settings is present and is either an array or an object — if not, return
null; (2) keep the existing array/object parsing and filtering (using
EXCLUDED_SETTING_KEYS and key trimming) but after building settings, if
settings.length === 0 return null; otherwise return { settings }. Reference the
function coercePreferencesSection and the variables settingsRaw,
EXCLUDED_SETTING_KEYS to locate where to add the presence checks and the final
empty-array guard.
- Around line 2121-2124: The code is overwriting the per-import "new" counters
(stats.newSites, stats.newAccounts, stats.newTokens) with section totals;
instead of assigning to those new* fields, store the section totals in separate
fields (e.g., stats.totalSites, stats.totalAccounts, stats.totalTokens) or
remove the overwrite entirely so the incremented new* counters remain unchanged;
update the lines that currently set stats.newSites = section.sites.length,
stats.newAccounts = section.accounts.length, and stats.newTokens =
section.accountTokens.length to use the new total* field names or delete them as
appropriate.
- Around line 24-29: The keyset pagination loop in batchQueryAll()/the while
(hasMore) block builds queries using gt(table.id, lastId) but does not include
deterministic ordering; add an explicit orderBy(asc(table.id)) to the query
constructed in both branches (the initial dbInstance.select().from(table) and
the dbInstance.select().from(table).where(gt(table.id, lastId))) before applying
.limit(batchSize). This ensures rows are consistently ordered by table.id so the
lastId cursor does not skip or duplicate rows during pagination.
In `@src/server/services/modelAvailabilityProbeService.ts`:
- Around line 538-541: The test reset helper
__resetModelAvailabilityProbeExecutionStateForTests currently clears
probeAccountLeases and probeSiteLeases but leaves siteProbeRecords intact,
causing per-site throttle state to leak between tests; update that function to
also clear the siteProbeRecords data structure (reset its entries or call its
clear method) so tests start with a fresh frequency limiter state alongside
probeAccountLeases and probeSiteLeases.
- Around line 345-353: The site quota is being consumed by
checkSiteProbeFrequency(siteId) even when the account lease isn't acquired;
change the flow so the lease is acquired first
(tryAcquireProbeAccountLease(accountId, siteId)) and only then call
checkSiteProbeFrequency(siteId) so that siteProbeRecords mutation happens only
for probes that actually proceed, or alternatively modify
checkSiteProbeFrequency to be non-mutating (or to return a decision without
updating siteProbeRecords) and perform the mutation only after a successful
tryAcquireProbeAccountLease; update the logic around
buildSkippedProbeAccountResult to reflect the correct skip reason when the lease
cannot be acquired.
In `@test-long-running-backup.js`:
- Around line 118-121: The test harness imports TypeScript sources from plain
JavaScript which will fail under Node — either convert the harness to TypeScript
or point imports at compiled JS: rename the file to test-long-running-backup.ts
and run via tsx so imports of migrate, db/index, backupService (importBackup,
exportBackup) and backgroundTaskService (startBackgroundTask) work, or keep the
.js harness and change those import paths to the built JS artifacts (e.g., dist/
versions) so db, schema, importBackup, exportBackup and startBackgroundTask
resolve at runtime.
In `@test-long-running.js`:
- Around line 36-54: The current for-loop based insertion uses only intermittent
1s sleeps and finishes before the requested runtime; change to a deadline-driven
loop using startTime (or compute endTime = startTime + seconds*1000) so inserts
continue until Date.now() >= endTime, or after the existing batch loop add an
await sleep for the remaining time before checking duration >= seconds; update
the insertion block that calls db.execute and the progress logging that uses
startTime (and the intermittent setTimeout sleep) to operate inside a
while(Date.now() < endTime) loop (or equivalent deadline check) and keep the
periodic await new Promise(resolve => setTimeout(resolve, 1000)) between batches
so the script actually runs for the requested duration.
- Around line 84-120: runTests currently only logs PASS/FAIL but never sets a
non-zero exit code; update runTests to track failures (e.g., a boolean like
hasFailure) and set it to true when test1Result is falsy, when any entry in
results is falsy, or when the db.execute check throws; at the end of runTests,
if hasFailure is true call process.exit(1) (or throw an Error) so CI fails;
additionally change the top-level caller (the runTests().catch(...)) to call
process.exit(1) in its catch handler to ensure unexpected exceptions also exit
non-zero. Use the function/variables runTests, test1Result, results, and
db.execute to locate where to add the checks and exit logic.
---
Outside diff comments:
In `@src/server/services/backupService.ts`:
- Around line 1651-1711: The code updates/inserts sites by identity
(buildSiteIdentityKey) but then continues to persist child rows using the
backup's raw numeric IDs (e.g., section.siteApiEndpoints.map(... siteId:
row.siteId ...)), which can point to wrong parents; fix by building an
importedId→persistedId map as you process each site (when checking existingSite
and after tx.insert or tx.update of schema.sites capture the actual persisted
id), store mapping keyed by the backup site.id, then before calling
batchInsertHelper (and for other child blocks dealing with
accountId/tokenId/routeId) replace row.siteId (and other parent ids) with the
mapped persisted id (or resolve to a newly inserted id) and use that mapped id
for updates/inserts and FK checks so children reference the correct local
records.
- Around line 1476-1498: The coercion in coerceAccountsSection() is too
permissive and is treating legacy nested objects with a non-empty accounts array
as a v2 AccountsBackupSection, which bypasses the legacy converter; update the
coercion logic (used by detectAccountsSection(), coerceAccountsSection(), and
before buildAllApiHubV2AccountsSection()/importAccountsSection()) to perform a
strict structural check for v2 account rows (e.g., require v2-specific fields)
and explicitly reject or return undefined for objects that contain legacy-only
keys like site_url or account_info so the legacy converter
(buildAllApiHubV2AccountsSection()) still runs instead of swallowing legacy
data.
- Around line 1931-2116: The runtime tables (runtimeState.modelAvailability,
tokenAvailability, siteAnnouncements, downstreamApiKeys, proxyLogs, checkinLogs)
are being blindly inserted back into the live DB causing duplicates and PK
conflicts; update the backup import to either (a) remove existing runtime rows
for the affected scope then insert, or (b) perform upserts/ON CONFLICT merges so
existing rows are replaced/merged. Concretely, in the transaction blocks that
write to schema.modelAvailability, schema.tokenModelAvailability,
schema.siteAnnouncements, schema.downstreamApiKeys, schema.proxyLogs and
schema.checkinLogs, replace the plain batchInsertHelper/tx.insert calls with
logic that either deletes matching rows first (using importedIndexes lookups to
limit scope) or uses an upsert API (onConflict/merge) to handle primary-key
conflicts and to ensure manual entries (isManual flag) are preserved/overwritten
as intended; also ensure proxyLogs preserves unique id handling (do not reuse
ids into an occupied DB) by generating new ids or using upsert semantics.
---
Nitpick comments:
In `@src/server/routes/api/settings.ts`:
- Around line 1839-1861: The route currently embeds the background job body and
side effects (calling importBackup, applyImportedSettingToRuntime, and
reloadBackupWebdavScheduler) inside the startBackgroundTask callback; extract
that logic into a dedicated service function (e.g., performBackupImport or
createBackupImportJob) that accepts the parsed backupData and returns the import
result, moves the loop that calls applyImportedSettingToRuntime and the
conditional reloadBackupWebdavScheduler call into that service, and
export/import that helper into the route; then change the startBackgroundTask
call in the route to pass the service function (or a thin wrapper that just
invokes it) so the route only handles request parsing and delegates the queued
work to the new service while preserving the task metadata
(type/title/dedupeKey/notifyOnFailure/successMessage/failureMessage) and return
value behavior.
In `@src/server/services/runtimeModelProbe.ts`:
- Around line 81-105: The sitePromptUsage Map in this file (used by
getRandomPrompt and populated with selected prompts from PROBE_PROMPTS) grows
unbounded because entries are never evicted; replace it with a bounded cache or
TTL/LRU (or implement a rolling index per-site keyed by siteId) to prune old
site entries: update references to sitePromptUsage and getRandomPrompt to use
the new cache API (evict least-recently-used or expire entries after the probe
window) and ensure you still track per-site used prompts set semantics and reset
behavior when all prompts are exhausted.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 5dd6408c-a676-4bc5-a98c-d6aaa60fa80d
📒 Files selected for processing (28)
.monkeycode/specs/db-batch-optimization/tasklist.md.trae/specs/database-timeout-optimization/checklist.md.trae/specs/database-timeout-optimization/spec.md.trae/specs/database-timeout-optimization/tasks.md.trae/specs/import-issue-analysis/checklist.md.trae/specs/import-issue-analysis/spec.md.trae/specs/import-issue-analysis/tasks.md.trae/specs/model-probe-optimization/checklist.md.trae/specs/model-probe-optimization/spec.md.trae/specs/model-probe-optimization/tasks.md.trae/specs/timeout-optimization/checklist.md.trae/specs/timeout-optimization/spec.md.trae/specs/timeout-optimization/tasks.mdCODE_WIKI.mdLONG_RUNNING_OPERATIONS_TEST_PLAN.mddocs/configuration.mddocs/operations.mdsrc/server/config.tssrc/server/db/index.tssrc/server/db/longRunningOperations.test.tssrc/server/routes/api/settings.tssrc/server/services/backupService.longRunning.test.tssrc/server/services/backupService.tssrc/server/services/modelAvailabilityProbeService.tssrc/server/services/runtimeModelProbe.tssrc/web/api.tstest-long-running-backup.jstest-long-running.js
| - [index.ts](file:///workspace/src/server/index.ts) - Fastify 服务启动和初始化 | ||
| - [config.ts](file:///workspace/src/server/config.ts) - 环境变量解析和配置 | ||
| - `db/` - 数据库 Schema、连接和迁移 | ||
| - `middleware/` - 认证等中间件 | ||
| - `routes/` - API 路由和代理路由 | ||
| - `services/` - 业务服务层 | ||
| - `transformers/` - 协议转换层 | ||
| - `proxy-core/` - 代理核心逻辑 | ||
|
|
||
| #### `src/web/` - Web 前端 | ||
| - [main.tsx](file:///workspace/src/web/main.tsx) - Vite 入口 | ||
| - [App.tsx](file:///workspace/src/web/App.tsx) - 路由和页面装配 |
There was a problem hiding this comment.
Use repository-relative links instead of file:///workspace URIs.
Line 35 and similar entries use local filesystem links, which will break in GitHub and published docs. Please switch to repo-relative paths (e.g., ./src/server/index.ts).
🔧 Suggested documentation fix pattern
-- [index.ts](file:///workspace/src/server/index.ts) - Fastify 服务启动和初始化
-- [config.ts](file:///workspace/src/server/config.ts) - 环境变量解析和配置
+- [index.ts](./src/server/index.ts) - Fastify 服务启动和初始化
+- [config.ts](./src/server/config.ts) - 环境变量解析和配置🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@CODE_WIKI.md` around lines 35 - 46, Replace all file:///workspace URI links
in CODE_WIKI.md with repository-relative paths so they work on GitHub and in
published docs; update the entries referencing index.ts, config.ts, db/,
middleware/, routes/, services/, transformers/, proxy-core/ and the src/web
files (main.tsx, App.tsx) to use ./src/server/index.ts, ./src/server/config.ts,
./src/server/... and ./src/web/main.tsx, ./src/web/App.tsx (or the appropriate
relative path) everywhere instead of file:///workspace/...; ensure markdown link
syntax remains valid and run a quick search/replace for the file:///workspace
prefix to catch all occurrences.
| # docker-compose.yml 示例 | ||
| services: | ||
| metapi: | ||
| image: metapi/metapi:latest |
There was a problem hiding this comment.
Unify Docker image naming across docs.
Line 388 uses metapi/metapi:latest, but CODE_WIKI.md (Line 391) uses 1467078763/metapi:latest. Please standardize to one canonical image reference across documents to avoid deployment confusion.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/configuration.md` at line 388, The Docker image reference is
inconsistent: "image: metapi/metapi:latest" appears here while CODE_WIKI.md uses
"1467078763/metapi:latest"; update the occurrence of "image:
metapi/metapi:latest" to the canonical "1467078763/metapi:latest" (or vice versa
if your canonical choice is the other string) so both documents use the exact
same image tag; ensure you change the exact token "image: metapi/metapi:latest"
to match the canonical image reference and run a quick grep to verify no other
mismatches remain.
| waitForConnections: true, | ||
| connectionLimit: 10, | ||
| queueLimit: 0, | ||
| connectTimeout: 300000, // 5 minutes |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify whether codebase currently relies on connection-acquire timeout knobs
# without corresponding query/operation timeout controls.
rg -n --type=ts -C2 \
"connectTimeout|connectionTimeoutMillis|waitForConnections|queueLimit|statement_timeout|query_timeout|max_execution_time|AbortSignal|timeout"Repository: cita-777/metapi
Length of output: 50371
🌐 Web query:
In mysql2 PoolOptions, does connectTimeoutlimit query execution time or only connection establishment? In node-postgres PoolConfig, what is the difference betweenconnectionTimeoutMillis and query/statement timeout controls?
💡 Result:
mysql2 PoolOptions.connectTimeout
connectTimeout only limits how long the driver will wait for the initial connection/handshake to be established—it is not a cap on how long a SQL query is allowed to run. This is the same semantics as the mysqljs/mysql option MySQL2 is compatible with (“timeout … during the initial connection”).[1]
In pooled usage, note this is separate from “waiting to get a connection from the pool” time; mysqljs/mysql distinguishes pool acquisition timeout (acquireTimeout) from connectTimeout because acquiring a pooled connection may not involve creating a new TCP connection.[1]
node-postgres PoolConfig.connectionTimeoutMillis vs query/statement timeouts
connectionTimeoutMillis(PoolConfig): how long the pool will wait when connecting a new client (or, practically, when trying to satisfypool.connect()by creating a new backend connection) before erroring. It’s about getting a connection, not about executing SQL.[2]- Query/statement timeout controls (Client/connection-level):
statement_timeout: server-side limit—PostgreSQL cancels a statement that runs too long.[3][4]query_timeout: client-side (node-postgres) limit—how long the client’s query call is allowed to wait before timing out (it’s distinct fromstatement_timeout).[3]
So: connectionTimeoutMillis protects you from hangs/pressure while obtaining/establishing connections; statement_timeout / query_timeout protect you from long-running queries (server-side vs client-side behavior respectively).[2][3][4]
Sources: [1] [2] [3] [4]
🏁 Script executed:
# Check for query-level timeout configurations in database-related code
rg -n --type=ts "statement_timeout|query_timeout|maxQueryTime|requestTimeout|timeout.*query|query.*timeout" src/server/db/ src/server/routes/api/ --max-count=20Repository: cita-777/metapi
Length of output: 41
🏁 Script executed:
# Check the actual database operations to see if timeouts are applied at call sites
rg -n --type=ts "db\.(select|insert|update|delete|query|execute)" src/server/routes/ src/server/services/ -A 2 | head -100Repository: cita-777/metapi
Length of output: 8210
🏁 Script executed:
# Look for timeout patterns in DB query execution and any long-running operation handling
rg -n --type=ts "longRunning|executeQuery|withTimeout" src/server/db/ -A 3 | head -50Repository: cita-777/metapi
Length of output: 41
🏁 Script executed:
# Check the actual content of the longRunningOperations test that was mentioned
cat -n src/server/db/longRunningOperations.test.ts | head -130Repository: cita-777/metapi
Length of output: 4946
🏁 Script executed:
# Check the complete db/index.ts file to see all pool configurations
cat -n src/server/db/index.ts | head -100Repository: cita-777/metapi
Length of output: 4009
🏁 Script executed:
# Search for any statement or query timeout settings applied at the query level
rg -n --type=ts "statement_timeout|queryTimeout|query_timeout|SET.*timeout" src/server/Repository: cita-777/metapi
Length of output: 41
Connection-acquire timeouts are being used as if they were execution timeouts.
Lines 68 and 84 set connectTimeout (MySQL) and connectionTimeoutMillis (Postgres) to 5 minutes, but these only protect connection establishment/acquisition, not long-running query duration. Combined with queueLimit: 0 (line 67), this allows unbounded queue buildup during database pressure or outages, prolonging request stalls rather than failing fast. No query-level timeout controls (statement_timeout for Postgres, connection-level query timeout for MySQL) are configured.
Consider setting a bounded queue limit and keeping acquisition timeouts tighter, while adding query/operation-level timeout controls at the call site or via database session settings.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/server/db/index.ts` around lines 65 - 68, The DB pool is using long
acquisition timeouts and an unbounded queue (connectTimeout,
connectionTimeoutMillis set to 300000 and queueLimit: 0), which lets requests
pile up during DB pressure; tighten those acquisition timeouts, set a bounded
queueLimit (e.g., non-zero small value), and add query-level timeouts instead of
relying on acquisition timeouts—either by setting Postgres session
statement_timeout or MySQL per-connection query timeout and/or enforcing
timeouts at the call sites where queries are executed.
| // 分批次插入数据,每批次1000条,共插入50000条 | ||
| for (let i = 0; i < 50; i++) { | ||
| const values = []; | ||
| for (let j = 0; j < 1000; j++) { | ||
| const data = `test_data_${i}_${j}_${Math.random()}`; | ||
| values.push(`('${data}')`); | ||
| } | ||
| await db.execute(` | ||
| INSERT INTO test_long_running (data) VALUES ${values.join(', ')} | ||
| `); | ||
|
|
||
| // 模拟处理时间 | ||
| if (i % 10 === 0) { | ||
| await new Promise(resolve => setTimeout(resolve, 1000)); | ||
| } | ||
| } |
There was a problem hiding this comment.
The simulated workload doesn't honor the requested duration.
Each branch only adds about 5 seconds of deliberate delay, so simulateLongRunningOperation(60) mostly measures machine speed instead of the seconds argument and will be flaky or fail on fast CI. Drive the helper from a real deadline, or sleep the remaining target time before returning.
Also applies to: 49-63, 76-90
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/server/db/longRunningOperations.test.ts` around lines 20 - 35, The test's
simulated workload inside simulateLongRunningOperation (the for-loops inserting
into test_long_running and the intermittent setTimeout delays) doesn't respect
the requested seconds argument, so change simulateLongRunningOperation to
measure a real deadline: record the start time, perform the batched inserts as
currently implemented but after the loop (or between batches) compute remaining
= seconds - elapsed and await a single sleep for Math.max(0, remaining * 1000)
before returning; ensure any per-batch short sleeps remain but do not replace
the final wait so the function reliably runs for at least the requested
duration.
| it('should handle operations lasting more than 60 seconds without timeout', async () => { | ||
| // 测试超过60秒的操作 | ||
| const completed = await simulateLongRunningOperation(60); | ||
| expect(completed).toBe(true); | ||
| }); | ||
|
|
||
| it('should handle multiple long-running operations sequentially', async () => { | ||
| // 测试多个长时间运行的操作 | ||
| const results = []; | ||
| for (let i = 0; i < 3; i++) { | ||
| const completed = await simulateLongRunningOperation(10); | ||
| results.push(completed); | ||
| } | ||
|
|
||
| // 所有操作都应该成功完成 | ||
| expect(results.every(r => r)).toBe(true); | ||
| }); | ||
|
|
||
| it('should maintain database connection after long operations', async () => { | ||
| // 执行长时间操作 | ||
| await simulateLongRunningOperation(30); | ||
|
|
||
| // 验证连接仍然可用 | ||
| const result = await db.execute('SELECT 1'); | ||
| expect(result).toBeDefined(); | ||
| }); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
fd -HI '^(vitest|vite)\.config\.(ts|mts|cts|js|mjs|cjs)$|^package\.json$' | while read -r file; do
echo "== $file =="
rg -n -C2 'testTimeout|hookTimeout|vitest|defineConfig' "$file" || true
doneRepository: cita-777/metapi
Length of output: 1776
🏁 Script executed:
cat -n vitest.config.tsRepository: cita-777/metapi
Length of output: 777
🏁 Script executed:
cat -n src/server/db/longRunningOperations.test.tsRepository: cita-777/metapi
Length of output: 5389
Add explicit timeouts to these Vitest test cases.
The test suite has no global testTimeout configured in vitest.config.ts, and the three test cases at lines 119-144 have no individual timeout overrides. With Vitest's default 10-second timeout, the tests expecting 10, 30, and 60+ seconds of operation will abort before assertions run. Set a suite or per-test timeout of at least 70 seconds (accounting for test overhead and database operations), or enable a global timeout in the Vitest config.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/server/db/longRunningOperations.test.ts` around lines 119 - 144, These
tests call simulateLongRunningOperation (and then db.execute) for 10–60+ seconds
but lack Vitest timeouts, so add explicit timeouts: either call
vi.setTimeout(70000) at the top of this test file/suite or set per-test timeouts
for the three tests (the "should handle operations lasting more than 60 seconds
without timeout", "should handle multiple long-running operations sequentially",
and "should maintain database connection after long operations") so each allows
at least 70_000 ms; ensure the timeout change surrounds tests that call
simulateLongRunningOperation and db.execute so they won't be aborted.
| if (!checkSiteProbeFrequency(siteId)) { | ||
| results.push(buildSkippedProbeAccountResult({ | ||
| accountId, | ||
| siteId: context.site.id, | ||
| message: 'model availability probe frequency limit exceeded for site', | ||
| })); | ||
| continue; | ||
| } | ||
| if (!tryAcquireProbeAccountLease(accountId, siteId)) { |
There was a problem hiding this comment.
Only consume the site quota after the lease is acquired.
checkSiteProbeFrequency(siteId) mutates siteProbeRecords, but it runs before tryAcquireProbeAccountLease(). Any skipped attempt caused by an existing account/site probe still burns the site's 5-minute budget, so duplicate/manual triggers can exhaust the quota without actually probing anything.
Suggested fix
- if (!checkSiteProbeFrequency(siteId)) {
- results.push(buildSkippedProbeAccountResult({
- accountId,
- siteId: context.site.id,
- message: 'model availability probe frequency limit exceeded for site',
- }));
- continue;
- }
if (!tryAcquireProbeAccountLease(accountId, siteId)) {
results.push(buildSkippedProbeAccountResult({
accountId,
siteId: context.site.id,
message: 'model availability probe already running for account or site',
}));
continue;
}
+ if (!checkSiteProbeFrequency(siteId)) {
+ releaseProbeAccountLease(accountId, siteId);
+ results.push(buildSkippedProbeAccountResult({
+ accountId,
+ siteId: context.site.id,
+ message: 'model availability probe frequency limit exceeded for site',
+ }));
+ continue;
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (!checkSiteProbeFrequency(siteId)) { | |
| results.push(buildSkippedProbeAccountResult({ | |
| accountId, | |
| siteId: context.site.id, | |
| message: 'model availability probe frequency limit exceeded for site', | |
| })); | |
| continue; | |
| } | |
| if (!tryAcquireProbeAccountLease(accountId, siteId)) { | |
| if (!tryAcquireProbeAccountLease(accountId, siteId)) { | |
| results.push(buildSkippedProbeAccountResult({ | |
| accountId, | |
| siteId: context.site.id, | |
| message: 'model availability probe already running for account or site', | |
| })); | |
| continue; | |
| } | |
| if (!checkSiteProbeFrequency(siteId)) { | |
| releaseProbeAccountLease(accountId, siteId); | |
| results.push(buildSkippedProbeAccountResult({ | |
| accountId, | |
| siteId: context.site.id, | |
| message: 'model availability probe frequency limit exceeded for site', | |
| })); | |
| continue; | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/server/services/modelAvailabilityProbeService.ts` around lines 345 - 353,
The site quota is being consumed by checkSiteProbeFrequency(siteId) even when
the account lease isn't acquired; change the flow so the lease is acquired first
(tryAcquireProbeAccountLease(accountId, siteId)) and only then call
checkSiteProbeFrequency(siteId) so that siteProbeRecords mutation happens only
for probes that actually proceed, or alternatively modify
checkSiteProbeFrequency to be non-mutating (or to return a decision without
updating siteProbeRecords) and perform the mutation only after a successful
tryAcquireProbeAccountLease; update the logic around
buildSkippedProbeAccountResult to reflect the correct skip reason when the lease
cannot be acquired.
| export function __resetModelAvailabilityProbeExecutionStateForTests(): void { | ||
| probeAccountLeases.clear(); | ||
| probeSiteLeases.clear(); | ||
| } |
There was a problem hiding this comment.
Reset the frequency limiter state in tests too.
__resetModelAvailabilityProbeExecutionStateForTests() clears the new lease sets, but siteProbeRecords survives. Tests that call the reset helper can still inherit the previous run's per-site throttle and fail nondeterministically.
Suggested fix
export function __resetModelAvailabilityProbeExecutionStateForTests(): void {
probeAccountLeases.clear();
probeSiteLeases.clear();
+ siteProbeRecords.clear();
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/server/services/modelAvailabilityProbeService.ts` around lines 538 - 541,
The test reset helper __resetModelAvailabilityProbeExecutionStateForTests
currently clears probeAccountLeases and probeSiteLeases but leaves
siteProbeRecords intact, causing per-site throttle state to leak between tests;
update that function to also clear the siteProbeRecords data structure (reset
its entries or call its clear method) so tests start with a fresh frequency
limiter state alongside probeAccountLeases and probeSiteLeases.
| await import('./src/server/db/migrate.ts'); | ||
| const { db, schema } = await import('./src/server/db/index.ts'); | ||
| const { importBackup, exportBackup } = await import('./src/server/services/backupService.ts'); | ||
| const { startBackgroundTask } = await import('./src/server/services/backgroundTaskService.ts'); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Locate the test harness file
find . -name "test-long-running-backup.js" -type fRepository: cita-777/metapi
Length of output: 88
🏁 Script executed:
# Check package.json for scripts and test setup
cat package.json | head -100Repository: cita-777/metapi
Length of output: 4638
🏁 Script executed:
# Look for build outputs or TypeScript loader setup
ls -la | grep -E "(tsconfig|esbuild|webpack|vitest|jest|tsx)"Repository: cita-777/metapi
Length of output: 485
🏁 Script executed:
# Search for how other test files are structured
fd -e test.ts -e test.js | head -20Repository: cita-777/metapi
Length of output: 815
🏁 Script executed:
# Read the test harness file to see its full structure and shebang
cat -n test-long-running-backup.jsRepository: cita-777/metapi
Length of output: 10924
🏁 Script executed:
# Check if there's any documentation or runner script
fd -e md -e txt | xargs grep -l "test-long-running-backup" 2>/dev/null | head -5Repository: cita-777/metapi
Length of output: 41
🏁 Script executed:
# Check build output directory structure
ls -la dist/server 2>/dev/null | head -20 || echo "dist/server not found or empty"Repository: cita-777/metapi
Length of output: 41
🏁 Script executed:
# Look for any tsconfig or loader configuration that might handle .ts imports in .js files
cat tsconfig.jsonRepository: cita-777/metapi
Length of output: 766
🏁 Script executed:
# Check if there's any loader configuration or environment setup
grep -r "loader\|--experimental\|NODE_OPTIONS" . --include="*.json" --include="*.js" --include="*.ts" --include=".env*" 2>/dev/null | head -20Repository: cita-777/metapi
Length of output: 790
🏁 Script executed:
# Check if build outputs exist in dist/server
ls -la dist/server 2>/dev/null | head -20 || echo "dist/server does not exist or is empty"Repository: cita-777/metapi
Length of output: 41
🏁 Script executed:
# Verify the actual issue: try to understand the intended execution path
grep -r "test-long-running" . --include="*.json" --include="*.md" --include="*.sh" 2>/dev/nullRepository: cita-777/metapi
Length of output: 41
This harness must either be renamed to .ts and run with tsx, or import built .js artifacts.
The file is plain JavaScript but imports TypeScript sources without a configured loader. Plain Node.js will fail with MODULE_NOT_FOUND at line 118. Either rename to test-long-running-backup.ts and run as tsx test-long-running-backup.ts, or update imports to use compiled JavaScript artifacts from dist/.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@test-long-running-backup.js` around lines 118 - 121, The test harness imports
TypeScript sources from plain JavaScript which will fail under Node — either
convert the harness to TypeScript or point imports at compiled JS: rename the
file to test-long-running-backup.ts and run via tsx so imports of migrate,
db/index, backupService (importBackup, exportBackup) and backgroundTaskService
(startBackgroundTask) work, or keep the .js harness and change those import
paths to the built JS artifacts (e.g., dist/ versions) so db, schema,
importBackup, exportBackup and startBackgroundTask resolve at runtime.
| // 分批次插入数据 | ||
| console.log('Inserting test data...'); | ||
| for (let i = 0; i < 50; i++) { | ||
| const values = []; | ||
| for (let j = 0; j < 1000; j++) { | ||
| const data = `test_data_${i}_${j}_${Math.random()}`; | ||
| values.push(`('${data}')`); | ||
| } | ||
|
|
||
| await db.execute(` | ||
| INSERT INTO test_long_running (data) VALUES ${values.join(', ')} | ||
| `); | ||
|
|
||
| if (i % 10 === 0) { | ||
| const currentTime = (Date.now() - startTime) / 1000; | ||
| console.log(`Progress: ${i * 1000} records inserted (${currentTime.toFixed(2)} seconds)`); | ||
| // 模拟处理时间 | ||
| await new Promise(resolve => setTimeout(resolve, 1000)); | ||
| } |
There was a problem hiding this comment.
This script doesn't actually run for the requested duration.
The loop only injects about 5 seconds of delay, so the 60/30/10 second targets depend on insert speed rather than the seconds argument. Use a deadline-based loop, or sleep the remaining time before evaluating duration >= seconds.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@test-long-running.js` around lines 36 - 54, The current for-loop based
insertion uses only intermittent 1s sleeps and finishes before the requested
runtime; change to a deadline-driven loop using startTime (or compute endTime =
startTime + seconds*1000) so inserts continue until Date.now() >= endTime, or
after the existing batch loop add an await sleep for the remaining time before
checking duration >= seconds; update the insertion block that calls db.execute
and the progress logging that uses startTime (and the intermittent setTimeout
sleep) to operate inside a while(Date.now() < endTime) loop (or equivalent
deadline check) and keep the periodic await new Promise(resolve =>
setTimeout(resolve, 1000)) between batches so the script actually runs for the
requested duration.
| async function runTests() { | ||
| console.log('=== Long Running Database Operations Test ==='); | ||
| console.log(`Database dialect: ${runtimeDbDialect}`); | ||
| console.log('==========================================='); | ||
|
|
||
| // 测试超过60秒的操作 | ||
| console.log('\nTest 1: Operation lasting more than 60 seconds'); | ||
| const test1Result = await simulateLongRunningOperation(60); | ||
|
|
||
| // 测试多个长时间运行的操作 | ||
| console.log('\nTest 2: Multiple long-running operations'); | ||
| const results = []; | ||
| for (let i = 0; i < 3; i++) { | ||
| console.log(`\nRunning operation ${i + 1}/3`); | ||
| const result = await simulateLongRunningOperation(10); | ||
| results.push(result); | ||
| } | ||
|
|
||
| // 测试数据库连接稳定性 | ||
| console.log('\nTest 3: Database connection stability'); | ||
| await simulateLongRunningOperation(30); | ||
| try { | ||
| const result = await db.execute('SELECT 1'); | ||
| console.log('✅ SUCCESS: Database connection is still stable'); | ||
| } catch (error) { | ||
| console.error('❌ ERROR: Database connection failed after long operation:', error); | ||
| } | ||
|
|
||
| console.log('\n=== Test Summary ==='); | ||
| console.log(`Test 1 (60+ seconds): ${test1Result ? 'PASS' : 'FAIL'}`); | ||
| console.log(`Test 2 (Multiple operations): ${results.every(r => r) ? 'PASS' : 'FAIL'}`); | ||
| console.log('Test 3 (Connection stability): Check logs above'); | ||
| console.log('=================='); | ||
| } | ||
|
|
||
| // 运行测试 | ||
| runTests().catch(console.error); |
There was a problem hiding this comment.
Fail the process when any scenario fails.
runTests() only prints PASS/FAIL; it never sets a non-zero exit code when Line 91, Line 98, or the connection-stability check fails. That makes the script unreliable for CI or automation because regressions still exit successfully.
🛠️ Suggested fix
async function runTests() {
console.log('=== Long Running Database Operations Test ===');
console.log(`Database dialect: ${runtimeDbDialect}`);
console.log('===========================================');
// 测试超过60秒的操作
console.log('\nTest 1: Operation lasting more than 60 seconds');
const test1Result = await simulateLongRunningOperation(60);
// 测试多个长时间运行的操作
console.log('\nTest 2: Multiple long-running operations');
const results = [];
for (let i = 0; i < 3; i++) {
console.log(`\nRunning operation ${i + 1}/3`);
const result = await simulateLongRunningOperation(10);
results.push(result);
}
+ const test2Passed = results.every((r) => r);
// 测试数据库连接稳定性
console.log('\nTest 3: Database connection stability');
- await simulateLongRunningOperation(30);
+ const test3OperationResult = await simulateLongRunningOperation(30);
+ let test3ConnectionOk = false;
try {
- const result = await db.execute('SELECT 1');
+ await db.execute('SELECT 1');
+ test3ConnectionOk = true;
console.log('✅ SUCCESS: Database connection is still stable');
} catch (error) {
console.error('❌ ERROR: Database connection failed after long operation:', error);
}
+ const test3Passed = test3OperationResult && test3ConnectionOk;
console.log('\n=== Test Summary ===');
console.log(`Test 1 (60+ seconds): ${test1Result ? 'PASS' : 'FAIL'}`);
- console.log(`Test 2 (Multiple operations): ${results.every(r => r) ? 'PASS' : 'FAIL'}`);
- console.log('Test 3 (Connection stability): Check logs above');
+ console.log(`Test 2 (Multiple operations): ${test2Passed ? 'PASS' : 'FAIL'}`);
+ console.log(`Test 3 (Connection stability): ${test3Passed ? 'PASS' : 'FAIL'}`);
console.log('==================');
+
+ if (!test1Result || !test2Passed || !test3Passed) {
+ process.exitCode = 1;
+ }
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| async function runTests() { | |
| console.log('=== Long Running Database Operations Test ==='); | |
| console.log(`Database dialect: ${runtimeDbDialect}`); | |
| console.log('==========================================='); | |
| // 测试超过60秒的操作 | |
| console.log('\nTest 1: Operation lasting more than 60 seconds'); | |
| const test1Result = await simulateLongRunningOperation(60); | |
| // 测试多个长时间运行的操作 | |
| console.log('\nTest 2: Multiple long-running operations'); | |
| const results = []; | |
| for (let i = 0; i < 3; i++) { | |
| console.log(`\nRunning operation ${i + 1}/3`); | |
| const result = await simulateLongRunningOperation(10); | |
| results.push(result); | |
| } | |
| // 测试数据库连接稳定性 | |
| console.log('\nTest 3: Database connection stability'); | |
| await simulateLongRunningOperation(30); | |
| try { | |
| const result = await db.execute('SELECT 1'); | |
| console.log('✅ SUCCESS: Database connection is still stable'); | |
| } catch (error) { | |
| console.error('❌ ERROR: Database connection failed after long operation:', error); | |
| } | |
| console.log('\n=== Test Summary ==='); | |
| console.log(`Test 1 (60+ seconds): ${test1Result ? 'PASS' : 'FAIL'}`); | |
| console.log(`Test 2 (Multiple operations): ${results.every(r => r) ? 'PASS' : 'FAIL'}`); | |
| console.log('Test 3 (Connection stability): Check logs above'); | |
| console.log('=================='); | |
| } | |
| // 运行测试 | |
| runTests().catch(console.error); | |
| async function runTests() { | |
| console.log('=== Long Running Database Operations Test ==='); | |
| console.log(`Database dialect: ${runtimeDbDialect}`); | |
| console.log('==========================================='); | |
| // 测试超过60秒的操作 | |
| console.log('\nTest 1: Operation lasting more than 60 seconds'); | |
| const test1Result = await simulateLongRunningOperation(60); | |
| // 测试多个长时间运行的操作 | |
| console.log('\nTest 2: Multiple long-running operations'); | |
| const results = []; | |
| for (let i = 0; i < 3; i++) { | |
| console.log(`\nRunning operation ${i + 1}/3`); | |
| const result = await simulateLongRunningOperation(10); | |
| results.push(result); | |
| } | |
| const test2Passed = results.every((r) => r); | |
| // 测试数据库连接稳定性 | |
| console.log('\nTest 3: Database connection stability'); | |
| const test3OperationResult = await simulateLongRunningOperation(30); | |
| let test3ConnectionOk = false; | |
| try { | |
| await db.execute('SELECT 1'); | |
| test3ConnectionOk = true; | |
| console.log('✅ SUCCESS: Database connection is still stable'); | |
| } catch (error) { | |
| console.error('❌ ERROR: Database connection failed after long operation:', error); | |
| } | |
| const test3Passed = test3OperationResult && test3ConnectionOk; | |
| console.log('\n=== Test Summary ==='); | |
| console.log(`Test 1 (60+ seconds): ${test1Result ? 'PASS' : 'FAIL'}`); | |
| console.log(`Test 2 (Multiple operations): ${test2Passed ? 'PASS' : 'FAIL'}`); | |
| console.log(`Test 3 (Connection stability): ${test3Passed ? 'PASS' : 'FAIL'}`); | |
| console.log('=================='); | |
| if (!test1Result || !test2Passed || !test3Passed) { | |
| process.exitCode = 1; | |
| } | |
| } | |
| // 运行测试 | |
| runTests().catch(console.error); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@test-long-running.js` around lines 84 - 120, runTests currently only logs
PASS/FAIL but never sets a non-zero exit code; update runTests to track failures
(e.g., a boolean like hasFailure) and set it to true when test1Result is falsy,
when any entry in results is falsy, or when the db.execute check throws; at the
end of runTests, if hasFailure is true call process.exit(1) (or throw an Error)
so CI fails; additionally change the top-level caller (the
runTests().catch(...)) to call process.exit(1) in its catch handler to ensure
unexpected exceptions also exit non-zero. Use the function/variables runTests,
test1Result, results, and db.execute to locate where to add the checks and exit
logic.
|
请处理 coderabbitai 给出的comments,如果 coderabbitai 给出了错误建议请在对应位置reply |
|
以及pr #460 是否需要关闭 |
Summary by CodeRabbit
Release Notes
New Features
Bug Fixes
Performance Improvements
Documentation