mirror of https://github.com/grafana/loki
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.
155 lines
3.3 KiB
155 lines
3.3 KiB
![]()
2 years ago
|
package queue
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"strings"
|
||
|
)
|
||
|
|
||
|
type QueuePath []string //nolint:revive
|
||
|
|
||
![]()
2 years ago
|
// TreeQueue is an hierarchical queue implementation where each sub-queue
|
||
![]()
2 years ago
|
// has the same guarantees to be chosen from.
|
||
|
// Each queue has also a local queue, which gets chosen with equal preference as the sub-queues.
|
||
![]()
2 years ago
|
type TreeQueue struct {
|
||
![]()
2 years ago
|
// local queue
|
||
|
ch RequestChannel
|
||
|
// index of where this item is located in the mapping
|
||
|
pos QueueIndex
|
||
|
// index of the sub-queues
|
||
|
current QueueIndex
|
||
|
// mapping for sub-queues
|
||
![]()
2 years ago
|
mapping *Mapping[*TreeQueue]
|
||
![]()
2 years ago
|
// name of the queue
|
||
|
name string
|
||
|
// maximum queue size of the local queue
|
||
|
size int
|
||
|
}
|
||
|
|
||
![]()
2 years ago
|
// newTreeQueue creates a new TreeQueue instance
|
||
|
func newTreeQueue(size int, name string) *TreeQueue {
|
||
|
m := &Mapping[*TreeQueue]{}
|
||
![]()
2 years ago
|
m.Init(64) // TODO(chaudum): What is a good initial value?
|
||
![]()
2 years ago
|
return &TreeQueue{
|
||
![]()
2 years ago
|
ch: make(RequestChannel, size),
|
||
|
pos: StartIndexWithLocalQueue,
|
||
|
current: StartIndexWithLocalQueue,
|
||
|
mapping: m,
|
||
|
name: name,
|
||
|
size: size,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// add recursively adds queues based on given path
|
||
![]()
2 years ago
|
func (q *TreeQueue) add(path QueuePath) *TreeQueue {
|
||
![]()
2 years ago
|
if len(path) == 0 {
|
||
|
return q
|
||
|
}
|
||
|
curr, remaining := path[0], path[1:]
|
||
|
queue, created := q.getOrCreate(curr)
|
||
|
if created {
|
||
|
q.mapping.Put(queue.Name(), queue)
|
||
|
}
|
||
|
return queue.add(remaining)
|
||
|
}
|
||
|
|
||
![]()
2 years ago
|
func (q *TreeQueue) getOrCreate(name string) (subq *TreeQueue, created bool) {
|
||
![]()
2 years ago
|
subq = q.mapping.GetByKey(name)
|
||
|
if subq == nil {
|
||
![]()
2 years ago
|
subq = newTreeQueue(q.size, name)
|
||
![]()
2 years ago
|
created = true
|
||
|
}
|
||
|
return subq, created
|
||
|
}
|
||
|
|
||
|
// Chan implements Queue
|
||
![]()
2 years ago
|
func (q *TreeQueue) Chan() RequestChannel {
|
||
![]()
2 years ago
|
return q.ch
|
||
|
}
|
||
|
|
||
|
// Dequeue implements Queue
|
||
![]()
2 years ago
|
func (q *TreeQueue) Dequeue() Request {
|
||
![]()
2 years ago
|
var item Request
|
||
|
|
||
|
// shortcut of there are not sub-queues
|
||
|
// always use local queue
|
||
|
if q.mapping.Len() == 0 {
|
||
|
if len(q.ch) > 0 {
|
||
|
return <-q.ch
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
maxIter := len(q.mapping.keys) + 1
|
||
|
for iters := 0; iters < maxIter; iters++ {
|
||
|
if q.current == StartIndexWithLocalQueue {
|
||
|
q.current++
|
||
|
if len(q.ch) > 0 {
|
||
|
item = <-q.ch
|
||
|
if item != nil {
|
||
|
return item
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
subq, err := q.mapping.GetNext(q.current)
|
||
|
if err == ErrOutOfBounds {
|
||
|
q.current = StartIndexWithLocalQueue
|
||
|
continue
|
||
|
}
|
||
|
if subq != nil {
|
||
|
q.current = subq.pos
|
||
|
item := subq.Dequeue()
|
||
|
if item != nil {
|
||
|
if subq.Len() == 0 {
|
||
|
q.mapping.Remove(subq.name)
|
||
|
}
|
||
|
return item
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Name implements Queue
|
||
![]()
2 years ago
|
func (q *TreeQueue) Name() string {
|
||
![]()
2 years ago
|
return q.name
|
||
|
}
|
||
|
|
||
|
// Len implements Queue
|
||
|
// It returns the length of the local queue and all sub-queues.
|
||
|
// This may be expensive depending on the size of the queue tree.
|
||
![]()
2 years ago
|
func (q *TreeQueue) Len() int {
|
||
![]()
2 years ago
|
count := len(q.ch)
|
||
|
for _, subq := range q.mapping.Values() {
|
||
|
count += subq.Len()
|
||
|
}
|
||
|
return count
|
||
|
}
|
||
|
|
||
|
// Index implements Mapable
|
||
![]()
2 years ago
|
func (q *TreeQueue) Pos() QueueIndex {
|
||
![]()
2 years ago
|
return q.pos
|
||
|
}
|
||
|
|
||
|
// Index implements Mapable
|
||
![]()
2 years ago
|
func (q *TreeQueue) SetPos(index QueueIndex) {
|
||
![]()
2 years ago
|
q.pos = index
|
||
|
}
|
||
|
|
||
|
// String makes the queue printable
|
||
![]()
2 years ago
|
func (q *TreeQueue) String() string {
|
||
![]()
2 years ago
|
sb := &strings.Builder{}
|
||
|
sb.WriteString("{")
|
||
![]()
2 years ago
|
fmt.Fprintf(sb, "name=%s, len=%d/%d, children=[", q.Name(), q.Len(), cap(q.ch))
|
||
![]()
2 years ago
|
subqs := q.mapping.Values()
|
||
|
for i, m := range subqs {
|
||
|
sb.WriteString(m.String())
|
||
|
if i < len(subqs)-1 {
|
||
|
sb.WriteString(",")
|
||
|
}
|
||
|
}
|
||
|
sb.WriteString("]")
|
||
|
sb.WriteString("}")
|
||
|
return sb.String()
|
||
|
}
|