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
+402 -49
View File
@@ -20,6 +20,7 @@ const (
systemBulkAttachKey = "_notify_bulk_attach"
bulkDedicatedRecordMagic = "NBR1"
bulkDedicatedRecordHeaderLen = 8
bulkDedicatedRecordMaxBytes = 20 * 1024 * 1024
defaultBulkDedicatedAttachLimit = 16
defaultBulkDedicatedActiveLimit = 4096
@@ -187,6 +188,9 @@ func encodeDirectSignalFrame(queue *stario.StarQueue, sequenceEn func(interface{
if payload == nil && len(plain) != 0 {
return nil, errTransportPayloadEncryptFailed
}
if err := validateTransportFramePayloadLen(payload); err != nil {
return nil, err
}
return queue.BuildMessage(payload), nil
}
@@ -210,7 +214,7 @@ func readDirectSignalFramePayload(conn net.Conn) ([]byte, error) {
if conn == nil {
return nil, net.ErrClosed
}
return newTransportFrameReader(conn, stario.NewQueue()).Next()
return newTransportFrameReader(conn, stario.NewQueueCtx(nil, 1, transportFrameMaxPayloadBytes)).Next()
}
func writeBulkDedicatedRecord(conn net.Conn, payload []byte) error {
@@ -218,17 +222,37 @@ func writeBulkDedicatedRecord(conn net.Conn, payload []byte) error {
}
func writeBulkDedicatedRecordWithDeadline(conn net.Conn, payload []byte, deadline time.Time) error {
return writeBulkDedicatedRecordWithDeadlineTrace(conn, payload, deadline, 0)
}
func writeBulkDedicatedRecordWithDeadlineTrace(conn net.Conn, payload []byte, deadline time.Time, traceDataID uint64) error {
if conn == nil {
return net.ErrClosed
}
if len(payload) > bulkDedicatedRecordMaxBytes {
return fmt.Errorf("%w: dedicated record payload=%d max=%d", errBulkFastPayloadInvalid, len(payload), bulkDedicatedRecordMaxBytes)
}
if deadline.IsZero() {
deadline = writeDeadlineFromTimeout(defaultBulkDataWriteTimeout)
}
var prepareStarted time.Time
if traceDataID != 0 {
prepareStarted = time.Now()
fmt.Printf("[bulk-debug] at=%s record write prepare data=%d conn=%T local=%v remote=%v bytes=%d\n", prepareStarted.Format(time.RFC3339Nano), traceDataID, conn, conn.LocalAddr(), conn.RemoteAddr(), len(payload))
prepareStarted = time.Now()
}
return withRawConnWriteLockDeadline(conn, deadline, func(conn net.Conn) error {
var header [bulkDedicatedRecordHeaderLen]byte
copy(header[:4], bulkDedicatedRecordMagic)
binary.BigEndian.PutUint32(header[4:8], uint32(len(payload)))
return writeNetBuffersFullUnlocked(conn, net.Buffers{header[:], payload})
if traceDataID == 0 {
return writeNetBuffersFullUnlocked(conn, net.Buffers{header[:], payload})
}
fmt.Printf("[bulk-debug] at=%s record socket write begin data=%d gate-and-deadline=%s\n", time.Now().Format(time.RFC3339Nano), traceDataID, time.Since(prepareStarted))
writeStarted := time.Now()
err := writeNetBuffersFullUnlocked(conn, net.Buffers{header[:], payload})
fmt.Printf("[bulk-debug] at=%s record socket write end data=%d elapsed=%s error=%v\n", time.Now().Format(time.RFC3339Nano), traceDataID, time.Since(writeStarted), err)
return err
})
}
@@ -244,22 +268,49 @@ func readBulkDedicatedRecord(conn net.Conn) ([]byte, error) {
}
func readBulkDedicatedRecordPooled(conn net.Conn) ([]byte, func(), error) {
return readBulkDedicatedRecordPooledTrace(conn, false)
}
func readBulkDedicatedRecordPooledTrace(conn net.Conn, debug bool) ([]byte, func(), error) {
if conn == nil {
return nil, nil, net.ErrClosed
}
var headerStarted time.Time
if debug {
headerStarted = time.Now()
fmt.Printf("[bulk-debug] at=%s record read header begin local=%v remote=%v\n", headerStarted.Format(time.RFC3339Nano), conn.LocalAddr(), conn.RemoteAddr())
headerStarted = time.Now()
}
var header [bulkDedicatedRecordHeaderLen]byte
if _, err := io.ReadFull(conn, header[:]); err != nil {
if debug {
fmt.Printf("[bulk-debug] at=%s record read header failed elapsed=%s error=%v\n", time.Now().Format(time.RFC3339Nano), time.Since(headerStarted), err)
}
return nil, nil, err
}
if string(header[:4]) != bulkDedicatedRecordMagic {
return nil, nil, fmt.Errorf("%w: record magic=%x", errBulkFastPayloadInvalid, header[:4])
}
size := int(binary.BigEndian.Uint32(header[4:8]))
if size < 0 {
wireSize := binary.BigEndian.Uint32(header[4:8])
if wireSize > bulkDedicatedRecordMaxBytes {
return nil, nil, errBulkFastPayloadInvalid
}
size := int(wireSize)
if debug {
fmt.Printf("[bulk-debug] at=%s record read payload begin bytes=%d header=%s\n", time.Now().Format(time.RFC3339Nano), size, time.Since(headerStarted))
}
payload := getModernPSKPayloadBuffer(size)
if _, err := io.ReadFull(conn, payload); err != nil {
var readStarted time.Time
if debug {
readStarted = time.Now()
fmt.Printf("[bulk-debug] at=%s record read buffer ready bytes=%d\n", readStarted.Format(time.RFC3339Nano), size)
readStarted = time.Now()
}
n, err := io.ReadFull(conn, payload)
if debug {
fmt.Printf("[bulk-debug] at=%s record read payload end bytes=%d/%d elapsed=%s error=%v\n", time.Now().Format(time.RFC3339Nano), n, size, time.Since(readStarted), err)
}
if err != nil {
putModernPSKPayloadBuffer(payload)
return nil, nil, err
}
@@ -302,37 +353,76 @@ func (c *ClientCommon) attachDedicatedBulkSidecar(ctx context.Context, bulk *bul
if ctx == nil {
ctx = context.Background()
}
laneID := bulk.dedicatedLaneIDSnapshot()
releaseActiveSlot, err := c.acquireBulkDedicatedActiveSlot(ctx)
if err != nil {
route := bulk.clientSessionRouteSnapshot()
if !route.bound() {
route = c.clientSessionRouteSnapshot()
}
attachCtx, cancelAttach := context.WithCancel(ctx)
defer cancelAttach()
stopRoute := func() bool { return false }
if route.transportStopCtx != nil {
stopRoute = context.AfterFunc(route.transportStopCtx, cancelAttach)
defer stopRoute()
}
checkRoute := func() error {
if err := c.ensureClientSessionRouteSendReady(route); err != nil {
return err
}
return nil
}
routeWaitError := func(waitErr error) error {
if routeErr := checkRoute(); routeErr != nil {
return routeErr
}
return waitErr
}
if err := checkRoute(); err != nil {
return err
}
laneID := bulk.dedicatedLaneIDSnapshot()
releaseActiveSlot, err := c.acquireBulkDedicatedActiveSlot(attachCtx)
if err != nil {
return routeWaitError(err)
}
needReleaseActive := true
defer func() {
if needReleaseActive {
releaseActiveSlot()
}
}()
if sidecar := c.clientDedicatedSidecarSnapshotForLane(laneID); sidecar != nil && sidecar.conn != nil {
if err := bulk.attachDedicatedConnShared(sidecar.conn); err == nil {
if sidecar := c.clientDedicatedSidecarSnapshotForLaneAtRoute(laneID, route); sidecar != nil {
if err := checkRoute(); err != nil {
return err
}
if err := sidecar.withConn(func(conn net.Conn) error {
return bulk.attachDedicatedConnShared(conn)
}); err == nil {
bulk.markDedicatedActiveReserved()
needReleaseActive = false
return nil
}
}
_, flight, leader := c.beginClientDedicatedSidecarAttach(laneID)
_, flight, leader, beginErr := c.beginClientDedicatedSidecarAttach(laneID, route)
if beginErr != nil {
return beginErr
}
if !leader {
if flight == nil {
return errTransportDetached
}
if err := flight.wait(ctx); err != nil {
if err := flight.wait(attachCtx); err != nil {
return routeWaitError(err)
}
if err := checkRoute(); err != nil {
return err
}
sidecar := c.clientDedicatedSidecarSnapshotForLane(laneID)
if sidecar == nil || sidecar.conn == nil {
sidecar := c.clientDedicatedSidecarSnapshotForLaneAtRoute(laneID, route)
if sidecar == nil {
return errTransportDetached
}
if err := bulk.attachDedicatedConnShared(sidecar.conn); err != nil {
if err := sidecar.withConn(func(conn net.Conn) error {
return bulk.attachDedicatedConnShared(conn)
}); err != nil {
return err
}
bulk.markDedicatedActiveReserved()
@@ -346,14 +436,20 @@ func (c *ClientCommon) attachDedicatedBulkSidecar(ctx context.Context, bulk *bul
defer func() {
c.finishClientDedicatedSidecarAttach(laneID, flight, flightErr)
}()
releaseAttachSlot, err := c.acquireBulkDedicatedAttachSlot(ctx)
releaseAttachSlot, err := c.acquireBulkDedicatedAttachSlot(attachCtx)
if err != nil {
flightErr = err
return err
flightErr = routeWaitError(err)
return flightErr
}
defer releaseAttachSlot()
if sidecar := c.clientDedicatedSidecarSnapshotForLane(laneID); sidecar != nil && sidecar.conn != nil {
if err := bulk.attachDedicatedConnShared(sidecar.conn); err == nil {
if sidecar := c.clientDedicatedSidecarSnapshotForLaneAtRoute(laneID, route); sidecar != nil {
if err := checkRoute(); err != nil {
flightErr = err
return err
}
if err := sidecar.withConn(func(conn net.Conn) error {
return bulk.attachDedicatedConnShared(conn)
}); err == nil {
bulk.markDedicatedActiveReserved()
needReleaseActive = false
flightErr = nil
@@ -367,25 +463,36 @@ func (c *ClientCommon) attachDedicatedBulkSidecar(ctx context.Context, bulk *bul
}
var lastErr error
for attempt := 1; attempt <= attempts; attempt++ {
if err := checkRoute(); err != nil {
flightErr = err
return err
}
c.bulkAttachAttemptCount.Add(1)
if attempt > 1 {
delay := backoff * time.Duration(1<<(attempt-2))
if delay > 3*time.Second {
delay = 3 * time.Second
}
if err := waitDedicatedAttachBackoff(ctx, delay); err != nil {
flightErr = err
return err
if err := waitDedicatedAttachBackoff(attachCtx, delay); err != nil {
flightErr = routeWaitError(err)
return flightErr
}
}
dialCtx := ctx
dialCtx := attachCtx
dialCancel := func() {}
if dialTimeout > 0 {
dialCtx, dialCancel = context.WithTimeout(ctx, dialTimeout)
dialCtx, dialCancel = context.WithTimeout(attachCtx, dialTimeout)
}
conn, err := c.dialDedicatedBulkConn(dialCtx, dialTimeout)
dialCancel()
if err == nil && conn == nil {
err = errTransportDetached
}
if err != nil {
if routeErr := checkRoute(); routeErr != nil {
flightErr = routeErr
return routeErr
}
lastErr = err
if attempt < attempts && isRetryableDedicatedAttachError(err) {
flightErr = err
@@ -396,15 +503,24 @@ func (c *ClientCommon) attachDedicatedBulkSidecar(ctx context.Context, bulk *bul
flightErr = err
return err
}
helloCtx := ctx
if err := checkRoute(); err != nil {
_ = conn.Close()
flightErr = err
return err
}
helloCtx := attachCtx
helloCancel := func() {}
if helloTimeout > 0 {
helloCtx, helloCancel = context.WithTimeout(ctx, helloTimeout)
helloCtx, helloCancel = context.WithTimeout(attachCtx, helloTimeout)
}
resp, err := c.sendDedicatedBulkAttachRequest(helloCtx, conn, bulk)
helloCancel()
if err != nil {
_ = conn.Close()
if routeErr := checkRoute(); routeErr != nil {
flightErr = routeErr
return routeErr
}
lastErr = err
if attempt < attempts && isRetryableDedicatedAttachError(err) {
flightErr = err
@@ -416,6 +532,11 @@ func (c *ClientCommon) attachDedicatedBulkSidecar(ctx context.Context, bulk *bul
flightErr = err
return err
}
if err := checkRoute(); err != nil {
_ = conn.Close()
flightErr = err
return err
}
if !resp.Accepted {
_ = conn.Close()
rejectedErr := &bulkAttachError{
@@ -439,18 +560,30 @@ func (c *ClientCommon) attachDedicatedBulkSidecar(ctx context.Context, bulk *bul
flightErr = rejectedErr
return rejectedErr
}
if err := checkRoute(); err != nil {
_ = conn.Close()
flightErr = err
return err
}
sidecar := newBulkDedicatedSidecar(conn, laneID)
activeSidecar, installed := c.installClientDedicatedSidecar(laneID, sidecar)
activeSidecar, installed, installErr := c.installClientDedicatedSidecarAtRoute(laneID, sidecar, route)
if installErr != nil {
sidecar.close()
flightErr = installErr
return installErr
}
if !installed {
sidecar.close()
sidecar = activeSidecar
}
if sidecar == nil || sidecar.conn == nil {
if sidecar == nil {
bulk.setDedicatedAttachLastCode(string(bulkAttachErrorCodeAttachFailed))
flightErr = errTransportDetached
return errTransportDetached
}
if err := bulk.attachDedicatedConnShared(sidecar.conn); err != nil {
if err := sidecar.withConn(func(conn net.Conn) error {
return bulk.attachDedicatedConnShared(conn)
}); err != nil {
if installed && c.clearClientDedicatedSidecar(laneID, sidecar) {
sidecar.close()
}
@@ -462,9 +595,17 @@ func (c *ClientCommon) attachDedicatedBulkSidecar(ctx context.Context, bulk *bul
}
return err
}
if err := checkRoute(); err != nil {
if installed && c.clearClientDedicatedSidecar(laneID, sidecar) {
sidecar.close()
}
bulk.markReset(err)
flightErr = err
return err
}
c.bulkAttachSuccessCount.Add(1)
if installed {
go c.readDedicatedSidecarLoop(sidecar)
go c.readDedicatedSidecarLoopAtRoute(sidecar, route)
}
bulk.markDedicatedActiveReserved()
needReleaseActive = false
@@ -651,6 +792,9 @@ func (c *ClientCommon) sendDedicatedBulkAttachRequest(ctx context.Context, conn
if bulk == nil {
return bulkAttachResponse{}, errBulkIDEmpty
}
if conn == nil {
return bulkAttachResponse{}, errTransportDetached
}
if ctx == nil {
ctx = context.Background()
}
@@ -716,15 +860,40 @@ func (c *ClientCommon) clientDedicatedBulkAttachTransportProtectionProfile() tra
}
func (c *ClientCommon) readDedicatedSidecarLoop(sidecar *bulkDedicatedSidecar) {
c.readDedicatedSidecarLoopAtRoute(sidecar, c.clientSessionRouteSnapshot())
}
func (c *ClientCommon) readDedicatedSidecarLoopAtRoute(sidecar *bulkDedicatedSidecar, route clientSessionRoute) {
if c == nil || sidecar == nil || sidecar.conn == nil {
return
}
debug := c.IsDebugMode()
routeCurrent := func() bool {
if !c.clientSessionRouteCurrent(route) {
return false
}
return route.transportStopCtx == nil || route.transportStopCtx.Err() == nil
}
for {
payload, payloadRelease, err := readBulkDedicatedRecordPooled(sidecar.conn)
if !routeCurrent() {
return
}
payload, payloadRelease, err := readBulkDedicatedRecordPooledTrace(sidecar.conn, debug)
if err != nil {
c.handleClientDedicatedSidecarFailure(sidecar, err)
return
}
if !routeCurrent() {
if payloadRelease != nil {
payloadRelease()
}
return
}
var dispatchStarted time.Time
if debug {
dispatchStarted = time.Now()
fmt.Printf("[bulk-debug] at=%s side=client lane=%d sidecar decode-dispatch begin bytes=%d\n", dispatchStarted.Format(time.RFC3339Nano), sidecar.laneID, len(payload))
}
profile := c.clientTransportProtectionSnapshot()
plain, plainRelease, err := decryptTransportPayloadCodecPooled(profile.mode, profile.runtime, profile.msgDe, profile.secretKey, payload, payloadRelease)
if err != nil {
@@ -732,6 +901,10 @@ func (c *ClientCommon) readDedicatedSidecarLoop(sidecar *bulkDedicatedSidecar) {
return
}
owner := newBulkReadPayloadOwner(plainRelease)
if !routeCurrent() {
owner.done()
return
}
runtime := c.getBulkRuntime()
if runtime == nil {
owner.done()
@@ -741,22 +914,29 @@ func (c *ClientCommon) readDedicatedSidecarLoop(sidecar *bulkDedicatedSidecar) {
currentDataID uint64
currentBulk *bulkHandle
skipDataID bool
staleRoute bool
)
err = walkDedicatedBulkInboundPayload(plain, func(dataID uint64, item bulkDedicatedBatchItem) error {
if !routeCurrent() {
staleRoute = true
currentBulk = nil
skipDataID = true
return nil
}
if dataID != currentDataID {
currentDataID = dataID
currentBulk = nil
skipDataID = false
bulk, ok := runtime.lookupByDataID(clientFileScope(), dataID)
bulk, ok := runtime.lookupInboundFrame(clientFileScope(), dataID)
if !ok {
c.bestEffortRejectInboundBulkData("", dataID, errBulkNotFound.Error())
c.bestEffortRejectInboundBulkDataAtRoute(route, "", dataID, errBulkNotFound.Error())
skipDataID = true
return nil
}
if !bulk.acceptsClientSessionEpoch(c.currentClientSessionEpoch()) {
if !bulk.acceptsClientSessionRoute(route) {
detachErr := transportDetachedSessionEpochError()
bulk.markReset(detachErr)
c.bestEffortRejectInboundBulkData(bulk.ID(), dataID, detachErr.Error())
c.bestEffortRejectInboundBulkDataAtRoute(route, bulk.ID(), dataID, detachErr.Error())
skipDataID = true
return nil
}
@@ -766,6 +946,20 @@ func (c *ClientCommon) readDedicatedSidecarLoop(sidecar *bulkDedicatedSidecar) {
if skipDataID || currentBulk == nil {
return nil
}
if !routeCurrent() {
staleRoute = true
currentBulk = nil
skipDataID = true
return nil
}
if !currentBulk.acceptsClientSessionRoute(route) {
detachErr := transportDetachedSessionEpochError()
currentBulk.markReset(detachErr)
c.bestEffortRejectInboundBulkDataAtRoute(route, currentBulk.ID(), dataID, detachErr.Error())
currentBulk = nil
skipDataID = true
return nil
}
var release func()
if item.Type == bulkFastPayloadTypeData {
release = owner.retainChunk()
@@ -794,6 +988,12 @@ func (c *ClientCommon) readDedicatedSidecarLoop(sidecar *bulkDedicatedSidecar) {
return
}
owner.done()
if debug {
fmt.Printf("[bulk-debug] at=%s side=client lane=%d sidecar decode-dispatch end elapsed=%s\n", time.Now().Format(time.RFC3339Nano), sidecar.laneID, time.Since(dispatchStarted))
}
if staleRoute {
return
}
}
}
@@ -802,6 +1002,16 @@ func (s *ServerCommon) handleBulkAttachSystemMessage(message Message) bool {
return false
}
current := messageLogicalConnSnapshot(&message)
currentTransport := message.TransportConn
if currentTransport == nil && current != nil {
currentTransport = current.CurrentTransportConn()
}
if currentTransport != nil && !currentTransport.IsCurrent() {
if current != nil {
_ = s.replyDedicatedBulkAttach(current, message, toBulkAttachResponseError(newBulkAttachError(bulkAttachErrorCodeAttachFailed, true, transportDetachedErrorForTransport(currentTransport).Error()), ""))
}
return true
}
var (
req bulkAttachRequest
logical *LogicalConn
@@ -854,6 +1064,9 @@ func (s *ServerCommon) resolveInboundDedicatedBulk(current *LogicalConn, req bul
}
}
bulk.markDedicatedAttachAttempt()
if bulkTransport := bulk.TransportConn(); bulkTransport != nil && !bulkTransport.IsCurrent() {
return nil, nil, newBulkAttachError(bulkAttachErrorCodeAttachFailed, true, transportDetachedErrorForTransport(bulkTransport).Error())
}
if !bulk.Dedicated() {
bulk.setDedicatedAttachLastCode(string(bulkAttachErrorCodeBulkNotDedicated))
return nil, nil, &bulkAttachError{
@@ -907,8 +1120,24 @@ func (s *ServerCommon) finishInboundDedicatedBulkAttach(current *LogicalConn, lo
if current == nil || logical == nil || bulk == nil {
return newBulkAttachError(bulkAttachErrorCodeInvalidRequest, false, errBulkLogicalConnNil.Error())
}
// Keep the target logical transport generation stable while the attach
// reply, sidecar publication, runtime rebinding and accept dispatch are
// committed. Reattach cleanup takes the same lock before retiring the old
// generation.
logical.transportLifecycleMu.Lock()
defer logical.transportLifecycleMu.Unlock()
scope := serverFileScope(logical)
laneID := bulk.dedicatedLaneIDSnapshot()
currentTransport := message.TransportConn
if currentTransport == nil {
currentTransport = current.CurrentTransportConn()
}
if currentTransport != nil && !currentTransport.IsCurrent() {
return newBulkAttachError(bulkAttachErrorCodeAttachFailed, true, transportDetachedErrorForTransport(currentTransport).Error())
}
if bulkTransport := bulk.TransportConn(); bulkTransport != nil && !bulkTransport.IsCurrent() {
return newBulkAttachError(bulkAttachErrorCodeAttachFailed, true, transportDetachedErrorForTransport(bulkTransport).Error())
}
conn, err := current.detachTransportForTransfer()
if err != nil {
return newBulkAttachError(bulkAttachErrorCodeAttachFailed, true, err.Error())
@@ -962,6 +1191,11 @@ func (s *ServerCommon) finishInboundDedicatedBulkAttach(current *LogicalConn, lo
bulk.setDedicatedAttachLastCode(string(bulkAttachErrorCodeAttachFailed))
return fail("bulk dedicated attach failed", err)
}
if bulkTransport := bulk.TransportConn(); bulkTransport != nil && !bulkTransport.IsCurrent() {
sidecar.close()
bulk.setDedicatedAttachLastCode(string(bulkAttachErrorCodeAttachFailed))
return fail("bulk dedicated attach transport replaced", transportDetachedErrorForTransport(bulkTransport))
}
if err := s.replyDedicatedBulkAttachDetached(current, conn, message, bulkAttachResponse{Accepted: true}); err != nil {
bulk.setDedicatedAttachLastCode(string(bulkAttachErrorCodeAttachFailed))
sidecar.close()
@@ -971,6 +1205,11 @@ func (s *ServerCommon) finishInboundDedicatedBulkAttach(current *LogicalConn, lo
stopCurrent("bulk dedicated attach reply failed", err)
return nil
}
if bulkTransport := bulk.TransportConn(); bulkTransport != nil && !bulkTransport.IsCurrent() {
sidecar.close()
bulk.setDedicatedAttachLastCode(string(bulkAttachErrorCodeAttachFailed))
return fail("bulk dedicated attach transport replaced", transportDetachedErrorForTransport(bulkTransport))
}
oldSidecar := s.installServerDedicatedSidecar(logical, laneID, sidecar)
if runtime := s.getBulkRuntime(); runtime != nil {
runtime.attachSharedDedicatedConn(scope, laneID, conn)
@@ -985,7 +1224,7 @@ func (s *ServerCommon) finishInboundDedicatedBulkAttach(current *LogicalConn, lo
oldSidecar.close()
}
go s.readDedicatedSidecarLoop(logical, sidecar)
s.startServerBulkAcceptDispatch(bulk, logical, messageTransportConnSnapshot(&message))
s.startServerBulkAcceptDispatch(bulk, logical, bulk.TransportConn())
if runtime := s.getBulkRuntime(); runtime != nil {
s.dispatchPendingServerBulkAccepts(scope, conn, bulk, logical)
}
@@ -1068,20 +1307,53 @@ func (s *ServerCommon) readDedicatedSidecarLoop(logical *LogicalConn, sidecar *b
if s == nil || logical == nil || sidecar == nil || sidecar.conn == nil {
return
}
runtime := s.getBulkRuntime()
debug := s.IsDebugMode()
scope := serverFileScope(logical)
sidecarCurrent := func() bool {
return s.serverDedicatedSidecarCurrent(logical, sidecar)
}
for {
if !sidecarCurrent() {
if debug {
fmt.Printf("[bulk-debug] at=%s side=server lane=%d sidecar stopped reason=stale-connection\n", time.Now().Format(time.RFC3339Nano), sidecar.laneID)
}
return
}
var readStarted time.Time
if debug {
readStarted = time.Now()
}
payload, payloadRelease, err := readBulkDedicatedRecordPooled(sidecar.conn)
if debug {
fmt.Printf("[bulk-debug] at=%s side=server lane=%d sidecar record read bytes=%d elapsed=%s error=%v\n", time.Now().Format(time.RFC3339Nano), sidecar.laneID, len(payload), time.Since(readStarted), err)
}
if err != nil {
if !sidecarCurrent() {
return
}
s.handleServerDedicatedSidecarFailure(logical, sidecar, err)
return
}
if !sidecarCurrent() {
if payloadRelease != nil {
payloadRelease()
}
return
}
plain, plainRelease, err := decryptTransportPayloadCodecPooled(logical.protectionModeSnapshot(), logical.modernPSKRuntimeSnapshot(), logical.msgDeSnapshot(), logical.secretKeySnapshot(), payload, payloadRelease)
if err != nil {
if !sidecarCurrent() {
return
}
s.handleServerDedicatedSidecarFailure(logical, sidecar, err)
return
}
owner := newBulkReadPayloadOwner(plainRelease)
if !sidecarCurrent() {
owner.done()
return
}
runtime := s.getBulkRuntime()
if runtime == nil {
owner.done()
continue
@@ -1090,15 +1362,39 @@ func (s *ServerCommon) readDedicatedSidecarLoop(logical *LogicalConn, sidecar *b
currentDataID uint64
currentBulk *bulkHandle
skipDataID bool
staleSidecar bool
)
err = walkDedicatedBulkInboundPayload(plain, func(dataID uint64, item bulkDedicatedBatchItem) error {
if debug && item.Type == bulkFastPayloadTypeRelease {
fmt.Printf("[bulk-debug] at=%s side=server lane=%d data=%d sidecar release received payload=%d\n", time.Now().Format(time.RFC3339Nano), sidecar.laneID, dataID, len(item.Payload))
}
if !sidecarCurrent() {
staleSidecar = true
currentBulk = nil
skipDataID = true
return nil
}
if dataID != currentDataID {
currentDataID = dataID
currentBulk = nil
skipDataID = false
bulk, ok := runtime.lookupByDataID(scope, dataID)
bulk, ok := runtime.lookupInboundFrame(scope, dataID)
if !ok {
s.bestEffortRejectInboundDedicatedData(logical, sidecar.conn, dataID, errBulkNotFound.Error())
if debug {
fmt.Printf("[bulk-debug] at=%s side=server id=- data=%d age=- sidecar inbound lookup failed type=%d\n", time.Now().Format(time.RFC3339Nano), dataID, item.Type)
}
if sidecarCurrent() {
s.bestEffortRejectInboundDedicatedData(logical, sidecar.conn, dataID, errBulkNotFound.Error())
} else {
staleSidecar = true
}
skipDataID = true
return nil
}
if !bulkDedicatedSidecarConnCurrent(bulk, sidecar) {
if debug {
bulk.debugf("sidecar reject lane=%d type=%d reason=connection-mismatch", sidecar.laneID, item.Type)
}
skipDataID = true
return nil
}
@@ -1108,12 +1404,42 @@ func (s *ServerCommon) readDedicatedSidecarLoop(logical *LogicalConn, sidecar *b
if skipDataID || currentBulk == nil {
return nil
}
if !sidecarCurrent() || !bulkDedicatedSidecarConnCurrent(currentBulk, sidecar) {
if !sidecarCurrent() {
staleSidecar = true
}
currentBulk = nil
skipDataID = true
return nil
}
var release func()
if item.Type == bulkFastPayloadTypeData {
release = owner.retainChunk()
}
if !sidecarCurrent() || !bulkDedicatedSidecarConnCurrent(currentBulk, sidecar) {
if release != nil {
release()
}
if !sidecarCurrent() {
staleSidecar = true
}
currentBulk = nil
skipDataID = true
return nil
}
dispatchErr := dispatchDedicatedBulkInboundItemWithRelease(currentBulk, item, release)
if dispatchErr != nil {
if debug {
currentBulk.debugf("sidecar dispatch failed lane=%d type=%d error=%v", sidecar.laneID, item.Type, dispatchErr)
}
if !sidecarCurrent() {
staleSidecar = true
currentBulk = nil
skipDataID = true
return nil
}
if !errors.Is(dispatchErr, io.EOF) {
_ = s.sendDedicatedBulkReset(context.Background(), logical, currentBulk, dispatchErr.Error())
currentBulk.markReset(dispatchErr)
@@ -1129,13 +1455,17 @@ func (s *ServerCommon) readDedicatedSidecarLoop(logical *LogicalConn, sidecar *b
return nil
})
if err != nil {
if plainRelease != nil {
plainRelease()
owner.done()
if !sidecarCurrent() {
return
}
s.handleServerDedicatedSidecarFailure(logical, sidecar, err)
return
}
owner.done()
if staleSidecar {
return
}
}
}
@@ -1193,6 +1523,9 @@ func (c *ClientCommon) dedicatedBulkSender(bulk *bulkHandle) (*bulkDedicatedSend
if actual != sender {
sender.stop()
}
if actual == nil {
return nil, io.ErrClosedPipe
}
return actual, nil
}
@@ -1200,7 +1533,8 @@ func (c *ClientCommon) dedicatedBulkLaneSender(bulk *bulkHandle) (*bulkDedicated
if c == nil || bulk == nil {
return nil, errBulkClientNil
}
sidecar := c.clientDedicatedSidecarSnapshotForLane(bulk.dedicatedLaneIDSnapshot())
route := bulk.clientSessionRouteSnapshot()
sidecar := c.clientDedicatedSidecarSnapshotForLaneAtRoute(bulk.dedicatedLaneIDSnapshot(), route)
conn := bulk.dedicatedConnSnapshot()
if sidecar == nil || sidecar.conn == nil || conn == nil || sidecar.conn != conn {
return nil, transportDetachedError("dedicated bulk sidecar not attached", nil)
@@ -1215,7 +1549,7 @@ func (c *ClientCommon) dedicatedBulkLaneSender(bulk *bulkHandle) (*bulkDedicated
return c.encodeDedicatedBulkBatchesPayloadPooledWithRuntime(laneRuntime, batches)
}, func(err error) {
c.handleClientDedicatedSidecarFailure(sidecar, err)
})
}, c.IsDebugMode())
})
if sender == nil {
return nil, transportDetachedError("dedicated bulk sidecar not attached", nil)
@@ -1335,6 +1669,9 @@ func (s *ServerCommon) dedicatedBulkSender(logical *LogicalConn, bulk *bulkHandl
if actual != sender {
sender.stop()
}
if actual == nil {
return nil, io.ErrClosedPipe
}
return actual, nil
}
@@ -1362,7 +1699,7 @@ func (s *ServerCommon) dedicatedBulkLaneSender(logical *LogicalConn, bulk *bulkH
return s.encodeDedicatedBulkBatchesPayloadPooledWithRuntime(logical, laneRuntime, batches)
}, func(err error) {
s.handleServerDedicatedSidecarFailure(logical, sidecar, err)
})
}, s.IsDebugMode())
})
if sender == nil {
return nil, transportDetachedError("dedicated bulk sidecar not attached", nil)
@@ -1457,7 +1794,11 @@ func (c *ClientCommon) encodeDedicatedBulkBatchPayload(dataID uint64, items []bu
}
profile := c.clientTransportProtectionSnapshot()
if runtime := profile.runtime; runtime != nil {
return runtime.sealFilledPayload(bulkDedicatedBatchPlainLen(items), func(dst []byte) error {
plainLen, err := bulkDedicatedBatchPlainLenChecked(items)
if err != nil {
return nil, err
}
return runtime.sealFilledPayload(plainLen, func(dst []byte) error {
return writeBulkDedicatedBatchPlain(dst, dataID, items)
})
}
@@ -1486,7 +1827,11 @@ func (c *ClientCommon) encodeDedicatedBulkBatchesPayloadPooledWithRuntime(runtim
return nil, nil, errBulkFastPayloadInvalid
}
if runtime != nil {
return runtime.sealFilledPayloadPooled(bulkDedicatedBatchesPlainLen(batches), func(dst []byte) error {
plainLen, err := bulkDedicatedBatchesPlainLenChecked(batches)
if err != nil {
return nil, nil, err
}
return runtime.sealFilledPayloadPooled(plainLen, func(dst []byte) error {
return writeBulkDedicatedBatchesPlain(dst, batches)
})
}
@@ -1522,7 +1867,11 @@ func (s *ServerCommon) encodeDedicatedBulkBatchPayload(logical *LogicalConn, dat
return nil, errBulkLogicalConnNil
}
if runtime := logical.modernPSKRuntimeSnapshot(); runtime != nil {
return runtime.sealFilledPayload(bulkDedicatedBatchPlainLen(items), func(dst []byte) error {
plainLen, err := bulkDedicatedBatchPlainLenChecked(items)
if err != nil {
return nil, err
}
return runtime.sealFilledPayload(plainLen, func(dst []byte) error {
return writeBulkDedicatedBatchPlain(dst, dataID, items)
})
}
@@ -1547,7 +1896,11 @@ func (s *ServerCommon) encodeDedicatedBulkBatchesPayloadPooledWithRuntime(logica
return nil, nil, errBulkFastPayloadInvalid
}
if runtime != nil {
return runtime.sealFilledPayloadPooled(bulkDedicatedBatchesPlainLen(batches), func(dst []byte) error {
plainLen, err := bulkDedicatedBatchesPlainLenChecked(batches)
if err != nil {
return nil, nil, err
}
return runtime.sealFilledPayloadPooled(plainLen, func(dst []byte) error {
return writeBulkDedicatedBatchesPlain(dst, batches)
})
}