Skip to content

Commit 0940b1b

Browse files
committed
cleanup old readme
1 parent 6815a24 commit 0940b1b

1 file changed

Lines changed: 83 additions & 128 deletions

File tree

docs/adr/adr-021-lazy-aggregation.md

Lines changed: 83 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -20,143 +20,98 @@ Leverage the existing empty batch mechanism and `dataHashForEmptyTxs` to maintai
2020

2121
1. **Modified Batch Retrieval**:
2222

23-
The batch retrieval mechanism has been modified to handle empty batches differently. Instead of discarding empty batches, we now return them with the ErrNoBatch error, allowing the caller to create empty blocks with proper timestamps. This ensures that block timing remains consistent even during periods of inactivity.
24-
25-
```go
26-
func (m *Manager) retrieveBatch(ctx context.Context) (*BatchData, error) {
27-
res, err := m.sequencer.GetNextBatch(ctx, req)
28-
if err != nil {
29-
return nil, err
30-
}
31-
32-
if res != nil && res.Batch != nil {
33-
m.logger.Debug("Retrieved batch",
34-
"txCount", len(res.Batch.Transactions),
35-
"timestamp", res.Timestamp)
36-
37-
var errRetrieveBatch error
38-
// Even if there are no transactions, return the batch with timestamp
39-
// This allows empty blocks to maintain proper timing
40-
if len(res.Batch.Transactions) == 0 {
41-
errRetrieveBatch = ErrNoBatch
42-
}
43-
// Even if there are no transactions, update lastBatchData so we don't
44-
// repeatedly emit the same empty batch, and persist it to metadata.
45-
if err := m.store.SetMetadata(ctx, LastBatchDataKey, convertBatchDataToBytes(res.BatchData)); err != nil {
46-
m.logger.Error("error while setting last batch hash", "error", err)
47-
}
48-
m.lastBatchData = res.BatchData
49-
return &BatchData{Batch: res.Batch, Time: res.Timestamp, Data: res.BatchData}, errRetrieveBatch
50-
}
51-
return nil, ErrNoBatch
52-
}
53-
```
23+
The batch retrieval mechanism has been modified to handle empty batches differently. Instead of discarding empty batches, we now return them with the ErrNoBatch error, allowing the caller to create empty blocks with proper timestamps. This ensures that block timing remains consistent even during periods of inactivity.
24+
25+
```go
26+
func (m *Manager) retrieveBatch(ctx context.Context) (*BatchData, error) {
27+
res, err := m.sequencer.GetNextBatch(ctx, req)
28+
if err != nil {
29+
return nil, err
30+
}
31+
32+
if res != nil && res.Batch != nil {
33+
m.logger.Debug("Retrieved batch",
34+
"txCount", len(res.Batch.Transactions),
35+
"timestamp", res.Timestamp)
36+
37+
var errRetrieveBatch error
38+
// Even if there are no transactions, return the batch with timestamp
39+
// This allows empty blocks to maintain proper timing
40+
if len(res.Batch.Transactions) == 0 {
41+
errRetrieveBatch = ErrNoBatch
42+
}
43+
// Even if there are no transactions, update lastBatchData so we don't
44+
// repeatedly emit the same empty batch, and persist it to metadata.
45+
if err := m.store.SetMetadata(ctx, LastBatchDataKey, convertBatchDataToBytes(res.BatchData)); err != nil {
46+
m.logger.Error("error while setting last batch hash", "error", err)
47+
}
48+
m.lastBatchData = res.BatchData
49+
return &BatchData{Batch: res.Batch, Time: res.Timestamp, Data: res.BatchData}, errRetrieveBatch
50+
}
51+
return nil, ErrNoBatch
52+
}
53+
```
5454

5555
2. **Empty Block Creation**:
5656

