@@ -3,6 +3,7 @@ package reprovider
3
3
import (
4
4
"context"
5
5
"errors"
6
+ "slices"
6
7
"strconv"
7
8
"sync"
8
9
"sync/atomic"
@@ -51,6 +52,7 @@ var logger = logging.Logger("dht/ReprovideSweep")
51
52
52
53
var (
53
54
ErrNodeOffline = errors .New ("reprovider: node is offline" )
55
+ ErrNoKeyProvided = errors .New ("reprovider: failed to provide any key" )
54
56
errTooManyIterationsDuringExploration = errors .New ("closestPeersToPrefix needed more than maxPrefixSearches iterations" )
55
57
)
56
58
@@ -83,10 +85,11 @@ type reprovideSweeper struct {
83
85
84
86
cids * trie.Trie [bit256.Key , mh.Multihash ]
85
87
86
- provideChan chan provideReq
87
- schedule * trie.Trie [bitstr.Key , time.Duration ]
88
- scheduleLk sync.Mutex
89
- scheduleTimer * clock.Timer
88
+ provideChan chan provideReq
89
+ schedule * trie.Trie [bitstr.Key , time.Duration ]
90
+ scheduleLk sync.Mutex
91
+ scheduleTimer * clock.Timer
92
+ scheduleTimerStartedAt time.Time
90
93
91
94
failedRegionsChan chan bitstr.Key
92
95
lateRegionsQueue []bitstr.Key
@@ -184,7 +187,9 @@ func (s *reprovideSweeper) run() {
184
187
case <- s .scheduleTimer .C :
185
188
s .handleReprovide ()
186
189
case prefix := <- s .failedRegionsChan :
187
- s .lateRegionsQueue = append (s .lateRegionsQueue , prefix )
190
+ if ! slices .Contains (s .lateRegionsQueue , prefix ) {
191
+ s .lateRegionsQueue = append (s .lateRegionsQueue , prefix )
192
+ }
188
193
if s .online .Load () {
189
194
go s .onlineCheck ()
190
195
}
@@ -265,7 +270,7 @@ func (s *reprovideSweeper) addPrefixToScheduleNoLock(prefix bitstr.Key) {
265
270
s .schedule .Add (prefix , reprovideTime )
266
271
267
272
currentTimeOffset := s .currentTimeOffset ()
268
- timeUntilReprovide := (reprovideTime - currentTimeOffset + s .reprovideInterval ) % s .reprovideInterval
273
+ timeUntilReprovide := (reprovideTime - currentTimeOffset + s .reprovideInterval - 1 ) % s .reprovideInterval + 1
269
274
if s .prefixCursor == "" {
270
275
s .scheduleNextReprovideNoLock (prefix , timeUntilReprovide )
271
276
} else {
@@ -287,6 +292,7 @@ func (s *reprovideSweeper) addPrefixToScheduleNoLock(prefix bitstr.Key) {
287
292
func (s * reprovideSweeper ) scheduleNextReprovideNoLock (prefix bitstr.Key , timeUntilReprovide time.Duration ) {
288
293
s .prefixCursor = prefix
289
294
s .scheduleTimer .Reset (timeUntilReprovide )
295
+ s .scheduleTimerStartedAt = s .clock .Now ()
290
296
}
291
297
292
298
func (s * reprovideSweeper ) getPrefixesForCids (cids []mh.Multihash ) map [bitstr.Key ]* trie.Trie [bit256.Key , mh.Multihash ] {
@@ -408,35 +414,75 @@ func (s *reprovideSweeper) networkProvide(prefixes map[bitstr.Key]*trie.Trie[bit
408
414
}
409
415
410
416
if atomic .LoadUint32 (& anySuccess ) == 0 {
411
- return errors . New ( "failed to provide any cid" )
417
+ return ErrNoKeyProvided
412
418
}
413
419
return nil
414
420
}
415
421
416
422
func (s * reprovideSweeper ) handleReprovide () {
417
423
online := s .online .Load ()
418
424
s .scheduleLk .Lock ()
419
- if online {
420
- // Remove prefix from trie if online, new schedule will be added as needed
421
- // after reprovide.
422
- s .schedule .Remove (s .prefixCursor )
423
- }
424
- // Get next prefix to reprovide, and set timer for it.
425
- next := nextNonEmptyLeaf (s .schedule , s .prefixCursor , s .order )
426
425
currentPrefix := s .prefixCursor
427
- // TODO: for all prefixes between next.Data and s.currentTimeOffset(), add them to failedRegionsChan
426
+ // Get next prefix to reprovide, and set timer for it.
427
+ next := nextNonEmptyLeaf (s .schedule , currentPrefix , s .order )
428
428
429
- var nextReprovideDelay time.Duration
430
- nextPrefix := currentPrefix
431
429
if next == nil {
432
- // Empty schedule, keep current prefixCursor, and wake up in
433
- // reprovideInterval.
434
- nextReprovideDelay = s .reprovideInterval
430
+ // Schedule is empty, don't reprovide anything.
431
+ s .scheduleLk .Unlock ()
432
+ return
433
+ }
434
+
435
+ currentTimeOffset := s .currentTimeOffset ()
436
+ var nextPrefix bitstr.Key
437
+ var timeUntilNextReprovide time.Duration
438
+ if next .Key == currentPrefix {
439
+ // There is a single prefix in the schedule.
440
+ nextPrefix = currentPrefix
441
+ timeUntilNextReprovide = (s .reprovideTimeForPrefix (currentPrefix )- currentTimeOffset + s .reprovideInterval - 1 )% s .reprovideInterval + 1
435
442
} else {
443
+ timeSinceTimerRunning := (currentTimeOffset - s .timeOffset (s .scheduleTimerStartedAt ) + s .reprovideInterval ) % s .reprovideInterval
444
+ timeSinceTimerUntilNext := (next .Data - s .timeOffset (s .scheduleTimerStartedAt ) + s .reprovideInterval ) % s .reprovideInterval
445
+
446
+ if s .scheduleTimerStartedAt .Add (s .reprovideInterval ).Before (s .clock .Now ()) {
447
+ // Alarm was programmed more than reprovideInterval ago, which means that
448
+ // no regions has been reprovided since. Add all regions to
449
+ // failedRegionsChan. This only happens if the main thread gets blocked
450
+ // for more than reprovideInterval.
451
+ nextKeyFound := false
452
+ scheduleEntries := allEntries (s .schedule , s .order )
453
+ for _ , entry := range scheduleEntries {
454
+ if ! nextKeyFound && entry .Data > currentTimeOffset {
455
+ next = entry
456
+ nextKeyFound = true
457
+ }
458
+ s .failedRegionsChan <- entry .Key
459
+ }
460
+ if ! nextKeyFound {
461
+ next = scheduleEntries [0 ]
462
+ }
463
+ timeUntilNextReprovide = (next .Data - currentTimeOffset + s .reprovideInterval - 1 )% s .reprovideInterval + 1
464
+ // Don't reprovide any region now, but schedule the next one. All regions
465
+ // are expected to be reprovided when the provider is catching up with
466
+ // failed regions.
467
+ s .scheduleNextReprovideNoLock (next .Key , timeUntilNextReprovide )
468
+ s .scheduleLk .Unlock ()
469
+ return
470
+ } else if timeSinceTimerUntilNext < timeSinceTimerRunning {
471
+ // next is scheduled in the past. While next is in the past, add next to
472
+ // failedRegions and take nextLeaf as next.
473
+
474
+ for timeSinceTimerUntilNext < timeSinceTimerRunning {
475
+ s .failedRegionsChan <- next .Key
476
+ next = nextNonEmptyLeaf (s .schedule , next .Key , s .order )
477
+ timeSinceTimerUntilNext = (next .Data - s .timeOffset (s .scheduleTimerStartedAt ) + s .reprovideInterval ) % s .reprovideInterval
478
+ }
479
+ }
480
+ // next is in the future
436
481
nextPrefix = next .Key
437
- nextReprovideDelay = (s . reprovideInterval + next .Data - s . currentTimeOffset ()) % s .reprovideInterval
482
+ timeUntilNextReprovide = (next .Data - currentTimeOffset + s . reprovideInterval - 1 ) % s .reprovideInterval + 1
438
483
}
439
- s .scheduleNextReprovideNoLock (nextPrefix , nextReprovideDelay )
484
+
485
+ s .scheduleNextReprovideNoLock (nextPrefix , timeUntilNextReprovide )
440
486
s .scheduleLk .Unlock ()
441
487
442
488
// If we are offline, don't even try to reprovide region.
@@ -445,6 +491,15 @@ func (s *reprovideSweeper) handleReprovide() {
445
491
return
446
492
}
447
493
494
+ // Remove prefix that is about to be reprovided from the late regions queue
495
+ // if present.
496
+ for i , r := range s .lateRegionsQueue {
497
+ if r == currentPrefix {
498
+ s .lateRegionsQueue = slices .Delete (s .lateRegionsQueue , i , i + 1 )
499
+ break
500
+ }
501
+ }
502
+
448
503
cids := s .cids .Copy () // NOTE: if many cids, this may have a large memory footprint
449
504
go s .provideForPrefix (currentPrefix , cids , regularReprovide )
450
505
}
@@ -692,7 +747,7 @@ func (s *reprovideSweeper) unscheduleSubsumedPrefixes(prefix bitstr.Key) {
692
747
if next == nil {
693
748
s .scheduleNextReprovideNoLock (prefix , s .reprovideInterval )
694
749
} else {
695
- timeUntilReprovide := (s .reprovideInterval + next .Data - s .currentTimeOffset ()) % s .reprovideInterval
750
+ timeUntilReprovide := (s .reprovideInterval + next .Data - s .currentTimeOffset ()- 1 ) % s .reprovideInterval + 1
696
751
s .scheduleNextReprovideNoLock (next .Key , timeUntilReprovide )
697
752
}
698
753
}
@@ -828,7 +883,13 @@ func (s *reprovideSweeper) scheduleNextReprovide(prefix bitstr.Key, lastReprovid
828
883
829
884
// currentTimeOffset returns the current time offset in the reprovide cycle.
830
885
func (s * reprovideSweeper ) currentTimeOffset () time.Duration {
831
- return s .clock .Now ().Sub (s .cycleStart ) % s .reprovideInterval
886
+ return s .timeOffset (s .clock .Now ())
887
+ }
888
+
889
+ // timeOffset returns the time offset in the reprovide cycle for the given
890
+ // time.
891
+ func (s * reprovideSweeper ) timeOffset (t time.Time ) time.Duration {
892
+ return t .Sub (s .cycleStart ) % s .reprovideInterval
832
893
}
833
894
834
895
const maxPrefixSize = 24
0 commit comments