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  }