昨天给「已并入 / 子段」做的收口写成每行去 entity_raw 判 `raw->>'ZMERGEDTO' IS NOT NULL OR raw->>'ZPARENT' IS NOT NULL`,而 entity_raw.raw 是 **TOAST 过的整行 Core Data JSON**:一次探针要取回并解析整行。地点库「每个地点 × 5 个子查询」 的量级下 —— 实测 /places 70.1 秒、首页 43.6 秒才出页面(而库里没有任何一条 SQL 超过 1 秒: 时间全花在成千上万次「取整行再解析」上,进程 CPU 只用了 3s/135s)。 这批「渲染若干页面」的布局用例因此从 20 秒涨到 226~309 秒,整套验收从 6 分钟变 20+ 分钟。 - schema.sql:加派生列 visits.is_merged + 回填 + 给回填用的部分索引 (只索引确实带合并标记的那几十行,不用取回 raw)。 - stats.SoloVisitSQL 改成读这一列(判据仍然只此一份,读侧全部经由它)。 - 写入侧跟着刷:ingest 在 insertEntityRaw 之后、backup_merge 的 recountPlaces 里, 共用 stats.RefreshMergedFlagsSQL。 - 直接改留档里的合并标记(工具脚本 / 用例)之后也要刷一次,schema.sql 与函数注释都写明了。 实测:/places 70084ms → 206ms,/ 43633ms → 698ms(比「关掉判据」的对照还快)。 新增 page_render_budget_test.go:给 7 个关键页卡 10 秒上限的灾难探测器 (功能全对、只是慢的那种退化,之前没有任何用例会红)。
46 lines
1.7 KiB
Go
46 lines
1.7 KiB
Go
package web
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// TestKeyPagesRenderFast 是一道**灾难探测器**,不是基准测试。
|
|
//
|
|
// 2026-09-30 踩过一次:给「已并入 / 子段」做的收口写成每行去 entity_raw 取回**整行 Core Data JSON**
|
|
// 再解析 raw->>'ZMERGEDTO',而 raw 是 TOAST 过的整行——/places 要 70 秒、首页 44 秒才出页面,
|
|
// 而那批「渲染若干页面」的布局用例因此从 20 秒涨到 300 秒,整套验收从 6 分钟变 20+ 分钟。
|
|
// 当时没有任何用例会红:功能全对,只是慢。
|
|
//
|
|
// 所以这里给每个关键页面卡一个**很松**的上限(修好后实测 0.04~1.6 秒,这里卡 10 秒):
|
|
// 只拦「数量级」的退化,不拦正常的机器抖动。真要做性能基准另说。
|
|
func TestKeyPagesRenderFast(t *testing.T) {
|
|
f := newMovementFixture(t)
|
|
const budget = 10 * time.Second
|
|
for _, tg := range []struct {
|
|
name string
|
|
url string
|
|
h http.HandlerFunc
|
|
}{
|
|
{"主页", "/", f.srv.home},
|
|
{"统计", "/stats", f.srv.statsPage},
|
|
{"地点库", "/places", f.srv.placesPage},
|
|
{"报告", "/report", f.srv.reportPage},
|
|
{"时间线", "/timeline", f.srv.timelinePage},
|
|
{"后台首页", "/admin", f.srv.adminHome},
|
|
{"数据编辑", "/admin/edit", f.srv.editPage},
|
|
} {
|
|
start := time.Now()
|
|
rec := httptest.NewRecorder()
|
|
tg.h(rec, httptest.NewRequest("GET", tg.url, nil).WithContext(f.ctx))
|
|
el := time.Since(start)
|
|
t.Logf("%-8s %8dms %d bytes", tg.name, el.Milliseconds(), rec.Body.Len())
|
|
if el > budget {
|
|
t.Errorf("%s 渲染用了 %s(上限 %s)——多半又出现了「每行去读整行留档」这类退化",
|
|
tg.name, el.Round(time.Millisecond), budget)
|
|
}
|
|
}
|
|
}
|