convex-backend ScenarioRunner 场景压测指南:由 LoadGenerator 驱动的客户端场景编写与扩展实战
数据库后端【免费下载链接】convex-backendThe open-source reactive database for app developers项目地址https://gitcode.com/gh_mirrors/co/convex-backend点击查看免费下载ScenarioRunner 是 convex-backend 仓库中与 LoadGenerator 配套的压测组件LoadGenerator 负责统筹调度与统计ScenarioRunner 则以真实 Convex 客户端WebSocket 同步的身份向被测后端发起 query / mutation / action / HTTP action 请求并回传指标与错误。本文以 scenario-runner/README.md 为主线结合其源码与 LoadGenerator 文档完整讲解运行机制、内置场景以及三步添加新场景的扩展方法读完即可在本地压测 Convex 后端并编写自己的压测场景。一、ScenarioRunner 在压测体系中的定位ScenarioRunner 本身不直接发起压测调度它被设计为 LoadGenerator 的执行端。在 crates/load_generator/README.md 中给出了完整的组件关系┌──────────┐ │ Stats │ │ Report │ ┌────────────────┐ ┌───────────────────┐ ┌───────────────┐ └──────────┘ │ │ │ │ queries, │ │ ▲ │ │ Scenarios │ │ mutations │ │ └─────────┬───│ LoadGenerator │──────────▶│ ScenarioRunner │──────────▶│ Backend │ ┌──────────────┐ │ │ │◀──────────│ │◀──────────│ │ │ │ │ │ │ Events │ │ │ │ │ Metrics │ │ └────────────────┘ └───────────────────┘ └───────────────┘ │ Collector │◀─┘ │(e.g. Datadog)│ │ │ └──────────────┘整个压测流水线分为四层LoadGeneratorRust负责预置provision一个 Convex 后端实例或对接一个已存在的实例将预定义或自定义的Scenario通过 WebSocket 下发给 ScenarioRunner收集事件、生成统计报告并可选择上报到 Datadog 等监控系统。ScenarioRunnerTypeScript/Node本文主角。它在127.0.0.1:load-generator-port上与 LoadGenerator 建立 WebSocket 连接index.ts接收场景消息、按配置的速率或线程数循环执行场景。Convex Client每个场景通过ConvexClient来自convex/browser向被测后端发起真实的同步请求最大程度还原真实客户端行为。Backend被测的 Convex 后端可以是 LoadGenerator 预置的实例也可以是自托管实例。二、启动方式先起 LoadGenerator再被拉起ScenarioRunner 的 README 明确指出不要直接手动启动 ScenarioRunner正确方式是运行 LoadGenerator由它来预置后端并用给定参数拉起 ScenarioRunner。2.1 LoadGenerator 帮助信息在仓库根目录执行cargo run -p load_generator --bin load-generator -- --help需要追踪日志时在运行命令前追加RUST_LOGinfo。仓库中预置的压测工作负载可通过 Justfile 中的命令自动运行。2.2 ScenarioRunner 的 CLI 接口虽然通常由 LoadGenerator 拉起但 ScenarioRunner 本身是一个 commander CLI见 index.ts其参数如下参数必填说明--deployment-url url是被测部署的 URL场景将对其发起请求--admin-key admin_key是访问部署的管理员密钥SnapshotExport 等场景会用到--scenarios json是JSON 格式的场景列表{scenarios: [...]}--load-generator-port port是连接 LoadGenerator 的端口WebSocket 地址为ws://127.0.0.1:port/sync--provision-host host否big brain预置服务的地址用于查询部署所属的 team/project--access-token token否访问 big brain 的令牌两个可选参数成对使用当provisionHost与accessToken同时给出时ScenarioRunner 会先请求部署的/instance_name再向${provisionHost}/api/deployment/${deploymentName}/team_and_project查询deploymentId构造出ProvisionerInfo供场景使用index.ts。2.3 构建与运行# 在 npm-packages/scenario-runner 目录下 npm run build # 先 tsc 编译再用 esbuild 打包为 dist/scenario-runner.jsnode 平台含 sourcemap npm run start # node dist/scenario-runner.js构建脚本见 package.json。依赖方面场景运行依赖convex工作区包、ws、sentry/noderandom-words、langchain、tiktoken等用于构造搜索与向量场景的测试数据。三、场景执行核心机制3.1 场景消息与分发LoadGenerator 下发的每条消息是ScenarioMessagetypes.tsexport type ScenarioMessage { scenario: ScenarioSpec; rate: number | null; // 每秒请求数null 表示 benchmark 模式 threads?: number; // benchmark 模式下的并发线程数 };runScenarioindex.ts根据scenarioSpec.name用 switch 分发到对应场景类RunFunction→RunFunction携带path与fn_typeObserveInsert→ObserveInsert携带search_indexesManyIntersections→ManyIntersections携带num_subscriptionsHoldSubscriptions→HoldSubscriptions携带num_subscriptions、hold_duration_secs、invalidation_interval_secs、num_invalidationsSnapshotExport、CloudBackup、Search、VectorSearch无额外参数RunHttpAction→RunHttpAction携带path与method每个场景都有类型化的参数定义ScenarioSpec见 types.tsdefault 分支使用satisfies never做穷尽检查确保新增场景名称后编译器强制要求补全分发逻辑。3.2 两种负载模式rate 模式与 benchmark 模式rate字段决定执行方式index.tsrate 模式rate为具体数值每秒请求数。每次执行前随机等待0 ~ 2 * (1000 / rate)毫秒模拟带抖动的泊松式到达分布。rate 0则直接跳过该场景。benchmark 模式rate null。此时使用threads默认 1创建对应数量的并发线程每个线程持有一个独立ConvexClient无限循环执行场景直到进程被终止用于打满后端吞吐。{ name: RunFunction, path: query_index:queryMessagesWithSearch, fn_type: query, benchmark: 80 }上面是 benchmark_query.json 中benchmark: 80的含义——80 个并发线程持续查询。而 prod.json 则展示了混合 rate 工作负载查询 10 rps、写入 2 rps、ObserveInsert 5 rps、VectorSearch 5 rps、SnapshotExport 0.0005 rps 等。3.3 防挂死保护两个与健壮性相关的细节值得注意closeWithTimeoutclient.close()只有在 WebSocketonclose事件触发后才 resolve。当连接处于半开/卡死状态如后端以 1011 InternalServerError 关闭或 WS 升级失败返回非 101 状态码该事件可能永不触发导致场景循环无限挂起、静默停止吞吐。因此用Promise.race加上CLOSE_TIMEOUT10 秒兜底index.ts。超时常量types.tsaction 与 HTTP action 超时 5 秒query 与 mutation 超时 2 秒因为 UDF 超时是 1 秒需要留出网络与后端开销导出场景最多 2 小时对应 500MB 数据库上限。四、Scenario 基类每个场景的公共能力所有场景类都继承抽象基类Scenario并实现IScenario接口scenario.ts。基类封装了指标上报、错误上报、超时执行与资源清理四大能力4.1 指标与错误上报场景通过构造函数注入的loadGenWS连接 LoadGenerator 的 WebSocket回传事件LoadGenerator 端据此生成统计报告sendCountMetric(value, name, path?)发送计数指标如mutation_send_timeout、export_completed。sendLatencyMetric(value, name, path?)发送延迟指标秒如mutation_completed、mutation_observed、query、vector_search。sendError(err, name)/sendDefaultError(err)上报错误事件含消息、错误名、场景名同时console.error并接入 SentrytracesSampleRate: 0.1。所有事件经JSON.stringify后通过 WebSocket 发送scenario.ts。指标名称使用联合类型ScenarioLatencyMetric/ScenarioCountMetric约束拼写错误在编译期即被拦截。4.2 超时执行工具executeOrTimeout(promise, timeoutDuration, timeoutMetricName, path?) executeOrTimeoutWithLatency(promise, timeoutDuration, timeoutMetricName, latencyMetricName, t0, path?)后者在 promise 完成时记录nowSeconds() - t0的延迟若超时则发送对应的超时计数指标。RunFunction用它分别测量 query / mutation / action 三种函数类型的延迟与超时run_function.ts。4.3 waitForQuery 订阅等待waitForQuery(client, query, args, isReady)订阅一个公开 query返回首个满足isReady(result)的结果的 Promise订阅会一直保持到场景结束并自动注册退订清理。ObserveInsert正是用它在插入 mutation 发出后等待订阅查询观察到这条新数据observe_insert.ts从而测得mutation 完成与mutation 被订阅查询观察到两个关键延迟。4.4 清理机制registerCleanUp(fn)注册清理回调如退订cleanUp()在每次场景运行结束后统一执行。清理是保证执行的——即使场景超时或出错也会运行scenario.ts避免订阅泄漏拖垮后续迭代。五、内置场景盘点scenarios/目录下已有 9 个场景实现覆盖 Convex 的主要能力面场景文件名验证目标RunFunctionrun_function.ts按pathfn_type调用任意 query/mutation/action测量延迟与超时RunHttpActionrun_http_action.ts通过 HTTP action 路由如basic、streaming发起请求ObserveInsertobserve_insert.ts插入一行数据测量mutation 完成与订阅观察到插入两个延迟search_indexes为 true 时使用带搜索索引的表场景名变为ObserveInsertWithSearchSearchsearch.ts全文搜索校验搜索结果与文档一致性search_document_mismatchVectorSearchvector_search.ts随机取一条含 1536 维 embedding对齐 OpenAI text-embeddings的文档执行向量搜索并校验命中 id 与分数0.99ManyIntersectionsmany_intersections.ts大量订阅并发下的写入观察num_subscriptions控制订阅数HoldSubscriptionshold_subscriptions.ts长期持有订阅周期性触发失效invalidation_interval_secs、num_invalidations压测订阅失效链路SnapshotExportsnapshot_export.ts请求并跟踪快照导出任务指标含request_export_succeeded、export_completed、export_in_progress、export_timeoutCloudBackupcloud_backup.ts请求云备份并等待完成场景背后的 Convex 函数位于 convex/ 目录insert.ts、update.ts、query_index.ts、search.ts、schedule.ts、vectorSearch.ts、components.ts组件查询/变更、openclaurd.ts低基数数据、含 1536 维向量等。convex.json默认将prodUrl指向http://127.0.0.1:8000本地后端。六、添加新场景三步完整指南README 给出了添加新场景的三步流程下面结合源码逐层展开。第一步命名场景并注册到分发控制流在 metrics.ts 的ScenarioName联合类型中加入新场景名。注意该文件的头部注释This file is automatically generated by cargo test -p load_generator——正确做法是先在 Rust 侧crates/load_generator/src/metrics.rs添加指标再通过cargo test -p load_generator自动生成该文件避免手工编辑被覆盖。在 types.ts 的ScenarioSpec中为新场景声明类型化参数若需要。在 index.ts 的runScenarioswitch 中添加 case构造对应场景实例并把ScenarioSpec中的参数传入。default 分支的satisfies never会强制编译器在漏加 case 时报错。第二步编写场景类在 scenarios/ 目录新建文件实现IScenario接口并继承Scenario基类import { ConvexClient } from convex/browser; import { Config, IScenario, Scenario } from ../scenario; import { ScenarioError } from ../metrics; export class MyScenario extends Scenario implements IScenario { constructor(config: Config, /* 你的参数 */) { super(MyScenario, config); // 保存参数 } async run(client: ConvexClient) { // 1. 用 client.query / client.mutation / client.action 发起请求 // 2. 用 this.executeOrTimeoutWithLatency(...) 包住请求以测延迟与超时 // 3. 用 this.waitForQuery(...) 订阅等待结果 // 4. 用 this.sendCountMetric / this.sendLatencyMetric 上报指标 } defaultErrorName(): ScenarioError { return mutation; // 或你自定义的错误名 } }要点构造函数第一个参数必须是Config含deploymentUrl、loadGenWS、可选的provisionerInfo并把name传给基类。run(client)中可以使用基类提供的全部工具见第四节务必对每个可能失败的请求套上executeOrTimeout系列防止单个请求卡死整个压测循环。如果场景建立了订阅用registerCleanUp注册退订确保异常后不泄漏。如果需要在 Rust 侧统计新指标如延迟、计数、错误名同步在crates/load_generator/src/metrics.rs中注册。写完场景类后从第一步的 switch 中调用它即可。第三步在 LoadGenerator 中登记场景ScenarioRunner 只是执行端LoadGenerator 必须知道新场景才能下发。需要在 Rust 侧crates/load_generator的Scenariostruct 中为新场景增加对应字段/变体README 明确要求add a new scenario to theScenariostruct。在 crates/load_generator/src/metrics.rs 注册新指标运行cargo test -p load_generator重新生成 metrics.ts保证两端指标枚举一致。在 workload JSON 中引用新场景例如 prod.json 中的模式{name: MyScenario, rate: 5}或 benchmark 模式{name: MyScenario, benchmark: 10}。七、编写自定义 Convex 函数配合压测除新建场景类外更轻量的扩展方式是只新增 Convex 函数并复用内置的RunFunction场景。把无参函数或接受固定参数的函数放入 convex/ 文件夹然后{ name: your_new_workload, scenarios: [ { name: RunFunction, path: your-new-module:your-function-name, fn_type: mutation, rate: 5 } ] }path格式为模块名:函数名fn_type可取query、mutation或actionrate可替换为benchmark线程数。这是 crates/load_generator/README.md 推荐的自定义场景快捷路径。八、对自托管 Convex 后端做压测ScenarioRunner 同样可用于压测自托管后端完整流程如下推送函数只在测试专用后端上执行勿在正式实例上操作把 scenario-runner 的 Convex 函数部署到自托管后端这会替换该后端的函数cd npm-packages/scenario-runner npx convex deploy --admin-keyyour-admin-key --urlyour-backend-url运行 LoadGenerator 指向现有实例复用 workloads/ 下的示例或自定义 workloadcd ../../crates/load_generator just self-hosted crates/load_generator/workloads/your-workload.json --existing-instance-url your-backend-url --existing-instance-admin-key your-admin-key--existing-instance-url与--existing-instance-admin-key让 LoadGenerator 跳过预置流程、直接使用现有实例其余场景下发与指标收集逻辑与预置模式完全一致。九、端到端压测一次完整的请求流以 prod.json 中ObserveInsertrate 5为例一次迭代的完整链路是ScenarioRunner 在ws://127.0.0.1:port/sync收到{scenario: {name: ObserveInsert, search_indexes: true}, rate: 5}。runScenario构造ObserveInsert实例等待平均 200ms 的随机抖动后开始迭代。场景先通过waitForQuery订阅queryMessagesWithArgs带rand过滤条件再发起insertMessageWithArgsmutation测出mutation_completed延迟随后等待订阅结果中出现rand匹配且timestamp startTime的记录测出mutation_observed延迟。两个延迟指标经 WebSocket 回传 LoadGenerator由其聚合成统计报告任何超时都会触发mutation_send_timeout/mutation_observed_timeout计数指标。迭代结束后cleanUp()退订进入下一次循环。这套真实客户端 订阅观察 延迟/超时双指标 端到端清理的模式正是 Convex 反应式数据库压测的核心价值不仅能测出函数执行延迟还能量化数据变更传播到订阅客户端的端到端延迟。赞分享数据库后端【免费下载链接】convex-backendThe open-source reactive database for app developers项目地址https://gitcode.com/gh_mirrors/co/convex-backend点击查看免费下载相关推荐Convex LoadGenerator 压测工具指南从负载场景编排到延迟统计报告Convex LoadGenerator 压测工具指南从负载场景编排到延迟统计报告 LoadGenerator 是 convex backend 仓库内置的负数据库后端Convex Backend 压测指南使用 LoadGenerator 对自托管 Convex 实例进行基准测试Convex Backend 压测指南使用 LoadGenerator 对自托管 Convex 实例进行基准测试 导读 本文基于 self hosted/ad数据库后端LifeOS Evals 实战编写多轮 Agent 场景的 CreateScenario 工作流与 ScenarioRunner 执行机制LifeOS Evals 实战编写多轮 Agent 场景的 CreateScenario 工作流与 ScenarioRunner 执行机制 LifeOS 的AI 技能人工智能AI 应用创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考