Like Prometheus, but for logs.
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.
 
 
 
 
 
 
loki/pkg/queue/treequeue.go

154 lines
3.3 KiB

package queue
import (
"fmt"
"strings"
)
type QueuePath []string //nolint:revive
// TreeQueue is an hierarchical queue implementation where each sub-queue
// 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.
type TreeQueue struct {
// 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
mapping *Mapping[*TreeQueue]
// name of the queue
name string
// maximum queue size of the local queue
size int
}
// newTreeQueue creates a new TreeQueue instance
func newTreeQueue(size int, name string) *TreeQueue {
m := &Mapping[*TreeQueue]{}
m.Init(64) // TODO(chaudum): What is a good initial value?
return &TreeQueue{
ch: make(RequestChannel, size),
pos: StartIndexWithLocalQueue,
current: StartIndexWithLocalQueue,
mapping: m,
name: name,
size: size,
}
}
// add recursively adds queues based on given path
func (q *TreeQueue) add(path QueuePath) *TreeQueue {
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)
}
func (q *TreeQueue) getOrCreate(name string) (subq *TreeQueue, created bool) {
subq = q.mapping.GetByKey(name)
if subq == nil {
subq = newTreeQueue(q.size, name)
created = true
}
return subq, created
}
// Chan implements Queue
func (q *TreeQueue) Chan() RequestChannel {
return q.ch
}
// Dequeue implements Queue
func (q *TreeQueue) Dequeue() Request {
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
func (q *TreeQueue) Name() string {
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.
func (q *TreeQueue) Len() int {
count := len(q.ch)
for _, subq := range q.mapping.Values() {
count += subq.Len()
}
return count
}
// Index implements Mapable
func (q *TreeQueue) Pos() QueueIndex {
return q.pos
}
// Index implements Mapable
func (q *TreeQueue) SetPos(index QueueIndex) {
q.pos = index
}
// String makes the queue printable
func (q *TreeQueue) String() string {
sb := &strings.Builder{}
sb.WriteString("{")
fmt.Fprintf(sb, "name=%s, len=%d/%d, children=[", q.Name(), q.Len(), cap(q.ch))
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()
}