fix(notify): 修复传输生命周期竞态,完善背压与协议边界

- 完善 stream/bulk DataID 分配、预留和双向命名空间,修复并发打开及 dedicated/shared 回退时的 ID 冲突
- 将收发、回复、恢复任务和 sidecar 绑定原始会话与物理连接,防止重连后的旧消息误操作新连接
- 加强 close/reset 身份校验及实例移除检查,修复 dedicated attach 失败、通道引用和资源回收竞态
- 收紧批量发送器停止准入,确保在途入队完成后统一清理请求、缓冲区和等待者
- 修复 record 满队列死锁、取消时序号消耗及关闭竞态,确保关闭有界并返回真实错误
- 增加协商式 record 逻辑半关闭,保留反向 ACK;通过 reset 传递 RecordFailure,避免背压掩盖原始失败原因
- 补齐帧长度、批次数量、序号溢出和未确认窗口校验,提前拒绝超限数据并按字节预算拆批
- 为入站分发增加全局及单连接的条数、字节预算和阻塞背压,关闭时唤醒等待者,消除正常断连日志噪音
- 完善 bulk 窗口释放失败处理与传输诊断,补充并发、重连、背压、协议边界及真实 TCP 回归覆盖
This commit is contained in:
2026-09-23 15:33:17 +08:00
parent 0826e17063
commit 1f2e74acca
79 changed files with 9190 additions and 1013 deletions
+246 -37
View File
@@ -10,6 +10,8 @@ type bulkDedicatedSidecar struct {
laneID uint32
conn net.Conn
closeOnce sync.Once
connMu sync.Mutex
closed bool
senderMu sync.Mutex
sender *bulkDedicatedLaneSender
@@ -17,6 +19,7 @@ type bulkDedicatedSidecar struct {
type bulkDedicatedLane struct {
id uint32
route clientSessionRoute
activeBulks int
sidecar *bulkDedicatedSidecar
attachFlight *bulkDedicatedAttachFlight
@@ -81,8 +84,12 @@ func (s *bulkDedicatedSidecar) close() {
return
}
s.closeOnce.Do(func() {
if s.conn != nil {
_ = s.conn.Close()
s.connMu.Lock()
s.closed = true
conn := s.conn
s.connMu.Unlock()
if conn != nil {
_ = conn.Close()
}
if sender := s.laneSenderSnapshot(); sender != nil {
sender.stop()
@@ -90,6 +97,21 @@ func (s *bulkDedicatedSidecar) close() {
})
}
// withConn serializes logical attachment with sidecar cleanup. Without this
// boundary a cleanup can close the socket after lookup but before the bulk
// handle installs it.
func (s *bulkDedicatedSidecar) withConn(fn func(net.Conn) error) error {
if s == nil || fn == nil {
return errTransportDetached
}
s.connMu.Lock()
defer s.connMu.Unlock()
if s.closed || s.conn == nil {
return errTransportDetached
}
return fn(s.conn)
}
func (s *bulkDedicatedSidecar) laneSenderSnapshot() *bulkDedicatedLaneSender {
if s == nil {
return nil
@@ -108,10 +130,13 @@ func (s *bulkDedicatedSidecar) laneSenderWithFactory(factory func(net.Conn) *bul
if s.sender != nil {
return s.sender
}
if s.conn == nil {
s.connMu.Lock()
if s.closed || s.conn == nil {
s.connMu.Unlock()
return nil
}
s.sender = factory(s.conn)
s.connMu.Unlock()
return s.sender
}
@@ -119,18 +144,13 @@ func (c *ClientCommon) clientDedicatedSidecarSnapshot() *bulkDedicatedSidecar {
if c == nil {
return nil
}
route := c.clientSessionRouteSnapshot()
c.bulkDedicatedSidecarMu.Lock()
defer c.bulkDedicatedSidecarMu.Unlock()
return firstClientDedicatedSidecarLocked(c.bulkDedicatedLanes)
}
func firstClientDedicatedSidecarLocked(lanes map[uint32]*bulkDedicatedLane) *bulkDedicatedSidecar {
var (
selected *bulkDedicatedSidecar
bestID uint32
)
for laneID, lane := range lanes {
if lane == nil || lane.sidecar == nil {
var selected *bulkDedicatedSidecar
var bestID uint32
for laneID, lane := range c.bulkDedicatedLanes {
if lane == nil || lane.sidecar == nil || !sameClientDedicatedLaneRoute(lane, route) {
continue
}
if selected == nil || laneID < bestID {
@@ -142,18 +162,31 @@ func firstClientDedicatedSidecarLocked(lanes map[uint32]*bulkDedicatedLane) *bul
}
func (c *ClientCommon) reserveBulkDedicatedLane() uint32 {
laneID, _ := c.reserveBulkDedicatedLaneAtRoute(c.clientSessionRouteSnapshot())
return laneID
}
func (c *ClientCommon) reserveBulkDedicatedLaneAtRoute(route clientSessionRoute) (uint32, error) {
if c == nil {
return normalizeBulkDedicatedLaneID(0)
return 0, errBulkClientNil
}
if route.bound() {
if err := c.ensureClientSessionRouteSendReady(route); err != nil {
return 0, err
}
}
c.bulkDedicatedSidecarMu.Lock()
defer c.bulkDedicatedSidecarMu.Unlock()
if route.bound() && !c.clientSessionRouteCurrent(route) {
c.bulkDedicatedSidecarMu.Unlock()
return 0, transportDetachedSessionEpochError()
}
if c.bulkDedicatedLanes == nil {
c.bulkDedicatedLanes = make(map[uint32]*bulkDedicatedLane)
}
limit := c.bulkDedicatedLaneLimitSnapshot()
var best *bulkDedicatedLane
for _, lane := range c.bulkDedicatedLanes {
if lane == nil {
if lane == nil || !sameClientDedicatedLaneRoute(lane, route) {
continue
}
if best == nil || lane.activeBulks < best.activeBulks || (lane.activeBulks == best.activeBulks && lane.id < best.id) {
@@ -161,16 +194,73 @@ func (c *ClientCommon) reserveBulkDedicatedLane() uint32 {
}
}
if best == nil || ((limit <= 0 || len(c.bulkDedicatedLanes) < limit) && best.activeBulks > 0) {
c.bulkDedicatedNextLaneID++
laneID := normalizeBulkDedicatedLaneID(c.bulkDedicatedNextLaneID)
best = &bulkDedicatedLane{id: laneID}
laneID := c.bulkDedicatedNextLaneID
for {
laneID++
laneID = normalizeBulkDedicatedLaneID(laneID)
if _, exists := c.bulkDedicatedLanes[laneID]; !exists {
break
}
}
c.bulkDedicatedNextLaneID = laneID
best = &bulkDedicatedLane{id: laneID, route: route}
c.bulkDedicatedLanes[laneID] = best
}
best.activeBulks++
return best.id
c.bulkDedicatedSidecarMu.Unlock()
return best.id, nil
}
func (c *ClientCommon) retainBulkDedicatedLane(laneID uint32) uint32 {
_ = c.retainBulkDedicatedLaneAtRoute(laneID, c.clientSessionRouteSnapshot())
return normalizeBulkDedicatedLaneID(laneID)
}
func (c *ClientCommon) retainBulkDedicatedLaneAtRoute(laneID uint32, route clientSessionRoute) error {
laneID = normalizeBulkDedicatedLaneID(laneID)
if c == nil {
return errBulkClientNil
}
if route.bound() {
if err := c.ensureClientSessionRouteSendReady(route); err != nil {
return err
}
}
c.bulkDedicatedSidecarMu.Lock()
if route.bound() && !c.clientSessionRouteCurrent(route) {
c.bulkDedicatedSidecarMu.Unlock()
return transportDetachedSessionEpochError()
}
if c.bulkDedicatedLanes == nil {
c.bulkDedicatedLanes = make(map[uint32]*bulkDedicatedLane)
}
lane := c.bulkDedicatedLanes[laneID]
var retiredSidecar *bulkDedicatedSidecar
var retiredFlight *bulkDedicatedAttachFlight
if lane == nil || !sameClientDedicatedLaneRoute(lane, route) {
if lane != nil {
retiredSidecar = lane.sidecar
retiredFlight = lane.attachFlight
}
lane = &bulkDedicatedLane{id: laneID, route: route}
c.bulkDedicatedLanes[laneID] = lane
}
lane.activeBulks++
c.bulkDedicatedSidecarMu.Unlock()
if retiredSidecar != nil {
retiredSidecar.close()
}
if retiredFlight != nil {
retiredFlight.finish(errTransportDetached)
}
return nil
}
func (c *ClientCommon) releaseBulkDedicatedLane(laneID uint32) {
c.releaseBulkDedicatedLaneAtRoute(laneID, c.clientSessionRouteSnapshot())
}
func (c *ClientCommon) releaseBulkDedicatedLaneAtRoute(laneID uint32, route clientSessionRoute) {
if c == nil {
return
}
@@ -178,7 +268,7 @@ func (c *ClientCommon) releaseBulkDedicatedLane(laneID uint32) {
c.bulkDedicatedSidecarMu.Lock()
defer c.bulkDedicatedSidecarMu.Unlock()
lane := c.bulkDedicatedLanes[laneID]
if lane == nil {
if lane == nil || !sameClientDedicatedLaneRoute(lane, route) {
return
}
if lane.activeBulks > 0 {
@@ -189,43 +279,105 @@ func (c *ClientCommon) releaseBulkDedicatedLane(laneID uint32) {
}
}
func sameClientDedicatedLaneRoute(lane *bulkDedicatedLane, route clientSessionRoute) bool {
if lane == nil {
return false
}
if lane.route.bound() || route.bound() {
return sameClientSessionRoute(lane.route, route)
}
return true
}
func (c *ClientCommon) clientDedicatedSidecarSnapshotForLane(laneID uint32) *bulkDedicatedSidecar {
if c == nil {
return nil
}
route := c.clientSessionRouteSnapshot()
return c.clientDedicatedSidecarSnapshotForLaneAtRoute(laneID, route)
}
func (c *ClientCommon) clientDedicatedSidecarSnapshotForLaneAtRoute(laneID uint32, route clientSessionRoute) *bulkDedicatedSidecar {
if c == nil {
return nil
}
if route.bound() && !c.clientSessionRouteCurrent(route) {
return nil
}
laneID = normalizeBulkDedicatedLaneID(laneID)
c.bulkDedicatedSidecarMu.Lock()
defer c.bulkDedicatedSidecarMu.Unlock()
if route.bound() && !c.clientSessionRouteCurrent(route) {
return nil
}
if lane := c.bulkDedicatedLanes[laneID]; lane != nil {
if !sameClientDedicatedLaneRoute(lane, route) {
return nil
}
return lane.sidecar
}
return nil
}
func (c *ClientCommon) beginClientDedicatedSidecarAttach(laneID uint32) (*bulkDedicatedSidecar, *bulkDedicatedAttachFlight, bool) {
func (c *ClientCommon) beginClientDedicatedSidecarAttach(laneID uint32, route clientSessionRoute) (*bulkDedicatedSidecar, *bulkDedicatedAttachFlight, bool, error) {
if c == nil {
return nil, nil, false
return nil, nil, false, errBulkClientNil
}
if err := c.ensureClientSessionRouteSendReady(route); err != nil {
return nil, nil, false, err
}
laneID = normalizeBulkDedicatedLaneID(laneID)
c.bulkDedicatedSidecarMu.Lock()
defer c.bulkDedicatedSidecarMu.Unlock()
if !c.clientSessionRouteCurrent(route) {
c.bulkDedicatedSidecarMu.Unlock()
return nil, nil, false, transportDetachedSessionEpochError()
}
if c.bulkDedicatedLanes == nil {
c.bulkDedicatedLanes = make(map[uint32]*bulkDedicatedLane)
}
lane := c.bulkDedicatedLanes[laneID]
if lane == nil {
lane = &bulkDedicatedLane{id: laneID}
var retiredSidecar *bulkDedicatedSidecar
var retiredFlight *bulkDedicatedAttachFlight
if lane == nil || !sameClientDedicatedLaneRoute(lane, route) {
if lane != nil {
retiredSidecar = lane.sidecar
retiredFlight = lane.attachFlight
}
lane = &bulkDedicatedLane{id: laneID, route: route}
c.bulkDedicatedLanes[laneID] = lane
}
if lane.sidecar != nil {
return lane.sidecar, nil, false
activeSidecar := lane.sidecar
c.bulkDedicatedSidecarMu.Unlock()
if retiredSidecar != nil {
retiredSidecar.close()
}
if retiredFlight != nil {
retiredFlight.finish(errTransportDetached)
}
return activeSidecar, nil, false, nil
}
if lane.attachFlight != nil {
return nil, lane.attachFlight, false
pendingFlight := lane.attachFlight
c.bulkDedicatedSidecarMu.Unlock()
if retiredSidecar != nil {
retiredSidecar.close()
}
if retiredFlight != nil {
retiredFlight.finish(errTransportDetached)
}
return nil, pendingFlight, false, nil
}
flight := newBulkDedicatedAttachFlight()
lane.attachFlight = flight
return nil, flight, true
c.bulkDedicatedSidecarMu.Unlock()
if retiredSidecar != nil {
retiredSidecar.close()
}
if retiredFlight != nil {
retiredFlight.finish(errTransportDetached)
}
return nil, flight, true, nil
}
func (c *ClientCommon) finishClientDedicatedSidecarAttach(laneID uint32, flight *bulkDedicatedAttachFlight, err error) {
@@ -245,25 +397,59 @@ func (c *ClientCommon) finishClientDedicatedSidecarAttach(laneID uint32, flight
}
func (c *ClientCommon) installClientDedicatedSidecar(laneID uint32, sidecar *bulkDedicatedSidecar) (*bulkDedicatedSidecar, bool) {
active, installed, _ := c.installClientDedicatedSidecarAtRoute(laneID, sidecar, c.clientSessionRouteSnapshot())
return active, installed
}
func (c *ClientCommon) installClientDedicatedSidecarAtRoute(laneID uint32, sidecar *bulkDedicatedSidecar, route clientSessionRoute) (*bulkDedicatedSidecar, bool, error) {
if c == nil || sidecar == nil {
return nil, false
return nil, false, errBulkClientNil
}
if route.bound() {
if err := c.ensureClientSessionRouteSendReady(route); err != nil {
return nil, false, err
}
}
laneID = normalizeBulkDedicatedLaneID(laneID)
c.bulkDedicatedSidecarMu.Lock()
defer c.bulkDedicatedSidecarMu.Unlock()
if route.bound() && !c.clientSessionRouteCurrent(route) {
c.bulkDedicatedSidecarMu.Unlock()
return nil, false, transportDetachedSessionEpochError()
}
if c.bulkDedicatedLanes == nil {
c.bulkDedicatedLanes = make(map[uint32]*bulkDedicatedLane)
}
lane := c.bulkDedicatedLanes[laneID]
if lane == nil {
lane = &bulkDedicatedLane{id: laneID}
var retiredSidecar *bulkDedicatedSidecar
var retiredFlight *bulkDedicatedAttachFlight
if lane == nil || !sameClientDedicatedLaneRoute(lane, route) {
if lane != nil {
retiredSidecar = lane.sidecar
retiredFlight = lane.attachFlight
}
lane = &bulkDedicatedLane{id: laneID, route: route}
c.bulkDedicatedLanes[laneID] = lane
}
if lane.sidecar != nil {
return lane.sidecar, false
activeSidecar := lane.sidecar
c.bulkDedicatedSidecarMu.Unlock()
if retiredSidecar != nil {
retiredSidecar.close()
}
if retiredFlight != nil {
retiredFlight.finish(errTransportDetached)
}
return activeSidecar, false, nil
}
lane.sidecar = sidecar
return sidecar, true
c.bulkDedicatedSidecarMu.Unlock()
if retiredSidecar != nil {
retiredSidecar.close()
}
if retiredFlight != nil {
retiredFlight.finish(errTransportDetached)
}
return sidecar, true, nil
}
func (c *ClientCommon) clearClientDedicatedSidecar(laneID uint32, sidecar *bulkDedicatedSidecar) bool {
@@ -285,9 +471,16 @@ func (c *ClientCommon) clearClientDedicatedSidecar(laneID uint32, sidecar *bulkD
}
func (c *ClientCommon) closeClientDedicatedSidecar() {
c.closeClientDedicatedSidecarWithError(errServiceShutdown)
}
func (c *ClientCommon) closeClientDedicatedSidecarWithError(closeErr error) {
if c == nil {
return
}
if closeErr == nil {
closeErr = errServiceShutdown
}
c.bulkDedicatedSidecarMu.Lock()
lanes := c.bulkDedicatedLanes
c.bulkDedicatedLanes = make(map[uint32]*bulkDedicatedLane)
@@ -300,7 +493,7 @@ func (c *ClientCommon) closeClientDedicatedSidecar() {
lane.sidecar.close()
}
if lane.attachFlight != nil {
lane.attachFlight.finish(errServiceShutdown)
lane.attachFlight.finish(closeErr)
}
}
}
@@ -358,6 +551,20 @@ func (s *ServerCommon) serverDedicatedSidecarSnapshotForLane(logical *LogicalCon
return nil
}
func (s *ServerCommon) serverDedicatedSidecarCurrent(logical *LogicalConn, sidecar *bulkDedicatedSidecar) bool {
if s == nil || logical == nil || sidecar == nil {
return false
}
return s.serverDedicatedSidecarSnapshotForLane(logical, sidecar.laneID) == sidecar
}
func bulkDedicatedSidecarConnCurrent(bulk *bulkHandle, sidecar *bulkDedicatedSidecar) bool {
if bulk == nil || sidecar == nil || sidecar.conn == nil {
return false
}
return bulk.dedicatedConnSnapshot() == sidecar.conn
}
func (s *ServerCommon) installServerDedicatedSidecar(logical *LogicalConn, laneID uint32, sidecar *bulkDedicatedSidecar) *bulkDedicatedSidecar {
if s == nil || logical == nil || sidecar == nil {
return nil
@@ -447,5 +654,7 @@ func (s *ServerCommon) attachServerDedicatedSidecarIfExists(logical *LogicalConn
if sidecar == nil || sidecar.conn == nil {
return
}
_ = bulk.attachDedicatedConnShared(sidecar.conn)
_ = sidecar.withConn(func(conn net.Conn) error {
return bulk.attachDedicatedConnShared(conn)
})
}