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
+105 -11
View File
@@ -62,18 +62,72 @@ func TestWriteFullToConnSerializesConcurrentWriters(t *testing.T) {
}
}
func newTestBulkBatchSender(binding *transportBinding) *bulkBatchSender {
return newTestBulkBatchSenderWithWriteTimeout(binding, nil)
}
func newTestBulkBatchSenderWithWriteTimeout(binding *transportBinding, writeTimeout func() time.Duration) *bulkBatchSender {
return newBulkBatchSender(binding, bulkBatchCodec{
encodeSingle: func(frame bulkFastFrame) ([]byte, func(), error) {
return append([]byte(nil), frame.Payload...), nil, nil
},
encodeBatch: func(frames []bulkFastFrame) ([]byte, func(), error) {
payload, err := encodeBulkFastBatchPlain(frames)
return payload, nil, err
},
}, writeTimeout)
}
func newTestStreamBatchSender(binding *transportBinding, writeTimeout func() time.Duration) *streamBatchSender {
return newStreamBatchSender(binding, streamBatchCodec{
encodeSingle: func(frame streamFastDataFrame) ([]byte, error) {
return encodeStreamFastFramePayload(frame)
},
encodeBatch: func(frames []streamFastDataFrame) ([]byte, error) {
return encodeStreamFastBatchPlain(frames)
},
}, writeTimeout)
}
func TestBulkBatchSenderRespectsWriteDeadlineWhenReceiverStalls(t *testing.T) {
left, right := net.Pipe()
defer left.Close()
defer right.Close()
sender := newBulkBatchSender(newTransportBinding(left, stario.NewQueue()))
ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
defer cancel()
sender := newTestBulkBatchSenderWithWriteTimeout(newTransportBinding(left, stario.NewQueue()), func() time.Duration {
return 50 * time.Millisecond
})
errCh := make(chan error, 1)
go func() {
errCh <- sender.submit(ctx, []byte("payload"))
errCh <- sender.submitData(context.Background(), 1, 1, bulkFastPathVersionV1, []byte("payload"))
}()
select {
case err := <-errCh:
if err == nil {
t.Fatal("sender.submit should fail when receiver stalls")
}
if !isTimeoutLikeError(err) {
t.Fatalf("sender.submit error = %v, want timeout-like error", err)
}
case <-time.After(time.Second):
t.Fatal("sender.submit should not hang when receiver stalls")
}
}
func TestStreamBatchSenderRespectsBindingWriteDeadlineWhenReceiverStalls(t *testing.T) {
left, right := net.Pipe()
defer left.Close()
defer right.Close()
sender := newTestStreamBatchSender(newTransportBinding(left, stario.NewQueue()), func() time.Duration {
return 50 * time.Millisecond
})
errCh := make(chan error, 1)
go func() {
errCh <- sender.submitData(context.Background(), 1, 1, streamFastPathVersionV2, []byte("payload"))
}()
select {
@@ -258,12 +312,12 @@ func TestWriteNetBuffersFullUnlockedUsesUnwrappedVectoredConn(t *testing.T) {
func TestBulkBatchSenderSkipsQueuedCanceledRequest(t *testing.T) {
conn := newBlockingPacketWriteConn()
binding := newTransportBinding(conn, stario.NewQueue())
sender := newBulkBatchSender(binding)
sender := newTestBulkBatchSender(binding)
defer sender.stop()
firstErrCh := make(chan error, 1)
go func() {
firstErrCh <- sender.submit(context.Background(), []byte("first"))
firstErrCh <- sender.submitData(context.Background(), 1, 1, bulkFastPathVersionV1, []byte("first"))
}()
select {
@@ -275,7 +329,7 @@ func TestBulkBatchSenderSkipsQueuedCanceledRequest(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
secondErrCh := make(chan error, 1)
go func() {
secondErrCh <- sender.submit(ctx, []byte("second"))
secondErrCh <- sender.submitData(ctx, 1, 2, bulkFastPathVersionV1, []byte("second"))
}()
time.Sleep(20 * time.Millisecond)
cancel()
@@ -306,16 +360,48 @@ func TestBulkBatchSenderSkipsQueuedCanceledRequest(t *testing.T) {
}
}
func TestBulkBatchSenderDoesNotDirectSubmitShareableV2Data(t *testing.T) {
sender := &bulkBatchSender{}
req := bulkBatchRequest{
frames: []bulkFastFrame{{
Type: bulkFastPayloadTypeData,
DataID: 1,
Seq: 1,
Payload: make([]byte, 256*1024),
}},
fastPathVersion: bulkFastPathVersionV2,
}
if sender.shouldDirectSubmit(req) {
t.Fatal("shareable v2 shared bulk data should queue for super-batch instead of direct submit")
}
}
func TestBulkBatchSenderDirectSubmitsUnbatchableRequest(t *testing.T) {
sender := &bulkBatchSender{}
req := bulkBatchRequest{
frames: []bulkFastFrame{{
Type: bulkFastPayloadTypeRelease,
DataID: 1,
Seq: 0,
Payload: []byte("rel"),
}},
fastPathVersion: bulkFastPathVersionV2,
}
if !sender.shouldDirectSubmit(req) {
t.Fatal("unbatchable shared bulk control request should still direct submit")
}
}
func TestBulkBatchSenderReturnsFlushResultAfterStartedContextCancel(t *testing.T) {
conn := newBlockingPacketWriteConn()
binding := newTransportBinding(conn, stario.NewQueue())
sender := newBulkBatchSender(binding)
sender := newTestBulkBatchSender(binding)
defer sender.stop()
ctx, cancel := context.WithCancel(context.Background())
errCh := make(chan error, 1)
go func() {
errCh <- sender.submit(ctx, []byte("payload"))
errCh <- sender.submitData(ctx, 1, 1, bulkFastPathVersionV1, []byte("payload"))
}()
select {
@@ -346,10 +432,18 @@ func TestBulkBatchSenderReturnsFlushResultAfterStartedContextCancel(t *testing.T
func TestTransportBindingStopBackgroundWorkersStopsSharedSender(t *testing.T) {
binding := newTransportBinding(newBlockingPacketWriteConn(), stario.NewQueue())
sender := binding.bulkBatchSenderSnapshot()
sender := binding.bulkBatchSenderSnapshotWithCodec(bulkBatchCodec{
encodeSingle: func(frame bulkFastFrame) ([]byte, func(), error) {
return append([]byte(nil), frame.Payload...), nil, nil
},
encodeBatch: func(frames []bulkFastFrame) ([]byte, func(), error) {
payload, err := encodeBulkFastBatchPlain(frames)
return payload, nil, err
},
}, nil)
binding.stopBackgroundWorkers()
err := sender.submit(context.Background(), []byte("payload"))
err := sender.submitData(context.Background(), 1, 1, bulkFastPathVersionV1, []byte("payload"))
if !errors.Is(err, errTransportDetached) {
t.Fatalf("sender.submit after stop = %v, want %v", err, errTransportDetached)
}