Skip to content

test(triple): add concurrent send/close guards for duplexHTTPCall#3462

Merged
AlexStocks merged 1 commit into
apache:developfrom
chaojixinren:fix/duplex-http-call-concurrent-safety
Jun 30, 2026
Merged

test(triple): add concurrent send/close guards for duplexHTTPCall#3462
AlexStocks merged 1 commit into
apache:developfrom
chaojixinren:fix/duplex-http-call-concurrent-safety

Conversation

@chaojixinren

Copy link
Copy Markdown

Description

Investigate whether connect-go v1.19.2's fix for the concurrent Send +
CloseAndReceive nil-pointer dereference (connectrpc/connect-go#919) applies to
Dubbo-go's forked Triple runtime under protocol/triple/triple_protocol.

Conclusion: duplexHTTPCall is not affected. newDuplexHTTPCall allocates
the request-body io.Pipe at construction and never reassigns
requestBodyWriter, so it is always non-nil and concurrent Write/CloseWrite
are safe in any order. The upstream race — where the pipe was created lazily on
the first Send and a racing CloseWrite could leave it nil — is structurally
impossible here, so no synchronization change is ported.

What this PR does:

  • adds TestDuplexHTTPCallConcurrentWriteAndCloseWrite and
    TestDuplexHTTPCallConcurrentWriteCloseRace as regression guards (pass under
    go test -race) that pin the construction-time pipe invariant
  • documents the invariant on requestBodyWriter to keep the allocation out of a
    lazy/first-send path

This is intentionally not a broad connect-go upgrade; it only checks and guards
the runtime safety property called out in the issue.

Fixes #3458

Checklist

  • I confirm the target branch is develop
  • Code has passed local testing
  • I have added tests that prove my fix is effective or that my feature works

Investigate whether connect-go v1.19.2's fix for the concurrent Send +
CloseAndReceive nil-pointer dereference (connectrpc/connect-go#919) applies
to Dubbo-go's forked Triple runtime.

duplexHTTPCall is not affected: newDuplexHTTPCall allocates the request-body
io.Pipe at construction and never reassigns requestBodyWriter, so it is always
non-nil and concurrent Write/CloseWrite are safe in any order. The upstream
race, where the pipe was created lazily on the first Send and a racing
CloseWrite could leave it nil, is structurally impossible here, so no
synchronization change is ported.

- add TestDuplexHTTPCallConcurrentWriteAndCloseWrite and
  TestDuplexHTTPCallConcurrentWriteCloseRace as regression guards (run under
  -race) that pin the construction-time pipe invariant
- document the invariant on requestBodyWriter to keep the allocation out of a
  lazy/first-send path

Ref: apache#3458
@codecov-commenter

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 53.90%. Comparing base (8b5bbcf) to head (4183de5).
⚠️ Report is 3 commits behind head on develop.

Additional details and impacted files
@@             Coverage Diff             @@
##           develop    #3462      +/-   ##
===========================================
+ Coverage    53.57%   53.90%   +0.33%     
===========================================
  Files          460      461       +1     
  Lines        35182    35478     +296     
===========================================
+ Hits         18847    19125     +278     
+ Misses       14857    14848       -9     
- Partials      1478     1505      +27     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@AlexStocks AlexStocks left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Summary

结论: ✅ 可 Merge

变更范围很小且精准:仅在 duplexHTTPCall 结构体上补充了 requestBodyWriter 构建时赋值不可变的注释不变量,并新增两个并发 guard test 作为回归保护。

核心判断:

  1. newDuplexHTTPCallio.Pipe() 在构造时一次性分配,requestBodyWriter 永不被重新赋值 — 从代码结构上杜绝了 connect-go#919 的 nil-pointer dereference 场景。
  2. d.response 的写入发生在 makeRequest()close(d.responseReady) 之前,所有读取方通过 BlockUntilResponseReady() 等待 channel 关闭后才访问 — channel close 提供了 Go memory model 的 happens-before 保证,无数据竞争。
  3. 两个测试明确定位为 guard(非 reproducer),在正确代码上永远通过;但如果 pipe 创建被移到 lazy path,assert.NotNil 会立即失败 — 回归保护设计合理。
  4. 测试在 -race 下运行可有效检测潜在 data race。

仅有一个 P2 建议,见 inline comment。

@@ -0,0 +1,184 @@
// Copyright 2021-2023 Buf Technologies, Inc.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 新文件的 copyright 年份为 2021-2023,建议更新为 2021-2025(或至少 2025)。仓库内部分旧文件沿用旧年份可以理解,但新增文件建议使用当前年份。

@AlexStocks AlexStocks merged commit 9c955e9 into apache:develop Jun 30, 2026
8 checks passed
@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants