core/txpool/blobpool: queue up billy conversion tasks

This commit is contained in:
Felix Lange 2025-09-24 16:27:58 +02:00
parent 284d4c29ae
commit e675f80e5d
3 changed files with 49 additions and 32 deletions

View file

@ -341,8 +341,6 @@ type BlobPool struct {
chain BlockChain // Chain object to access the state through chain BlockChain // Chain object to access the state through
cQueue *conversionQueue // The queue for performing legacy sidecar conversion (TODO: remove after Osaka) cQueue *conversionQueue // The queue for performing legacy sidecar conversion (TODO: remove after Osaka)
sidecarMigrationDoneCh chan struct{} // Channel for signaling when all txs at the boundary have been converted (TODO: remove after Osaka)
head atomic.Pointer[types.Header] // Current head of the chain head atomic.Pointer[types.Header] // Current head of the chain
state *state.StateDB // Current state at the head of the chain state *state.StateDB // Current state at the head of the chain
gasTip atomic.Pointer[uint256.Int] // Currently accepted minimum gas tip gasTip atomic.Pointer[uint256.Int] // Currently accepted minimum gas tip
@ -1046,9 +1044,6 @@ func (p *BlobPool) convertLegacySidecars(ids map[common.Address]map[uint64]uint6
} }
} }
} }
if p.sidecarMigrationDoneCh != nil {
close(p.sidecarMigrationDoneCh)
}
log.Info("Completed blob transaction conversion", "discarded", failure, "injected", success, "elapsed", common.PrettyDuration(time.Since(start))) log.Info("Completed blob transaction conversion", "discarded", failure, "injected", success, "elapsed", common.PrettyDuration(time.Since(start)))
} }

View file

@ -2196,7 +2196,6 @@ func TestSidecarConversion(t *testing.T) {
} }
// Kick off migration. // Kick off migration.
pool.sidecarMigrationDoneCh = make(chan struct{})
pool.Reset(header0, header1) pool.Reset(header0, header1)
// Add the v0 sidecar tx, but don't block so we can keep doing other stuff // Add the v0 sidecar tx, but don't block so we can keep doing other stuff
@ -2245,7 +2244,7 @@ func TestSidecarConversion(t *testing.T) {
} }
// Wait for the pool migration to complete. // Wait for the pool migration to complete.
<-pool.sidecarMigrationDoneCh <-pool.cQueue.anyBillyConversionDone
// Verify all transactions in the pool were converted and verify the // Verify all transactions in the pool were converted and verify the
// subsequent cell proofs. // subsequent cell proofs.

View file

@ -43,19 +43,27 @@ type txConvert struct {
// it is performed in the background by a single thread, ensuring the main Geth // it is performed in the background by a single thread, ensuring the main Geth
// process is not overloaded. // process is not overloaded.
type conversionQueue struct { type conversionQueue struct {
tasks chan *txConvert tasks chan *txConvert
billy chan func() startBilly chan func()
quit chan struct{} quit chan struct{}
closed chan struct{} closed chan struct{}
billyQueue []func()
billyTaskDone chan struct{}
// This channel will be closed when the first billy conversion finishes.
// It's added for unit tests to synchronize with the conversion progress.
anyBillyConversionDone chan struct{}
} }
// newConversionQueue constructs the conversion queue. // newConversionQueue constructs the conversion queue.
func newConversionQueue() *conversionQueue { func newConversionQueue() *conversionQueue {
q := &conversionQueue{ q := &conversionQueue{
tasks: make(chan *txConvert), tasks: make(chan *txConvert),
billy: make(chan func()), startBilly: make(chan func()),
quit: make(chan struct{}), quit: make(chan struct{}),
closed: make(chan struct{}), closed: make(chan struct{}),
anyBillyConversionDone: make(chan struct{}),
} }
go q.loop() go q.loop()
return q return q
@ -78,7 +86,7 @@ func (q *conversionQueue) convert(tx *types.Transaction) error {
// launchBillyConversion starts a conversion task in the background. // launchBillyConversion starts a conversion task in the background.
func (q *conversionQueue) launchBillyConversion(fn func()) error { func (q *conversionQueue) launchBillyConversion(fn func()) error {
select { select {
case q.billy <- fn: case q.startBilly <- fn:
return nil return nil
case <-q.closed: case <-q.closed:
return errors.New("conversion queue closed") return errors.New("conversion queue closed")
@ -122,16 +130,17 @@ func (q *conversionQueue) loop() {
defer close(q.closed) defer close(q.closed)
var ( var (
billyDone = make(chan struct{}) done chan struct{} // Non-nil if background routine is active
billyTasks int // Count of running billy conversion tasks interrupt *atomic.Int32 // Flag to signal conversion interruption
done chan struct{} // Non-nil if background routine is active
interrupt *atomic.Int32 // Flag to signal conversion interruption
// The pending tasks for sidecar conversion. We assume the number of legacy // The pending tasks for sidecar conversion. We assume the number of legacy
// blob transactions requiring conversion will not be excessive. However, // blob transactions requiring conversion will not be excessive. However,
// a hard cap is applied as a protective measure. // a hard cap is applied as a protective measure.
txTasks []*txConvert txTasks []*txConvert
firstBilly = true
) )
for { for {
select { select {
case t := <-q.tasks: case t := <-q.tasks:
@ -150,18 +159,19 @@ func (q *conversionQueue) loop() {
go q.run(tasks, done, interrupt) go q.run(tasks, done, interrupt)
} }
case fn := <-q.billy:
billyTasks++
go func() {
fn()
billyDone <- struct{}{}
}()
case <-done: case <-done:
done, interrupt = nil, nil done, interrupt = nil, nil
case <-billyDone: case fn := <-q.startBilly:
billyTasks-- q.billyQueue = append(q.billyQueue, fn)
q.runNextBillyTask()
case <-q.billyTaskDone:
if firstBilly {
close(q.anyBillyConversionDone)
firstBilly = false
}
q.runNextBillyTask()
case <-q.quit: case <-q.quit:
if done != nil { if done != nil {
@ -169,12 +179,25 @@ func (q *conversionQueue) loop() {
interrupt.Store(1) interrupt.Store(1)
<-done <-done
} }
for billyTasks > 0 { if q.billyTaskDone != nil {
log.Debug("Waiting for blobpool billy conversion to exit") log.Debug("Waiting for blobpool billy conversion to exit")
<-billyDone <-q.billyTaskDone
billyTasks--
} }
return return
} }
} }
} }
func (q *conversionQueue) runNextBillyTask() {
if len(q.billyQueue) == 0 {
q.billyTaskDone = nil
return
}
fn := q.billyQueue[0]
q.billyQueue = append(q.billyQueue[:0], q.billyQueue[1:]...)
done := make(chan struct{})
go func() { defer close(done); fn() }()
q.billyTaskDone = done
}