mirror of
https://github.com/fluencelabs/tendermint
synced 2025-04-25 14:52:17 +00:00
Fixes https://github.com/tendermint/tendermint/issues/1189 For every TxEventBuffer.Flush() invoking, we were invoking a: b.events = make([]EventDataTx, 0, b.capacity) whose intention is to innocently clear the events slice but maintain the underlying capacity. However, unfortunately this is memory and garbage collection intensive which is linear in the number of events added. If an attack had access to our code somehow, invoking .Flush() in tight loops would be a sure way to cause huge GC pressure, and say if they added about 1e9 events maliciously, every Flush() would take at least 3.2seconds which is enough to now control our application. The new using of the capacity preserving slice clearing idiom takes a constant time regardless of the number of elements with zero allocations so we are killing many birds with one stone i.e b.events = b.events[:0] For benchmarking results, please see https://gist.github.com/odeke-em/532c14ab67d71c9c0b95518a7a526058 for a reference on how things can get out of hand easily.
51 lines
1.3 KiB
Go
51 lines
1.3 KiB
Go
package types
|
|
|
|
// Interface assertions
|
|
var _ TxEventPublisher = (*TxEventBuffer)(nil)
|
|
|
|
// TxEventBuffer is a buffer of events, which uses a slice to temporarily store
|
|
// events.
|
|
type TxEventBuffer struct {
|
|
next TxEventPublisher
|
|
capacity int
|
|
events []EventDataTx
|
|
}
|
|
|
|
// NewTxEventBuffer accepts a TxEventPublisher and returns a new buffer with the given
|
|
// capacity.
|
|
func NewTxEventBuffer(next TxEventPublisher, capacity int) *TxEventBuffer {
|
|
return &TxEventBuffer{
|
|
next: next,
|
|
capacity: capacity,
|
|
events: make([]EventDataTx, 0, capacity),
|
|
}
|
|
}
|
|
|
|
// Len returns the number of events cached.
|
|
func (b TxEventBuffer) Len() int {
|
|
return len(b.events)
|
|
}
|
|
|
|
// PublishEventTx buffers an event to be fired upon finality.
|
|
func (b *TxEventBuffer) PublishEventTx(e EventDataTx) error {
|
|
b.events = append(b.events, e)
|
|
return nil
|
|
}
|
|
|
|
// Flush publishes events by running next.PublishWithTags on all cached events.
|
|
// Blocks. Clears cached events.
|
|
func (b *TxEventBuffer) Flush() error {
|
|
for _, e := range b.events {
|
|
err := b.next.PublishEventTx(e)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Clear out the elements and set the length to 0
|
|
// but maintain the underlying slice's capacity.
|
|
// See Issue https://github.com/tendermint/tendermint/issues/1189
|
|
b.events = b.events[:0]
|
|
return nil
|
|
}
|