Files
weichen 1ef1fdeac9
ci / vet-and-test (pull_request) Successful in 42s
Make notice redelivery iterative instead of self-recursive
Why: deliverNotices re-invoked itself synchronously from a defer after a failed startNoticeRun requeued the batch - a persistently failing delivery (busy conversation, missing row, slow DB) recursed without bound and overflowed the stack (real-PG CI job 2352, internal/runmgr).

Evidence: regression test TestNoticeDeliveryFailureIsBounded (missing conversation row) hangs on the old code and passes with the fix; full backend suite green on SQLite and against real PostgreSQL 18.6 + Redis; clean-HEAD reproduction isolated an unrelated count=2 admin-login artifact.

Impact: failed batches now return after requeue (retried on the next enqueue or restart); notices arriving during delivery are picked up by a bounded loop tail instead of defer recursion; no behavior change on the success path.

Refs: Dev PR #333; plan R8 real-database gate.
2026-10-11 08:12:03 +00:00

47 lines
1.4 KiB
Go

package runmgr
import (
"testing"
"time"
"agent-system-backend/internal/store"
core "agent-system-core"
)
// U3 真库门禁回归(job 2352 栈溢出):投递失败(会话行不存在)时旧实现的
// defer 同步再入 deliverNotices 无限自递归。修复后:单次有界返回、通知
// 保留在队列,由下一次触发或重启重投驱动。
func TestNoticeDeliveryFailureIsBounded(t *testing.T) {
runner := &gateRunner{parts: []core.Part{{Type: core.PartText, Text: "结果"}}}
c, _ := newSpawnEnv(t, runner)
c.mgr.SetWorkspaceRoot(t.TempDir())
convID := core.ID(store.NewID("cnv"))
c.mgr.enqueueNotice(convID, bgNotice{owner: c.run.Owner, name: "检索", payload: core.AgentResultPayload{Status: "completed"}})
done := make(chan struct{})
go func() {
c.mgr.deliverNotices(convID)
close(done)
}()
select {
case <-done:
case <-time.After(5 * time.Second):
t.Fatal("deliverNotices 未返回:失败重投仍在递归/自旋")
}
c.mgr.noticeMu.Lock()
queued := len(c.mgr.notices[convID])
c.mgr.noticeMu.Unlock()
if queued != 1 {
t.Fatalf("失败应保留待投通知:queued=%d", queued)
}
// 第二次触发同样有界(不因重试堆积)。
c.mgr.deliverNotices(convID)
c.mgr.noticeMu.Lock()
queued = len(c.mgr.notices[convID])
c.mgr.noticeMu.Unlock()
if queued != 1 {
t.Fatalf("重复失败不应堆积通知:queued=%d", queued)
}
}