You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

159 lines
4.0 KiB

  1. package v1
  2. import (
  3. "container/heap"
  4. "sort"
  5. tmsync "github.com/tendermint/tendermint/internal/libs/sync"
  6. )
  7. var _ heap.Interface = (*TxPriorityQueue)(nil)
  8. // TxPriorityQueue defines a thread-safe priority queue for valid transactions.
  9. type TxPriorityQueue struct {
  10. mtx tmsync.RWMutex
  11. txs []*WrappedTx
  12. }
  13. func NewTxPriorityQueue() *TxPriorityQueue {
  14. pq := &TxPriorityQueue{
  15. txs: make([]*WrappedTx, 0),
  16. }
  17. heap.Init(pq)
  18. return pq
  19. }
  20. // GetEvictableTxs attempts to find and return a list of *WrappedTx than can be
  21. // evicted to make room for another *WrappedTx with higher priority. If no such
  22. // list of *WrappedTx exists, nil will be returned. The returned list of *WrappedTx
  23. // indicate that these transactions can be removed due to them being of lower
  24. // priority and that their total sum in size allows room for the incoming
  25. // transaction according to the mempool's configured limits.
  26. func (pq *TxPriorityQueue) GetEvictableTxs(priority, txSize, totalSize, cap int64) []*WrappedTx {
  27. pq.mtx.RLock()
  28. defer pq.mtx.RUnlock()
  29. txs := make([]*WrappedTx, len(pq.txs))
  30. copy(txs, pq.txs)
  31. sort.Slice(txs, func(i, j int) bool {
  32. return txs[i].priority < txs[j].priority
  33. })
  34. var (
  35. toEvict []*WrappedTx
  36. i int
  37. )
  38. currSize := totalSize
  39. // Loop over all transactions in ascending priority order evaluating those
  40. // that are only of less priority than the provided argument. We continue
  41. // evaluating transactions until there is sufficient capacity for the new
  42. // transaction (size) as defined by txSize.
  43. for i < len(txs) && txs[i].priority < priority {
  44. toEvict = append(toEvict, txs[i])
  45. currSize -= int64(txs[i].Size())
  46. if currSize+txSize <= cap {
  47. return toEvict
  48. }
  49. i++
  50. }
  51. return nil
  52. }
  53. // NumTxs returns the number of transactions in the priority queue. It is
  54. // thread safe.
  55. func (pq *TxPriorityQueue) NumTxs() int {
  56. pq.mtx.RLock()
  57. defer pq.mtx.RUnlock()
  58. return len(pq.txs)
  59. }
  60. // RemoveTx removes a specific transaction from the priority queue.
  61. func (pq *TxPriorityQueue) RemoveTx(tx *WrappedTx) {
  62. pq.mtx.Lock()
  63. defer pq.mtx.Unlock()
  64. if tx.heapIndex < len(pq.txs) {
  65. heap.Remove(pq, tx.heapIndex)
  66. }
  67. }
  68. // PushTx adds a valid transaction to the priority queue. It is thread safe.
  69. func (pq *TxPriorityQueue) PushTx(tx *WrappedTx) {
  70. pq.mtx.Lock()
  71. defer pq.mtx.Unlock()
  72. heap.Push(pq, tx)
  73. }
  74. // PopTx removes the top priority transaction from the queue. It is thread safe.
  75. func (pq *TxPriorityQueue) PopTx() *WrappedTx {
  76. pq.mtx.Lock()
  77. defer pq.mtx.Unlock()
  78. x := heap.Pop(pq)
  79. if x != nil {
  80. return x.(*WrappedTx)
  81. }
  82. return nil
  83. }
  84. // Push implements the Heap interface.
  85. //
  86. // NOTE: A caller should never call Push. Use PushTx instead.
  87. func (pq *TxPriorityQueue) Push(x interface{}) {
  88. n := len(pq.txs)
  89. item := x.(*WrappedTx)
  90. item.heapIndex = n
  91. pq.txs = append(pq.txs, item)
  92. }
  93. // Pop implements the Heap interface.
  94. //
  95. // NOTE: A caller should never call Pop. Use PopTx instead.
  96. func (pq *TxPriorityQueue) Pop() interface{} {
  97. old := pq.txs
  98. n := len(old)
  99. item := old[n-1]
  100. old[n-1] = nil // avoid memory leak
  101. item.heapIndex = -1 // for safety
  102. pq.txs = old[0 : n-1]
  103. return item
  104. }
  105. // Len implements the Heap interface.
  106. //
  107. // NOTE: A caller should never call Len. Use NumTxs instead.
  108. func (pq *TxPriorityQueue) Len() int {
  109. return len(pq.txs)
  110. }
  111. // Less implements the Heap interface. It returns true if the transaction at
  112. // position i in the queue is of less priority than the transaction at position j.
  113. func (pq *TxPriorityQueue) Less(i, j int) bool {
  114. // If there exists two transactions with the same priority, consider the one
  115. // that we saw the earliest as the higher priority transaction.
  116. if pq.txs[i].priority == pq.txs[j].priority {
  117. return pq.txs[i].timestamp.Before(pq.txs[j].timestamp)
  118. }
  119. // We want Pop to give us the highest, not lowest, priority so we use greater
  120. // than here.
  121. return pq.txs[i].priority > pq.txs[j].priority
  122. }
  123. // Swap implements the Heap interface. It swaps two transactions in the queue.
  124. func (pq *TxPriorityQueue) Swap(i, j int) {
  125. pq.txs[i], pq.txs[j] = pq.txs[j], pq.txs[i]
  126. pq.txs[i].heapIndex = i
  127. pq.txs[j].heapIndex = j
  128. }