Returns the (ordered) union of all of the changes made to multiple channels.
(ctx context.Context, chans base.Set, options ChangesOptions)
| 707 | |
| 708 | // Returns the (ordered) union of all of the changes made to multiple channels. |
| 709 | func (col *DatabaseCollectionWithUser) SimpleMultiChangesFeed(ctx context.Context, chans base.Set, options ChangesOptions) (<-chan *ChangeEntry, error) { |
| 710 | |
| 711 | to := "" |
| 712 | if col.user != nil && col.user.Name() != "" { |
| 713 | to = fmt.Sprintf(" (to %s)", col.user.Name()) |
| 714 | } else { |
| 715 | to = " (to ADMIN)" |
| 716 | } |
| 717 | |
| 718 | base.InfofCtx(ctx, base.KeyChanges, "MultiChangesFeed(channels: %s, options: %s) ... %s", base.UD(chans), options, base.UD(to)) |
| 719 | output := make(chan *ChangeEntry, 50) |
| 720 | |
| 721 | collectionID := col.GetCollectionID() |
| 722 | go func() { |
| 723 | |
| 724 | defer func() { |
| 725 | if panicked := recover(); panicked != nil { |
| 726 | base.WarnfCtx(ctx, "[%s] Unexpected panic sending changes - terminating changes: \n %s", panicked, debug.Stack()) |
| 727 | } else { |
| 728 | base.InfofCtx(ctx, base.KeyChanges, "MultiChangesFeed done %s", base.UD(to)) |
| 729 | } |
| 730 | close(output) |
| 731 | }() |
| 732 | |
| 733 | var changeWaiter *ChangeWaiter |
| 734 | var lowSequence uint64 |
| 735 | var currentCachedSequence uint64 // The highest contiguous sequence buffered over the caching feed |
| 736 | var lateSequenceFeeds map[channels.ID]*lateSequenceFeed |
| 737 | var useLateSequenceFeeds bool // LateSequence feeds are only used for continuous, or one-shot where options.RequestPlusSeq > currentCachedSequence |
| 738 | var userCounter uint64 // Wait counter used to identify changes to the user document |
| 739 | var changedChannels channels.ChangedKeys // Tracks channels added/removed to the user during changes processing. |
| 740 | var userChanged bool // Whether the user document has changed in a given iteration loop |
| 741 | var deferredBackfill bool // Whether there's a backfill identified in the user doc that's deferred while the SG cache catches up |
| 742 | |
| 743 | // Retrieve the current max cached sequence - ensures there isn't a race between the subsequent channel cache queries |
| 744 | currentCachedSequence = col.changeCache().getChannelCache().GetHighCacheSequence() |
| 745 | |
| 746 | // If changes feed requires more than one ChangesLoop iteration, initialize changeWaiter |
| 747 | if options.Wait || options.RequestPlusSeq > currentCachedSequence { |
| 748 | trackUnusedSequences := options.RequestPlusSeq > 0 |
| 749 | changeWaiter = col.startChangeWaiter(trackUnusedSequences) // Waiter is updated with the actual channel set (post-user reload) at the start of the outer changes loop |
| 750 | userCounter = changeWaiter.CurrentUserCount() |
| 751 | // Reload user to pick up user changes that happened between auth and the change waiter |
| 752 | // initialization. Without this, notification for user doc changes in that window (a) won't be |
| 753 | // included in the initial changes loop iteration, and (b) won't wake up the ChangeWaiter. |
| 754 | if col.user != nil { |
| 755 | previousRoles := col.user.RoleNames() |
| 756 | if err := col.ReloadUser(ctx); err != nil { |
| 757 | base.WarnfCtx(ctx, "Error reloading user during changes initialization %q: %v", base.UD(col.user.Name()), err) |
| 758 | change := makeErrorEntry("User not found during reload - terminating changes feed") |
| 759 | output <- &change |
| 760 | return |
| 761 | } |
| 762 | changedRoles := col.user.RoleNames().CompareKeys(previousRoles) |
| 763 | if len(changedRoles) > 0 { |
| 764 | changeWaiter.RefreshUserKeys(col.User(), col.dbCtx.MetadataKeys) |
| 765 | } |
| 766 | } |
no test coverage detected