openlang

httptest Server.Close 卡住,而且我的测试没有任何失败信息

httptest.Server.Close 在 Go 1.23 上开启 keep-alive 时有可能卡住吗?我跑的是 go test -timeout 5s ./...,新写的测试既不通过也不失败。它一直挂到超时,然后我拿到 panic: test timed out after 5s 和 goroutine dump,quickfix 里一点断言的文字都没有。

dump 里,测试 goroutine 停在 httptest.(*Server).Close,在 sync.WaitGroup.Wait 里等着,是被我 defer 的 srv.Close() 调用的。handler goroutine 在 chan receive,第三个在 persistConn.roundTrip 里的 select。

-count=1、-race 和 DisableKeepAlives 都没有任何变化。同一个文件里的 JSON 测试是能过的。

gate := make(chan struct{})
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	<-gate // gateway holds the ack
	w.WriteHeader(http.StatusAccepted)
}))
defer srv.Close()

c := NewClient(srv.URL, log.New(io.Discard, "", 0))
errc := make(chan error, 1)
go func() { errc <- c.Send(context.Background(), testBatch()) }()

time.Sleep(100 * time.Millisecond)
if got := c.Pending(); got != 1 {
	t.Fatalf("Pending() = %d while ack is held, want 1", got)
}

close(gate)

为什么 Close 会在这里阻塞,我的失败信息又跑哪去了?

从英语翻译查看原文

后续讨论 3

顺着彼此的依据读下去
  1. 固定链接

    啊,原来 Fatalf 确实触发了,然后 Close 在等我自己的 handler。说得通,我盯着那份 dump 看了半天也没联系起来。谢了。

    顺序上有一点,defer 是后进先出,所以放在 defer srv.Close() 后面的 defer close(gate) 会先于它执行,这样没问题。但我的测试也显式调用了 close(gate) 来放行 ack,然后再读 errc。这样正常路径上会重复 close 然后 panic。我觉得用 sync.Once 包住 close,两处都调用,是最干净的办法。或者有更简单的写法吗?

    第二个问题,丢失的那条消息只是缓冲的原因吗?我猜用 ./... 又没加 -v 的时候,go test 会把输出存着,等包跑完才打印,而超时 panic 先把它干掉了。会不会 -v 能在卡住之前就把 Fatalf 那行流式打出来?我去试试。这边没有 pbcopy,我就重定向到文件,再用 nvim 打开 ))

    从英语翻译查看原文
  2. 固定链接

    你的 Fatalf 会去执行 defer 的 srv.Close(),而这时 handler 还停在 <-gate 上。Close 要等那个 handler,所以基本上就是死锁。要在 Close 能跑之前先放行 gate。消息丢失是因为超时 panic 在缓冲输出刷出之前就把进程杀掉了。试试 -v。

    从英语翻译查看原文

同一个问题的其他角度

  1. -WhatIf 清理脚本说发现 13 个过期归档,可我一共只有 9 个文件

    在我的 fedora 笔记本上清理旧的备份归档(pwsh 7.4,emacs,对,这已经快变成我给班上做的演示了)。脚本会在 ~/srv/archives 下找出比 KeepDays 更早的 *.tar.gz 并删掉,我会先带 -WhatIf 跑一遍…

    回复 2最近活动 powershell
  2. 我的 init 只给一个 buffer 设置了 fill-column

    我的 init 里写了很久的 (setq fill-column 72),直到刚才才发现新建 buffer 里按 M-q 还是在 70 处折行。(在 *scratch* 里是在 72 处折行,我就是这样骗了自己。) fill-column 是自动 b…

    回复 3最近活动 emacs-lisp
  3. SilentlyContinue 就是拼写更好看的 On Error Resume Next

    从接手的工作簿换成接手的 PowerShell 脚本,结果在新外套里撞见了老朋友。一半的 cmdlet 调用上带着 -ErrorAction SilentlyContinue,其余的带着 2>$null(大概是以防万一吧)。 它比看上去更糟,是因为它…

    回复 0最近活动 powershell