57-
The block publishing logic has been enhanced to create empty blocks when a batch with no transactions is received. This uses the special `dataHashForEmptyTxs` value to indicate an empty batch, maintaining the block height consistency with the DA layer while minimizing overhead.
58-
59-
```go
60-
// In publishBlock method
61-
batchData, err := m.retrieveBatch(ctx)
62-
if err != nil {
63-
if errors.Is(err, ErrNoBatch) {
64-
if batchData == nil {
65-
m.logger.Info("No batch retrieved from sequencer, skipping block production")
66-
return nil
67-
}
68-
m.logger.Info("Creating empty block, height: ", newHeight)
69-
} else {
70-
return fmt.Errorf("failed to get transactions from batch: %w", err)
71-
}
72-
} else {
73-
if batchData.Before(lastHeaderTime) {
74-
return fmt.Errorf("timestamp is not monotonically increasing: %s < %s", batchData.Time, m.getLastBlockTime())
75-
}
76-
m.logger.Info("Creating and publishing block, height: ", newHeight)
77-
m.logger.Debug("block info", "num_tx", len(batchData.Batch.Transactions))
78-
}
79-
80-
header, data, err = m.createBlock(ctx, newHeight, lastSignature, lastHeaderHash, batchData)
81-
if err != nil {
82-
return err
83-
}
84-
85-
if err = m.store.SaveBlockData(ctx, header, data, &signature); err != nil {
86-
return SaveBlockError{err}
87-
}
88-
```
57+
The block publishing logic has been enhanced to create empty blocks when a batch with no transactions is received. This uses the special `dataHashForEmptyTxs` value to indicate an empty batch, maintaining the block height consistency with the DA layer while minimizing overhead.
58+
59+
```go
60+
// In publishBlock method
61+
batchData, err := m.retrieveBatch(ctx)
62+
if err != nil {
63+
if errors.Is(err, ErrNoBatch) {
64+
if batchData == nil {
65+
m.logger.Info("No batch retrieved from sequencer, skipping block production")
66+
return nil
67+
}
68+
m.logger.Info("Creating empty block, height: ", newHeight)
69+
} else {
70+
return fmt.Errorf("failed to get transactions from batch: %w", err)
71+
}
72+
} else {
73+
if batchData.Before(lastHeaderTime) {
74+
return fmt.Errorf("timestamp is not monotonically increasing: %s < %s", batchData.Time, m.getLastBlockTime())
75+
}
76+
m.logger.Info("Creating and publishing block, height: ", newHeight)
77+
m.logger.Debug("block info", "num_tx", len(batchData.Batch.Transactions))
78+
}
79+
80+
header, data, err = m.createBlock(ctx, newHeight, lastSignature, lastHeaderHash, batchData)
81+
if err != nil {
82+
return err
83+
}
84+
85+
if err = m.store.SaveBlockData(ctx, header, data, &signature); err != nil {
86+
return SaveBlockError{err}
87+
}
88+
```
8989

9090
3. **Lazy Aggregation Loop**:
9191

