// Updates the Channels property of a document object with current & past channels. // Returns the set of channels that have changed (document joined or left in this revision) func (doc *document) updateChannels(newChannels base.Set) (changedChannels base.Set) { var changed []string oldChannels := doc.Channels if oldChannels == nil { oldChannels = channels.ChannelMap{} doc.Channels = oldChannels } else { // Mark every no-longer-current channel as unsubscribed: curSequence := doc.Sequence for channel, removal := range oldChannels { if removal == nil && !newChannels.Contains(channel) { oldChannels[channel] = &channels.ChannelRemoval{ Seq: curSequence, RevID: doc.CurrentRev, Deleted: doc.hasFlag(channels.Deleted)} changed = append(changed, channel) } } } // Mark every current channel as subscribed: for channel, _ := range newChannels { if value, exists := oldChannels[channel]; value != nil || !exists { oldChannels[channel] = nil changed = append(changed, channel) } } if changed != nil { base.LogTo("CRUD", "\tDoc %q in channels %q", doc.ID, newChannels) changedChannels = channels.SetOf(changed...) } return }
// Handles a newly-arrived LogEntry. func (c *changeCache) processEntry(change *LogEntry) base.Set { c.lock.Lock() defer c.lock.Unlock() if c.logsDisabled { return nil } sequence := change.Sequence nextSequence := c.nextSequence if _, found := c.receivedSeqs[sequence]; found { base.LogTo("Cache+", " Ignoring duplicate of #%d", sequence) return nil } c.receivedSeqs[sequence] = struct{}{} // FIX: c.receivedSeqs grows monotonically. Need a way to remove old sequences. var changedChannels base.Set if sequence == nextSequence || nextSequence == 0 { // This is the expected next sequence so we can add it now: changedChannels = c._addToCache(change) // Also add any pending sequences that are now contiguous: changedChannels = changedChannels.Union(c._addPendingLogs()) } else if sequence > nextSequence { // There's a missing sequence (or several), so put this one on ice until it arrives: heap.Push(&c.pendingLogs, change) numPending := len(c.pendingLogs) base.LogTo("Cache", " Deferring #%d (%d now waiting for #%d...#%d)", sequence, numPending, nextSequence, c.pendingLogs[0].Sequence-1) changeCacheExpvars.Get("maxPending").(*base.IntMax).SetIfMax(int64(numPending)) if numPending > c.options.CachePendingSeqMaxNum { // Too many pending; add the oldest one: changedChannels = c._addPendingLogs() } } else if sequence > c.initialSequence { // Out-of-order sequence received! // Remove from skipped sequence queue if !c.WasSkipped(sequence) { // Error removing from skipped sequences base.LogTo("Cache", " Received unexpected out-of-order change - not in skippedSeqs (seq %d, expecting %d) doc %q / %q", sequence, nextSequence, change.DocID, change.RevID) } else { base.LogTo("Cache", " Received previously skipped out-of-order change (seq %d, expecting %d) doc %q / %q ", sequence, nextSequence, change.DocID, change.RevID) change.Skipped = true } changedChannels = c._addToCache(change) // Add to cache before removing from skipped, to ensure lowSequence doesn't get incremented until results are available // in cache c.RemoveSkipped(sequence) } return changedChannels }
// Check for matching entry names, ignoring sequence func (set TimedSet) Equals(other base.Set) bool { for name, _ := range set { if !other.Contains(name) { return false } } for name, _ := range other { if !set.Contains(name) { return false } } return true }
// Updates membership to match the given Set. Newly added members will have the given sequence. func (set TimedSet) UpdateAtSequence(other base.Set, sequence uint64) bool { changed := false for name, _ := range set { if !other.Contains(name) { delete(set, name) changed = true } } for name, _ := range other { if !set.Contains(name) { set[name] = NewVbSimpleSequence(sequence) changed = true } } return changed }
// Add the first change(s) from pendingLogs if they're the next sequence. If not, and we've been // waiting too long for nextSequence, move nextSequence to skipped queue. // Returns the channels that changed. func (c *changeCache) _addPendingLogs() base.Set { var changedChannels base.Set for len(c.pendingLogs) > 0 { change := c.pendingLogs[0] isNext := change.Sequence == c.nextSequence if isNext { heap.Pop(&c.pendingLogs) changedChannels = changedChannels.Union(c._addToCache(change)) } else if len(c.pendingLogs) > c.options.CachePendingSeqMaxNum || time.Since(c.pendingLogs[0].TimeReceived) >= c.options.CachePendingSeqMaxWait { changeCacheExpvars.Add("outOfOrder", 1) c.PushSkipped(c.nextSequence) c.nextSequence++ } else { break } } return changedChannels }
// If a channel list contains the all-channel wildcard, replace it with all the user's accessible channels. func (user *userImpl) ExpandWildCardChannel(channels base.Set) base.Set { if channels.Contains(ch.AllChannelWildcard) { channels = user.InheritedChannels().AsSet() } return channels }
// Returns a set with any "*" channel removed. func IgnoringStar(set base.Set) base.Set { return set.Removing(UserStarChannel) }