fix(mr): arm one retry timer per earliest retry in the mergeability scheduler
- commit
- 33a90cd8090c83698b8b0a6daea5f664552e4e47
- author
- brice · · 2026-10-19 15:14 UTC
- committer
- brice · · 2026-10-19 18:30 UTC
- parent
- 41023de
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
1 file · +12 -2
M internal/usecase/mergeability.go +12 -2
| @@ -198,6 +198,12 @@ func (s *Mergeability) retry(key retryKey) { | |||
| 198 | 198 | // earliest is due and puts it back in the normal queue. | |
| 199 | 199 | func (s *Mergeability) scheduler() { | |
| 200 | 200 | defer s.wg.Done() | |
| 201 | + | // The timer armed for waited; a kick re-arms it only when the earliest | |
| 202 | + | // retry changed, so a stale kick never starts a second timer. | |
| 203 | + | var ( | |
| 204 | + | after <-chan time.Time | |
| 205 | + | waited retryItem | |
| 206 | + | ) | |
| 201 | 207 | for { | |
| 202 | 208 | s.retryMu.Lock() | |
| 203 | 209 | var head retryItem | |
| @@ -207,6 +213,7 @@ func (s *Mergeability) scheduler() { | |||
| 207 | 213 | } | |
| 208 | 214 | s.retryMu.Unlock() | |
| 209 | 215 | if !has { | |
| 216 | + | after = nil | |
| 210 | 217 | select { | |
| 211 | 218 | case <-s.ctx.Done(): | |
| 212 | 219 | return | |
| @@ -214,13 +221,16 @@ func (s *Mergeability) scheduler() { | |||
| 214 | 221 | continue | |
| 215 | 222 | } | |
| 216 | 223 | } | |
| 217 | - | after := s.cfg.After(max(0, head.due.Sub(s.cfg.Clock.Now()))) | |
| 224 | + | if after == nil || head.key != waited.key || !head.due.Equal(waited.due) { | |
| 225 | + | after, waited = s.cfg.After(max(0, head.due.Sub(s.cfg.Clock.Now()))), head | |
| 226 | + | } | |
| 218 | 227 | select { | |
| 219 | 228 | case <-s.ctx.Done(): | |
| 220 | 229 | return | |
| 221 | 230 | case <-s.retryKick: | |
| 222 | - | continue // an earlier retry arrived | |
| 231 | + | continue // an earlier retry may have arrived | |
| 223 | 232 | case <-after: | |
| 233 | + | after = nil | |
| 224 | 234 | } | |
| 225 | 235 | for _, k := range s.popDue(head.key) { | |
| 226 | 236 | if s.ctx.Err() != nil { | |
| @@ -198,6 +198,12 @@ func (s *Mergeability) retry(key retryKey) { | |||||
| 198 | // earliest is due and puts it back in the normal queue. | 198 | // earliest is due and puts it back in the normal queue. | ||
| 199 | func (s *Mergeability) scheduler() { | 199 | func (s *Mergeability) scheduler() { | ||
| 200 | defer s.wg.Done() | 200 | defer s.wg.Done() | ||
| 201 | + | // The timer armed for waited; a kick re-arms it only when the earliest | |||
| 202 | + | // retry changed, so a stale kick never starts a second timer. | |||
| 203 | + | var ( | |||
| 204 | + | after <-chan time.Time | |||
| 205 | + | waited retryItem | |||
| 206 | + | ) | |||
| 201 | for { | 207 | for { | ||
| 202 | s.retryMu.Lock() | 208 | s.retryMu.Lock() | ||
| 203 | var head retryItem | 209 | var head retryItem | ||
| @@ -207,6 +213,7 @@ func (s *Mergeability) scheduler() { | |||||
| 207 | } | 213 | } | ||
| 208 | s.retryMu.Unlock() | 214 | s.retryMu.Unlock() | ||
| 209 | if !has { | 215 | if !has { | ||
| 216 | + | after = nil | |||
| 210 | select { | 217 | select { | ||
| 211 | case <-s.ctx.Done(): | 218 | case <-s.ctx.Done(): | ||
| 212 | return | 219 | return | ||
| @@ -214,13 +221,16 @@ func (s *Mergeability) scheduler() { | |||||
| 214 | continue | 221 | continue | ||
| 215 | } | 222 | } | ||
| 216 | } | 223 | } | ||
| 217 | - | after := s.cfg.After(max(0, head.due.Sub(s.cfg.Clock.Now()))) | 224 | + | if after == nil || head.key != waited.key || !head.due.Equal(waited.due) { |
| 225 | + | after, waited = s.cfg.After(max(0, head.due.Sub(s.cfg.Clock.Now()))), head | |||
| 226 | + | } | |||
| 218 | select { | 227 | select { | ||
| 219 | case <-s.ctx.Done(): | 228 | case <-s.ctx.Done(): | ||
| 220 | return | 229 | return | ||
| 221 | case <-s.retryKick: | 230 | case <-s.retryKick: | ||
| 222 | - | continue // an earlier retry arrived | 231 | + | continue // an earlier retry may have arrived |
| 223 | case <-after: | 232 | case <-after: | ||
| 233 | + | after = nil | |||
| 224 | } | 234 | } | ||
| 225 | for _, k := range s.popDue(head.key) { | 235 | for _, k := range s.popDue(head.key) { | ||
| 226 | if s.ctx.Err() != nil { | 236 | if s.ctx.Err() != nil { | ||