// Returns of all the changes made to multiple channels. func (db *Database) MultiChangesFeed(chans channels.Set, options ChangesOptions) (<-chan *ChangeEntry, error) { if len(chans) == 0 { return nil, nil } else if len(chans) == 1 && !chans.Contains("*") { for channel, _ := range chans { base.LogTo("Changes", "ChangesFeed(%s, %+v) ...", channel, options) return db.ChangesFeed(channel, options) } } base.LogTo("Changes", "MultiChangesFeed(%s, %+v) ...", chans, options) waitMode := options.Wait options.Wait = false output := make(chan *ChangeEntry, kChangesPageSize) go func() { defer close(output) for { // Restrict to available channels, expand wild-card, and find since when these channels // have been available to the user: var channelsSince channels.TimedSet if db.user != nil { channelsSince = db.user.FilterToAvailableChannels(chans) } else { channelsSince = chans.AtSequence(1) } base.LogTo("Changes", "MultiChangesFeed: channels expand to %s ...", channelsSince) feeds := make([]<-chan *ChangeEntry, 0, len(channelsSince)) for name, _ := range channelsSince { feedOpts := options if channelsSince[name] > options.Since { feedOpts.Since = 0 // channel wasn't available before, so start from beginning } feed, err := db.ChangesFeed(name, feedOpts) if err != nil { base.Warn("Error reading changes feed %q: %v", name, err) return } feeds = append(feeds, feed) } current := make([]*ChangeEntry, len(feeds)) var lastSeqSent uint64 for { //FIX: This assumes Reverse or Limit aren't set in the options // Read more entries to fill up the current[] array: for i, cur := range current { if cur == nil && feeds[i] != nil { var ok bool current[i], ok = <-feeds[i] if !ok { feeds[i] = nil } } } // Find the current entry with the minimum sequence: var minSeq uint64 = math.MaxUint64 var minEntry *ChangeEntry for _, cur := range current { if cur != nil && cur.Seq < minSeq { minSeq = cur.Seq minEntry = cur } } if minEntry == nil { break // Exit the loop when there are no more entries } // Clear the current entries for the sequence just sent: for i, cur := range current { if cur != nil && cur.Seq == minEntry.Seq { current[i] = nil // Also concatenate the matching entries' Removed arrays: if cur != minEntry && cur.Removed != nil { if minEntry.Removed == nil { minEntry.Removed = cur.Removed } else { minEntry.Removed = minEntry.Removed.Union(cur.Removed) } } } } // Send the entry, and repeat the loop: output <- minEntry lastSeqSent = minEntry.Seq } // If nothing found, and in wait mode: wait for the db to change, then run again if lastSeqSent > 0 || !waitMode || !db.WaitForRevision() { break } // Before checking again, update the User object in case its channel access has // changed while waiting: if err := db.ReloadUser(); err != nil { base.Warn("Error reloading user %q: %v", db.user.Name(), err) return } } base.LogTo("Changes", "MultiChangesFeed done") }() return output, nil }
func (role *roleImpl) initRole(name string, channels ch.Set) error { channels = channels.ExpandingStar() role.Name_ = name role.ExplicitChannels_ = channels.AtSequence(1) return role.validate() }