fix(notify): 根治低带宽控制面阻塞与传输写入卡死
- 为控制消息增加优先级、公平调度、队列字节预算和自适应批处理 - 支持可取消的写门等待,收紧 shared/dedicated bulk、stream 和 Reply 写入边界 - 修复 bulk reset/close、连接 handoff 和安全 profile 切换时序 - 保留旧取消与超时错误契约,新增阶段化 TransportSendError - 增加 ReplyCtx、ReplyObjCtx、写超时配置及黑洞连接和竞态回归测试
This commit is contained in:
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user