MM-23896: Fix clearing of batched emails on user activity (#14340)

On user activity, we were clearing the job.pendingNotifications map.
But we had already created a copy of the notifications slice while
iterating the map. Therefore, if we pass the copied slice, it would
still have the old notifications which were originally deleted.

The unit tests would not catch this because it was testing the
job.pendingNotifications map and not actually checking if the email
handler was being called or not. We fix that now.

Co-authored-by: mattermod <mattermod@users.noreply.github.com>
Этот коммит содержится в:
Agniva De Sarker
2020-04-28 00:59:48 +05:30
коммит произвёл GitHub
родитель b29b70da09
Коммит f79b7567b1
2 изменённых файлов: 26 добавлений и 4 удалений

Просмотреть файл

@@ -182,13 +182,13 @@ func (job *EmailBatchingJob) checkPendingNotifications(now time.Time, handler fu
}
}
// send the email notification if it's been long enough
if now.Sub(time.Unix(batchStartTime/1000, 0)) > time.Duration(interval)*time.Second {
// send the email notification if there are notifications to send AND it's been long enough
if len(job.pendingNotifications[userId]) > 0 && now.Sub(time.Unix(batchStartTime/1000, 0)) > time.Duration(interval)*time.Second {
job.server.Go(func(userId string, notifications []*batchedNotification) func() {
return func() {
handler(userId, notifications)
}
}(userId, notifications))
}(userId, job.pendingNotifications[userId]))
delete(job.pendingNotifications, userId)
}
}

Просмотреть файл

@@ -4,6 +4,7 @@
package app
import (
"sync/atomic"
"testing"
"time"
@@ -115,7 +116,28 @@ func TestCheckPendingNotifications(t *testing.T) {
_, err = th.App.Srv().Store.Channel().UpdateMember(channelMember)
require.Nil(t, err)
job.checkPendingNotifications(time.Unix(10002, 0), func(string, []*batchedNotification) {})
// We reset the interval to something shorter
err = th.App.Srv().Store.Preference().Save(&model.Preferences{{
UserId: th.BasicUser.Id,
Category: model.PREFERENCE_CATEGORY_NOTIFICATIONS,
Name: model.PREFERENCE_NAME_EMAIL_INTERVAL,
Value: "10",
}})
require.Nil(t, err)
var wasCalled int32
job.checkPendingNotifications(time.Unix(10050, 0), func(string, []*batchedNotification) {
atomic.StoreInt32(&wasCalled, int32(1))
})
// A hack to check whether the handler was called.
// It's not straightforward to just wait for it using a channel because the test should
// NOT call the handler, and it will be called only if the test fails.
time.Sleep(1 * time.Second)
// We do a check outside the email handler, because otherwise, failing from
// inside the handler doesn't let the .Go() function exit cleanly, and it gets
// stuck during server shutdown, trying to wait for the goroutine to exit
require.Equal(t, atomic.LoadInt32(&wasCalled), int32(0), "email handler should not have been called")
require.Nil(t, job.pendingNotifications[th.BasicUser.Id])
require.Empty(t, job.pendingNotifications[th.BasicUser.Id], "should've remove queued post since user acted")