mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-26 06:36:43 +00:00
DEV: Multi goroutine implementation
This commit is contained in:
parent
ec1caf10ed
commit
1edc65acdc
1 changed files with 76 additions and 10 deletions
|
|
@ -193,6 +193,13 @@ type Firehose struct {
|
||||||
// Testing state, only used in tests and private configs
|
// Testing state, only used in tests and private configs
|
||||||
testingBuffer *bytes.Buffer
|
testingBuffer *bytes.Buffer
|
||||||
testingIgnoreGenesisBlock bool
|
testingIgnoreGenesisBlock bool
|
||||||
|
|
||||||
|
// Worker queue for block printing
|
||||||
|
blockOutputQueue chan *blockOutput
|
||||||
|
flushOutputDone sync.WaitGroup
|
||||||
|
startBlockInitialized bool
|
||||||
|
startBlockNum uint64
|
||||||
|
startBlockCond *sync.Cond
|
||||||
}
|
}
|
||||||
|
|
||||||
const FirehoseProtocolVersion = "3.0"
|
const FirehoseProtocolVersion = "3.0"
|
||||||
|
|
@ -246,6 +253,8 @@ func NewFirehose(config *FirehoseConfig) *Firehose {
|
||||||
callStack: NewCallStack(),
|
callStack: NewCallStack(),
|
||||||
deferredCallState: NewDeferredCallState(),
|
deferredCallState: NewDeferredCallState(),
|
||||||
latestCallEnterSuicided: false,
|
latestCallEnterSuicided: false,
|
||||||
|
|
||||||
|
startBlockCond: sync.NewCond(&sync.Mutex{}),
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.private != nil {
|
if config.private != nil {
|
||||||
|
|
@ -258,11 +267,46 @@ func NewFirehose(config *FirehoseConfig) *Firehose {
|
||||||
if config.ConcurrentBlockFlushing {
|
if config.ConcurrentBlockFlushing {
|
||||||
log.Info("Firehose concurrent block flushing enabled, starting block " +
|
log.Info("Firehose concurrent block flushing enabled, starting block " +
|
||||||
"print worker goroutine")
|
"print worker goroutine")
|
||||||
firehose.blockPrintQueue = make(chan *blockPrintJob, 100)
|
const numWorkers = 10 // TODO: This becomes a parameter
|
||||||
|
firehose.blockPrintQueue = make(chan *blockPrintJob, 100) // TODO: Optimal buffer size tbd
|
||||||
|
firehose.blockOutputQueue = make(chan *blockOutput, 100)
|
||||||
|
for i := 0; i < numWorkers; i++ {
|
||||||
firehose.blockFlushDone.Add(1)
|
firehose.blockFlushDone.Add(1)
|
||||||
go firehose.blockPrintWorker()
|
go firehose.blockPrintWorker()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Output channel to order the flushing linearly
|
||||||
|
firehose.flushOutputDone.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer firehose.flushOutputDone.Done()
|
||||||
|
|
||||||
|
buffer := make(map[uint64][]byte)
|
||||||
|
|
||||||
|
firehose.startBlockCond.L.Lock()
|
||||||
|
for !firehose.startBlockInitialized {
|
||||||
|
firehose.startBlockCond.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
nextExpected := firehose.startBlockNum
|
||||||
|
firehose.startBlockCond.L.Unlock()
|
||||||
|
|
||||||
|
for result := range firehose.blockOutputQueue {
|
||||||
|
buffer[result.blockNum] = result.data
|
||||||
|
|
||||||
|
for {
|
||||||
|
data, ok := buffer[nextExpected]
|
||||||
|
if !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
firehose.flushToFirehose(data)
|
||||||
|
delete(buffer, nextExpected)
|
||||||
|
nextExpected++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
return firehose
|
return firehose
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -502,6 +546,14 @@ func (f *Firehose) OnBlockEnd(err error) {
|
||||||
|
|
||||||
// Flush block to firehose and optionally use goroutine
|
// Flush block to firehose and optionally use goroutine
|
||||||
if f.concurrentBlockFlushing {
|
if f.concurrentBlockFlushing {
|
||||||
|
f.startBlockCond.L.Lock()
|
||||||
|
if !f.startBlockInitialized {
|
||||||
|
f.startBlockNum = f.block.Number
|
||||||
|
f.startBlockInitialized = true
|
||||||
|
f.startBlockCond.Broadcast()
|
||||||
|
}
|
||||||
|
f.startBlockCond.L.Unlock()
|
||||||
|
|
||||||
job := &blockPrintJob{
|
job := &blockPrintJob{
|
||||||
block: f.block,
|
block: f.block,
|
||||||
finality: f.blockFinality,
|
finality: f.blockFinality,
|
||||||
|
|
@ -1856,14 +1908,13 @@ func (f *Firehose) panicInvalidState(msg string, callerSkip int) string {
|
||||||
|
|
||||||
// printBlockToFirehose is a helper function to print a block to Firehose protocl format.
|
// printBlockToFirehose is a helper function to print a block to Firehose protocl format.
|
||||||
func (f *Firehose) printBlockToFirehose(block *pbeth.Block, finalityStatus *FinalityStatus) {
|
func (f *Firehose) printBlockToFirehose(block *pbeth.Block, finalityStatus *FinalityStatus) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
|
||||||
marshalled, err := proto.Marshal(block)
|
marshalled, err := proto.Marshal(block)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Errorf("failed to marshal block: %w", err))
|
panic(fmt.Errorf("failed to marshal block: %w", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: If multiple goroutine, this becomes shared resource
|
|
||||||
f.outputBuffer.Reset()
|
|
||||||
|
|
||||||
previousHash := block.PreviousID()
|
previousHash := block.PreviousID()
|
||||||
previousNum := 0
|
previousNum := 0
|
||||||
if block.Number > 0 {
|
if block.Number > 0 {
|
||||||
|
|
@ -1882,9 +1933,9 @@ func (f *Firehose) printBlockToFirehose(block *pbeth.Block, finalityStatus *Fina
|
||||||
}
|
}
|
||||||
|
|
||||||
// **Important* The final space in the Sprintf template is mandatory!
|
// **Important* The final space in the Sprintf template is mandatory!
|
||||||
f.outputBuffer.WriteString(fmt.Sprintf("FIRE BLOCK %d %s %d %s %d %d ", block.Number, hex.EncodeToString(block.Hash), previousNum, previousHash, libNum, block.MustTime().UnixNano()))
|
buf.WriteString(fmt.Sprintf("FIRE BLOCK %d %s %d %s %d %d ", block.Number, hex.EncodeToString(block.Hash), previousNum, previousHash, libNum, block.MustTime().UnixNano()))
|
||||||
|
|
||||||
encoder := base64.NewEncoder(base64.StdEncoding, f.outputBuffer)
|
encoder := base64.NewEncoder(base64.StdEncoding, &buf)
|
||||||
if _, err = encoder.Write(marshalled); err != nil {
|
if _, err = encoder.Write(marshalled); err != nil {
|
||||||
panic(fmt.Errorf("write to encoder should have been infaillible: %w", err))
|
panic(fmt.Errorf("write to encoder should have been infaillible: %w", err))
|
||||||
}
|
}
|
||||||
|
|
@ -1893,9 +1944,16 @@ func (f *Firehose) printBlockToFirehose(block *pbeth.Block, finalityStatus *Fina
|
||||||
panic(fmt.Errorf("closing encoder should have been infaillible: %w", err))
|
panic(fmt.Errorf("closing encoder should have been infaillible: %w", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
f.outputBuffer.WriteString("\n")
|
buf.WriteString("\n")
|
||||||
|
|
||||||
f.flushToFirehose(f.outputBuffer.Bytes())
|
if f.concurrentBlockFlushing {
|
||||||
|
f.blockOutputQueue <- &blockOutput{
|
||||||
|
blockNum: block.Number,
|
||||||
|
data: buf.Bytes(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
f.flushToFirehose(buf.Bytes())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// printToFirehose is an easy way to print to Firehose format, it essentially
|
// printToFirehose is an easy way to print to Firehose format, it essentially
|
||||||
|
|
@ -2850,6 +2908,14 @@ func (f *Firehose) CloseBlockPrintQueue() {
|
||||||
f.closeChannels.Do(func() {
|
f.closeChannels.Do(func() {
|
||||||
close(f.blockPrintQueue)
|
close(f.blockPrintQueue)
|
||||||
f.blockFlushDone.Wait()
|
f.blockFlushDone.Wait()
|
||||||
|
|
||||||
|
close(f.blockOutputQueue)
|
||||||
|
f.flushOutputDone.Wait()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type blockOutput struct {
|
||||||
|
blockNum uint64
|
||||||
|
data []byte
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue