-
Notifications
You must be signed in to change notification settings - Fork 820
Fix data race in user list of a queue #6160
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -38,13 +38,12 @@ type querier struct { | |
// 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 | ||
userQueuesMx sync.RWMutex | ||
|
||
// 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 | ||
users []string | ||
userQueues map[string]*userQueue | ||
queuesMx sync.RWMutex | ||
|
||
// How long to wait before removing a querier which has got disconnected | ||
// but hasn't notified about a graceful shutdown. | ||
|
@@ -102,8 +101,8 @@ func (q *queues) len() int { | |
} | ||
|
||
func (q *queues) deleteQueue(userID string) { | ||
q.userQueuesMx.Lock() | ||
defer q.userQueuesMx.Unlock() | ||
q.queuesMx.Lock() | ||
defer q.queuesMx.Unlock() | ||
|
||
uq := q.userQueues[userID] | ||
if uq == nil { | ||
|
@@ -134,8 +133,8 @@ func (q *queues) getOrAddQueue(userID string, maxQueriers int) userRequestQueue | |
maxQueriers = 0 | ||
} | ||
|
||
q.userQueuesMx.Lock() | ||
defer q.userQueuesMx.Unlock() | ||
q.queuesMx.Lock() | ||
defer q.queuesMx.Unlock() | ||
|
||
uq := q.userQueues[userID] | ||
priorityEnabled := q.limits.QueryPriority(userID).Enabled | ||
|
@@ -222,6 +221,9 @@ func (q *queues) createUserRequestQueue(userID string) userRequestQueue { | |
func (q *queues) getNextQueueForQuerier(lastUserIndex int, querierID string) (userRequestQueue, string, int) { | ||
uid := lastUserIndex | ||
|
||
q.queuesMx.RLock() | ||
defer q.queuesMx.RUnlock() | ||
|
||
for iters := 0; iters < len(q.users); iters++ { | ||
uid = uid + 1 | ||
|
||
|
@@ -236,9 +238,6 @@ func (q *queues) getNextQueueForQuerier(lastUserIndex int, querierID string) (us | |
continue | ||
} | ||
|
||
q.userQueuesMx.RLock() | ||
defer q.userQueuesMx.RUnlock() | ||
Comment on lines
-239
to
-240
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. actually this shouldn't have been in a for loop, as the defer doesn't get called until the function returns, not when each loop iteration is done. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the defer is fine... As it is a Rlock so re-entranable. By moving this lock out of the for loop, is the main point to protect |
||
|
||
uq := q.userQueues[u] | ||
|
||
if uq.queriers != nil { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also checked if there's an opportunity for me to not use defer and manually unlock (since now i'm locking two objects at the same time). But the slices and maps are pass by reference in golang, so it was better for me to keep the lock until the function returns (we continue to read properties of those objects until the function returns)