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