-
Notifications
You must be signed in to change notification settings - Fork 816
Add metric for flush queue length #113
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
c775779
ef293e3
9f6cc02
4fa69c4
276e676
ccfd65d
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 |
---|---|---|
|
@@ -15,13 +15,13 @@ type priorityQueue struct { | |
|
||
type op interface { | ||
Key() string | ||
Priority() int64 | ||
Priority() int64 // The larger the number the higher the priority. | ||
} | ||
|
||
type queue []op | ||
|
||
func (q queue) Len() int { return len(q) } | ||
func (q queue) Less(i, j int) bool { return q[i].Priority() < q[j].Priority() } | ||
func (q queue) Less(i, j int) bool { return q[i].Priority() > q[j].Priority() } | ||
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 get this sort of thing wrong all the time. I think it might help if the interface had a doc comment that said "The larger the number the higher the priority." 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. +1 |
||
func (q queue) Swap(i, j int) { q[i], q[j] = q[j], q[i] } | ||
|
||
// Push and Pop use pointer receivers because they modify the slice's length, | ||
|
@@ -47,6 +47,12 @@ func newPriorityQueue() *priorityQueue { | |
return pq | ||
} | ||
|
||
func (pq *priorityQueue) Length() int { | ||
pq.lock.Lock() | ||
defer pq.lock.Unlock() | ||
return len(pq.queue) | ||
} | ||
|
||
func (pq *priorityQueue) Close() { | ||
pq.lock.Lock() | ||
defer pq.lock.Unlock() | ||
|
@@ -67,10 +73,13 @@ func (pq *priorityQueue) Enqueue(op op) { | |
return | ||
} | ||
|
||
pq.hit[op.Key()] = struct{}{} | ||
heap.Push(&pq.queue, op) | ||
pq.cond.Broadcast() | ||
} | ||
|
||
// Dequeue will return the op with the highest priority; block if queue is | ||
// empty; returns nil if queue is closed. | ||
func (pq *priorityQueue) Dequeue() op { | ||
pq.lock.Lock() | ||
defer pq.lock.Unlock() | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
package ingester | ||
|
||
import ( | ||
"runtime" | ||
"strconv" | ||
"testing" | ||
"time" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
type item int64 | ||
|
||
func (i item) Priority() int64 { | ||
return int64(i) | ||
} | ||
|
||
func (i item) Key() string { | ||
return strconv.FormatInt(int64(i), 10) | ||
} | ||
|
||
func TestPriorityQueueBasic(t *testing.T) { | ||
queue := newPriorityQueue() | ||
assert.Equal(t, 0, queue.Length(), "Expected length = 0") | ||
|
||
queue.Enqueue(item(1)) | ||
assert.Equal(t, 1, queue.Length(), "Expected length = 1") | ||
|
||
i, ok := queue.Dequeue().(item) | ||
assert.True(t, ok, "Expected cast to succeed") | ||
assert.Equal(t, item(1), i, "Expected to dequeue item(1)") | ||
|
||
queue.Close() | ||
assert.Nil(t, queue.Dequeue(), "Expect nil dequeue") | ||
} | ||
|
||
func TestPriorityQueuePriorities(t *testing.T) { | ||
queue := newPriorityQueue() | ||
queue.Enqueue(item(1)) | ||
queue.Enqueue(item(2)) | ||
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'd suggest adding another test which is almost the same as this one, except that it enqueues the items in the opposite order. 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. +1 |
||
|
||
assert.Equal(t, item(2), queue.Dequeue().(item), "Expected to dequeue item(2)") | ||
assert.Equal(t, item(1), queue.Dequeue().(item), "Expected to dequeue item(1)") | ||
|
||
queue.Close() | ||
assert.Nil(t, queue.Dequeue(), "Expect nil dequeue") | ||
} | ||
|
||
func TestPriorityQueuePriorities2(t *testing.T) { | ||
queue := newPriorityQueue() | ||
queue.Enqueue(item(2)) | ||
queue.Enqueue(item(1)) | ||
|
||
assert.Equal(t, item(2), queue.Dequeue().(item), "Expected to dequeue item(2)") | ||
assert.Equal(t, item(1), queue.Dequeue().(item), "Expected to dequeue item(1)") | ||
|
||
queue.Close() | ||
assert.Nil(t, queue.Dequeue(), "Expect nil dequeue") | ||
} | ||
|
||
func TestPriorityQueueDedupe(t *testing.T) { | ||
queue := newPriorityQueue() | ||
queue.Enqueue(item(1)) | ||
queue.Enqueue(item(1)) | ||
|
||
assert.Equal(t, 1, queue.Length(), "Expected length = 1") | ||
assert.Equal(t, item(1), queue.Dequeue().(item), "Expected to dequeue item(1)") | ||
|
||
queue.Close() | ||
assert.Nil(t, queue.Dequeue(), "Expect nil dequeue") | ||
} | ||
|
||
func TestPriorityQueueWait(t *testing.T) { | ||
queue := newPriorityQueue() | ||
|
||
done := make(chan struct{}) | ||
go func() { | ||
assert.Nil(t, queue.Dequeue(), "Expect nil dequeue") | ||
close(done) | ||
}() | ||
|
||
runtime.Gosched() | ||
queue.Close() | ||
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. This test has a race condition, I think. Sometimes it will test: queue := newPriorityQueue()
queue.Close()
assert.Nil(t, queue.Dequeue(), "Expect nil dequeue on closed queue.") And other times it will test: queue := newPriorityQueue()
assert.Nil(t, queue.Dequeue(), "Expect nil dequeue")
// above blocks until the following happens:
queue.Close() I had a bit of think and couldn't come up with a way of guaranteeing the second ordering, only of making it more likely by adding a delay. Initially, I wasn't sure which path you wanted to test. I think it would be more clear if you added another test that explicitly dealt with the first case. 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.
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. Right, that's the second example. However, it won't test that all the time. In fact, it might never be testing it because I think adding a delay before calling 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. Yeah in general this stuff is hard to test in go. Could stick a https://golang.org/pkg/runtime/#Gosched there, but again, only probabilistic. |
||
select { | ||
case <-done: | ||
case <-time.After(100 * time.Millisecond): | ||
t.Fatal("Close didn't unblock Dequeue.") | ||
} | ||
} |
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.
Why not
sum(q.Length() for q in i.flushQueues)
?