processEntry handles a newly-arrived LogEntry and returns the changes channels from this revision. This can be any existing, removed or newly added channels. Its possible for channels slice returned to have duplicates in it. It is the callers responsibility to de-duplicate before notifying any chang
(ctx context.Context, change *LogEntry)
| 753 | // This can be any existing, removed or newly added channels. Its possible for channels slice returned to have duplicates |
| 754 | // in it. It is the callers responsibility to de-duplicate before notifying any changes. |
| 755 | func (c *changeCache) processEntry(ctx context.Context, change *LogEntry) []channels.ID { |
| 756 | c.lock.Lock() |
| 757 | defer c.lock.Unlock() |
| 758 | if c.logsDisabled { |
| 759 | return nil |
| 760 | } |
| 761 | |
| 762 | sequence := change.Sequence |
| 763 | if change.Sequence > c.internalStats.highSeqFeed { |
| 764 | c.internalStats.highSeqFeed = change.Sequence |
| 765 | } |
| 766 | |
| 767 | // Duplicate handling - there are a few cases where processEntry can be called multiple times for a sequence: |
| 768 | // - recentSequences for rapidly updated documents |
| 769 | // - principal mutations that don't increment sequence |
| 770 | // We can cancel processing early in these scenarios. |
| 771 | // Check if this is a duplicate of an already processed sequence |
| 772 | if sequence < c.nextSequence && !change.Skipped { |
| 773 | // check for presence in skippedSeqs, it's possible that change.skipped can be marked false in recent sequence handling |
| 774 | // but this change is subsequently pushed to skipped before acquiring cache mutex in this function |
| 775 | if !c.WasSkipped(sequence) { |
| 776 | base.DebugfCtx(ctx, base.KeyCache, " Ignoring duplicate of #%d", sequence) |
| 777 | return nil |
| 778 | } else { |
| 779 | change.Skipped = true |
| 780 | } |
| 781 | } |
| 782 | |
| 783 | // Check if this is a duplicate of a pending sequence |
| 784 | if _, found := c.receivedSeqs[sequence]; found { |
| 785 | base.DebugfCtx(ctx, base.KeyCache, " Ignoring duplicate of #%d", sequence) |
| 786 | return nil |
| 787 | } |
| 788 | c.receivedSeqs[sequence] = struct{}{} |
| 789 | |
| 790 | changedChannels := make([]channels.ID, 0, len(change.Channels)) |
| 791 | if sequence == c.nextSequence || c.nextSequence == 0 { |
| 792 | // This is the expected next sequence so we can add it now: |
| 793 | changedChannels = c._addToCache(ctx, change) |
| 794 | // Also add any pending sequences that are now contiguous: |
| 795 | changedChannels = append(changedChannels, c._addPendingLogs(ctx)...) |
| 796 | } else if sequence > c.nextSequence { |
| 797 | // There's a missing sequence (or several), so put this one on ice until it arrives: |
| 798 | heap.Push(&c.pendingLogs, change) |
| 799 | numPending := len(c.pendingLogs) |
| 800 | c.internalStats.pendingSeqLen = numPending |
| 801 | if base.LogDebugEnabled(ctx, base.KeyCache) { |
| 802 | base.DebugfCtx(ctx, base.KeyCache, " Deferring #%d (%d now waiting for #%d...#%d) doc %q / %q", |
| 803 | sequence, numPending, c.nextSequence, c.pendingLogs[0].Sequence-1, base.UD(change.DocID), change.RevID) |
| 804 | } |
| 805 | // Update max pending high watermark stat |
| 806 | if numPending > c.internalStats.maxPending { |
| 807 | c.internalStats.maxPending = numPending |
| 808 | } |
| 809 | |
| 810 | if numPending > c.options.CachePendingSeqMaxNum { |
| 811 | // Too many pending; add the oldest one: |
| 812 | changedChannels = append(changedChannels, c._addPendingLogs(ctx)...) |