fix(notify): 根治低带宽控制面阻塞与传输写入卡死

- 为控制消息增加优先级、公平调度、队列字节预算和自适应批处理
- 支持可取消的写门等待,收紧 shared/dedicated bulk、stream 和 Reply 写入边界
- 修复 bulk reset/close、连接 handoff 和安全 profile 切换时序
- 保留旧取消与超时错误契约,新增阶段化 TransportSendError
- 增加 ReplyCtx、ReplyObjCtx、写超时配置及黑洞连接和竞态回归测试
This commit is contained in:
2026-08-14 10:16:37 +08:00
parent 98ef9e7fcc
commit 0826e17063
45 changed files with 4231 additions and 293 deletions
+95 -2
View File
@@ -6,11 +6,47 @@ import (
"io"
"math"
"net"
"sync"
"sync/atomic"
"testing"
"time"
)
type reattachBlockingConn struct {
started chan struct{}
finished chan struct{}
once sync.Once
}
func newReattachBlockingConn() *reattachBlockingConn {
return &reattachBlockingConn{started: make(chan struct{}), finished: make(chan struct{})}
}
func (c *reattachBlockingConn) Read([]byte) (int, error) { return 0, net.ErrClosed }
func (c *reattachBlockingConn) Close() error {
if c != nil {
c.once.Do(func() { close(c.finished) })
}
return nil
}
func (c *reattachBlockingConn) LocalAddr() net.Addr { return nil }
func (c *reattachBlockingConn) RemoteAddr() net.Addr { return nil }
func (c *reattachBlockingConn) SetDeadline(time.Time) error { return nil }
func (c *reattachBlockingConn) SetReadDeadline(time.Time) error { return nil }
func (c *reattachBlockingConn) SetWriteDeadline(time.Time) error { return nil }
func (c *reattachBlockingConn) Write([]byte) (int, error) {
closeOnce := func() {
select {
case <-c.started:
default:
close(c.started)
}
}
closeOnce()
<-c.finished
return 0, net.ErrClosed
}
func TestClientWriteToTransportUsesRuntimeConn(t *testing.T) {
client := NewClient().(*ClientCommon)
fallbackLeft, fallbackRight := net.Pipe()
@@ -23,12 +59,12 @@ func TestClientWriteToTransportUsesRuntimeConn(t *testing.T) {
client.conn = fallbackLeft
runtimeCtx, runtimeCancel := context.WithCancel(context.Background())
defer runtimeCancel()
client.setClientSessionRuntime(&clientSessionRuntime{
client.setClientSessionRuntimeWithCloseOld(&clientSessionRuntime{
conn: runtimeLeft,
stopCtx: runtimeCtx,
stopFn: runtimeCancel,
epoch: 1,
})
}, true)
payload := []byte("runtime-conn")
recvCh := make(chan []byte, 1)
@@ -350,3 +386,60 @@ func TestSetClientSessionRuntimeStopsOldBindingWorkersOnReattach(t *testing.T) {
t.Fatalf("old sender submit after reattach = %v, want %v", err, errTransportDetached)
}
}
func TestSetClientSessionRuntimeClosesOldConnBeforeStoppingWorkers(t *testing.T) {
client := NewClient().(*ClientCommon)
stopCtx, stopFn := context.WithCancel(context.Background())
defer stopFn()
queue := stario.NewQueueCtx(stopCtx, 4, math.MaxUint32)
oldConn := newReattachBlockingConn()
oldBinding := newTransportBinding(oldConn, queue)
oldSender := newTestBulkBatchSender(oldBinding)
oldBinding.bulkMu.Lock()
oldBinding.bulkSender = oldSender
oldBinding.bulkMu.Unlock()
client.setClientSessionRuntime(&clientSessionRuntime{
transport: oldBinding,
conn: oldConn,
stopCtx: stopCtx,
stopFn: stopFn,
queue: queue,
epoch: 1,
})
writeDone := make(chan error, 1)
go func() {
writeDone <- oldSender.submitData(context.Background(), 1, 1, bulkFastPathVersionV1, []byte("blocked"))
}()
select {
case <-oldConn.started:
case <-time.After(time.Second):
t.Fatal("old sender did not enter physical write")
}
newLeft, newRight := net.Pipe()
defer newLeft.Close()
defer newRight.Close()
newBinding := newTransportBinding(newLeft, queue)
reattachDone := make(chan struct{})
go func() {
client.setClientSessionRuntimeWithCloseOld(&clientSessionRuntime{
transport: newBinding,
conn: newLeft,
stopCtx: stopCtx,
stopFn: stopFn,
queue: queue,
epoch: 2,
}, true)
close(reattachDone)
}()
select {
case <-reattachDone:
case <-time.After(time.Second):
t.Fatal("reattach remained blocked while old sender was writing")
}
select {
case <-writeDone:
case <-time.After(time.Second):
t.Fatal("old sender did not exit after old connection close")
}
}