13 KiB
13 KiB
K6 作品列表压测计划(使用 spacetime-migration-7.json 作为数据源)
目标
使用 K6 对 Genarrative 的“作品列表”相关接口进行压测,并将用户提供的 spacetime-migration-7.json 作为压测数据源;数据处理时只导入作品列表相关数据,不导入用户、会话、钱包、埋点、运行存档等非作品表,避免把敏感或无关数据带入压测环境。
当前上下文
- 工作区:
/c/proj/Genarrative - 原始迁移文件:
C:\Users\DSK\AppData\Local\hermes\cache\documents\doc_150e84029b2d_spacetime-migration-7.json - 已确认原始迁移文件结构:
schema_version = 1tables = 53- 作品相关表中当前有数据的重点表:
puzzle_work_profile:80 行custom_world_profile:1 行match3d_work_profile:0 行big_fish_*:当前样本中相关表为 0 行
- 原始文件还包含
user_account、auth_identity、refresh_session、profile_wallet_ledger、asset_object、运行记录等数据,压测导入时必须过滤。
- 当前仓库未发现现成 K6 脚本或
k6相关文件,需要新增压测脚本与数据提取脚本。 package.json当前有dev/dev:rust/test/check等脚本,未发现 K6 npm script。
范围约束
本次只导入/使用
- 作品列表表:
puzzle_work_profilecustom_world_profile- 后续若接口覆盖其他玩法,可扩展:
match3d_work_profilesquare_hole_work_profile(以实际 SpacetimeDB 表名为准)big_fish_work_profile(以实际 SpacetimeDB 表名为准)visual_novel_work_profile(以实际 SpacetimeDB 表名为准)
- 为作品列表卡片展示所需的最小字段:
- 稳定 ID:
profile_id、work_id或public_work_code - 标题:
work_title/level_name/world_name - 描述:
work_description/summary/summary_text/subtitle - 作者:
owner_user_id、author_display_name、author_public_user_code - 封面:
cover_image_src、cover_asset_id(如果接口只返回 asset id,则压测阶段不额外导入二进制 asset) - 状态与计数:
publication_status、published_at、play_count、like_count、remix_count - 作品内容摘要:
levels_json、profile_payload_json、theme_tags_json等列表渲染或进入作品详情可能需要的 JSON 字段
- 稳定 ID:
本次不导入/不使用
- 认证与账号:
user_account、auth_identity、refresh_session、auth_store_snapshot - 用户资产与钱包:
profile_wallet_ledger、profile_dashboard_state、profile_redeem_*、profile_invite_* - 游玩历史/存档/运行态:
profile_played_world、public_work_play_daily_stat、puzzle_runtime_run、profile_save_archive、runtime_snapshot等 - AI 任务过程:
ai_task、ai_task_stage、ai_text_chunk - asset 二进制与绑定:
asset_object、asset_entity_binding,除非后续确认作品列表接口强依赖它们;即便需要,也只导入作品列表封面所需的最小 metadata,不导入原始大对象。
推荐目录与文件
建议新增:
.hermes/plans/2026-05-11_195214-k6-works-list-load-test-plan.md # 本计划
scripts/loadtest/extract-works-list-data.mjs # 从迁移文件提取作品列表数据
scripts/loadtest/k6-works-list.js # K6 压测脚本
scripts/loadtest/data/works-list.sample.json # 过滤后的样例数据(不要提交敏感原始迁移全量)
scripts/loadtest/README.md # 执行说明与指标阈值
可选新增 npm scripts:
{
"loadtest:extract-works": "node scripts/loadtest/extract-works-list-data.mjs",
"loadtest:k6:works": "k6 run scripts/loadtest/k6-works-list.js"
}
数据提取方案
输入
默认读取:
node scripts/loadtest/extract-works-list-data.mjs \
--input "C:/Users/DSK/AppData/Local/hermes/cache/documents/doc_150e84029b2d_spacetime-migration-7.json" \
--output scripts/loadtest/data/works-list.local.json
输出结构
建议输出为 K6 直接可读的 JSON:
{
"source": "spacetime-migration-7.json",
"generatedAt": "<iso datetime>",
"tables": {
"puzzle_work_profile": [
{
"profile_id": "...",
"work_id": "...",
"owner_user_id": "...",
"work_title": "...",
"work_description": "...",
"publication_status": "Published",
"published_at": { "__timestamp_micros_since_unix_epoch__": 0 },
"play_count": 0,
"like_count": 0,
"levels_json": "..."
}
],
"custom_world_profile": []
},
"workIds": {
"puzzle": ["<profile_id>"],
"customWorld": ["<profile_id>"]
}
}
过滤原则
- 按
tables[].name白名单过滤,只保留作品 profile 表。 - 对每个 row 再按字段白名单过滤,避免误带账号、手机号、token、钱包流水等字段。
- 对特别大的字段进行处理:
cover_image_src如果是data:image/...base64,默认替换为占位符或截断,避免压测数据文件过大。levels_json、profile_payload_json保留原文,但可以记录大小;如果过大,再提供--compact选项只保留摘要。
- 输出
.local.json默认加入.gitignore;如果要提交样例数据,只提交脱敏/裁剪后的works-list.sample.json。
K6 压测接口矩阵
需要先确认本地 api-server 实际端口。默认以 http://127.0.0.1:8787 为例,实际运行时通过环境变量覆盖:
BASE_URL=http://127.0.0.1:<actual-api-port> k6 run scripts/loadtest/k6-works-list.js
初版建议覆盖以下“作品列表”读接口,具体路径以仓库服务端路由为准,实施时需要通过搜索 api-server 路由确认:
| 场景 | 目的 | 候选路径 |
|---|---|---|
| 拼图作品列表 | 作品列表主场景之一,当前数据量最多 | /api/creation/puzzle/works 或实际 puzzle works list route |
| RPG/自定义世界作品列表 | 使用 custom_world_profile 数据 |
/api/creation/custom-world/works 或实际 custom world works route |
| 作品详情/启动前读取 | 模拟用户从列表点进作品 | /api/creation/*/works/:profileId 或 /api/runtime/*/works/:profileId |
| 公开作品库 | 如果首页/发现页依赖 | /api/runtime/*/works 或 gallery/list route |
注意:不要凭空固定 endpoint。实施阶段先用
search_files/ 路由源码确认真实路径,再写入 K6 脚本。
K6 场景设计
阶段 1:基线 smoke
目的:确认脚本、数据和目标服务可用。
export const options = {
scenarios: {
smoke: {
executor: 'constant-vus',
vus: 1,
duration: '30s'
}
},
thresholds: {
http_req_failed: ['rate<0.01'],
http_req_duration: ['p(95)<800']
}
};
阶段 2:常规读压
目的:模拟日常列表浏览。
constant-vus: 10/25/50 三档- 每个 VU 随机选择作品类型和列表分页参数
sleep(0.5~2s)模拟用户停留- 阈值建议:
http_req_failed < 1%p95 < 800msp99 < 1500ms
阶段 3:峰值/突刺
目的:模拟首页入口或活动导致的作品列表突增。
ramping-arrival-rate- 从 5 RPS 增长到 100 RPS,维持 2~5 分钟,再降回
- 单独输出
checks:列表接口状态码、响应 JSON shape、items 数量
阶段 4:容量探索
目的:找瓶颈,不作为每次回归必跑。
- 每轮提升 RPS 或 VU
- 观察:api-server CPU/内存、SpacetimeDB 日志、错误率、p95/p99
- 一旦
http_req_failed >= 5%或 p95 持续超过 2s,停止继续加压并记录容量点。
K6 脚本设计要点
- 使用
SharedArray加载works-list.local.json,避免每个 VU 重复解析大 JSON。 - 基于数据源里的
profile_id/work_id随机抽样,保证请求覆盖真实作品 ID。 - 对列表接口添加分页/排序 query,例如:
?limit=20&offset=0?pageSize=20&cursor=...(以真实 API 为准)
- 使用
check()验证:- HTTP 200
- 响应体是 JSON
items或works是数组- 列表项包含
profileId/profile_id、标题字段、状态字段
- 使用
Trend/Rate细分指标:works_list_durationworks_detail_durationworks_list_shape_error_rate
- 支持环境变量:
BASE_URL=http://127.0.0.1:8787 \
WORKS_DATA=scripts/loadtest/data/works-list.local.json \
SCENARIO=baseline \
k6 run scripts/loadtest/k6-works-list.js
实施步骤
- 确认路由
- 搜索 api-server / BFF 的作品列表路由。
- 明确各玩法对应 endpoint、鉴权要求、分页参数、返回字段。
- 实现数据提取脚本
- 新增
scripts/loadtest/extract-works-list-data.mjs。 - 只按表白名单读取作品列表 profile 表。
- 对字段做白名单与脱敏/截断。
- 输出
works-list.local.json。
- 新增
- 生成本地压测数据
- 用用户提供的迁移文件生成
scripts/loadtest/data/works-list.local.json。 - 验证输出只包含作品表和作品字段。
- 用用户提供的迁移文件生成
- 实现 K6 脚本
- 新增
scripts/loadtest/k6-works-list.js。 - 支持
BASE_URL、WORKS_DATA、SCENARIO。 - 覆盖列表接口,必要时增加详情/启动前读取接口。
- 新增
- 新增执行说明
- 在
scripts/loadtest/README.md写明:安装 K6、启动本地 dev 栈、提取数据、运行 smoke/baseline/spike、查看结果。
- 在
- 本地验证
- 启动 Genarrative dev 栈;注意端口可能漂移,使用实际 api-server 端口。
- 跑 smoke:
SCENARIO=smoke。 - 确认失败率、p95、响应 shape。
- 可选集成 npm scripts
- 如果团队希望标准化入口,再加入
package.jsonscripts。
- 如果团队希望标准化入口,再加入
- 记录结果
- 将 smoke/baseline/spike 的结果摘要追加到
scripts/loadtest/README.md或单独保存到.hermes/plans/的结果文档中。
- 将 smoke/baseline/spike 的结果摘要追加到
启动与运行建议
本地服务启动按当前 Genarrative dev 栈约定:
npm run dev
如果 SpacetimeDB/API/Vite 端口被占用,项目脚本会寻找可用端口;压测时必须从启动日志中读取实际 api-server 地址,并传给 K6:
BASE_URL=http://127.0.0.1:<actual-api-port> \
WORKS_DATA=scripts/loadtest/data/works-list.local.json \
SCENARIO=smoke \
k6 run scripts/loadtest/k6-works-list.js
验证标准
数据源验证
works-list.local.json中只出现作品 profile 表。- 不出现以下字段或内容:
password_hashrefresh_token_hashphone_number_e164phone_number_maskedwallet_ledger_idauth_identityuser_account
puzzle_work_profile行数应接近原始文件中的 80 行。custom_world_profile行数应接近原始文件中的 1 行。
K6 smoke 验证
- 所有目标接口返回 2xx。
http_req_failed < 1%。- 响应 JSON shape 与 shared contracts 对齐:
items或works数组。 - K6 输出中能区分不同 endpoint 的耗时。
性能阈值初稿
- Smoke:
p95 < 800ms,失败率< 1% - Baseline:
p95 < 1000ms,p99 < 2000ms,失败率< 1% - Spike:允许短暂 p95 抖动,但 1 分钟内应恢复;失败率
< 5%
阈值后续需要结合本地机器性能、SpacetimeDB 本地模式和正式部署规格调整。
风险与注意事项
- 原始迁移文件包含敏感数据。 必须只提取作品列表白名单字段,禁止把原始 JSON 全量提交到仓库。
- base64 封面可能导致压测数据膨胀。 默认截断或替换为占位符,除非本次明确要测封面 payload 对响应体积的影响。
- 本地 SpacetimeDB 与 api-server 端口会漂移。 不要硬编码端口,运行时通过
BASE_URL注入。 - 列表接口可能需要鉴权。 若实际接口要求登录,不要导入真实 refresh session;应使用本地测试账号或专门的压测 token 生成流程。
- 作品表名/接口路径可能与候选名称不完全一致。 实施前必须以源码路由为准。
- 本计划仅保存压测方案,不执行实际压测。 后续执行时再创建/修改脚本、导出过滤数据、跑 K6 并记录结果。
开放问题
- 压测目标是本地 dev 栈、测试环境,还是预发/生产只读接口?不同环境阈值和安全边界不同。
- “作品列表”是否只包含拼图和自定义世界,还是要覆盖 match3d、square-hole、big-fish、visual-novel 的统一列表入口?
- 是否允许使用专门压测账号/token?如果接口无鉴权则无需处理。
- 是否需要测封面/asset 加载,还是只测作品列表 JSON API?