@@ -221,6 +221,7 @@ func (s *watchableStore) syncWatchersLoop() {
221
221
waitDuration := 100 * time .Millisecond
222
222
delayTicker := time .NewTicker (waitDuration )
223
223
defer delayTicker .Stop ()
224
+ var evs []mvccpb.Event
224
225
225
226
for {
226
227
s .mu .RLock ()
@@ -230,7 +231,7 @@ func (s *watchableStore) syncWatchersLoop() {
230
231
231
232
unsyncedWatchers := 0
232
233
if lastUnsyncedWatchers > 0 {
233
- unsyncedWatchers = s .syncWatchers ()
234
+ unsyncedWatchers , evs = s .syncWatchers (evs )
234
235
}
235
236
syncDuration := time .Since (st )
236
237
@@ -339,12 +340,12 @@ func (s *watchableStore) moveVictims() (moved int) {
339
340
// 2. iterate over the set to get the minimum revision and remove compacted watchers
340
341
// 3. use minimum revision to get all key-value pairs and send those events to watchers
341
342
// 4. remove synced watchers in set from unsynced group and move to synced group
342
- func (s * watchableStore ) syncWatchers () int {
343
+ func (s * watchableStore ) syncWatchers (evs []mvccpb. Event ) ( int , []mvccpb. Event ) {
343
344
s .mu .Lock ()
344
345
defer s .mu .Unlock ()
345
346
346
347
if s .unsynced .size () == 0 {
347
- return 0
348
+ return 0 , []mvccpb. Event {}
348
349
}
349
350
350
351
s .store .revMu .RLock ()
@@ -357,20 +358,7 @@ func (s *watchableStore) syncWatchers() int {
357
358
compactionRev := s .store .compactMainRev
358
359
359
360
wg , minRev := s .unsynced .choose (maxWatchersPerSync , curRev , compactionRev )
360
- minBytes , maxBytes := NewRevBytes (), NewRevBytes ()
361
- minBytes = RevToBytes (Revision {Main : minRev }, minBytes )
362
- maxBytes = RevToBytes (Revision {Main : curRev + 1 }, maxBytes )
363
-
364
- // UnsafeRange returns keys and values. And in boltdb, keys are revisions.
365
- // values are actual key-value pairs in backend.
366
- tx := s .store .b .ReadTx ()
367
- tx .RLock ()
368
- revs , vs := tx .UnsafeRange (schema .Key , minBytes , maxBytes , 0 )
369
- evs := kvsToEvents (s .store .lg , wg , revs , vs )
370
- // Must unlock after kvsToEvents, because vs (come from boltdb memory) is not deep copy.
371
- // We can only unlock after Unmarshal, which will do deep copy.
372
- // Otherwise we will trigger SIGSEGV during boltdb re-mmap.
373
- tx .RUnlock ()
361
+ evs = rangeEventsWithReuse (s .store .lg , s .store .b , evs , minRev , curRev + 1 )
374
362
375
363
victims := make (watcherBatch )
376
364
wb := newWatcherBatch (wg , evs )
@@ -419,21 +407,68 @@ func (s *watchableStore) syncWatchers() int {
419
407
}
420
408
slowWatcherGauge .Set (float64 (s .unsynced .size () + vsz ))
421
409
422
- return s .unsynced .size ()
410
+ return s .unsynced .size (), evs
411
+ }
412
+
413
+ // rangeEventsWithReuse returns events in range [minRev, maxRev), while reusing already provided events.
414
+ func rangeEventsWithReuse (lg * zap.Logger , b backend.Backend , evs []mvccpb.Event , minRev , maxRev int64 ) []mvccpb.Event {
415
+ if len (evs ) == 0 {
416
+ return rangeEvents (lg , b , minRev , maxRev )
417
+ }
418
+ // append from left
419
+ if evs [0 ].Kv .ModRevision > minRev {
420
+ evs = append (rangeEvents (lg , b , minRev , evs [0 ].Kv .ModRevision ), evs ... )
421
+ }
422
+ // cut from left
423
+ prefixIndex := 0
424
+ for prefixIndex < len (evs ) && evs [prefixIndex ].Kv .ModRevision < minRev {
425
+ prefixIndex ++
426
+ }
427
+ evs = evs [prefixIndex :]
428
+
429
+ if len (evs ) == 0 {
430
+ return rangeEvents (lg , b , minRev , maxRev )
431
+ }
432
+ // append from right
433
+ if evs [len (evs )- 1 ].Kv .ModRevision + 1 < maxRev {
434
+ evs = append (evs , rangeEvents (lg , b , evs [len (evs )- 1 ].Kv .ModRevision + 1 , maxRev )... )
435
+ }
436
+ // cut from right
437
+ suffixIndex := len (evs ) - 1
438
+ for suffixIndex >= 0 && evs [suffixIndex ].Kv .ModRevision >= maxRev {
439
+ suffixIndex --
440
+ }
441
+ evs = evs [:suffixIndex + 1 ]
442
+ return evs
443
+ }
444
+
445
+ // rangeEvents returns events in range [minRev, maxRev).
446
+ func rangeEvents (lg * zap.Logger , b backend.Backend , minRev , maxRev int64 ) []mvccpb.Event {
447
+ minBytes , maxBytes := NewRevBytes (), NewRevBytes ()
448
+ minBytes = RevToBytes (Revision {Main : minRev }, minBytes )
449
+ maxBytes = RevToBytes (Revision {Main : maxRev }, maxBytes )
450
+
451
+ // UnsafeRange returns keys and values. And in boltdb, keys are revisions.
452
+ // values are actual key-value pairs in backend.
453
+ tx := b .ReadTx ()
454
+ tx .RLock ()
455
+ revs , vs := tx .UnsafeRange (schema .Key , minBytes , maxBytes , 0 )
456
+ evs := kvsToEvents (lg , revs , vs )
457
+ // Must unlock after kvsToEvents, because vs (come from boltdb memory) is not deep copy.
458
+ // We can only unlock after Unmarshal, which will do deep copy.
459
+ // Otherwise we will trigger SIGSEGV during boltdb re-mmap.
460
+ tx .RUnlock ()
461
+ return evs
423
462
}
424
463
425
464
// kvsToEvents gets all events for the watchers from all key-value pairs
426
- func kvsToEvents (lg * zap.Logger , wg * watcherGroup , revs , vals [][]byte ) (evs []mvccpb.Event ) {
465
+ func kvsToEvents (lg * zap.Logger , revs , vals [][]byte ) (evs []mvccpb.Event ) {
427
466
for i , v := range vals {
428
467
var kv mvccpb.KeyValue
429
468
if err := kv .Unmarshal (v ); err != nil {
430
469
lg .Panic ("failed to unmarshal mvccpb.KeyValue" , zap .Error (err ))
431
470
}
432
471
433
- if ! wg .contains (string (kv .Key )) {
434
- continue
435
- }
436
-
437
472
ty := mvccpb .PUT
438
473
if isTombstone (revs [i ]) {
439
474
ty = mvccpb .DELETE
0 commit comments