fix(store): PG DSN 默认用 127.0.0.1 免每连接 DNS 解析;订正压测报告 C=256 归因

DSN 由 localhost 改 127.0.0.1(gateway + mcp-go):pgx 每新建连接解析一次主机名,
高并发连接池扩容时省掉这层 DNS 开销与一类失败模式。

诚实订正:复测发现 DSN 改完 C=256 仍 0 完成(错误从 lookup localhost 变 dial 127.0.0.1 canceled)。
故 C=256 的「停摆」根因不是 DNS,而是单节点在 256 并发下整体饱和(任务排队 + 每任务 3 次 PG
状态写 + 网关 handler 顶不住),属单节点容量上限、非 bug;C≤128 优雅可用,正解是横向扩。
LOAD_TEST_REPORT §4.3/§6 与 project_analysis 容量实测已据实更新。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Blizzard
2026-06-26 12:49:35 +08:00
parent 468107ea7a
commit 2e3cb8105d
4 changed files with 22 additions and 10 deletions
+14 -6
View File
@@ -57,10 +57,18 @@
每个任务要走的**一长串同步跳数**(JetStream 落盘 + 多次 NATS 回写 + Redis 录制 + 多次 PG 写)的累加成本。
要把单节点吞吐再往上抬,得减少每任务的同步跳数(如状态写批量化/异步化),而非简单加资源。
### 4.3 C=256 硬崩的根因(易修)
网关日志:`failed to connect ... lookup localhost: operation was canceled`
DSN 用了主机名 `localhost`,pgx 每新建一条连接都做一次 DNS 解析;极端并发下连接抖动 → 大量 DNS 查询
在请求超时里被取消 → 雪崩。**修法简单**:DSN `127.0.0.1`(免 DNS)+ 连接复用调优。属配置级修复,非架构问题
### 4.3 C=256 的「停摆」:单节点饱和,非 bug
现象:C=256 在 10s 窗口内 0 完成
初判曾归因于 DSN `localhost` 致 pgx 每连接 DNS 解析、极端并发下 DNS 查询被取消
**已把 DSN 改为 `127.0.0.1`**(确实消除了 DNS 那一层 + 每连接解析开销,是值得的卫生改进),
但复测 C=256 仍 0 完成 —— 错误从 `lookup localhost` 变成 `dial tcp 127.0.0.1:5432: operation was canceled`
**真实根因**:单节点在 256 并发下整体饱和 —— 任务排队 + PG 状态写路径(每任务 3 写、池 25)+ 网关
handler/连接 顶不住,单任务延迟超过测试窗口,故窗口内零完成(那些 "canceled" 是窗口到点取消在途请求的尾部噪声)。
**这不是要修的 bug,是单节点容量上限的表现**:C≤128 优雅降速可用,256 已远超单 dispatcher 的 ~110/s 甜区。
**正解是横向扩**(加 dispatcher 副本,队列组自动分摊)——正好印证 §4.4 的判断。
单节点要再抬高,则需状态写批量/异步化(降低每任务的同步 PG 跳数)。
### 4.4 核心判断:平台不是瓶颈,GPU 才是
- 平台固有开销 **~42ms / 任务**。
@@ -96,11 +104,11 @@ DSN 用了主机名 `localhost`pgx 每新建一条连接都做一次 DNS 解
---
## 6. 待补(后续)
- ✅ DSN `localhost → 127.0.0.1`(已修,消除每连接 DNS 解析;但不解决 §4.3 的单节点饱和)。
- 长稳压测(小时级):看内存 / 文件句柄 / goroutine 是否泄漏。
- 真实 LLM 端到端容量:受在线 provider 限流,需自部署模型后再测。
- DSN `localhost → 127.0.0.1`(顺手修,消除 §4.3 崩溃)。
- 状态写批量/异步化探索(抬高单节点天花板)。
- 多副本 + NATS 集群下的容量复测。
- 多副本 + NATS 集群下的容量复测(验证横向扩是否线性)
---
+2 -2
View File
@@ -235,8 +235,8 @@ dispatcher 开 `LLM_FORCE_STUB=1 LLM_STUB_*_MS=0` 绕开真实 LLM 推理,量*
1. **单节点平台天花板 ≈ 110 全链路任务/秒**,单任务固有开销 ~42ms,饱和点 ~并发 32–64。
2. **吞吐瓶颈不是 DB 连接数**(池 25→80 吞吐不变),而是每任务多跳管线(JetStream fsync + 多次
NATS 回写 + Redis 录制 + 3 次 PG 状态写)的综合成本。
3. **C=256 硬崩**根因:DSN 用 `localhost`,pgx 每新建连接解析一次,极端并发下连接churn致 DNS 取消
→ 易修(DSN 换 `127.0.0.1` / 连接复用)
3. **C=256 停摆**是单节点饱和(任务排队 + PG 状态写 + 网关 handler 顶不住),非 bug;C≤128 优雅可用。
DSN 已由 `localhost``127.0.0.1`(消除每连接 DNS 解析,卫生改进,但不解决饱和)。正解是横向扩
4. **关键判断**:平台开销 42ms ≪ 真实 LLM 出一轮答案的秒级延迟,**平台不是瓶颈,GPU 才是**。
单 GPU 跑 Qwen 32B 约出几十轮/秒,远低于平台的 ~110/s → 横向拆服务(队列组多副本喂满 GPU 集群)
的意义成立;要提总吞吐应加 dispatcher 副本与 GPU,而非优化平台代码。
+3 -1
View File
@@ -30,7 +30,9 @@ func main() {
defer func() { _ = shutdownTrace(context.Background()) }()
natsURL := envOr("NATS_URL", "nats://localhost:4222")
pgDSN := envOr("POSTGRES_DSN", "postgres://sundynix:sundynix@localhost:5432/sundynix?sslmode=disable")
// DSN 用 127.0.0.1 而非 localhostpgx 每新建连接解析一次主机名,高并发下连接池扩容时
// 大量 localhost DNS 查询会在请求超时里被取消而雪崩(见 LOAD_TEST_REPORT §4.3)。IP 免 DNS。
pgDSN := envOr("POSTGRES_DSN", "postgres://sundynix:sundynix@127.0.0.1:5432/sundynix?sslmode=disable")
redisAddr := envOr("REDIS_ADDR", "localhost:6379")
// 对象存储(大文档正文):默认连 docker-compose 暴露的 MinIO;连不上则降级内联存 PG。
blobStore := blob.Open(
+3 -1
View File
@@ -30,7 +30,9 @@ func main() {
defer func() { _ = shutdownTrace(context.Background()) }()
natsURL := envOr("NATS_URL", "nats://localhost:4222")
pgDSN := envOr("POSTGRES_DSN", "postgres://sundynix:sundynix@localhost:5432/sundynix?sslmode=disable")
// DSN 用 127.0.0.1 而非 localhost:免 pgx 每连接 DNS 解析,避免高并发连接池扩容时 DNS 雪崩
// (见 LOAD_TEST_REPORT §4.3)。
pgDSN := envOr("POSTGRES_DSN", "postgres://sundynix:sundynix@127.0.0.1:5432/sundynix?sslmode=disable")
redisAddr := envOr("REDIS_ADDR", "localhost:6379")
milvusAddr := envOr("MILVUS_ADDR", "localhost:19530")
embBase := envOr("EMBED_BASE_URL", "") // OpenAI 兼容 embeddings 端点(空=向量检索降级)