fix: 修复 dedicated bulk attach 竞态并优化 short write 补写路径
- 客户端 dedicated attach 回复改为精确读取单帧,避免 attach reply 与后续 NBR1 数据粘连后被误解析 - 服务端 accepted attach 改为先 detach transport,再直接回 attach reply,随后立即切入 dedicated bulk read loop - transport 读循环在 stop 或 transport ownership 失效后不再继续上推已读数据,避免 handoff 后首包被旧 reader 吃掉 - dedicated bulk record 写路径改为 full-write,消除 short write 导致的 invalid bulk fast payload - 优化 vectored write 补写策略:先尝试一次 writev,未写完时直接顺序补完剩余 buffers,减少重复 WriteTo 开销 - 放宽 vectored write 能力识别,支持通过 UnwrapConn/WriteBuffers 命中 fast path - 修复 dedicated batch 排队路径 payload 复用问题,改为深拷贝 queued items - 补齐 dedicated attach、short write、payload clone、transport stop/handoff 等回归测试
This commit is contained in:
+79
-1
@@ -13,6 +13,14 @@ import (
|
||||
var transportConnWriteLocks sync.Map
|
||||
var errTransportFrameQueueUnavailable = errors.New("transport frame queue is unavailable")
|
||||
|
||||
type vectoredBuffersWriter interface {
|
||||
WriteBuffers(*net.Buffers) (int64, error)
|
||||
}
|
||||
|
||||
type vectoredConnUnwrapper interface {
|
||||
UnwrapConn() net.Conn
|
||||
}
|
||||
|
||||
func writeFullToConn(conn net.Conn, data []byte) error {
|
||||
if conn == nil {
|
||||
return net.ErrClosed
|
||||
@@ -26,8 +34,15 @@ func writeFullToConnUnlocked(conn net.Conn, data []byte) error {
|
||||
if conn == nil {
|
||||
return net.ErrClosed
|
||||
}
|
||||
return writeFullToWriterUnlocked(conn, data)
|
||||
}
|
||||
|
||||
func writeFullToWriterUnlocked(writer io.Writer, data []byte) error {
|
||||
if writer == nil {
|
||||
return io.ErrClosedPipe
|
||||
}
|
||||
for len(data) > 0 {
|
||||
n, err := conn.Write(data)
|
||||
n, err := writer.Write(data)
|
||||
if n > 0 {
|
||||
data = data[n:]
|
||||
}
|
||||
@@ -41,6 +56,69 @@ func writeFullToConnUnlocked(conn net.Conn, data []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeNetBuffersFullUnlocked(conn net.Conn, buffers net.Buffers) error {
|
||||
if conn == nil {
|
||||
return net.ErrClosed
|
||||
}
|
||||
writer, writeFn := vectoredWriteStrategy(conn)
|
||||
if writeFn == nil {
|
||||
return writeRemainingBuffersUnlocked(conn, buffers)
|
||||
}
|
||||
n, err := writeFn(&buffers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(buffers) == 0 {
|
||||
return nil
|
||||
}
|
||||
if n == 0 {
|
||||
return io.ErrNoProgress
|
||||
}
|
||||
return writeRemainingBuffersUnlocked(writer, buffers)
|
||||
}
|
||||
|
||||
func vectoredWriteStrategy(conn net.Conn) (io.Writer, func(*net.Buffers) (int64, error)) {
|
||||
current := conn
|
||||
for depth := 0; depth < 8 && current != nil; depth++ {
|
||||
if writer, ok := current.(vectoredBuffersWriter); ok {
|
||||
target := current
|
||||
return target, writer.WriteBuffers
|
||||
}
|
||||
switch target := current.(type) {
|
||||
case *net.TCPConn:
|
||||
return target, func(bufs *net.Buffers) (int64, error) {
|
||||
return bufs.WriteTo(target)
|
||||
}
|
||||
case *net.UnixConn:
|
||||
return target, func(bufs *net.Buffers) (int64, error) {
|
||||
return bufs.WriteTo(target)
|
||||
}
|
||||
}
|
||||
unwrapper, ok := current.(vectoredConnUnwrapper)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
next := unwrapper.UnwrapConn()
|
||||
if next == nil || next == current {
|
||||
break
|
||||
}
|
||||
current = next
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func writeRemainingBuffersUnlocked(writer io.Writer, buffers net.Buffers) error {
|
||||
for _, part := range buffers {
|
||||
if len(part) == 0 {
|
||||
continue
|
||||
}
|
||||
if err := writeFullToWriterUnlocked(writer, part); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func withRawConnWriteLock(conn net.Conn, fn func(net.Conn) error) error {
|
||||
return withRawConnWriteLockDeadline(conn, time.Time{}, fn)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user