delaying_queue.go 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. /*
  2. Copyright 2016 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package workqueue
  14. import (
  15. "container/heap"
  16. "sync"
  17. "time"
  18. utilruntime "k8s.io/apimachinery/pkg/util/runtime"
  19. "k8s.io/utils/clock"
  20. )
  21. // DelayingInterface is an Interface that can Add an item at a later time. This makes it easier to
  22. // requeue items after failures without ending up in a hot-loop.
  23. type DelayingInterface interface {
  24. Interface
  25. // AddAfter adds an item to the workqueue after the indicated duration has passed
  26. AddAfter(item interface{}, duration time.Duration)
  27. }
  28. // NewDelayingQueue constructs a new workqueue with delayed queuing ability.
  29. // NewDelayingQueue does not emit metrics. For use with a MetricsProvider, please use
  30. // NewNamedDelayingQueue instead.
  31. func NewDelayingQueue() DelayingInterface {
  32. return NewDelayingQueueWithCustomClock(clock.RealClock{}, "")
  33. }
  34. // NewDelayingQueueWithCustomQueue constructs a new workqueue with ability to
  35. // inject custom queue Interface instead of the default one
  36. func NewDelayingQueueWithCustomQueue(q Interface, name string) DelayingInterface {
  37. return newDelayingQueue(clock.RealClock{}, q, name)
  38. }
  39. // NewNamedDelayingQueue constructs a new named workqueue with delayed queuing ability
  40. func NewNamedDelayingQueue(name string) DelayingInterface {
  41. return NewDelayingQueueWithCustomClock(clock.RealClock{}, name)
  42. }
  43. // NewDelayingQueueWithCustomClock constructs a new named workqueue
  44. // with ability to inject real or fake clock for testing purposes
  45. func NewDelayingQueueWithCustomClock(clock clock.WithTicker, name string) DelayingInterface {
  46. return newDelayingQueue(clock, NewNamed(name), name)
  47. }
  48. func newDelayingQueue(clock clock.WithTicker, q Interface, name string) *delayingType {
  49. ret := &delayingType{
  50. Interface: q,
  51. clock: clock,
  52. heartbeat: clock.NewTicker(maxWait),
  53. stopCh: make(chan struct{}),
  54. waitingForAddCh: make(chan *waitFor, 1000),
  55. metrics: newRetryMetrics(name),
  56. }
  57. go ret.waitingLoop()
  58. return ret
  59. }
  60. // delayingType wraps an Interface and provides delayed re-enquing
  61. type delayingType struct {
  62. Interface
  63. // clock tracks time for delayed firing
  64. clock clock.Clock
  65. // stopCh lets us signal a shutdown to the waiting loop
  66. stopCh chan struct{}
  67. // stopOnce guarantees we only signal shutdown a single time
  68. stopOnce sync.Once
  69. // heartbeat ensures we wait no more than maxWait before firing
  70. heartbeat clock.Ticker
  71. // waitingForAddCh is a buffered channel that feeds waitingForAdd
  72. waitingForAddCh chan *waitFor
  73. // metrics counts the number of retries
  74. metrics retryMetrics
  75. }
  76. // waitFor holds the data to add and the time it should be added
  77. type waitFor struct {
  78. data t
  79. readyAt time.Time
  80. // index in the priority queue (heap)
  81. index int
  82. }
  83. // waitForPriorityQueue implements a priority queue for waitFor items.
  84. //
  85. // waitForPriorityQueue implements heap.Interface. The item occurring next in
  86. // time (i.e., the item with the smallest readyAt) is at the root (index 0).
  87. // Peek returns this minimum item at index 0. Pop returns the minimum item after
  88. // it has been removed from the queue and placed at index Len()-1 by
  89. // container/heap. Push adds an item at index Len(), and container/heap
  90. // percolates it into the correct location.
  91. type waitForPriorityQueue []*waitFor
  92. func (pq waitForPriorityQueue) Len() int {
  93. return len(pq)
  94. }
  95. func (pq waitForPriorityQueue) Less(i, j int) bool {
  96. return pq[i].readyAt.Before(pq[j].readyAt)
  97. }
  98. func (pq waitForPriorityQueue) Swap(i, j int) {
  99. pq[i], pq[j] = pq[j], pq[i]
  100. pq[i].index = i
  101. pq[j].index = j
  102. }
  103. // Push adds an item to the queue. Push should not be called directly; instead,
  104. // use `heap.Push`.
  105. func (pq *waitForPriorityQueue) Push(x interface{}) {
  106. n := len(*pq)
  107. item := x.(*waitFor)
  108. item.index = n
  109. *pq = append(*pq, item)
  110. }
  111. // Pop removes an item from the queue. Pop should not be called directly;
  112. // instead, use `heap.Pop`.
  113. func (pq *waitForPriorityQueue) Pop() interface{} {
  114. n := len(*pq)
  115. item := (*pq)[n-1]
  116. item.index = -1
  117. *pq = (*pq)[0:(n - 1)]
  118. return item
  119. }
  120. // Peek returns the item at the beginning of the queue, without removing the
  121. // item or otherwise mutating the queue. It is safe to call directly.
  122. func (pq waitForPriorityQueue) Peek() interface{} {
  123. return pq[0]
  124. }
  125. // ShutDown stops the queue. After the queue drains, the returned shutdown bool
  126. // on Get() will be true. This method may be invoked more than once.
  127. func (q *delayingType) ShutDown() {
  128. q.stopOnce.Do(func() {
  129. q.Interface.ShutDown()
  130. close(q.stopCh)
  131. q.heartbeat.Stop()
  132. })
  133. }
  134. // AddAfter adds the given item to the work queue after the given delay
  135. func (q *delayingType) AddAfter(item interface{}, duration time.Duration) {
  136. // don't add if we're already shutting down
  137. if q.ShuttingDown() {
  138. return
  139. }
  140. q.metrics.retry()
  141. // immediately add things with no delay
  142. if duration <= 0 {
  143. q.Add(item)
  144. return
  145. }
  146. select {
  147. case <-q.stopCh:
  148. // unblock if ShutDown() is called
  149. case q.waitingForAddCh <- &waitFor{data: item, readyAt: q.clock.Now().Add(duration)}:
  150. }
  151. }
  152. // maxWait keeps a max bound on the wait time. It's just insurance against weird things happening.
  153. // Checking the queue every 10 seconds isn't expensive and we know that we'll never end up with an
  154. // expired item sitting for more than 10 seconds.
  155. const maxWait = 10 * time.Second
  156. // waitingLoop runs until the workqueue is shutdown and keeps a check on the list of items to be added.
  157. func (q *delayingType) waitingLoop() {
  158. defer utilruntime.HandleCrash()
  159. // Make a placeholder channel to use when there are no items in our list
  160. never := make(<-chan time.Time)
  161. // Make a timer that expires when the item at the head of the waiting queue is ready
  162. var nextReadyAtTimer clock.Timer
  163. waitingForQueue := &waitForPriorityQueue{}
  164. heap.Init(waitingForQueue)
  165. waitingEntryByData := map[t]*waitFor{}
  166. for {
  167. if q.Interface.ShuttingDown() {
  168. return
  169. }
  170. now := q.clock.Now()
  171. // Add ready entries
  172. for waitingForQueue.Len() > 0 {
  173. entry := waitingForQueue.Peek().(*waitFor)
  174. if entry.readyAt.After(now) {
  175. break
  176. }
  177. entry = heap.Pop(waitingForQueue).(*waitFor)
  178. q.Add(entry.data)
  179. delete(waitingEntryByData, entry.data)
  180. }
  181. // Set up a wait for the first item's readyAt (if one exists)
  182. nextReadyAt := never
  183. if waitingForQueue.Len() > 0 {
  184. if nextReadyAtTimer != nil {
  185. nextReadyAtTimer.Stop()
  186. }
  187. entry := waitingForQueue.Peek().(*waitFor)
  188. nextReadyAtTimer = q.clock.NewTimer(entry.readyAt.Sub(now))
  189. nextReadyAt = nextReadyAtTimer.C()
  190. }
  191. select {
  192. case <-q.stopCh:
  193. return
  194. case <-q.heartbeat.C():
  195. // continue the loop, which will add ready items
  196. case <-nextReadyAt:
  197. // continue the loop, which will add ready items
  198. case waitEntry := <-q.waitingForAddCh:
  199. if waitEntry.readyAt.After(q.clock.Now()) {
  200. insert(waitingForQueue, waitingEntryByData, waitEntry)
  201. } else {
  202. q.Add(waitEntry.data)
  203. }
  204. drained := false
  205. for !drained {
  206. select {
  207. case waitEntry := <-q.waitingForAddCh:
  208. if waitEntry.readyAt.After(q.clock.Now()) {
  209. insert(waitingForQueue, waitingEntryByData, waitEntry)
  210. } else {
  211. q.Add(waitEntry.data)
  212. }
  213. default:
  214. drained = true
  215. }
  216. }
  217. }
  218. }
  219. }
  220. // insert adds the entry to the priority queue, or updates the readyAt if it already exists in the queue
  221. func insert(q *waitForPriorityQueue, knownEntries map[t]*waitFor, entry *waitFor) {
  222. // if the entry already exists, update the time only if it would cause the item to be queued sooner
  223. existing, exists := knownEntries[entry.data]
  224. if exists {
  225. if existing.readyAt.After(entry.readyAt) {
  226. existing.readyAt = entry.readyAt
  227. heap.Fix(q, existing.index)
  228. }
  229. return
  230. }
  231. heap.Push(q, entry)
  232. knownEntries[entry.data] = entry
  233. }