From 8a274e120daabe2056891dbba7ffff79166a29dc Mon Sep 17 00:00:00 2001 From: AskaEth Date: Tue, 13 Oct 2026 21:54:38 +0800 Subject: [PATCH] =?UTF-8?q?docs(memory):=20=E5=88=B0=E6=9C=9F=E6=B8=85?= =?UTF-8?q?=E6=89=AB=E7=9A=84=E6=B3=A8=E9=87=8A=E6=94=B9=E7=94=A8=E7=AC=A6?= =?UTF-8?q?=E5=8F=B7=E5=BC=95=E7=94=A8=EF=BC=8C=E4=B8=8D=E5=86=8D=E5=86=99?= =?UTF-8?q?=E4=BC=9A=E6=BC=82=E7=9A=84=E8=A1=8C=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit review r2(p2-1items)指出:r1 那笔注释里的行号是旧值 —— 我在文件更前面插了 12 行之后 `Stop` 整体下移,而注释里仍写着「`Stop` 在 `tidy_service.go:214` 就放锁, 到 `:219` 才 Wait」(214 是 `Stop` 的文档注释行、219 是 `s.mu.Lock()`)。 行号会漂、符号不会,因此改成符号 + 相对位置: - 「在同一个 `func (s *TidyService) Stop` 里,`s.mu.Unlock()` 在前、`s.wg.Wait()` (另一个 goroutine 里)在后,两者之间没有锁」; - 「`Start` 只在组合根启动期调一次(`cmd/cyrene/main.go` 的 `a.memoryTidy.Start(ctx)`)、`Stop` 只在关停链里调一次(`cmd/cyrene/wire_services.go` 的 `_ = a.memoryTidy.Stop(ctx)`)」。 同一类问题只此一处遗留(`expire.go` 里「照 `preinsert_gc.go:65-69` 的批删范式」 同样改成符号:`ingestReviewSweepBatchSize` / `maxIngestReviewSweepBatches` 那一对 常量)。已 grep 全部改动文件确认再无 `*.go:<行号>` 形式的引用。 (本提交信息刻意不复述任何行号。) --- internal/runtime/memory/expire.go | 5 +++-- internal/runtime/memory/tidy_service.go | 12 +++++++----- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/internal/runtime/memory/expire.go b/internal/runtime/memory/expire.go index b0e0468..b7731ef 100644 --- a/internal/runtime/memory/expire.go +++ b/internal/runtime/memory/expire.go @@ -47,8 +47,9 @@ const ( // expireSweepBatchSize 是单批取行 / 删除的行数上限,maxExpireSweepBatches 是 // 一轮清扫的批数上限。两者一起把「一轮清扫」的时长、单次内存占用与**连接池 - // 占用**(本项目的 SQLite 池是单连接)都变得有界 —— 照 `preinsert_gc.go:65-69` - // 的批删范式(`design/23 §19.3-3`「一批删除有条数 / 批数上限」)。 + // 占用**(本项目的 SQLite 池是单连接)都变得有界 —— 照 `preinsert_gc.go` 里 + // `ingestReviewSweepBatchSize` / `maxIngestReviewSweepBatches` 那一对常量的 + // 批删范式(`design/23 §19.3-3`「一批删除有条数 / 批数上限」)。 // 上限之积 = 一轮最多删 1 万行;积压时剩下的留给下一轮,不一次 DELETE 全表。 expireSweepBatchSize = 500 maxExpireSweepBatches = 20 diff --git a/internal/runtime/memory/tidy_service.go b/internal/runtime/memory/tidy_service.go index cd31663..ee9cb31 100644 --- a/internal/runtime/memory/tidy_service.go +++ b/internal/runtime/memory/tidy_service.go @@ -141,11 +141,13 @@ func (s *TidyService) Start(ctx context.Context) { // 或「Wait 漏掉一笔尚未 Add 的 goroutine(那会当场泄漏一个清扫 goroutine)」 // 这两种窗口。 // - // ⚠️ 这把锁**不**覆盖 `Stop` 的 `wg.Wait`:`Stop` 在 `tidy_service.go:214` - // 就放锁,到 `:219`(另一个 goroutine 里)才 Wait。因此 WaitGroup 的复用纪律 - // (「上一轮 Wait 返回前不得再 Add」)**不是**由这把锁给的,而是靠 - // 「`Start` / `Stop` 在整个进程生命周期各只调用一次」维持:`Start` 只在 - // `cmd/cyrene/main.go:97` 调、`Stop` 只在 `cmd/cyrene/wire_services.go:212` 调。 + // ⚠️ 这把锁**不**覆盖 `Stop` 的 `wg.Wait`:在同一个 `func (s *TidyService) Stop` + // 里,`s.mu.Unlock()` 在前、`s.wg.Wait()`(另一个 goroutine 里)在后,两者之间 + // 没有锁。因此 WaitGroup 的复用纪律(「上一轮 Wait 返回前不得再 Add」)**不是** + // 由这把锁给的,而是靠「`Start` / `Stop` 在整个进程生命周期各只调用一次」维持: + // `Start` 只在组合根启动期调一次(`cmd/cyrene/main.go` 的 + // `a.memoryTidy.Start(ctx)`)、`Stop` 只在关停链里调一次(`cmd/cyrene/wire_services.go` + // 的 `_ = a.memoryTidy.Stop(ctx)`)。 // 交叉调用(`Start` 与在飞的 `Stop` 并发)在 `-race` 下会 `panic: sync: // WaitGroup is reused before previous Wait has returned` —— 实测该 panic 在 // M3 之前的基线上同样可复现(既有性质,非本切片引入),只是当前调用点**不可达**。