@@ -68,7 +68,7 @@ type changeCache struct {
6868 initialSequence uint64 // DB's current sequence at startup time.
6969 receivedSeqs map [uint64 ]struct {} // Set of all sequences received
7070 pendingLogs LogPriorityQueue // Out-of-sequence entries waiting to be cached
71- notifyChange func (context.Context , channels.Set ) // Client callback that notifies of channel changes
71+ notifyChangeFunc func (context.Context , channels.Set ) // Client callback that notifies of channel changes
7272 started base.AtomicBool // Set by the Start method
7373 stopped base.AtomicBool // Set by the Stop method
7474 skippedSeqs * SkippedSequenceSkiplist // Skipped sequences still pending on the DCP caching feed
@@ -145,15 +145,15 @@ func DefaultCacheOptions() CacheOptions {
145145
146146// Initializes a new changeCache.
147147// lastSequence is the last known database sequence assigned.
148- // notifyChange is an optional function that will be called to notify of channel changes.
148+ // notifyChangeFunc is an optional function that will be called to notify of channel changes.
149149// After calling Init(), you must call .Start() to start using the cache, otherwise it will be in a locked state
150150// and callers will block on trying to obtain the lock.
151151
152152func (c * changeCache ) Init (ctx context.Context , dbContext * DatabaseContext , channelCache ChannelCache , notifyChange func (context.Context , channels.Set ), options * CacheOptions , metaKeys * base.MetadataKeys ) error {
153153 c .db = dbContext
154154 c .logCtx = ctx
155155
156- c .notifyChange = notifyChange
156+ c .notifyChangeFunc = notifyChange
157157 c .receivedSeqs = make (map [uint64 ]struct {})
158158 c .terminator = make (chan bool )
159159 c .initTime = time .Now ()
@@ -277,11 +277,10 @@ func (c *changeCache) InsertPendingEntries(ctx context.Context) error {
277277 // Trigger _addPendingLogs to process any entries that have been pending too long:
278278 c .lock .Lock ()
279279 changedChannels := c ._addPendingLogs (ctx )
280- if c .notifyChange != nil && len (changedChannels ) > 0 {
281- c .notifyChange (ctx , changedChannels )
282- }
283280 c .lock .Unlock ()
284281
282+ c .notifyChange (ctx , changedChannels )
283+
285284 return nil
286285}
287286
@@ -445,7 +444,8 @@ func (c *changeCache) DocChanged(event sgbucket.FeedEvent, docType DocumentType)
445444 UnusedSequence : true ,
446445 }
447446 changedChannels := c .processEntry (ctx , change )
448- changedChannelsCombined = changedChannelsCombined .Update (changedChannels )
447+ channelSet := channels .SetFromArrayNoValidate (changedChannels )
448+ changedChannelsCombined = changedChannelsCombined .Update (channelSet )
449449 }
450450 base .DebugfCtx (ctx , base .KeyCache , "Received unused sequences in unused_sequences property for (%q / %q): %v" , base .UD (docID ), syncData .CurrentRev , syncData .UnusedSequences )
451451 }
@@ -491,7 +491,8 @@ func (c *changeCache) DocChanged(event sgbucket.FeedEvent, docType DocumentType)
491491 }
492492
493493 changedChannels := c .processEntry (ctx , change )
494- changedChannelsCombined = changedChannelsCombined .Update (changedChannels )
494+ channelSet := channels .SetFromArrayNoValidate (changedChannels )
495+ changedChannelsCombined = changedChannelsCombined .Update (channelSet )
495496 }
496497 }
497498 if len (seqsCached ) > 0 {
@@ -523,11 +524,12 @@ func (c *changeCache) DocChanged(event sgbucket.FeedEvent, docType DocumentType)
523524 }
524525
525526 changedChannels := c .processEntry (ctx , change )
526- changedChannelsCombined = changedChannelsCombined .Update (changedChannels )
527+ channelSet := channels .SetFromArrayNoValidate (changedChannels )
528+ changedChannelsCombined = changedChannelsCombined .Update (channelSet )
527529
528530 // Notify change listeners for all of the changed channels
529- if c .notifyChange != nil && len (changedChannelsCombined ) > 0 {
530- c .notifyChange (ctx , changedChannelsCombined )
531+ if c .notifyChangeFunc != nil && len (changedChannelsCombined ) > 0 {
532+ c .notifyChangeFunc (ctx , changedChannelsCombined )
531533 }
532534
533535}
@@ -538,6 +540,13 @@ type cachePrincipal struct {
538540 Sequence uint64 `json:"sequence"`
539541}
540542
543+ func (c * changeCache ) notifyChange (ctx context.Context , chs []channels.ID ) {
544+ if c .notifyChangeFunc == nil || len (chs ) == 0 {
545+ return
546+ }
547+ c .notifyChangeFunc (ctx , channels .SetFromArrayNoValidate (chs ))
548+ }
549+
541550func (c * changeCache ) Remove (ctx context.Context , collectionID uint32 , docIDs []string , startTime time.Time ) (count int ) {
542551 return c .channelCache .Remove (ctx , collectionID , docIDs , startTime )
543552}
@@ -571,14 +580,16 @@ func (c *changeCache) releaseUnusedSequence(ctx context.Context, sequence uint64
571580
572581 // Since processEntry may unblock pending sequences, if there were any changed channels we need
573582 // to notify any change listeners that are working changes feeds for these channels
583+ var channelSet channels.Set
574584 changedChannels := c .processEntry (ctx , change )
575585 if changedChannels == nil {
576- changedChannels = channels .SetOfNoValidate (unusedSeqChannelID )
586+ channelSet = channels .SetOfNoValidate (unusedSeqChannelID )
577587 } else {
578- changedChannels .Add (unusedSeqChannelID )
588+ channelSet = channels .SetFromArrayNoValidate (changedChannels )
589+ channelSet .Add (unusedSeqChannelID )
579590 }
580- if c .notifyChange != nil && len (changedChannels ) > 0 {
581- c .notifyChange (ctx , changedChannels )
591+ if c .notifyChangeFunc != nil && len (channelSet ) > 0 {
592+ c .notifyChangeFunc (ctx , channelSet )
582593 }
583594}
584595
@@ -598,36 +609,38 @@ func (c *changeCache) releaseUnusedSequenceRange(ctx context.Context, fromSequen
598609 UnusedSequence : true ,
599610 }
600611 changedChannels := c .processEntry (ctx , change )
601- allChangedChannels = allChangedChannels .Update (changedChannels )
602- if c .notifyChange != nil {
603- c .notifyChange (ctx , allChangedChannels )
612+ channelSet := channels .SetFromArrayNoValidate (changedChannels )
613+ allChangedChannels = allChangedChannels .Update (channelSet )
614+ if c .notifyChangeFunc != nil {
615+ c .notifyChangeFunc (ctx , allChangedChannels )
604616 }
605617 return
606618 }
607619
608620 // push unused range to either pending or skipped lists based on current state of the change cache
609- allChangedChannels = c .processUnusedRange (ctx , fromSequence , toSequence , allChangedChannels , timeReceived )
621+ changedChannels := c .processUnusedRange (ctx , fromSequence , toSequence , timeReceived )
622+ allChangedChannels .Update (channels .SetFromArrayNoValidate (changedChannels ))
610623
611- if c .notifyChange != nil {
612- c .notifyChange (ctx , allChangedChannels )
624+ if c .notifyChangeFunc != nil {
625+ c .notifyChangeFunc (ctx , allChangedChannels )
613626 }
614627}
615628
616629// processUnusedRange handles pushing unused range to pending or skipped lists
617- func (c * changeCache ) processUnusedRange (ctx context.Context , fromSequence , toSequence uint64 , allChangedChannels channels. Set , timeReceived channels.FeedTimestamp ) channels.Set {
630+ func (c * changeCache ) processUnusedRange (ctx context.Context , fromSequence , toSequence uint64 , timeReceived channels.FeedTimestamp ) [] channels.ID {
618631 c .lock .Lock ()
619632 defer c .lock .Unlock ()
620633
621634 var numSkipped int64
635+ var changedChannels []channels.ID
622636 if toSequence < c .nextSequence {
623637 // batch remove from skipped
624638 numSkipped = c .skippedSeqs .processUnusedSequenceRangeAtSkipped (ctx , fromSequence , toSequence )
625639 } else if fromSequence >= c .nextSequence {
626640 // whole range to pending
627641 c ._pushRangeToPending (fromSequence , toSequence , timeReceived )
628642 // unblock any pending sequences we can after new range(s) have been pushed to pending
629- changedChannels := c ._addPendingLogs (ctx )
630- allChangedChannels = allChangedChannels .Update (changedChannels )
643+ changedChannels = append (changedChannels , c ._addPendingLogs (ctx )... )
631644 c .internalStats .pendingSeqLen = len (c .pendingLogs )
632645 } else {
633646 // An unused sequence range than includes c.nextSequence in the middle of the range
@@ -641,7 +654,7 @@ func (c *changeCache) processUnusedRange(ctx context.Context, fromSequence, toSe
641654 if numSkipped == 0 {
642655 c .db .BroadcastSlowMode .CompareAndSwap (true , false )
643656 }
644- return allChangedChannels
657+ return changedChannels
645658}
646659
647660// _pushRangeToPending will push an unused sequence range to pendingLogs
@@ -711,13 +724,14 @@ func (c *changeCache) processPrincipalDoc(ctx context.Context, docID string, doc
711724 base .InfofCtx (ctx , base .KeyChanges , "Received #%d (%q)" , change .Sequence , base .UD (change .DocID ))
712725
713726 changedChannels := c .processEntry (ctx , change )
714- if c .notifyChange != nil && len (changedChannels ) > 0 {
715- c .notifyChange (ctx , changedChannels )
716- }
727+
728+ c .notifyChange (ctx , changedChannels )
717729}
718730
719- // Handles a newly-arrived LogEntry.
720- func (c * changeCache ) processEntry (ctx context.Context , change * LogEntry ) channels.Set {
731+ // processEntry handles a newly-arrived LogEntry and returns the changes channels from this revision.
732+ // This can be any existing, removed or newly added channels. Its possible for channels slice returned to have duplicates
733+ // in it. It is the callers responsibility to de-duplicate before notifying any changes.
734+ func (c * changeCache ) processEntry (ctx context.Context , change * LogEntry ) []channels.ID {
721735 c .lock .Lock ()
722736 defer c .lock .Unlock ()
723737 if c .logsDisabled {
@@ -752,12 +766,12 @@ func (c *changeCache) processEntry(ctx context.Context, change *LogEntry) channe
752766 }
753767 c .receivedSeqs [sequence ] = struct {}{}
754768
755- var changedChannels channels.Set
769+ var changedChannels [] channels.ID
756770 if sequence == c .nextSequence || c .nextSequence == 0 {
757771 // This is the expected next sequence so we can add it now:
758772 changedChannels = c ._addToCache (ctx , change )
759773 // Also add any pending sequences that are now contiguous:
760- changedChannels = changedChannels . Update ( c ._addPendingLogs (ctx ))
774+ changedChannels = append ( changedChannels , c ._addPendingLogs (ctx )... )
761775 } else if sequence > c .nextSequence {
762776 // There's a missing sequence (or several), so put this one on ice until it arrives:
763777 heap .Push (& c .pendingLogs , change )
@@ -774,7 +788,7 @@ func (c *changeCache) processEntry(ctx context.Context, change *LogEntry) channe
774788
775789 if numPending > c .options .CachePendingSeqMaxNum {
776790 // Too many pending; add the oldest one:
777- changedChannels = c ._addPendingLogs (ctx )
791+ changedChannels = append ( changedChannels , c ._addPendingLogs (ctx ) ... )
778792 }
779793 } else if sequence > c .initialSequence {
780794 // Out-of-order sequence received!
@@ -786,7 +800,7 @@ func (c *changeCache) processEntry(ctx context.Context, change *LogEntry) channe
786800 base .DebugfCtx (ctx , base .KeyCache , " Received previously skipped out-of-order change (seq %d, expecting %d) doc %q / %q " , sequence , c .nextSequence , base .UD (change .DocID ), change .RevID )
787801 }
788802
789- changedChannels = changedChannels . Update ( c ._addToCache (ctx , change ))
803+ changedChannels = append ( changedChannels , c ._addToCache (ctx , change )... )
790804 // Add to cache before removing from skipped, to ensure lowSequence doesn't get incremented until results are available
791805 // in cache
792806 err := c .RemoveSkipped (sequence )
@@ -799,7 +813,7 @@ func (c *changeCache) processEntry(ctx context.Context, change *LogEntry) channe
799813
800814// Adds an entry to the appropriate channels' caches, returning the affected channels. lateSequence
801815// flag indicates whether it was a change arriving out of sequence
802- func (c * changeCache ) _addToCache (ctx context.Context , change * LogEntry ) channels.Set {
816+ func (c * changeCache ) _addToCache (ctx context.Context , change * LogEntry ) [] channels.ID {
803817
804818 if change .Sequence >= c .nextSequence {
805819 c .nextSequence = change .Sequence + 1
@@ -836,11 +850,12 @@ func (c *changeCache) _addToCache(ctx context.Context, change *LogEntry) channel
836850 return updatedChannels
837851}
838852
839- // Add the first change(s) from pendingLogs if they're the next sequence. If not, and we've been
853+ // _addPendingLogs Add the first change(s) from pendingLogs if they're the next sequence. If not, and we've been
840854// waiting too long for nextSequence, move nextSequence to skipped queue.
841- // Returns the channels that changed.
842- func (c * changeCache ) _addPendingLogs (ctx context.Context ) channels.Set {
843- var changedChannels channels.Set
855+ // Returns the channels that changed. This may return the same channel more than once, channels should be deduplicated
856+ // before notifying the changes.
857+ func (c * changeCache ) _addPendingLogs (ctx context.Context ) []channels.ID {
858+ var changedChannels []channels.ID
844859 var isNext bool
845860
846861 for len (c .pendingLogs ) > 0 {
@@ -849,7 +864,7 @@ func (c *changeCache) _addPendingLogs(ctx context.Context) channels.Set {
849864
850865 if isNext {
851866 oldestPending = c ._popPendingLog (ctx )
852- changedChannels = changedChannels . Update ( c ._addToCache (ctx , oldestPending ))
867+ changedChannels = append ( changedChannels , c ._addToCache (ctx , oldestPending )... )
853868 } else if oldestPending .Sequence < c .nextSequence {
854869 // oldest pending is lower than next sequence, should be ignored
855870 base .InfofCtx (ctx , base .KeyCache , "Oldest entry in pending logs %v (%d, %d) is earlier than cache next sequence (%d), ignoring as sequence has already been cached" , base .UD (oldestPending .DocID ), oldestPending .Sequence , oldestPending .EndSequence , c .nextSequence )
0 commit comments