package mempool
|
|
|
|
import (
|
|
"container/heap"
|
|
"sort"
|
|
"sync"
|
|
)
|
|
|
|
var _ heap.Interface = (*TxPriorityQueue)(nil)
|
|
|
|
// TxPriorityQueue defines a thread-safe priority queue for valid transactions.
|
|
type TxPriorityQueue struct {
|
|
mtx sync.RWMutex
|
|
txs []*WrappedTx
|
|
}
|
|
|
|
func NewTxPriorityQueue() *TxPriorityQueue {
|
|
pq := &TxPriorityQueue{
|
|
txs: make([]*WrappedTx, 0),
|
|
}
|
|
|
|
heap.Init(pq)
|
|
|
|
return pq
|
|
}
|
|
|
|
// GetEvictableTxs attempts to find and return a list of *WrappedTx than can be
|
|
// evicted to make room for another *WrappedTx with higher priority. If no such
|
|
// list of *WrappedTx exists, nil will be returned. The returned list of *WrappedTx
|
|
// indicate that these transactions can be removed due to them being of lower
|
|
// priority and that their total sum in size allows room for the incoming
|
|
// transaction according to the mempool's configured limits.
|
|
func (pq *TxPriorityQueue) GetEvictableTxs(priority, txSize, totalSize, cap int64) []*WrappedTx {
|
|
pq.mtx.RLock()
|
|
defer pq.mtx.RUnlock()
|
|
|
|
txs := make([]*WrappedTx, len(pq.txs))
|
|
copy(txs, pq.txs)
|
|
|
|
sort.Slice(txs, func(i, j int) bool {
|
|
return txs[i].priority < txs[j].priority
|
|
})
|
|
|
|
var (
|
|
toEvict []*WrappedTx
|
|
i int
|
|
)
|
|
|
|
currSize := totalSize
|
|
|
|
// Loop over all transactions in ascending priority order evaluating those
|
|
// that are only of less priority than the provided argument. We continue
|
|
// evaluating transactions until there is sufficient capacity for the new
|
|
// transaction (size) as defined by txSize.
|
|
for i < len(txs) && txs[i].priority < priority {
|
|
toEvict = append(toEvict, txs[i])
|
|
currSize -= int64(txs[i].Size())
|
|
|
|
if currSize+txSize <= cap {
|
|
return toEvict
|
|
}
|
|
|
|
i++
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// NumTxs returns the number of transactions in the priority queue. It is
|
|
// thread safe.
|
|
func (pq *TxPriorityQueue) NumTxs() int {
|
|
pq.mtx.RLock()
|
|
defer pq.mtx.RUnlock()
|
|
|
|
return len(pq.txs)
|
|
}
|
|
|
|
// RemoveTx removes a specific transaction from the priority queue.
|
|
func (pq *TxPriorityQueue) RemoveTx(tx *WrappedTx) {
|
|
pq.mtx.Lock()
|
|
defer pq.mtx.Unlock()
|
|
|
|
if tx.heapIndex < len(pq.txs) {
|
|
heap.Remove(pq, tx.heapIndex)
|
|
}
|
|
}
|
|
|
|
// PushTx adds a valid transaction to the priority queue. It is thread safe.
|
|
func (pq *TxPriorityQueue) PushTx(tx *WrappedTx) {
|
|
pq.mtx.Lock()
|
|
defer pq.mtx.Unlock()
|
|
|
|
heap.Push(pq, tx)
|
|
}
|
|
|
|
// PopTx removes the top priority transaction from the queue. It is thread safe.
|
|
func (pq *TxPriorityQueue) PopTx() *WrappedTx {
|
|
pq.mtx.Lock()
|
|
defer pq.mtx.Unlock()
|
|
|
|
x := heap.Pop(pq)
|
|
if x != nil {
|
|
return x.(*WrappedTx)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Push implements the Heap interface.
|
|
//
|
|
// NOTE: A caller should never call Push. Use PushTx instead.
|
|
func (pq *TxPriorityQueue) Push(x interface{}) {
|
|
n := len(pq.txs)
|
|
item := x.(*WrappedTx)
|
|
item.heapIndex = n
|
|
pq.txs = append(pq.txs, item)
|
|
}
|
|
|
|
// Pop implements the Heap interface.
|
|
//
|
|
// NOTE: A caller should never call Pop. Use PopTx instead.
|
|
func (pq *TxPriorityQueue) Pop() interface{} {
|
|
old := pq.txs
|
|
n := len(old)
|
|
item := old[n-1]
|
|
old[n-1] = nil // avoid memory leak
|
|
item.heapIndex = -1 // for safety
|
|
pq.txs = old[0 : n-1]
|
|
return item
|
|
}
|
|
|
|
// Len implements the Heap interface.
|
|
//
|
|
// NOTE: A caller should never call Len. Use NumTxs instead.
|
|
func (pq *TxPriorityQueue) Len() int {
|
|
return len(pq.txs)
|
|
}
|
|
|
|
// Less implements the Heap interface. It returns true if the transaction at
|
|
// position i in the queue is of less priority than the transaction at position j.
|
|
func (pq *TxPriorityQueue) Less(i, j int) bool {
|
|
// If there exists two transactions with the same priority, consider the one
|
|
// that we saw the earliest as the higher priority transaction.
|
|
if pq.txs[i].priority == pq.txs[j].priority {
|
|
return pq.txs[i].timestamp.Before(pq.txs[j].timestamp)
|
|
}
|
|
|
|
// We want Pop to give us the highest, not lowest, priority so we use greater
|
|
// than here.
|
|
return pq.txs[i].priority > pq.txs[j].priority
|
|
}
|
|
|
|
// Swap implements the Heap interface. It swaps two transactions in the queue.
|
|
func (pq *TxPriorityQueue) Swap(i, j int) {
|
|
pq.txs[i], pq.txs[j] = pq.txs[j], pq.txs[i]
|
|
pq.txs[i].heapIndex = i
|
|
pq.txs[j].heapIndex = j
|
|
}
|