From 016b5daa1cde1a147701aafce94f67057fbb7aa6 Mon Sep 17 00:00:00 2001 From: Jonathan Date: Tue, 5 Sep 2017 16:39:07 -0400 Subject: PLT-7444: If there is activity in Mattermost before the email batch is sent, do not send the email (#7342) * Changed email batching short-circuit logic to look at last viewed at timestamp in channel member struct instead of in user's status struct, since the latter is only updated if the user's status is set to online * Fixed unit tests * Reduced right-hand drift * Reduced total number of store calls by loading all channel member objects for user exactly once per team that the user received notifications for --- app/email_batching.go | 40 +++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-) (limited to 'app/email_batching.go') diff --git a/app/email_batching.go b/app/email_batching.go index 2e643d2b1..4b557c678 100644 --- a/app/email_batching.go +++ b/app/email_batching.go @@ -122,24 +122,42 @@ func (job *EmailBatchingJob) handleNewNotifications() { } func (job *EmailBatchingJob) checkPendingNotifications(now time.Time, handler func(string, []*batchedNotification)) { - // look for users who've acted since pending posts were received for userId, notifications := range job.pendingNotifications { - schan := Srv.Store.Status().Get(userId) - pchan := Srv.Store.Preference().Get(userId, model.PREFERENCE_CATEGORY_NOTIFICATIONS, model.PREFERENCE_NAME_EMAIL_INTERVAL) batchStartTime := notifications[0].post.CreateAt + inspectedTeamNames := make(map[string]string) + for _, notification := range notifications { + // at most, we'll do one check for each team that notifications were sent for + if inspectedTeamNames[notification.teamName] != "" { + continue + } + tchan := Srv.Store.Team().GetByName(notifications[0].teamName) + if result := <-tchan; result.Err != nil { + l4g.Error("Unable to find Team id for notification", result.Err) + continue + } else if team, ok := result.Data.(*model.Team); ok { + inspectedTeamNames[notification.teamName] = team.Id + } - // check if the user has been active and would've seen any new posts - if result := <-schan; result.Err != nil { - l4g.Error(utils.T("api.email_batching.check_pending_emails.status.app_error"), result.Err) - delete(job.pendingNotifications, userId) - continue - } else if status := result.Data.(*model.Status); status.LastActivityAt >= batchStartTime { - delete(job.pendingNotifications, userId) - continue + // if the user has viewed any channels in this team since the notification was queued, delete + // all queued notifications + mchan := Srv.Store.Channel().GetMembersForUser(inspectedTeamNames[notification.teamName], userId) + if result := <-mchan; result.Err != nil { + l4g.Error("Unable to find ChannelMembers for user", result.Err) + continue + } else if channelMembers, ok := result.Data.(*model.ChannelMembers); ok { + for _, channelMember := range *channelMembers { + if channelMember.LastViewedAt >= batchStartTime { + l4g.Info("Deleted notifications for user %s", userId) + delete(job.pendingNotifications, userId) + break + } + } + } } // get how long we need to wait to send notifications to the user var interval int64 + pchan := Srv.Store.Preference().Get(userId, model.PREFERENCE_CATEGORY_NOTIFICATIONS, model.PREFERENCE_NAME_EMAIL_INTERVAL) if result := <-pchan; result.Err != nil { // use the default batching interval if an error ocurrs while fetching user preferences interval, _ = strconv.ParseInt(model.PREFERENCE_EMAIL_INTERVAL_BATCHING_SECONDS, 10, 64) -- cgit v1.2.3-1-g7c22