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:
+246
-37
@@ -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)
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user