// Returns a list of all the changes made on a channel. // Does NOT handle the Wait option. Does NOT check authorization. func (db *Database) changesFeed(channel string, options ChangesOptions) (<-chan *ChangeEntry, error) { since := options.Since[channel] channelLog, err := db.changesWriter.getChangeLog(channel, since) if err != nil { base.Warn("Error reading channel-log %q (using view instead): %v", channel, err) channelLog = nil } rebuildLog := channelLog == nil && err == nil && (EnableStarChannelLog || channel != "*") var log []*channels.LogEntry if channelLog != nil { log = channelLog.Entries } var viewFeed <-chan *ChangeEntry if channelLog == nil || channelLog.Since > since { var upToSeq uint64 if channelLog != nil { upToSeq = channelLog.Since } // Channel log may not go back far enough, so also fetch view-based change feed: viewFeed, err = db.changesFeedFromView(channel, options, upToSeq) if err != nil { return nil, err } } feed := make(chan *ChangeEntry, 5) go func() { defer close(feed) // First, if we need to backfill from the view, write its early entries to the channel: if viewFeed != nil { newLog := channels.ChangeLog{Since: since} for change := range viewFeed { if channelLog != nil && change.seqNo > channelLog.Since { // TODO: Close the view-based feed somehow break } select { case <-options.Terminator: base.LogTo("Changes+", "Aborting changesFeed (reading from view)") return case feed <- change: } if rebuildLog { // If there wasn't any channel log, build up a new one from the view: entry := channels.LogEntry{ Sequence: change.seqNo, DocID: change.ID, RevID: change.Changes[0]["rev"], } if change.Deleted { entry.Flags |= channels.Deleted } if change.Removed != nil { entry.Flags |= channels.Removed } newLog.Add(entry) newLog.TruncateTo(MaxChangeLogLength) } } if rebuildLog { // Save the missing channel log we just rebuilt: base.LogTo("Changes", "Saving rebuilt channel log %q with %d sequences", channel, len(newLog.Entries)) db.changesWriter.addChangeLog(channel, &newLog) } } // Now write each log entry to the 'feed' channel in turn: for _, logEntry := range log { if !options.Conflicts && (logEntry.Flags&channels.Hidden) != 0 { //continue // FIX: had to comment this out. // This entry is shadowed by a conflicting one. We would like to skip it. // The problem is that if this is the newest revision of this doc, then the // doc will appear under this sequence # in the changes view, which means // we won't emit the doc at all because we already stopped emitting entries // from the view before this point. } change := ChangeEntry{ seqNo: logEntry.Sequence, ID: logEntry.DocID, Deleted: (logEntry.Flags & channels.Deleted) != 0, Changes: []ChangeRev{{"rev": logEntry.RevID}}, } if logEntry.Flags&channels.Removed != 0 { change.Removed = channels.SetOf(channel) } else if options.IncludeDocs || options.Conflicts { doc, _ := db.GetDoc(logEntry.DocID) db.addDocToChangeEntry(doc, &change, options.IncludeDocs, false) } select { case <-options.Terminator: base.LogTo("Changes+", "Aborting changesFeed") return case feed <- &change: } if options.Limit > 0 { options.Limit-- if options.Limit == 0 { break } } } }() return feed, nil }
// Returns a list of all the changes made on a channel. // Does NOT handle the Wait option. Does NOT check authorization. func (db *Database) changesFeed(channel string, options ChangesOptions) (<-chan *ChangeEntry, error) { since := options.Since[channel] channelLog, err := db.GetChangeLog(channel, since) if err != nil { base.Warn("Error reading channel-log %q (using view instead) %v", channel, err) channelLog = nil } rebuildLog := channelLog == nil && err == nil && (EnableStarChannelLog || channel != "*") var log []*channels.LogEntry if channelLog != nil { log = channelLog.Entries } var viewFeed <-chan *ChangeEntry if channelLog == nil || channelLog.Since > since { // Channel log may not go back far enough, so also fetch view-based change feed: viewFeed, err = db.changesFeedFromView(channel, options) if err != nil { return nil, err } } feed := make(chan *ChangeEntry, 5) go func() { defer close(feed) // First, if we need to backfill from the view, write its early entries to the channel: if viewFeed != nil { newLog := channels.ChangeLog{Since: since} for change := range viewFeed { if len(log) > 0 && change.seqNo >= log[0].Sequence { // TODO: Close the view-based feed somehow break } feed <- change if rebuildLog { // If there wasn't any channel log, build up a new one from the view: entry := channels.LogEntry{ Sequence: change.seqNo, DocID: change.ID, RevID: change.Changes[0]["rev"], } if change.Deleted { entry.Flags |= channels.Deleted } if change.Removed != nil { entry.Flags |= channels.Removed } newLog.Add(entry) newLog.TruncateTo(MaxChangeLogLength) } } if rebuildLog { // Save the missing channel log we just rebuilt: base.LogTo("Changes", "Saving rebuilt channel log %q with %d sequences", channel, len(newLog.Entries)) if _, err := db.AddChangeLog(channel, &newLog); err != nil { base.Warn("ChangesFeed: AddChangeLog failed, %v", err) } } } // Now write each log entry to the 'feed' channel in turn: for _, logEntry := range log { hidden := (logEntry.Flags & channels.Hidden) != 0 if logEntry.RevID == "" || (hidden && !options.Conflicts) { continue } change := ChangeEntry{ seqNo: logEntry.Sequence, ID: logEntry.DocID, Deleted: (logEntry.Flags & channels.Deleted) != 0, Changes: []ChangeRev{{"rev": logEntry.RevID}}, } if logEntry.Flags&channels.Removed != 0 { change.Removed = channels.SetOf(channel) } else if options.IncludeDocs || options.Conflicts { doc, _ := db.getDoc(logEntry.DocID) db.addDocToChangeEntry(doc, &change, options.IncludeDocs, false) } feed <- &change if options.Limit > 0 { options.Limit-- if options.Limit == 0 { break } } } }() return feed, nil }