openlang

httptest Server.Close가 멈추는데 테스트에서 실패 메시지가 안 나와요

Go 1.23에서 keep-alive 때문에 httptest.Server.Close가 멈추는 일이 있을 수 있나요? go test -timeout 5s ./...를 돌리는데 새 테스트가 통과도 실패도 안 하고 타임아웃까지 그냥 있어요. 그러다 panic: test timed out after 5s랑 goroutine 덤프가 나오는데, quickfix 어디에도 assertion 텍스트가 없어요.

덤프를 보면 테스트 goroutine은 httptest.(*Server).Close에 있고, 제가 defer로 건 srv.Close()에서 호출된 sync.WaitGroup.Wait에서 기다리고 있어요. 핸들러 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. 고유 링크

    Close는 처리 중인 요청을 기다려요. 문서에 있어요: https://pkg.go.dev/net/http/httptest#Server.Close

    핸들러가 <-gate에서 멈춰 있으니까 keep-alive는 문제가 아니에요. Fatalf가 발생하면 close(gate)가 실행되기도 전에 defer가 Close를 실행해요. 그래서 멈추는 거예요. gate를 만들자마자 defer close(gate)를 하고, 출력은 go test -v 2>&1 | pbcopy로 가져오세요.

    영어에서 번역원문 보기
  2. 고유 링크

    아, Fatalf가 실제로 발생하고 Close가 제 핸들러를 기다리는 거였군요. 이해됐어요, 덤프를 한참 봤는데 연결을 못 했어요. 고마워요.

    순서 관련해서 하나만요. defer는 후입선출로 실행되니까 defer srv.Close() 뒤에 둔 defer close(gate)가 먼저 실행되니 괜찮아요. 그런데 제 테스트는 ack를 풀려고 close(gate)를 명시적으로 호출한 다음 errc를 읽기도 해요. 그러면 정상 경로에서는 double close가 되어서 panic이 나겠죠. close를 sync.Once로 감싸서 두 군데에서 호출하는 게 가장 깔끔할 것 같은데, 더 간단한 패턴이 있을까요?

    두 번째 질문인데, 메시지가 안 나온 건 그냥 버퍼링 때문인가요? ./...에 -v 없이 돌리면 go test가 패키지가 끝날 때까지 출력을 쥐고 있고, 타임아웃 panic이 그 전에 프로세스를 죽이는 거라고 짐작하고 있어요. -v를 쓰면 멈추기 전에 Fatalf 줄이 스트리밍될 수도 있을까요? 한번 해볼게요. 여기엔 pbcopy가 없어서 파일로 리다이렉트해서 nvim으로 열어볼게요 ))

    영어에서 번역원문 보기
  3. 고유 링크

    Fatalf가 실행되면 핸들러가 아직 <-gate에서 멈춰 있는 상태에서 defer로 걸어둔 srv.Close()가 돌아요. Close는 그 핸들러를 기다리니까 사실상 데드락이에요. Close가 실행되기 전에 gate를 먼저 풀어 주세요. 메시지가 사라진 건 버퍼에 있는 출력이 flush되기 전에 타임아웃 panic이 프로세스를 죽여서예요. -v로 해 보세요.

    영어에서 번역원문 보기

같은 질문의 다른 갈래

  1. -WhatIf 정리 스크립트가 만료된 아카이브를 13개라고 하는데 파일은 전부 9개뿐이에요

    fedora 노트북에서 오래된 백업 아카이브를 정리하고 있어요 (pwsh 7.4, emacs, 그리고 네, 이거 수업 데모가 되어가고 있어요). 스크립트는 ~/srv/archives 아래에서 KeepDays보다 오래된 *.t…

    답글 2최근 활동 powershell
  2. init의 setq가 fill-column을 버퍼 하나에서만 바꿨다

    init에 (setq fill-column 72)를 오래전부터 넣어 뒀는데, 새 버퍼에서 M-q를 누르면 여전히 70에서 줄이 바뀐다는 걸 이제야 알았어요. (*scratch*에서는 72에서 바뀌어서 그동안 스스로를 속였어요…

    답글 3최근 활동 emacs-lisp
  3. SilentlyContinue는 철자만 나아진 On Error Resume Next

    물려받은 워크북에서 물려받은 PowerShell 스크립트로 넘어왔더니 옛 친구가 새 옷을 입고 있네요. cmdlet 호출 절반에는 -ErrorAction SilentlyContinue, 나머지에는 2>$null이 붙어 있어요…

    답글 0최근 활동 powershell