Commit 8d12f2b9 by luoqi

fix(report): 回访对账的表名写错(单数)+ 更正"测试机有逃生口"这个错误论断

查上一条修复时,拿测试机做对照发现两件事:

① **`patient_return_visit` 少了个 s**(daily-health-report.service.ts:256)。
   真实表名是 `patient_return_visits`(schema.prisma:455 的 @@map)。
   报错被 pacPatientCount 的 try/catch 吞成 WARN + 返回 null → 日报照发,
   只是「回访」那行长期没有 PAC 侧计数,静默缺了一项对账。
   生产上看不到这个 WARN,因为它的日报根本没跑到这一步就先被连接池超时打死了 ——
   一个 bug 把另一个 bug 遮住了。

② **「测试服 URL 里显式 connection_limit=30,所以不受影响」是错的**,而这句话被
   prisma.service.ts 的注释、测试文件的注释一路传下来,也是我判断"测试机不用验"的依据。
   实测(`docker inspect`)两台容器里的 DATABASE_URL 都是
   `postgresql://…@postgres:5432/pac?schema=public` —— **没有 connection_limit**:
   compose 的 environment: 段整条覆盖了 .env 里的值(.env.example 早就写了这条,只是没人联想到)。
   佐证:测试机 09-01 的健康日报也报了同一句 `connection limit: 5`。
   → 测试机跟生产是同一个池、同一个 bug,**这个修复在测试机验得出来**。

判断逃生口生不生效只能看容器实际环境变量,不能看 .env 文件;三处注释都已改口径。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
parent db15fe0e
......@@ -48,6 +48,9 @@ POSTGRES_DB=pac
# 池 = N×6+5 封顶 40);全都 ≤1 才落回 Prisma 默认池。
# Prisma 默认池是「**物理核**×2+1」,不是逻辑核 —— 生产 nproc=4 但物理核只有 2,默认只有 **5** 条。
# URL 里一旦显式写了 connection_limit,自动放大**整个失效**(逃生口,写了就得自己负责够用)。
# ⛔ 但 compose 模式下**在本文件里写 connection_limit 是没用的** —— compose 的 environment:
# 段会整条覆盖 DATABASE_URL。要确认到底生效的是什么,只能看
# `docker inspect <容器> --format '{{range .Config.Env}}{{println .}}{{end}}' | grep DATABASE_URL`。
# 2026-09-02 生产事故就出在这:开了召回并发却没算进池,批次占满 5 条,每日健康日报连续三天超时。
DATABASE_URL=postgresql://pac:pac@localhost:5532/pac?schema=public
......
......@@ -27,8 +27,12 @@ import { tenantGuardExtension } from './tenant-guard.extension';
* ⚠️ **Prisma 默认池是「物理核」×2+1,不是逻辑核** —— 这里以前写错过,导致低估了生产的池:
* 生产 47.99.62.30:`nproc`=4 但 `Core(s) per socket`=2 / `Thread(s) per core`=2,
* 物理核只有 2 → 默认池 = 2×2+1 = **5**(不是按 4 核算的 9)。5 条连接扛不住 4 路并发 + 日报。
* 测试服 47.251.104.47:URL 里显式 `connection_limit=30` → 本函数提前返回,不受影响
* (那里观测到的 9 条连接 = 8 个 worker + 1 空闲,不是池上限 —— 别再据此推断)。
* ⛔ **别再说「测试服 .env 里写了 connection_limit=30 所以不受影响」** —— 这话流传了很久,是错的:
* compose 会在 `environment:` 段覆盖 .env 里的 DATABASE_URL,容器里实际拿到的是
* `postgresql://…@postgres:5432/pac?schema=public`,**没有 connection_limit**
* (2026-09-02 `docker inspect` 实测两台都如此)。测试服同样是默认池,
* 09-01 那天它的健康日报也报了同一句 `connection limit: 5`。
* 判断逃生口生不生效,要看**容器实际环境变量**,不是看 .env 文件。
*
* 生产 RDS `max_connections=820`(2026-09-02 实测,当时全库仅 23 条在用),
* 封顶 40 对它绰绰有余;封顶注释里那个 100 是早年自建 PG 的值,保守留着。
......
......@@ -253,7 +253,7 @@ export class DailyHealthReportService {
}
if (pac.kind === 'return_visit') {
const r = await this.prisma.$queryRaw<Array<{ c: bigint }>>`
SELECT count(distinct patient_id) AS c FROM patient_return_visit WHERE host_id = ${hostId}::uuid`;
SELECT count(distinct patient_id) AS c FROM patient_return_visits WHERE host_id = ${hostId}::uuid`;
return Number(r[0]?.c ?? 0);
}
if (pac.kind === 'image_ai') {
......
......@@ -10,8 +10,8 @@ import { withCohortDerivedPool } from '../src/prisma/prisma.service';
* ① 批次栅栏 → worker pool:缺口正好是 E[8 次抽样最大值](≈p90)与均值之比;
* 那个 75.7 秒的患者会把同批 7 个 worker 一起冻住。
* ② --concurrency 没放大连接池 —— **潜在**缺陷,咬不咬人看该机 .env:
* 测试服 URL 显式 `connection_limit=30` → 不受影响
* 生产 URL 没写 → 走 Prisma 默认池
* (原文说"测试服 URL 显式 connection_limit=30 所以不受影响" —— 2026-09-02 证伪:
* compose 覆盖了 DATABASE_URL,两台容器里都没有 connection_limit,都是默认池)
*
* 🔴 2026-09-02 补:上面那句"4 核机 = 9"是**错的**,已在实现里更正 ——
* Prisma 默认池按**物理核**算(×2+1),生产 nproc=4 但物理核只有 2 → 池 = **5**。
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment