5
5
"sync/atomic"
6
6
"time"
7
7
8
+ "github.com/go-logr/logr"
8
9
"github.com/google/btree"
9
10
"k8s.io/apimachinery/pkg/util/sets"
10
11
"k8s.io/client-go/util/workqueue"
@@ -36,6 +37,7 @@ type Opts[T comparable] struct {
36
37
// limiter with an initial delay of five milliseconds and a max delay of 1000 seconds.
37
38
RateLimiter workqueue.TypedRateLimiter [T ]
38
39
MetricProvider workqueue.MetricsProvider
40
+ Log logr.Logger
39
41
}
40
42
41
43
// Opt allows to configure a PriorityQueue.
@@ -57,6 +59,7 @@ func New[T comparable](name string, o ...Opt[T]) PriorityQueue[T] {
57
59
}
58
60
59
61
pq := & priorityqueue [T ]{
62
+ log : opts .Log ,
60
63
items : map [T ]* item [T ]{},
61
64
queue : btree .NewG (32 , less [T ]),
62
65
becameReady : sets.Set [T ]{},
@@ -75,6 +78,7 @@ func New[T comparable](name string, o ...Opt[T]) PriorityQueue[T] {
75
78
}
76
79
77
80
go pq .spin ()
81
+ go pq .logState ()
78
82
if _ , ok := pq .metrics .(noMetrics [T ]); ! ok {
79
83
go pq .updateUnfinishedWorkLoop ()
80
84
}
@@ -83,6 +87,7 @@ func New[T comparable](name string, o ...Opt[T]) PriorityQueue[T] {
83
87
}
84
88
85
89
type priorityqueue [T comparable ] struct {
90
+ log logr.Logger
86
91
// lock has to be acquired for any access any of items, queue, addedCounter
87
92
// or becameReady
88
93
lock sync.Mutex
@@ -141,14 +146,14 @@ func (w *priorityqueue[T]) AddWithOpts(o AddOpts, items ...T) {
141
146
}
142
147
if _ , ok := w .items [key ]; ! ok {
143
148
item := & item [T ]{
144
- key : key ,
145
- addedCounter : w .addedCounter ,
146
- priority : o .Priority ,
147
- readyAt : readyAt ,
149
+ Key : key ,
150
+ AddedCounter : w .addedCounter ,
151
+ Priority : o .Priority ,
152
+ ReadyAt : readyAt ,
148
153
}
149
154
w .items [key ] = item
150
155
w .queue .ReplaceOrInsert (item )
151
- if item .readyAt == nil {
156
+ if item .ReadyAt == nil {
152
157
w .metrics .add (key )
153
158
}
154
159
w .addedCounter ++
@@ -158,15 +163,15 @@ func (w *priorityqueue[T]) AddWithOpts(o AddOpts, items ...T) {
158
163
// The b-tree de-duplicates based on ordering and any change here
159
164
// will affect the order - Just delete and re-add.
160
165
item , _ := w .queue .Delete (w .items [key ])
161
- if o .Priority > item .priority {
162
- item .priority = o .Priority
166
+ if o .Priority > item .Priority {
167
+ item .Priority = o .Priority
163
168
}
164
169
165
- if item .readyAt != nil && (readyAt == nil || readyAt .Before (* item .readyAt )) {
170
+ if item .ReadyAt != nil && (readyAt == nil || readyAt .Before (* item .ReadyAt )) {
166
171
if readyAt == nil {
167
172
w .metrics .add (key )
168
173
}
169
- item .readyAt = readyAt
174
+ item .ReadyAt = readyAt
170
175
}
171
176
172
177
w .queue .ReplaceOrInsert (item )
@@ -210,14 +215,14 @@ func (w *priorityqueue[T]) spin() {
210
215
// track what we want to delete and do it after we are done ascending.
211
216
var toDelete []* item [T ]
212
217
w .queue .Ascend (func (item * item [T ]) bool {
213
- if item .readyAt != nil {
214
- if readyAt := item .readyAt .Sub (w .now ()); readyAt > 0 {
218
+ if item .ReadyAt != nil {
219
+ if readyAt := item .ReadyAt .Sub (w .now ()); readyAt > 0 {
215
220
nextReady = w .tick (readyAt )
216
221
return false
217
222
}
218
- if ! w .becameReady .Has (item .key ) {
219
- w .metrics .add (item .key )
220
- w .becameReady .Insert (item .key )
223
+ if ! w .becameReady .Has (item .Key ) {
224
+ w .metrics .add (item .Key )
225
+ w .becameReady .Insert (item .Key )
221
226
}
222
227
}
223
228
@@ -228,16 +233,16 @@ func (w *priorityqueue[T]) spin() {
228
233
}
229
234
230
235
// Item is locked, we can not hand it out
231
- if w .locked .Has (item .key ) {
236
+ if w .locked .Has (item .Key ) {
232
237
return true
233
238
}
234
239
235
- w .metrics .get (item .key )
236
- w .locked .Insert (item .key )
240
+ w .metrics .get (item .Key )
241
+ w .locked .Insert (item .Key )
237
242
w .waiters .Add (- 1 )
238
- delete (w .items , item .key )
243
+ delete (w .items , item .Key )
239
244
toDelete = append (toDelete , item )
240
- w .becameReady .Delete (item .key )
245
+ w .becameReady .Delete (item .Key )
241
246
w .get <- * item
242
247
243
248
return true
@@ -268,7 +273,7 @@ func (w *priorityqueue[T]) GetWithPriority() (_ T, priority int, shutdown bool)
268
273
w .notifyItemOrWaiterAdded ()
269
274
item := <- w .get
270
275
271
- return item .key , item .priority , w .shutdown .Load ()
276
+ return item .Key , item .Priority , w .shutdown .Load ()
272
277
}
273
278
274
279
func (w * priorityqueue [T ]) Get () (item T , shutdown bool ) {
@@ -316,7 +321,7 @@ func (w *priorityqueue[T]) Len() int {
316
321
317
322
var result int
318
323
w .queue .Ascend (func (item * item [T ]) bool {
319
- if item .readyAt == nil || item .readyAt .Compare (w .now ()) <= 0 {
324
+ if item .ReadyAt == nil || item .ReadyAt .Compare (w .now ()) <= 0 {
320
325
result ++
321
326
return true
322
327
}
@@ -326,36 +331,64 @@ func (w *priorityqueue[T]) Len() int {
326
331
return result
327
332
}
328
333
334
+ func (w * priorityqueue [T ]) logState () {
335
+ t := time .Tick (10 * time .Second )
336
+ for {
337
+ select {
338
+ case <- w .done :
339
+ return
340
+ case <- t :
341
+ }
342
+
343
+ // Log level may change at runtime, so keep the
344
+ // loop going even if a given level is currently
345
+ // not enabled.
346
+ if ! w .log .V (1 ).Enabled () {
347
+ continue
348
+ }
349
+ items := make ([]* item [T ], 0 , len (w .items ))
350
+ w .lock .Lock ()
351
+ w .queue .Ascend (func (item * item [T ]) bool {
352
+ items = append (items , item )
353
+ return true
354
+ })
355
+ w .lock .Unlock ()
356
+
357
+ w .log .V (1 ).Info ("workqueue_state" , "items" , items )
358
+ }
359
+ }
360
+
329
361
func less [T comparable ](a , b * item [T ]) bool {
330
- if a .readyAt == nil && b .readyAt != nil {
362
+ if a .ReadyAt == nil && b .ReadyAt != nil {
331
363
return true
332
364
}
333
- if b .readyAt == nil && a .readyAt != nil {
365
+ if b .ReadyAt == nil && a .ReadyAt != nil {
334
366
return false
335
367
}
336
- if a .readyAt != nil && b .readyAt != nil && ! a .readyAt .Equal (* b .readyAt ) {
337
- return a .readyAt .Before (* b .readyAt )
368
+ if a .ReadyAt != nil && b .ReadyAt != nil && ! a .ReadyAt .Equal (* b .ReadyAt ) {
369
+ return a .ReadyAt .Before (* b .ReadyAt )
338
370
}
339
- if a .priority != b .priority {
340
- return a .priority > b .priority
371
+ if a .Priority != b .Priority {
372
+ return a .Priority > b .Priority
341
373
}
342
374
343
- return a .addedCounter < b .addedCounter
375
+ return a .AddedCounter < b .AddedCounter
344
376
}
345
377
346
378
type item [T comparable ] struct {
347
- key T
348
- addedCounter uint64
349
- priority int
350
- readyAt * time.Time
379
+ Key T `json:"key"`
380
+ AddedCounter uint64 `json:"addedCounter"`
381
+ Priority int `json:"priority"`
382
+ ReadyAt * time.Time `json:"readyAt,omitempty"`
351
383
}
352
384
353
385
func (w * priorityqueue [T ]) updateUnfinishedWorkLoop () {
354
- t := time .NewTicker (500 * time .Millisecond ) // borrowed from workqueue: https://github.com/kubernetes/kubernetes/blob/67a807bf142c7a2a5ecfdb2a5d24b4cdea4cc79c/staging/src/k8s.io/client-go/util/workqueue/queue.go#L182
355
- defer t . Stop ()
356
- for range t . C {
357
- if w . shutdown . Load () {
386
+ t := time .Tick (500 * time .Millisecond ) // borrowed from workqueue: https://github.com/kubernetes/kubernetes/blob/67a807bf142c7a2a5ecfdb2a5d24b4cdea4cc79c/staging/src/k8s.io/client-go/util/workqueue/queue.go#L182
387
+ for {
388
+ select {
389
+ case <- w . done :
358
390
return
391
+ case <- t :
359
392
}
360
393
w .metrics .updateUnfinishedWork ()
361
394
}
0 commit comments