Skip to content

Commit 8d1b1c2

Browse files
authored
core/txpool/blobpool: auto-start next conversion batch after completion (#33301)
This change fixes a stall in the legacy blob sidecar conversion pipeline where tasks that arrived during an active batch could remain unprocessed indefinitely after that batch completed, unless a new external event arrived. The root cause was that the loop did not restart processing in the case <-done: branch even when txTasks had accumulated work, relying instead on a future event to retrigger the scheduler. This behavior is inconsistent with the billy task pipeline, which immediately chains to the next task via runNextBillyTask() without requiring an external trigger. The fix adds a symmetric restart path in `case <-done`: that checks `len(txTasks) > 0`, clones the accumulated tasks, clears the queue, and launches a new run with a fresh done and interrupt. This preserves batching semantics, prevents indefinite blocking of callers of convert(), and remains safe during shutdown since the quit path still interrupts and awaits the active batch. No public interfaces or logging were changed.
1 parent 6426257 commit 8d1b1c2

File tree

2 files changed

+76
-0
lines changed

2 files changed

+76
-0
lines changed

core/txpool/blobpool/conversion.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,12 @@ func (q *conversionQueue) loop() {
161161

162162
case <-done:
163163
done, interrupt = nil, nil
164+
if len(txTasks) > 0 {
165+
done, interrupt = make(chan struct{}), new(atomic.Int32)
166+
tasks := slices.Clone(txTasks)
167+
txTasks = txTasks[:0]
168+
go q.run(tasks, done, interrupt)
169+
}
164170

165171
case fn := <-q.startBilly:
166172
q.billyQueue = append(q.billyQueue, fn)

core/txpool/blobpool/conversion_test.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ package blobpool
1919
import (
2020
"crypto/ecdsa"
2121
"crypto/sha256"
22+
"sync"
2223
"testing"
24+
"time"
2325

2426
"github.com/ethereum/go-ethereum/common"
2527
"github.com/ethereum/go-ethereum/core/types"
@@ -99,3 +101,71 @@ func TestConversionQueueDoubleClose(t *testing.T) {
99101
queue.close()
100102
queue.close() // Should not panic
101103
}
104+
105+
func TestConversionQueueAutoRestartBatch(t *testing.T) {
106+
queue := newConversionQueue()
107+
defer queue.close()
108+
109+
key, _ := crypto.GenerateKey()
110+
111+
// Create a heavy transaction to ensure the first batch runs long enough
112+
// for subsequent tasks to be queued while it is active.
113+
heavy := makeMultiBlobTx(0, 1, 1, 1, int(params.BlobTxMaxBlobs), 0, key, types.BlobSidecarVersion0)
114+
115+
var wg sync.WaitGroup
116+
wg.Add(1)
117+
heavyDone := make(chan error, 1)
118+
go func() {
119+
defer wg.Done()
120+
heavyDone <- queue.convert(heavy)
121+
}()
122+
123+
// Give the conversion worker a head start so that the following tasks are
124+
// enqueued while the first batch is running.
125+
time.Sleep(200 * time.Millisecond)
126+
127+
tx1 := makeTx(1, 1, 1, 1, key)
128+
tx2 := makeTx(2, 1, 1, 1, key)
129+
130+
wg.Add(2)
131+
done1 := make(chan error, 1)
132+
done2 := make(chan error, 1)
133+
go func() { defer wg.Done(); done1 <- queue.convert(tx1) }()
134+
go func() { defer wg.Done(); done2 <- queue.convert(tx2) }()
135+
136+
select {
137+
case err := <-done1:
138+
if err != nil {
139+
t.Fatalf("tx1 conversion error: %v", err)
140+
}
141+
case <-time.After(30 * time.Second):
142+
t.Fatal("timeout waiting for tx1 conversion")
143+
}
144+
145+
select {
146+
case err := <-done2:
147+
if err != nil {
148+
t.Fatalf("tx2 conversion error: %v", err)
149+
}
150+
case <-time.After(30 * time.Second):
151+
t.Fatal("timeout waiting for tx2 conversion")
152+
}
153+
154+
select {
155+
case err := <-heavyDone:
156+
if err != nil {
157+
t.Fatalf("heavy conversion error: %v", err)
158+
}
159+
case <-time.After(30 * time.Second):
160+
t.Fatal("timeout waiting for heavy conversion")
161+
}
162+
163+
wg.Wait()
164+
165+
if tx1.BlobTxSidecar().Version != types.BlobSidecarVersion1 {
166+
t.Fatalf("tx1 sidecar version mismatch: have %d, want %d", tx1.BlobTxSidecar().Version, types.BlobSidecarVersion1)
167+
}
168+
if tx2.BlobTxSidecar().Version != types.BlobSidecarVersion1 {
169+
t.Fatalf("tx2 sidecar version mismatch: have %d, want %d", tx2.BlobTxSidecar().Version, types.BlobSidecarVersion1)
170+
}
171+
}

0 commit comments

Comments
 (0)