delaying_queue.go 8.0 KB

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