fix(notify): 根治低带宽控制面阻塞与传输写入卡死

- 为控制消息增加优先级、公平调度、队列字节预算和自适应批处理
- 支持可取消的写门等待,收紧 shared/dedicated bulk、stream 和 Reply 写入边界
- 修复 bulk reset/close、连接 handoff 和安全 profile 切换时序
- 保留旧取消与超时错误契约,新增阶段化 TransportSendError
- 增加 ReplyCtx、ReplyObjCtx、写超时配置及黑洞连接和竞态回归测试
This commit is contained in:
2026-08-14 10:16:37 +08:00
parent 98ef9e7fcc
commit 0826e17063
45 changed files with 4231 additions and 293 deletions
+22 -1
View File
@@ -418,6 +418,11 @@ func (s *bulkBatchSender) flush(requests []bulkBatchRequest) error {
}
}()
writeTimeout := s.transportWriteTimeout()
if writeTimeout <= 0 {
writeTimeout = defaultBulkDataWriteTimeout
}
requestDeadline := bulkBatchRequestsEarliestDeadline(requests)
writeDeadline := earlierWriteDeadline(writeDeadlineFromTimeout(writeTimeout), requestDeadline)
frames := make([][]byte, 0, len(payloads))
payloadBytes := 0
for _, payload := range payloads {
@@ -425,13 +430,25 @@ func (s *bulkBatchSender) flush(requests []bulkBatchRequest) error {
payloadBytes += len(payload.payload)
}
started := time.Now()
err = s.binding.withConnWriteLockDeadline(writeDeadlineFromTimeout(writeTimeout), func(conn net.Conn) error {
lockAcquired, err := s.binding.withConnWriteLockContextStopDeadlineManaged(context.Background(), s.stopCh, writeDeadline, func(conn net.Conn) error {
return writeFramedPayloadBatchUnlocked(conn, queue, frames)
})
s.binding.observeBulkAdaptivePayloadWrite(payloadBytes, time.Since(started), writeTimeout, err)
if lockAcquired && err != nil {
// A failed framed write may have emitted only part of a frame.
s.binding.closeConn()
}
return err
}
func bulkBatchRequestsEarliestDeadline(requests []bulkBatchRequest) time.Time {
var deadline time.Time
for _, req := range requests {
deadline = earlierWriteDeadline(deadline, req.deadline)
}
return deadline
}
func (s *bulkBatchSender) encodeRequests(requests []bulkBatchRequest) ([]bulkBatchEncodedPayload, error) {
if len(requests) == 0 {
return nil, nil
@@ -609,6 +626,10 @@ func (s *bulkBatchSender) stop() {
close(s.stopCh)
})
<-s.doneCh
// Direct submissions flush on the caller goroutine rather than run(). Wait
// for that path too before declaring the binding safe to hand off.
s.flushMu.Lock()
s.flushMu.Unlock()
}
func (s *bulkBatchSender) failPending(err error) {