mirror of
https://github.com/hanzoai/traces.git
synced 2026-10-30 18:20:41 +00:00
* clean up queues on a timer Signed-off-by: Joe Elliott <[email protected]> * cleanup queues Signed-off-by: Joe Elliott <[email protected]> * changelog Signed-off-by: Joe Elliott <[email protected]> * review cleanup Signed-off-by: Joe Elliott <[email protected]> * changelog Signed-off-by: Joe Elliott <[email protected]> --------- Signed-off-by: Joe Elliott <[email protected]>
131 lines
3.0 KiB
Go
131 lines
3.0 KiB
Go
package queue
|
|
|
|
// This struct holds user queues for pending requests. It also keeps track of connected queriers,
|
|
// and mapping between users and queriers.
|
|
type queues struct {
|
|
userQueues map[string]*userQueue
|
|
|
|
// List of all users with queues, used for iteration when searching for next queue to handle.
|
|
// Users removed from the middle are replaced with "". To avoid skipping users during iteration, we only shrink
|
|
// this list when there are ""'s at the end of it.
|
|
users []string
|
|
|
|
maxUserQueueSize int
|
|
}
|
|
|
|
type userQueue struct {
|
|
ch chan Request
|
|
|
|
// Points back to 'users' field in queues. Enables quick cleanup.
|
|
index int
|
|
}
|
|
|
|
func newUserQueues(maxUserQueueSize int) *queues {
|
|
return &queues{
|
|
userQueues: map[string]*userQueue{},
|
|
users: nil,
|
|
maxUserQueueSize: maxUserQueueSize,
|
|
}
|
|
}
|
|
|
|
func (q *queues) len() int {
|
|
return len(q.userQueues)
|
|
}
|
|
|
|
func (q *queues) deleteQueue(userID string) {
|
|
uq := q.userQueues[userID]
|
|
if uq == nil {
|
|
return
|
|
}
|
|
|
|
delete(q.userQueues, userID)
|
|
q.users[uq.index] = ""
|
|
|
|
// Shrink users list size if possible. This is safe, and no users will be skipped during iteration.
|
|
for ix := len(q.users) - 1; ix >= 0 && q.users[ix] == ""; ix-- {
|
|
q.users = q.users[:ix]
|
|
}
|
|
}
|
|
|
|
// deleteEmptyQueues removes all user queues that have no length. in addition it returns true if any
|
|
// queues were removed.
|
|
func (q *queues) deleteEmptyQueues() bool {
|
|
removedQueue := false
|
|
|
|
// look for 0 len queues and remove them
|
|
for userID, uq := range q.userQueues {
|
|
if len(uq.ch) == 0 {
|
|
removedQueue = true
|
|
q.deleteQueue(userID)
|
|
}
|
|
}
|
|
|
|
return removedQueue
|
|
}
|
|
|
|
// Returns existing or new queue for user.
|
|
func (q *queues) getOrAddQueue(userID string) chan Request {
|
|
// Empty user is not allowed, as that would break our users list ("" is used for free spot).
|
|
if userID == "" {
|
|
return nil
|
|
}
|
|
|
|
uq := q.userQueues[userID]
|
|
|
|
if uq == nil {
|
|
uq = &userQueue{
|
|
ch: make(chan Request, q.maxUserQueueSize),
|
|
index: -1,
|
|
}
|
|
q.userQueues[userID] = uq
|
|
|
|
// Add user to the list of users... find first free spot, and put it there.
|
|
for ix, u := range q.users {
|
|
if u == "" {
|
|
uq.index = ix
|
|
q.users[ix] = userID
|
|
break
|
|
}
|
|
}
|
|
|
|
// ... or add to the end.
|
|
if uq.index < 0 {
|
|
uq.index = len(q.users)
|
|
q.users = append(q.users, userID)
|
|
}
|
|
}
|
|
|
|
return uq.ch
|
|
}
|
|
|
|
// Finds next queue for the querier. To support fair scheduling between users, client is expected
|
|
// to pass last user index returned by this function as argument. Is there was no previous
|
|
// last user index, use -1.
|
|
func (q *queues) getNextQueueForQuerier(lastUserIndex int) (chan Request, string, int) {
|
|
uid := lastUserIndex
|
|
|
|
for iters := 0; iters < len(q.users); iters++ {
|
|
uid = uid + 1
|
|
|
|
// Don't use "mod len(q.users)", as that could skip users at the beginning of the list
|
|
// for example when q.users has shrunk since last call.
|
|
if uid >= len(q.users) {
|
|
uid = 0
|
|
}
|
|
|
|
u := q.users[uid]
|
|
if u == "" {
|
|
continue
|
|
}
|
|
|
|
q := q.userQueues[u]
|
|
|
|
if len(q.ch) == 0 {
|
|
continue
|
|
}
|
|
|
|
return q.ch, u, uid
|
|
}
|
|
return nil, "", uid
|
|
}
|