这篇文章上次修改于 287 天前,可能其部分内容已经发生变化,如有疑问可询问作者。
结论
根据 sequencer 介绍,sequencer 一般几分钟就会向 L1 提交一次 batch
如果发生如下情况,则 sequencer 不会向 L1 提交 batch:
- 没有新的 tx,不过即便没有用户的 tx,在每个 L2 块形成时也会产生一笔 internal tx
- 没有设置
config.l1BlockBound = l1BlockBoundIgnore
,且块号或时间戳超过 L1 bounds 如果延时没达到 1h 且 batch 还没满
满足如下条件之一则形成一个 batch
- 数据量 >= 16 MiB
- segment 数量 >= 100 * 1024
- compressedBuffer.Len() >= 100000 - 40
另外,如果设置了 config.DataPoster.UseNoOpStorage = true
,则会等到 L1 交易确认后才会进行查看下一次 batch
详细信息
只贴关键代码
每隔开 10s 调用一次 maybePostSequencerBatch
func (b *BatchPoster) Start(ctxIn context.Context) {
...
b.CallIteratively(func(ctx context.Context) time.Duration {
...
if !b.redisLock.AttemptLock(ctx) {
b.building = nil
return b.config().PollInterval // 10 * time.Second
}
posted, err := b.maybePostSequencerBatch(ctx)
...
if err != nil {
...
return b.config().ErrorDelay // 10 * time.Second
} else if posted {
return 0
} else {
return b.config().PollInterval // 10 * time.Second
}
}
...
}
func (b *BatchPoster) maybePostSequencerBatch(ctx context.Context) (bool, error) {
// 1. 没有新的 tx,则不会提交 batch
...
msgCount, err := b.streamer.GetMessageCount()
...
if msgCount <= batchPosition.MessageCount {
// There's nothing after the newest batch, therefore batch posting was not required
return false, nil
}
...
forcePostBatch := time.Since(firstMsgTime) >= config.MaxDelay // 1h
...
// 2. 块号或时间戳超过 L1 bounds,则不会提交 batch
for b.building.msgCount < msgCount {
...
if msg.Message.Header.BlockNumber > l1BoundMaxBlockNumber || msg.Message.Header.Timestamp > l1BoundMaxTimestamp {
b.lastHitL1Bounds = time.Now()
log.Info(
"not posting more messages because block number or timestamp exceed L1 bounds",
"blockNumber", msg.Message.Header.BlockNumber,
"l1BoundMaxBlockNumber", l1BoundMaxBlockNumber,
"timestamp", msg.Message.Header.Timestamp,
"l1BoundMaxTimestamp", l1BoundMaxTimestamp,
)
break
}
...
success, err := b.building.segments.AddMessage(msg)
...
if !success {
// this batch is full
if !config.WaitForMaxDelay {
forcePostBatch = true
}
b.building.haveUsefulMessage = true
break
}
if msg.Message.Header.Kind != arbostypes.L1MessageType_BatchPostingReport {
b.building.haveUsefulMessage = true
}
b.building.msgCount++
}
// 3. 如果不是 forcePostBatch 且 batch 还没满,则不会提交 batch
// 满足如下条件之一则形成一个 batch
// 1) 数据量 >= 16 MiB
// 2) segment 数量 >= 100 * 1024
// 3) compressedBuffer.Len() >= 100000 - 40
if !forcePostBatch || !b.building.haveUsefulMessage {
// the batch isn't full yet and we've posted a batch recently
// don't post anything for now
return false, nil
}
// 原因 2 会可能会导致 b.building.segments.rawSegments 为空
sequencerMsg, err := b.building.segments.CloseAndGetBytes()
...
if sequencerMsg == nil {
log.Debug("BatchPoster: batch nil", "sequence nr.", batchPosition.NextSeqNum, "from", batchPosition.MessageCount, "prev delayed", batchPosition.DelayedMessageCount)
b.building = nil // a closed batchSegments can't be reused
return false, nil
}
...
// 可能要等待 L1 交易被确认
// If we aren't queueing up transactions, wait for the receipt before moving on to the next batch.
if config.DataPoster.UseNoOpStorage {
receipt, err := b.l1Reader.WaitForTxApproval(ctx, tx)
if err != nil {
return false, fmt.Errorf("error waiting for tx receipt: %w", err)
}
log.Info("Got successful receipt from batch poster transaction", "txHash", tx.Hash(), "blockNumber", receipt.BlockNumber, "blockHash", receipt.BlockHash)
}
return true, nil
}
}
原因 3 中,batch full 满足如下条件之一即可
- 数据量 >= 16 MiB
- segment 数量 >= 100 * 1024
- compressedBuffer.Len() >= 100000 - 40
func (s *batchSegments) testForOverflow(isHeader bool) (bool, error) {
// we've reached the max decompressed size
if s.totalUncompressedSize > arbstate.MaxDecompressedLen {
return true, nil
}
// we've reached the max number of segments
if len(s.rawSegments) >= arbstate.MaxSegmentsPerSequencerMessage {
return true, nil
}
...
err := s.compressedWriter.Flush()
...
s.lastCompressedSize = s.compressedBuffer.Len()
s.newUncompressedSize = 0
if s.lastCompressedSize >= s.sizeLimit {
return true, nil
}
return false, nil
}
没有评论