fix: close stream adaptive gaps and switch notify to stario v0.1.1

- make stream fast path honor adaptive soft payload limits end-to-end
  - split oversized fast-stream payloads into sequential frames before batching
  - use adaptive soft cap when encoding stream batch payloads
  - move timeout-like error detection into production code for adaptive tx
  - tune notify FrameReader read size explicitly to avoid throughput regression
  - drop local stario replace and depend on released b612.me/stario v0.1.1
This commit is contained in:
2026-04-18 16:05:57 +08:00
parent 4f760f2807
commit f038a89771
76 changed files with 12656 additions and 906 deletions
+370 -43
View File
@@ -7,22 +7,25 @@ import (
)
type BulkOpenRequest struct {
BulkID string
DataID uint64
Range BulkRange
Metadata BulkMetadata
ReadTimeout time.Duration
WriteTimeout time.Duration
Dedicated bool
AttachToken string
ChunkSize int
WindowBytes int
MaxInFlight int
BulkID string
DataID uint64
FastPathVersion uint8
Range BulkRange
Metadata BulkMetadata
ReadTimeout time.Duration
WriteTimeout time.Duration
Dedicated bool
DedicatedLaneID uint32
AttachToken string
ChunkSize int
WindowBytes int
MaxInFlight int
}
type BulkOpenResponse struct {
BulkID string
DataID uint64
FastPathVersion uint8
Accepted bool
Dedicated bool
AttachToken string
@@ -53,6 +56,18 @@ type BulkResetResponse struct {
Error string
}
type BulkReadyRequest struct {
BulkID string
DataID uint64
Error string
}
type BulkReadyResponse struct {
BulkID string
Accepted bool
Error string
}
type BulkReleaseRequest struct {
BulkID string
DataID uint64
@@ -73,6 +88,9 @@ func bindClientBulkControl(c *ClientCommon) {
c.SetLink(BulkResetSignalKey, func(msg *Message) {
c.handleInboundBulkReset(msg)
})
c.SetLink(BulkReadySignalKey, func(msg *Message) {
c.handleInboundBulkReady(msg)
})
c.SetLink(BulkReleaseSignalKey, func(msg *Message) {
c.handleInboundBulkRelease(msg)
})
@@ -91,14 +109,188 @@ func bindServerBulkControl(s *ServerCommon) {
s.SetLink(BulkResetSignalKey, func(msg *Message) {
s.handleInboundBulkReset(msg)
})
s.SetLink(BulkReadySignalKey, func(msg *Message) {
s.handleInboundBulkReady(msg)
})
s.SetLink(BulkReleaseSignalKey, func(msg *Message) {
s.handleInboundBulkRelease(msg)
})
}
func bulkAcceptInfoFromClientBulk(bulk *bulkHandle) BulkAcceptInfo {
if bulk == nil {
return BulkAcceptInfo{}
}
return BulkAcceptInfo{
ID: bulk.ID(),
Range: bulk.Range(),
Metadata: bulk.Metadata(),
Dedicated: bulk.Dedicated(),
TransportGeneration: bulk.TransportGeneration(),
Bulk: bulk,
}
}
func bulkAcceptInfoFromServerBulk(bulk *bulkHandle, logical *LogicalConn, transport *TransportConn) BulkAcceptInfo {
if bulk == nil {
return BulkAcceptInfo{}
}
if logical == nil {
logical = bulk.LogicalConn()
}
if transport == nil {
transport = bulk.TransportConn()
}
return BulkAcceptInfo{
ID: bulk.ID(),
Range: bulk.Range(),
Metadata: bulk.Metadata(),
Dedicated: bulk.Dedicated(),
LogicalConn: logical,
TransportConn: transport,
TransportGeneration: bulk.TransportGeneration(),
Bulk: bulk,
}
}
func dispatchBulkAccept(handler func(BulkAcceptInfo) error, bulk *bulkHandle, info BulkAcceptInfo) error {
if bulk == nil {
return errBulkNotFound
}
if !bulk.markAcceptDispatched() {
return nil
}
var dispatchErr error
defer func() {
bulk.finishAcceptDispatch(dispatchErr)
}()
if handler == nil {
dispatchErr = errBulkHandlerNotConfigured
bulk.markReset(dispatchErr)
return dispatchErr
}
if err := handler(info); err != nil {
dispatchErr = err
bulk.markReset(dispatchErr)
return dispatchErr
}
return nil
}
func (c *ClientCommon) clientBulkAcceptReadyNotifier(bulk *bulkHandle) func(error) {
return func(readyErr error) {
if c == nil || bulk == nil {
return
}
req := BulkReadyRequest{
BulkID: bulk.ID(),
DataID: bulk.dataIDSnapshot(),
}
if readyErr != nil {
req.Error = readyErr.Error()
}
ctx, cancel := context.WithTimeout(context.Background(), defaultBulkAcceptReadyTimeout)
defer cancel()
if _, err := sendBulkReadyClient(ctx, c, req); err != nil && bulk.Context().Err() == nil {
bulk.markReset(err)
}
}
}
func sendBulkReadyServer(ctx context.Context, s *ServerCommon, logical *LogicalConn, transport *TransportConn, req BulkReadyRequest) error {
if s == nil {
return errBulkServerNil
}
if transport != nil {
if _, err := sendBulkReadyServerTransport(ctx, s, transport, req); err == nil {
return nil
} else if !errors.Is(err, errTransportDetached) && !errors.Is(err, errBulkTransportNil) {
return err
}
}
if logical == nil {
return errBulkLogicalConnNil
}
_, err := sendBulkReadyServerLogical(ctx, s, logical, req)
return err
}
func (s *ServerCommon) serverBulkAcceptReadyNotifier(bulk *bulkHandle, logical *LogicalConn, transport *TransportConn) func(error) {
return func(readyErr error) {
if s == nil || bulk == nil {
return
}
req := BulkReadyRequest{
BulkID: bulk.ID(),
DataID: bulk.dataIDSnapshot(),
}
if readyErr != nil {
req.Error = readyErr.Error()
}
ctx, cancel := context.WithTimeout(context.Background(), defaultBulkAcceptReadyTimeout)
defer cancel()
if err := sendBulkReadyServer(ctx, s, logical, transport, req); err != nil && bulk.Context().Err() == nil {
bulk.markReset(err)
}
}
}
func (c *ClientCommon) startClientBulkAcceptDispatch(bulk *bulkHandle) {
if c == nil || bulk == nil {
return
}
bulk.setAcceptNotify(c.clientBulkAcceptReadyNotifier(bulk))
go func() {
_ = c.dispatchClientBulkAccept(bulk)
}()
}
func (s *ServerCommon) startServerBulkAcceptDispatch(bulk *bulkHandle, logical *LogicalConn, transport *TransportConn) {
if s == nil || bulk == nil {
return
}
if logical == nil {
logical = bulk.LogicalConn()
}
if transport == nil {
transport = bulk.TransportConn()
}
bulk.setAcceptNotify(s.serverBulkAcceptReadyNotifier(bulk, logical, transport))
go func() {
_ = s.dispatchServerBulkAccept(bulk, logical, transport)
}()
}
func (c *ClientCommon) dispatchClientBulkAccept(bulk *bulkHandle) error {
if c == nil {
return errBulkClientNil
}
runtime := c.getBulkRuntime()
if runtime == nil {
return errBulkRuntimeNil
}
return dispatchBulkAccept(runtime.handlerSnapshot(), bulk, bulkAcceptInfoFromClientBulk(bulk))
}
func (s *ServerCommon) dispatchServerBulkAccept(bulk *bulkHandle, logical *LogicalConn, transport *TransportConn) error {
if s == nil {
return errBulkServerNil
}
runtime := s.getBulkRuntime()
if runtime == nil {
return errBulkRuntimeNil
}
return dispatchBulkAccept(runtime.handlerSnapshot(), bulk, bulkAcceptInfoFromServerBulk(bulk, logical, transport))
}
func (c *ClientCommon) handleInboundBulkOpen(msg *Message) {
req, err := decodeBulkOpenRequest(msg)
resp := BulkOpenResponse{BulkID: req.BulkID, DataID: req.DataID, Dedicated: req.Dedicated}
resp := BulkOpenResponse{
BulkID: req.BulkID,
DataID: req.DataID,
FastPathVersion: negotiateBulkFastPathVersion(req.FastPathVersion),
Dedicated: req.Dedicated,
}
if err != nil {
resp.Error = err.Error()
replyBulkControlIfNeeded(msg, resp)
@@ -133,13 +325,6 @@ func (c *ClientCommon) handleInboundBulkOpen(msg *Message) {
replyBulkControlIfNeeded(msg, resp)
return
}
handler := runtime.handlerSnapshot()
if handler == nil {
bulk.markReset(errBulkHandlerNotConfigured)
resp.Error = errBulkHandlerNotConfigured.Error()
replyBulkControlIfNeeded(msg, resp)
return
}
if req.Dedicated {
if err := c.attachDedicatedBulkSidecar(context.Background(), bulk); err != nil {
bulk.markReset(err)
@@ -147,17 +332,14 @@ func (c *ClientCommon) handleInboundBulkOpen(msg *Message) {
replyBulkControlIfNeeded(msg, resp)
return
}
resp.Accepted = true
resp.DataID = bulk.dataIDSnapshot()
resp.TransportGeneration = bulk.TransportGeneration()
replyBulkControlIfNeeded(msg, resp)
c.startClientBulkAcceptDispatch(bulk)
return
}
info := BulkAcceptInfo{
ID: bulk.ID(),
Range: bulk.Range(),
Metadata: bulk.Metadata(),
Dedicated: bulk.Dedicated(),
TransportGeneration: bulk.TransportGeneration(),
Bulk: bulk,
}
if err := handler(info); err != nil {
bulk.markReset(err)
if err := c.dispatchClientBulkAccept(bulk); err != nil {
resp.Error = err.Error()
replyBulkControlIfNeeded(msg, resp)
return
@@ -170,7 +352,12 @@ func (c *ClientCommon) handleInboundBulkOpen(msg *Message) {
func (s *ServerCommon) handleInboundBulkOpen(msg *Message) {
req, err := decodeBulkOpenRequest(msg)
resp := BulkOpenResponse{BulkID: req.BulkID, DataID: req.DataID, Dedicated: req.Dedicated}
resp := BulkOpenResponse{
BulkID: req.BulkID,
DataID: req.DataID,
FastPathVersion: negotiateBulkFastPathVersion(req.FastPathVersion),
Dedicated: req.Dedicated,
}
if err != nil {
resp.Error = err.Error()
replyBulkControlIfNeeded(msg, resp)
@@ -218,25 +405,24 @@ func (s *ServerCommon) handleInboundBulkOpen(msg *Message) {
replyBulkControlIfNeeded(msg, resp)
return
}
handler := runtime.handlerSnapshot()
if handler == nil {
s.attachServerDedicatedSidecarIfExists(logical, bulk)
if runtime.handlerSnapshot() == nil {
bulk.markReset(errBulkHandlerNotConfigured)
resp.Error = errBulkHandlerNotConfigured.Error()
replyBulkControlIfNeeded(msg, resp)
return
}
info := BulkAcceptInfo{
ID: bulk.ID(),
Range: bulk.Range(),
Metadata: bulk.Metadata(),
Dedicated: bulk.Dedicated(),
LogicalConn: logical,
TransportConn: transport,
TransportGeneration: bulk.TransportGeneration(),
Bulk: bulk,
if req.Dedicated {
resp.Accepted = true
resp.DataID = bulk.dataIDSnapshot()
resp.TransportGeneration = bulk.TransportGeneration()
replyBulkControlIfNeeded(msg, resp)
if bulk.dedicatedAttachedSnapshot() {
s.startServerBulkAcceptDispatch(bulk, logical, messageTransportConnSnapshot(msg))
}
return
}
if err := handler(info); err != nil {
bulk.markReset(err)
if err := s.dispatchServerBulkAccept(bulk, logical, transport); err != nil {
resp.Error = err.Error()
replyBulkControlIfNeeded(msg, resp)
return
@@ -357,6 +543,41 @@ func (c *ClientCommon) handleInboundBulkRelease(msg *Message) {
bulk.releaseOutboundWindow(req.Bytes, req.Chunks)
}
func (c *ClientCommon) handleInboundBulkReady(msg *Message) {
req, err := decodeBulkReadyRequest(msg)
resp := BulkReadyResponse{BulkID: req.BulkID}
if err != nil {
resp.Error = err.Error()
replyBulkControlIfNeeded(msg, resp)
return
}
runtime := c.getBulkRuntime()
if runtime == nil {
resp.Error = errBulkRuntimeNil.Error()
replyBulkControlIfNeeded(msg, resp)
return
}
bulk, ok := runtime.lookup(clientFileScope(), req.BulkID)
if !ok && req.DataID != 0 {
bulk, ok = runtime.lookupByDataID(clientFileScope(), req.DataID)
}
if !ok {
resp.Error = errBulkNotFound.Error()
replyBulkControlIfNeeded(msg, resp)
return
}
if resp.BulkID == "" {
resp.BulkID = bulk.ID()
}
readyErr := bulkReadyRemoteError(req.Error)
bulk.markAcceptReady(readyErr)
if readyErr != nil {
bulk.markReset(readyErr)
}
resp.Accepted = true
replyBulkControlIfNeeded(msg, resp)
}
func (s *ServerCommon) handleInboundBulkReset(msg *Message) {
req, err := decodeBulkResetRequest(msg)
resp := BulkResetResponse{BulkID: req.BulkID}
@@ -390,6 +611,43 @@ func (s *ServerCommon) handleInboundBulkReset(msg *Message) {
replyBulkControlIfNeeded(msg, resp)
}
func (s *ServerCommon) handleInboundBulkReady(msg *Message) {
req, err := decodeBulkReadyRequest(msg)
resp := BulkReadyResponse{BulkID: req.BulkID}
if err != nil {
resp.Error = err.Error()
replyBulkControlIfNeeded(msg, resp)
return
}
runtime := s.getBulkRuntime()
if runtime == nil {
resp.Error = errBulkRuntimeNil.Error()
replyBulkControlIfNeeded(msg, resp)
return
}
logical := messageLogicalConnSnapshot(msg)
scope := serverFileScope(logical)
bulk, ok := runtime.lookup(scope, req.BulkID)
if !ok && req.DataID != 0 {
bulk, ok = runtime.lookupByDataID(scope, req.DataID)
}
if !ok {
resp.Error = errBulkNotFound.Error()
replyBulkControlIfNeeded(msg, resp)
return
}
if resp.BulkID == "" {
resp.BulkID = bulk.ID()
}
readyErr := bulkReadyRemoteError(req.Error)
bulk.markAcceptReady(readyErr)
if readyErr != nil {
bulk.markReset(readyErr)
}
resp.Accepted = true
replyBulkControlIfNeeded(msg, resp)
}
func (s *ServerCommon) handleInboundBulkRelease(msg *Message) {
req, err := decodeBulkReleaseRequest(msg)
if err != nil {
@@ -625,11 +883,26 @@ func decodeBulkReleaseRequest(msg *Message) (BulkReleaseRequest, error) {
return req, nil
}
func decodeBulkReadyRequest(msg *Message) (BulkReadyRequest, error) {
var req BulkReadyRequest
if msg == nil {
return BulkReadyRequest{}, errBulkIDEmpty
}
if err := msg.Value.Orm(&req); err != nil {
return BulkReadyRequest{}, err
}
if req.BulkID == "" && req.DataID == 0 {
return BulkReadyRequest{}, errBulkIDEmpty
}
return req, nil
}
func decodeBulkOpenResponse(msg Message) (BulkOpenResponse, error) {
var resp BulkOpenResponse
if err := msg.Value.Orm(&resp); err != nil {
return BulkOpenResponse{}, err
}
resp.FastPathVersion = normalizeBulkFastPathVersion(resp.FastPathVersion)
return resp, bulkControlResultError("open", resp.Accepted, resp.Error, nil)
}
@@ -649,6 +922,14 @@ func decodeBulkResetResponse(msg Message) (BulkResetResponse, error) {
return resp, bulkControlResultError("reset", resp.Accepted, resp.Error, nil)
}
func decodeBulkReadyResponse(msg Message) (BulkReadyResponse, error) {
var resp BulkReadyResponse
if err := msg.Value.Orm(&resp); err != nil {
return BulkReadyResponse{}, err
}
return resp, bulkControlResultError("ready", resp.Accepted, resp.Error, nil)
}
func bulkControlResultError(op string, accepted bool, message string, callErr error) error {
if callErr != nil {
return callErr
@@ -697,6 +978,52 @@ func bulkRemoteResetError(message string) error {
return errors.New(message)
}
func bulkReadyRemoteError(message string) error {
if message == "" {
return nil
}
return bulkControlMessageError(message)
}
func sendBulkReadyClient(ctx context.Context, c Client, req BulkReadyRequest) (BulkReadyResponse, error) {
if c == nil {
return BulkReadyResponse{}, errBulkClientNil
}
msg, err := c.SendObjCtx(ctx, BulkReadySignalKey, req)
if err != nil {
return BulkReadyResponse{}, err
}
return decodeBulkReadyResponse(msg)
}
func sendBulkReadyServerLogical(ctx context.Context, s Server, logical *LogicalConn, req BulkReadyRequest) (BulkReadyResponse, error) {
if s == nil {
return BulkReadyResponse{}, errBulkServerNil
}
if logical == nil {
return BulkReadyResponse{}, errBulkLogicalConnNil
}
msg, err := s.SendObjCtxLogical(ctx, logical, BulkReadySignalKey, req)
if err != nil {
return BulkReadyResponse{}, err
}
return decodeBulkReadyResponse(msg)
}
func sendBulkReadyServerTransport(ctx context.Context, s Server, transport *TransportConn, req BulkReadyRequest) (BulkReadyResponse, error) {
if s == nil {
return BulkReadyResponse{}, errBulkServerNil
}
if transport == nil {
return BulkReadyResponse{}, errBulkTransportNil
}
msg, err := s.SendObjCtxTransport(ctx, transport, BulkReadySignalKey, req)
if err != nil {
return BulkReadyResponse{}, err
}
return decodeBulkReadyResponse(msg)
}
func bulkTransportGeneration(logical *LogicalConn, transport *TransportConn) uint64 {
return streamTransportGeneration(logical, transport)
}