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:
+60
-22
@@ -100,31 +100,43 @@ func (c *ClientCommon) handleTransportReadResultWithSession(stopCtx context.Cont
|
||||
}
|
||||
|
||||
func (c *ClientCommon) handleTransportReadResultWithSessionDispatcher(stopCtx context.Context, conn net.Conn, queue *stario.StarQueue, readNum int, data []byte, err error, epoch uint64, dispatcher *inboundDispatcher) bool {
|
||||
binding := newTransportBinding(conn, queue)
|
||||
route := c.clientSessionRouteSnapshot()
|
||||
if route.binding == nil || route.binding.connSnapshot() != conn || route.binding.queueSnapshot() != queue || route.epoch != epoch {
|
||||
route = clientSessionRoute{binding: newTransportBinding(conn, queue), epoch: epoch, sessionStopCtx: stopCtx, transportStopCtx: stopCtx}
|
||||
}
|
||||
return c.handleTransportReadResultAtRoute(stopCtx, route, readNum, data, err, dispatcher)
|
||||
}
|
||||
|
||||
func (c *ClientCommon) handleTransportReadResultAtRoute(stopCtx context.Context, route clientSessionRoute, readNum int, data []byte, err error, dispatcher *inboundDispatcher) bool {
|
||||
binding := route.binding
|
||||
queue := binding.queueSnapshot()
|
||||
if err == os.ErrDeadlineExceeded {
|
||||
if readNum != 0 && queue != nil {
|
||||
if !c.pushMessageFast(queue, data[:readNum], dispatcher) {
|
||||
queue.ParseMessage(data[:readNum], "b612")
|
||||
if !c.pushMessageFastAtRoute(route, queue, data[:readNum], dispatcher) {
|
||||
queue.ParseMessage(data[:readNum], route.inboundQueueKey())
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
if err != nil {
|
||||
if c.showError || c.debugMode {
|
||||
fmt.Println("client read error", err)
|
||||
}
|
||||
select {
|
||||
case <-sessionStopChan(stopCtx):
|
||||
c.closeClientTransportBinding(binding)
|
||||
return false
|
||||
default:
|
||||
}
|
||||
c.stopClientSessionIfCurrent(epoch, "client read error", err)
|
||||
// An expected shutdown closes the socket, which surfaces here as a
|
||||
// read on a closed connection. Only report reads that happen while the
|
||||
// session is still supposed to be running.
|
||||
if c.showError || c.debugMode {
|
||||
fmt.Println("client read error", err)
|
||||
}
|
||||
c.stopClientSessionIfCurrent(route.epoch, "client read error", err)
|
||||
return false
|
||||
}
|
||||
if queue != nil {
|
||||
if !c.pushMessageFast(queue, data[:readNum], dispatcher) {
|
||||
queue.ParseMessage(data[:readNum], "b612")
|
||||
if !c.pushMessageFastAtRoute(route, queue, data[:readNum], dispatcher) {
|
||||
queue.ParseMessage(data[:readNum], route.inboundQueueKey())
|
||||
}
|
||||
}
|
||||
return true
|
||||
@@ -144,6 +156,15 @@ func (c *ClientCommon) readTransportPayloadPooled(conn net.Conn, reader *stario.
|
||||
}
|
||||
|
||||
func (c *ClientCommon) handleTransportPayloadReadResultWithSession(stopCtx context.Context, binding *transportBinding, payload []byte, release func(), err error, epoch uint64, dispatcher *inboundDispatcher) bool {
|
||||
route := c.clientSessionRouteSnapshot()
|
||||
if route.binding != binding || route.epoch != epoch {
|
||||
route = clientSessionRoute{binding: binding, epoch: epoch, sessionStopCtx: stopCtx, transportStopCtx: stopCtx}
|
||||
}
|
||||
return c.handleTransportPayloadReadResultAtRoute(stopCtx, route, payload, release, err, dispatcher)
|
||||
}
|
||||
|
||||
func (c *ClientCommon) handleTransportPayloadReadResultAtRoute(stopCtx context.Context, route clientSessionRoute, payload []byte, release func(), err error, dispatcher *inboundDispatcher) bool {
|
||||
binding := route.binding
|
||||
if err == os.ErrDeadlineExceeded {
|
||||
return true
|
||||
}
|
||||
@@ -151,23 +172,28 @@ func (c *ClientCommon) handleTransportPayloadReadResultWithSession(stopCtx conte
|
||||
if release != nil {
|
||||
release()
|
||||
}
|
||||
if c.showError || c.debugMode {
|
||||
fmt.Println("client read error", err)
|
||||
}
|
||||
select {
|
||||
case <-sessionStopChan(stopCtx):
|
||||
c.closeClientTransportBinding(binding)
|
||||
return false
|
||||
default:
|
||||
}
|
||||
c.stopClientSessionIfCurrent(epoch, "client read error", err)
|
||||
// See handleTransportReadResultAtRoute: shutdown reads are expected.
|
||||
if c.showError || c.debugMode {
|
||||
fmt.Println("client read error", err)
|
||||
}
|
||||
c.stopClientSessionIfCurrent(route.epoch, "client read error", err)
|
||||
return false
|
||||
}
|
||||
c.dispatchTransportPayloadFast(payload, release, dispatcher)
|
||||
c.dispatchTransportPayloadFastAtRoute(route, payload, release, dispatcher)
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *ClientCommon) dispatchTransportPayloadFast(payload []byte, release func(), dispatcher *inboundDispatcher) {
|
||||
c.dispatchTransportPayloadFastAtRoute(c.clientSessionRouteSnapshot(), payload, release, dispatcher)
|
||||
}
|
||||
|
||||
func (c *ClientCommon) dispatchTransportPayloadFastAtRoute(route clientSessionRoute, payload []byte, release func(), dispatcher *inboundDispatcher) {
|
||||
if len(payload) == 0 {
|
||||
if release != nil {
|
||||
release()
|
||||
@@ -181,12 +207,12 @@ func (c *ClientCommon) dispatchTransportPayloadFast(payload []byte, release func
|
||||
}
|
||||
return
|
||||
}
|
||||
if c.tryDispatchBorrowedTransportPlain(plain, plainRelease) {
|
||||
if c.tryDispatchBorrowedTransportPlainAtRoute(route, plain, plainRelease) {
|
||||
return
|
||||
}
|
||||
if dispatcher == nil {
|
||||
now := time.Now()
|
||||
err := c.dispatchInboundTransportPlain(plain, now)
|
||||
err := c.dispatchInboundTransportPlainAtRoute(route, plain, now)
|
||||
if plainRelease != nil {
|
||||
plainRelease()
|
||||
}
|
||||
@@ -201,10 +227,10 @@ func (c *ClientCommon) dispatchTransportPayloadFast(payload []byte, release func
|
||||
plainRelease()
|
||||
}
|
||||
c.wg.Add(1)
|
||||
if !dispatcher.Dispatch(clientInboundDispatchSource(), func() {
|
||||
if !dispatcher.DispatchSized(clientInboundDispatchSource(), len(owned), func() {
|
||||
defer c.wg.Done()
|
||||
now := time.Now()
|
||||
if err := c.dispatchInboundTransportPlain(owned, now); err != nil && (c.showError || c.debugMode) {
|
||||
if err := c.dispatchInboundTransportPlainAtRoute(route, owned, now); err != nil && (c.showError || c.debugMode) {
|
||||
fmt.Println("client decode envelope error", err)
|
||||
}
|
||||
}) {
|
||||
@@ -213,11 +239,15 @@ func (c *ClientCommon) dispatchTransportPayloadFast(payload []byte, release func
|
||||
}
|
||||
|
||||
func (c *ClientCommon) pushMessageFast(queue *stario.StarQueue, data []byte, dispatcher *inboundDispatcher) bool {
|
||||
return c.pushMessageFastAtRoute(c.clientSessionRouteSnapshot(), queue, data, dispatcher)
|
||||
}
|
||||
|
||||
func (c *ClientCommon) pushMessageFastAtRoute(route clientSessionRoute, queue *stario.StarQueue, data []byte, dispatcher *inboundDispatcher) bool {
|
||||
if queue == nil || dispatcher == nil || len(data) == 0 {
|
||||
return false
|
||||
}
|
||||
if err := queue.ParseMessageView(data, "b612", func(frame stario.FrameView) error {
|
||||
c.dispatchTransportPayloadFast(frame.Payload, nil, dispatcher)
|
||||
if err := queue.ParseMessageView(data, route.inboundQueueKey(), func(frame stario.FrameView) error {
|
||||
c.dispatchTransportPayloadFastAtRoute(route, frame.Payload, nil, dispatcher)
|
||||
return nil
|
||||
}); err != nil && (c.showError || c.debugMode) {
|
||||
fmt.Println("client parse inbound frame error", err)
|
||||
@@ -248,6 +278,10 @@ func (c *ClientCommon) writePayloadToTransportContext(ctx context.Context, paylo
|
||||
|
||||
func (c *ClientCommon) writePayloadToTransportContextTimeout(ctx context.Context, payload []byte, writeTimeout time.Duration) error {
|
||||
binding := c.clientTransportBindingSnapshot()
|
||||
return c.writePayloadToTransportBindingContextTimeout(ctx, binding, payload, writeTimeout)
|
||||
}
|
||||
|
||||
func (c *ClientCommon) writePayloadToTransportBindingContextTimeout(ctx context.Context, binding *transportBinding, payload []byte, writeTimeout time.Duration) error {
|
||||
if binding == nil {
|
||||
return net.ErrClosed
|
||||
}
|
||||
@@ -273,6 +307,10 @@ func (c *ClientCommon) writeControlPayloadToTransport(ctx context.Context, paylo
|
||||
|
||||
func (c *ClientCommon) writeControlPayloadToTransportTimeout(ctx context.Context, payload []byte, priority controlPriority, writeTimeout time.Duration) error {
|
||||
binding := c.clientTransportBindingSnapshot()
|
||||
return c.writeControlPayloadToTransportBindingTimeout(ctx, binding, payload, priority, writeTimeout)
|
||||
}
|
||||
|
||||
func (c *ClientCommon) writeControlPayloadToTransportBindingTimeout(ctx context.Context, binding *transportBinding, payload []byte, priority controlPriority, writeTimeout time.Duration) error {
|
||||
if binding == nil {
|
||||
return net.ErrClosed
|
||||
}
|
||||
@@ -282,11 +320,11 @@ func (c *ClientCommon) writeControlPayloadToTransportTimeout(ctx context.Context
|
||||
}
|
||||
conn := binding.connSnapshot()
|
||||
if conn == nil || isPacketTransportConn(conn) {
|
||||
return c.writePayloadToTransportContextTimeout(ctx, payload, writeTimeout)
|
||||
return c.writePayloadToTransportBindingContextTimeout(ctx, binding, payload, writeTimeout)
|
||||
}
|
||||
sender := binding.controlBatchSenderSnapshot()
|
||||
if sender == nil {
|
||||
return c.writePayloadToTransportContextTimeout(ctx, payload, writeTimeout)
|
||||
return c.writePayloadToTransportBindingContextTimeout(ctx, binding, payload, writeTimeout)
|
||||
}
|
||||
return sender.submitContext(ctx, payload, shorterPositiveDuration(c.maxWriteTimeoutSnapshot(), writeTimeout), priority)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user