92-
A dedicated lazy aggregation loop has been implemented with dual timer mechanisms. The `lazyTimer` ensures blocks are produced at regular intervals even during network inactivity, while the `blockTimer` handles normal block production when transactions are available. Transaction notifications from the `Reaper` to the `Manager` are now handled via the `txNotifyCh` channel: when the `Reaper` detects new transactions, it calls `Manager.NotifyNewTransactions()`, which performs a non-blocking signal on this channel. See the tests in `block/lazy_aggregation_test.go` for verification of this behavior.
93-
94-
```go
95-
if r.manager != nil && len(newTxs) > 0 {
96-
r.logger.Debug("Notifying manager of new transactions")
97-
r.manager.NotifyNewTransactions() // Signals txNotifyCh
98-
}
99-
100-
// In Manager.NotifyNewTransactions
101-
func (m *Manager) NotifyNewTransactions() {
102-
select {
103-
case m.txNotifyCh <- struct{}{}:
104-
// Successfully sent notification
105-
default:
106-
// Channel buffer is full, notification already pending
107-
}
108-
}
109-
// Modified lazyAggregationLoop
110-
func (m *Manager) lazyAggregationLoop(ctx context.Context, blockTimer *time.Timer) {
111-
// lazyTimer triggers block publication even during inactivity
112-
lazyTimer := time.NewTimer(0)
113-
defer lazyTimer.Stop()
114-
115-
for {
116-
select {
117-
case <-ctx.Done():
118-
return
119-
120-
case <-lazyTimer.C:
121-
m.logger.Debug("Lazy timer triggered block production")
122-
m.produceBlock(ctx, "lazy_timer", lazyTimer, blockTimer)
123-
124-
case <-blockTimer.C:
125-
if m.txsAvailable {
126-
m.produceBlock(ctx, "block_timer", lazyTimer, blockTimer)
127-
m.txsAvailable = false
128-
} else {
129-
// Ensure we keep ticking even when there are no txs
130-
blockTimer.Reset(m.config.Node.BlockTime.Duration)
131-
}
132-
case <-m.txNotifyCh:
133-
m.txsAvailable = true
134-
}
135-
}
136-
}
137-
```
92+
A dedicated lazy aggregation loop has been implemented with dual timer mechanisms. The `lazyTimer` ensures blocks are produced at regular intervals even during network inactivity, while the `blockTimer` handles normal block production when transactions are available. Transaction notifications from the `Reaper` to the `Manager` are now handled via the `txNotifyCh` channel: when the `Reaper` detects new transactions, it calls `Manager.NotifyNewTransactions()`, which performs a non-blocking signal on this channel. See the tests in `block/lazy_aggregation_test.go` for verification of this behavior.
13893

13994
4. **Block Production**:
14095

141-
The block production function centralizes the logic for publishing blocks and resetting timers. It records the start time, attempts to publish a block, and then intelligently resets both timers based on the elapsed time. This ensures that block production remains on schedule even if the block creation process takes significant time.
142-
143-
```go
144-
func (m *Manager) produceBlock(ctx context.Context, trigger string, lazyTimer, blockTimer *time.Timer) {
145-
// Record the start time
146-
start := time.Now()
147-
148-
// Attempt to publish the block
149-
if err := m.publishBlock(ctx); err != nil && ctx.Err() == nil {
150-
m.logger.Error("error while publishing block", "trigger", trigger, "error", err)
151-
} else {
152-
m.logger.Debug("Successfully published block", "trigger", trigger)
153-
}
154-
155-
// Reset both timers for the next aggregation window
156-
lazyTimer.Reset(getRemainingSleep(start, m.config.Node.LazyBlockInterval.Duration))
157-
blockTimer.Reset(getRemainingSleep(start, m.config.Node.BlockTime.Duration))
158-
}
159-
```
96+
The block production function centralizes the logic for publishing blocks and resetting timers. It records the start time, attempts to publish a block, and then intelligently resets both timers based on the elapsed time. This ensures that block production remains on schedule even if the block creation process takes significant time.
97+
98+
```go
99+
func (m *Manager) produceBlock(ctx context.Context, trigger string, lazyTimer, blockTimer *time.Timer) {
100+
// Record the start time
101+
start := time.Now()
102+
103+
// Attempt to publish the block
104+
if err := m.publishBlock(ctx); err != nil && ctx.Err() == nil {
105+
m.logger.Error("error while publishing block", "trigger", trigger, "error", err)
106+
} else {
107+
m.logger.Debug("Successfully published block", "trigger", trigger)
108+
}
109+
110+
// Reset both timers for the next aggregation window
111+
lazyTimer.Reset(getRemainingSleep(start, m.config.Node.LazyBlockInterval.Duration))
112+
blockTimer.Reset(getRemainingSleep(start, m.config.Node.BlockTime.Duration))
113+
}
114+
```
160115

161116
### Key Changes
162117

0 commit comments

Comments
 (0)