github.com/koko1123/flow-go-1@v0.29.6/module/mempool/queue/queue.go (about) 1 package queue 2 3 import ( 4 "github.com/koko1123/flow-go-1/model/flow" 5 ) 6 7 type Node struct { 8 Item Blockify 9 Children []*Node 10 } 11 12 // Blockify becuase Blocker seems a bit off. 13 // Make items behave like a block, so it can be queued 14 type Blockify interface { 15 flow.Entity 16 Height() uint64 17 ParentID() flow.Identifier 18 } 19 20 // Queue is a fork-aware queue/tree of blocks for use in execution Node, where parallel forks 21 // can be processed simultaneously. For fast lookup which is predicted to be common case 22 // all nodes are kept as one queue, which is expected to split into separate queues once 23 // a fork (multiple children) is reached. 24 // Note that this is not a thread-safe structure and external synchronisation is required 25 // to use in concurrent environment 26 type Queue struct { 27 Head *Node 28 Highest *Node 29 Nodes map[flow.Identifier]*Node 30 } 31 32 // Make queue an entity so it can be stored in mempool 33 34 func (q *Queue) ID() flow.Identifier { 35 return q.Head.Item.ID() 36 } 37 38 func (q *Queue) Checksum() flow.Identifier { 39 return q.Head.Item.Checksum() 40 } 41 42 // Size returns number of elements in the queue 43 func (q *Queue) Size() int { 44 return len(q.Nodes) 45 } 46 47 // Returns difference between lowest and highest element in the queue 48 // Formally, the Queue stores a tree. The height of the tree is the 49 // number of edges on the longest downward path between the root and any leaf. 50 func (q *Queue) Height() uint64 { 51 return q.Highest.Item.Height() - q.Head.Item.Height() 52 } 53 54 // traverse Node children recursively and populate m 55 func traverse(node *Node, m map[flow.Identifier]*Node, highest *Node) { 56 m[node.Item.ID()] = node 57 for _, node := range node.Children { 58 if node.Item.Height() > highest.Item.Height() { 59 *highest = *node 60 } 61 traverse(node, m, highest) 62 } 63 } 64 65 func NewQueue(blockify Blockify) *Queue { 66 n := &Node{ 67 Item: blockify, 68 Children: nil, 69 } 70 return &Queue{ 71 Head: n, 72 Highest: n, 73 Nodes: map[flow.Identifier]*Node{n.Item.ID(): n}, 74 } 75 } 76 77 // rebuildQueue makes a new queue from a Node which was already part of other queue 78 // and fills lookup cache 79 func rebuildQueue(n *Node) *Queue { 80 // rebuild map-cache 81 cache := make(map[flow.Identifier]*Node) 82 highest := *n //copy n 83 traverse(n, cache, &highest) 84 85 return &Queue{ 86 Head: n, 87 Nodes: cache, 88 Highest: &highest, 89 } 90 } 91 92 // Special case for removing single-childed head element 93 func dequeue(queue *Queue) *Queue { 94 onlyChild := queue.Head.Children[0] 95 96 cache := make(map[flow.Identifier]*Node) 97 98 //copy all but head caches 99 headID := queue.Head.Item.ID() // ID computation is about as expensive 1000 Go int additions 100 for key, val := range queue.Nodes { 101 if key != headID { 102 cache[key] = val 103 } 104 } 105 106 return &Queue{ 107 Head: onlyChild, 108 Nodes: cache, 109 Highest: queue.Highest, 110 } 111 } 112 113 // TryAdd tries to add a new element to the queue. 114 // A element can only be added if the parent exists in the queue. 115 // TryAdd(elmt) is an idempotent operation for the same elmt, i.e. 116 // after the first, subsequent additions of the same elements are NoOps. 117 // Returns: 118 // stored = True if and only if _after_ the operation, the element is stored in the 119 // queue. This is the case if (a) element was newly added to the queue or 120 // (b) element was already stored in the queue _before_ the call. 121 // new = Indicates if element was new to the queue, when `stored` was true. It lets 122 // distinguish (a) and (b) cases. 123 // Adding an element fails with return value `false` for `stored` in the following cases: 124 // - element.ParentID() is _not_ stored in the queue 125 // - element's height is _unequal to_ its parent's height + 1 126 func (q *Queue) TryAdd(element Blockify) (stored bool, new bool) { 127 if _, found := q.Nodes[element.ID()]; found { 128 // (b) element was already stored in the queue _before_ the call. 129 return true, false 130 } 131 // at this point, we are sure that the element is _not_ in the queue and therefore, 132 // the element cannot be referenced as a child by any other element in the queue 133 n, ok := q.Nodes[element.ParentID()] 134 if !ok { 135 return false, false 136 } 137 if n.Item.Height() != element.Height()-1 { 138 return false, false 139 } 140 newNode := &Node{ 141 Item: element, 142 Children: nil, 143 } 144 // we know: element is _not_ (yet) in the queue 145 // => it cannot be in _any_ nodes Children list 146 // => the following operation is guaranteed to _not_ produce 147 // duplicates in the Children list 148 n.Children = append(n.Children, newNode) 149 q.Nodes[element.ID()] = newNode 150 if element.Height() > q.Highest.Item.Height() { 151 q.Highest = newNode 152 } 153 return true, true 154 } 155 156 // Dismount removes the head element, returns it and it's children as new queues 157 func (q *Queue) Dismount() (Blockify, []*Queue) { 158 159 queues := make([]*Queue, len(q.Head.Children)) 160 if len(q.Head.Children) == 1 { //optimize for most common single-child case 161 queues[0] = dequeue(q) 162 } else { 163 for i, child := range q.Head.Children { 164 queues[i] = rebuildQueue(child) 165 } 166 } 167 return q.Head.Item, queues 168 }