# 单租户 10 万学生容量验证手册 本工具用合成数据验证租户后台学生列表在单租户最多 100000 名学生时的 PostgreSQL 查询形状、cursor 分页和子串搜索。它不会连接 API,直接使用与 `GET /api/tenant-admin/students` 相同的 SQL 核心和下列索引: - `idx_memberships_student_keyset_page` - `idx_platform_users_identity_search_trgm` ## 安全边界 - 只能用于 `local` / `test` / `ci` 数据库,且 `app_private.environment_safety.allow_destructive_tests` 必须为 `true`。 - 任何写入、基准或清理模式都必须显式传入 `SMOKE_SEED_LOCAL_OR_CI_ONLY`。 - 租户 slug 必须以 `capacity-test-` 开头。如果同名租户没有匹配的 `metadata.capacityHarness` 标记,工具会拒绝使用它。 - 合成用户同时使用 `legacy_id` 和 `raw_profile.capacityHarness` 标记。清理前如果发现真实 `auth_user_id`、其他租户 membership、租户 owner 或专用租户中的非 harness membership,工具会拒绝操作。 - 禁止使用 `tikupro-pg` 或任何生产数据库。当前本机 `127.0.0.1:5432` 映射到 `tikupro-pg`,因此工具也会对 localhost/loopback 的 5432 端口在连接前硬拒绝。本工具的本地结果 is not a production SLA。 不要为了跑工具而在现有服务器数据库上修改环境标记。应该新建专用的非生产 PostgreSQL/Supabase 实例,运行全部迁移,再设置测试环境标记。 ## 1. 先看计划 默认模式不连接数据库、不写数据: ```bash npm run perf:tenant-students:plan ``` 如果希望在计划中显示脱敏后的 host / port / database / user,可以同时提供 `DATABASE_URL`;工具不会输出密码。 ## 2. 小规模 smoke 以专用隔离库为例:smoke 使用独立的 `capacity-test-students-smoke` 租户生成 250 名学生,执行 5 组查询、写出 JSON/Markdown 证据,然后在成功或失败路径清理专用租户和合成用户。它不会清理 `capacity-test-students-100k` 中人为保留的基准夹具。 ```bash DATABASE_URL='postgresql://postgres:postgres@127.0.0.1:55432/postgres' \ npm run test:tenant-students:capacity:smoke ``` 报告默认保存到已忽略的 `docs/refactor/performance-reports/`。smoke 返回后应确认 `cleanup.deletedTenant=1` 且 `cleanup.deletedUsers=250`。 ## 3. 生成 10 万行并跑基准 `run` 会保留夹具,便于重复采样或查看查询计划: ```bash DATABASE_URL='postgresql://postgres:postgres@127.0.0.1:55432/postgres' \ npm run perf:tenant-students:run -- \ --count=100000 \ --batch-size=10000 \ --iterations=20 \ --warmup-iterations=3 \ --confirm=SMOKE_SEED_LOCAL_OR_CI_ONLY ``` 写入是 set-based 的 `generate_series` + UPSERT,每批在独立事务中完成。同一个 count 可重复执行;如果已有夹具行数高于新 count,工具会要求先清理,避免隐式删数据。 正式上线证据应使用一次性 `evidence` 模式,它会在同一个受保护流程中完成 seed、benchmark、cleanup,并把清理结果与四类残留计数写入同一份 JSON: ```bash DATABASE_URL='' \ npm run perf:tenant-students:evidence -- \ --count=100000 \ --batch-size=10000 \ --iterations=20 \ --warmup-iterations=3 \ --confirm=SMOKE_SEED_LOCAL_OR_CI_ONLY ``` `evidence` 模式成功时必须包含 `cleanup.cleanupVerified=true`,且 `remaining.tenants/platformUsers/memberships/profiles` 全部为 `0`。 每组证据包含: - 实际 `platform_users` / `tenant_memberships` / `student_profiles` 行数。 - 工具内部记录的 seed / benchmark / cleanup / total 阶段耗时。 - 首页和约 90% 深度 cursor 页。 - 姓名、手机号、email substring 搜索。 - 应用端观测 P50/P95,以及每组 SQL 的 `EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON)`。 - 实际使用的 plan node、index name、shared/local/temp buffer 摘要与完整 JSON plan。 ## 4. 重复基准 ```bash DATABASE_URL='postgresql://postgres:postgres@127.0.0.1:55432/postgres' \ npm run perf:tenant-students:benchmark -- \ --iterations=30 \ --confirm=SMOKE_SEED_LOCAL_OR_CI_ONLY ``` 深 cursor 使用约 90% 位置的真实 `(created_at, membership_id)` 锚点,不使用 OFFSET 制造查询。 ## 5. 清理 ```bash DATABASE_URL='postgresql://postgres:postgres@127.0.0.1:55432/postgres' \ npm run perf:tenant-students:cleanup -- \ --confirm=SMOKE_SEED_LOCAL_OR_CI_ONLY ``` 清理只会命中: - `slug=capacity-test-students-100k` 且带匹配 `metadata.capacityHarness` 的专用租户。 - `raw_profile.capacityHarness.namespace=tiku.student-capacity.v1` 且 tenant slug 相同的合成用户。 自定义租户名时,六个命令都必须传入同一个 `--tenant-slug=capacity-test-...`。 ## 验收口径 这个工具首先是数据量和查询计划验证,不应单独用它承诺线上 SLA。正式验收还需要在与生产同规格的非生产环境复跑,并与 API 并发压测、PostgreSQL 调优证据、连接池和云盘 I/O 指标一起归档。