MM-57326: [Shared Channels] Message priority, acknowledgement and persistent notifications need to be synced (#30736)

Этот коммит содержится в:
catalintomai
2025-06-16 02:30:21 +02:00
коммит произвёл GitHub
родитель fa1c77d9b0
Коммит 85391de22a
27 изменённых файлов: 2667 добавлений и 101 удалений

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

@@ -8570,6 +8570,48 @@ func (s *RetryLayerPostStore) Update(rctx request.CTX, newPost *model.Post, oldP
}
func (s *RetryLayerPostAcknowledgementStore) BatchDelete(acknowledgements []*model.PostAcknowledgement) error {
tries := 0
for {
err := s.PostAcknowledgementStore.BatchDelete(acknowledgements)
if err == nil {
return nil
}
if !isRepeatableError(err) {
return err
}
tries++
if tries >= 3 {
err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures")
return err
}
timepkg.Sleep(100 * timepkg.Millisecond)
}
}
func (s *RetryLayerPostAcknowledgementStore) BatchSave(acknowledgements []*model.PostAcknowledgement) ([]*model.PostAcknowledgement, error) {
tries := 0
for {
result, err := s.PostAcknowledgementStore.BatchSave(acknowledgements)
if err == nil {
return result, nil
}
if !isRepeatableError(err) {
return result, err
}
tries++
if tries >= 3 {
err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures")
return result, err
}
timepkg.Sleep(100 * timepkg.Millisecond)
}
}
func (s *RetryLayerPostAcknowledgementStore) Delete(acknowledgement *model.PostAcknowledgement) error {
tries := 0
@@ -8633,6 +8675,27 @@ func (s *RetryLayerPostAcknowledgementStore) GetForPost(postID string) ([]*model
}
func (s *RetryLayerPostAcknowledgementStore) GetForPostSince(postID string, since int64, excludeRemoteID string, inclDeleted bool) ([]*model.PostAcknowledgement, error) {
tries := 0
for {
result, err := s.PostAcknowledgementStore.GetForPostSince(postID, since, excludeRemoteID, inclDeleted)
if err == nil {
return result, nil
}
if !isRepeatableError(err) {
return result, err
}
tries++
if tries >= 3 {
err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures")
return result, err
}
timepkg.Sleep(100 * timepkg.Millisecond)
}
}
func (s *RetryLayerPostAcknowledgementStore) GetForPosts(postIds []string) ([]*model.PostAcknowledgement, error) {
tries := 0
@@ -8654,11 +8717,32 @@ func (s *RetryLayerPostAcknowledgementStore) GetForPosts(postIds []string) ([]*m
}
func (s *RetryLayerPostAcknowledgementStore) Save(postID string, userID string, acknowledgedAt int64) (*model.PostAcknowledgement, error) {
func (s *RetryLayerPostAcknowledgementStore) GetSingle(userID string, postID string, remoteID string) (*model.PostAcknowledgement, error) {
tries := 0
for {
result, err := s.PostAcknowledgementStore.Save(postID, userID, acknowledgedAt)
result, err := s.PostAcknowledgementStore.GetSingle(userID, postID, remoteID)
if err == nil {
return result, nil
}
if !isRepeatableError(err) {
return result, err
}
tries++
if tries >= 3 {
err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures")
return result, err
}
timepkg.Sleep(100 * timepkg.Millisecond)
}
}
func (s *RetryLayerPostAcknowledgementStore) SaveWithModel(acknowledgement *model.PostAcknowledgement) (*model.PostAcknowledgement, error) {
tries := 0
for {
result, err := s.PostAcknowledgementStore.SaveWithModel(acknowledgement)
if err == nil {
return result, nil
}
@@ -8822,6 +8906,27 @@ func (s *RetryLayerPostPersistentNotificationStore) UpdateLastActivity(postIds [
}
func (s *RetryLayerPostPriorityStore) Delete(postID string) error {
tries := 0
for {
err := s.PostPriorityStore.Delete(postID)
if err == nil {
return nil
}
if !isRepeatableError(err) {
return err
}
tries++
if tries >= 3 {
err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures")
return err
}
timepkg.Sleep(100 * timepkg.Millisecond)
}
}
func (s *RetryLayerPostPriorityStore) GetForPost(postID string) (*model.PostPriority, error) {
tries := 0
@@ -8864,6 +8969,27 @@ func (s *RetryLayerPostPriorityStore) GetForPosts(ids []string) ([]*model.PostPr
}
func (s *RetryLayerPostPriorityStore) Save(priority *model.PostPriority) (*model.PostPriority, error) {
tries := 0
for {
result, err := s.PostPriorityStore.Save(priority)
if err == nil {
return result, nil
}
if !isRepeatableError(err) {
return result, err
}
tries++
if tries >= 3 {
err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures")
return result, err
}
timepkg.Sleep(100 * timepkg.Millisecond)
}
}
func (s *RetryLayerPreferenceStore) CleanupFlagsBatch(limit int64) (int64, error) {
tries := 0

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

@@ -23,7 +23,7 @@ func newSqlPostAcknowledgementStore(sqlStore *SqlStore) store.PostAcknowledgemen
func (s *SqlPostAcknowledgementStore) Get(postID, userID string) (*model.PostAcknowledgement, error) {
query := s.getQueryBuilder().
Select("PostId", "UserId", "AcknowledgedAt").
Select("PostId", "UserId", "ChannelId", "AcknowledgedAt", "RemoteId").
From("PostAcknowledgements").
Where(sq.And{
sq.Eq{"PostId": postID},
@@ -44,38 +44,20 @@ func (s *SqlPostAcknowledgementStore) Get(postID, userID string) (*model.PostAck
return &acknowledgement, nil
}
func (s *SqlPostAcknowledgementStore) Save(postID, userID string, acknowledgedAt int64) (*model.PostAcknowledgement, error) {
if acknowledgedAt == 0 {
acknowledgedAt = model.GetMillis()
}
acknowledgement := &model.PostAcknowledgement{
UserId: userID,
PostId: postID,
AcknowledgedAt: acknowledgedAt,
}
func (s *SqlPostAcknowledgementStore) SaveWithModel(acknowledgement *model.PostAcknowledgement) (*model.PostAcknowledgement, error) {
if err := acknowledgement.IsValid(); err != nil {
return nil, err
}
acknowledgement.PreSave()
transaction, err := s.GetMaster().Beginx()
if err != nil {
return nil, errors.Wrap(err, "begin_transaction")
}
defer finalizeTransactionX(transaction, &err)
query := s.getQueryBuilder().
Insert("PostAcknowledgements").
Columns("PostId", "UserId", "AcknowledgedAt").
Values(acknowledgement.PostId, acknowledgement.UserId, acknowledgement.AcknowledgedAt)
if s.DriverName() == model.DatabaseDriverMysql {
query = query.SuffixExpr(sq.Expr("ON DUPLICATE KEY UPDATE AcknowledgedAt = ?", acknowledgement.AcknowledgedAt))
} else {
query = query.SuffixExpr(sq.Expr("ON CONFLICT (postid, userid) DO UPDATE SET AcknowledgedAt = ?", acknowledgement.AcknowledgedAt))
}
query := s.buildUpsertQuery(acknowledgement)
_, err = transaction.ExecBuilder(query)
if err != nil {
return nil, err
@@ -131,7 +113,7 @@ func (s *SqlPostAcknowledgementStore) GetForPost(postID string) ([]*model.PostAc
var acknowledgements []*model.PostAcknowledgement
query := s.getQueryBuilder().
Select("PostId", "UserId", "AcknowledgedAt").
Select("PostId", "UserId", "ChannelId", "AcknowledgedAt", "RemoteId").
From("PostAcknowledgements").
Where(sq.And{
sq.NotEq{"AcknowledgedAt": 0},
@@ -157,7 +139,7 @@ func (s *SqlPostAcknowledgementStore) GetForPosts(postIds []string) ([]*model.Po
}
query := s.getQueryBuilder().
Select("PostId", "UserId", "AcknowledgedAt").
Select("PostId", "UserId", "ChannelId", "AcknowledgedAt", "RemoteId").
From("PostAcknowledgements").
Where(sq.And{
sq.Eq{"PostId": postIds[i:j]},
@@ -176,6 +158,89 @@ func (s *SqlPostAcknowledgementStore) GetForPosts(postIds []string) ([]*model.Po
return acknowledgements, nil
}
func (s *SqlPostAcknowledgementStore) GetForPostSince(postID string, since int64, excludeRemoteID string, inclDeleted bool) ([]*model.PostAcknowledgement, error) {
var acknowledgements []*model.PostAcknowledgement
query := s.getQueryBuilder().
Select("PostId", "UserId", "ChannelId", "AcknowledgedAt", "RemoteId").
From("PostAcknowledgements").
Where(sq.Eq{"PostId": postID})
if !inclDeleted {
query = query.Where(sq.NotEq{"AcknowledgedAt": 0})
}
if since > 0 {
query = query.Where(sq.Gt{"AcknowledgedAt": since})
}
if excludeRemoteID != "" {
query = query.Where(sq.NotEq{"COALESCE(RemoteId, '')": excludeRemoteID})
}
err := s.GetReplica().SelectBuilder(&acknowledgements, query)
if err != nil {
return nil, errors.Wrapf(err, "failed to get PostAcknowledgements for postID=%s since=%d", postID, since)
}
return acknowledgements, nil
}
func (s *SqlPostAcknowledgementStore) GetSingle(userID, postID, remoteID string) (*model.PostAcknowledgement, error) {
query := s.getQueryBuilder().
Select("PostId", "UserId", "ChannelId", "AcknowledgedAt", "RemoteId").
From("PostAcknowledgements").
Where(sq.And{
sq.Eq{"PostId": postID},
sq.Eq{"UserId": userID},
})
if remoteID != "" {
query = query.Where(sq.Eq{"RemoteId": remoteID})
} else {
query = query.Where(sq.Or{
sq.Eq{"RemoteId": ""},
sq.Eq{"RemoteId": nil},
})
}
var acknowledgement model.PostAcknowledgement
err := s.GetReplica().GetBuilder(&acknowledgement, query)
if err != nil {
if err == sql.ErrNoRows {
return nil, store.NewErrNotFound("PostAcknowledgement", postID)
}
return nil, err
}
return &acknowledgement, nil
}
// buildUpsertQuery creates an upsert query for a PostAcknowledgement
func (s *SqlPostAcknowledgementStore) buildUpsertQuery(acknowledgement *model.PostAcknowledgement) sq.InsertBuilder {
columnsToInsert := []string{"PostId", "UserId", "ChannelId", "AcknowledgedAt", "RemoteId"}
var remoteIdValue any
if acknowledgement.RemoteId != nil {
remoteIdValue = *acknowledgement.RemoteId
} else {
remoteIdValue = nil
}
valuesToInsert := []any{acknowledgement.PostId, acknowledgement.UserId, acknowledgement.ChannelId, acknowledgement.AcknowledgedAt, remoteIdValue}
query := s.getQueryBuilder().
Insert("PostAcknowledgements").
Columns(columnsToInsert...).
Values(valuesToInsert...)
if s.DriverName() == model.DatabaseDriverMysql {
query = query.SuffixExpr(sq.Expr("ON DUPLICATE KEY UPDATE AcknowledgedAt = ?", acknowledgement.AcknowledgedAt))
} else {
query = query.SuffixExpr(sq.Expr("ON CONFLICT (postid, userid) DO UPDATE SET AcknowledgedAt = ?", acknowledgement.AcknowledgedAt))
}
return query
}
func updatePost(transaction *sqlxTxWrapper, postId string) error {
_, err := transaction.Exec(
`UPDATE
@@ -190,3 +255,116 @@ func updatePost(transaction *sqlxTxWrapper, postId string) error {
return err
}
func (s *SqlPostAcknowledgementStore) BatchSave(acknowledgements []*model.PostAcknowledgement) ([]*model.PostAcknowledgement, error) {
if len(acknowledgements) == 0 {
return []*model.PostAcknowledgement{}, nil
}
// Populate missing ChannelId fields and validate all acknowledgements
for _, ack := range acknowledgements {
// If ChannelId is not set, look it up from the post
if ack.ChannelId == "" {
postQuery := s.getQueryBuilder().
Select("ChannelId").
From("Posts").
Where(sq.Eq{"Id": ack.PostId})
var channelId string
err := s.GetReplica().GetBuilder(&channelId, postQuery)
if err != nil {
return nil, errors.Wrapf(err, "failed to get channel id for post %s", ack.PostId)
}
ack.ChannelId = channelId
}
if err := ack.IsValid(); err != nil {
return nil, err
}
}
transaction, err := s.GetMaster().Beginx()
if err != nil {
return nil, errors.Wrap(err, "begin_transaction")
}
defer finalizeTransactionX(transaction, &err)
// Keep track of which posts need to be updated
postsToUpdate := make(map[string]bool)
// Insert all acknowledgements
for _, ack := range acknowledgements {
ack.PreSave()
query := s.buildUpsertQuery(ack)
_, err = transaction.ExecBuilder(query)
if err != nil {
return nil, err
}
postsToUpdate[ack.PostId] = true
}
// Update the UpdateAt timestamp for all affected posts
for postID := range postsToUpdate {
err = updatePost(transaction, postID)
if err != nil {
return nil, err
}
}
err = transaction.Commit()
if err != nil {
return nil, errors.Wrap(err, "commit_transaction")
}
return acknowledgements, nil
}
func (s *SqlPostAcknowledgementStore) BatchDelete(acknowledgements []*model.PostAcknowledgement) error {
if len(acknowledgements) == 0 {
return nil
}
transaction, err := s.GetMaster().Beginx()
if err != nil {
return errors.Wrap(err, "begin_transaction")
}
defer finalizeTransactionX(transaction, &err)
// Keep track of which posts need to be updated
postsToUpdate := make(map[string]bool)
// Set AcknowledgedAt to 0 for all acknowledgements
for _, ack := range acknowledgements {
query := s.getQueryBuilder().
Update("PostAcknowledgements").
Set("AcknowledgedAt", 0).
Where(sq.And{
sq.Eq{"PostId": ack.PostId},
sq.Eq{"UserId": ack.UserId},
})
_, err = transaction.ExecBuilder(query)
if err != nil {
return err
}
postsToUpdate[ack.PostId] = true
}
// Update the UpdateAt timestamp for all affected posts
for postID := range postsToUpdate {
err = updatePost(transaction, postID)
if err != nil {
return err
}
}
err = transaction.Commit()
if err != nil {
return errors.Wrap(err, "commit_transaction")
}
return nil
}

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

@@ -5,6 +5,7 @@ package sqlstore
import (
sq "github.com/mattermost/squirrel"
"github.com/pkg/errors"
"github.com/mattermost/mattermost/server/public/model"
"github.com/mattermost/mattermost/server/v8/channels/store"
@@ -22,7 +23,7 @@ func newSqlPostPriorityStore(sqlStore *SqlStore) store.PostPriorityStore {
func (s *SqlPostPriorityStore) GetForPost(postId string) (*model.PostPriority, error) {
query := s.getQueryBuilder().
Select("Priority", "RequestedAck", "PersistentNotifications").
Select("PostId", "ChannelId", "Priority", "RequestedAck", "PersistentNotifications").
From("PostsPriority").
Where(sq.Eq{"PostId": postId})
@@ -46,12 +47,12 @@ func (s *SqlPostPriorityStore) GetForPosts(postIds []string) ([]*model.PostPrior
}
query := s.getQueryBuilder().
Select("PostId", "Priority", "RequestedAck", "PersistentNotifications").
Select("PostId", "ChannelId", "Priority", "RequestedAck", "PersistentNotifications").
From("PostsPriority").
Where(sq.Eq{"PostId": postIds[i:j]})
var priorityBatch []*model.PostPriority
err := s.GetReplica().SelectBuilder(&priority, query)
err := s.GetReplica().SelectBuilder(&priorityBatch, query)
if err != nil {
return nil, err
@@ -62,3 +63,130 @@ func (s *SqlPostPriorityStore) GetForPosts(postIds []string) ([]*model.PostPrior
return priority, nil
}
func (s *SqlPostPriorityStore) Save(priority *model.PostPriority) (*model.PostPriority, error) {
tx, err := s.GetMaster().Beginx()
if err != nil {
return nil, errors.Wrap(err, "begin_transaction")
}
defer finalizeTransactionX(tx, &err)
// Delete existing priority
deleteQuery := s.getQueryBuilder().
Delete("PostsPriority").
Where(sq.Eq{"PostId": priority.PostId})
if _, err := tx.ExecBuilder(deleteQuery); err != nil {
return nil, errors.Wrap(err, "delete_existing_priority")
}
// Insert new priority
insertQuery := s.getQueryBuilder().
Insert("PostsPriority").
Columns("PostId", "ChannelId", "Priority", "RequestedAck", "PersistentNotifications").
Values(priority.PostId, priority.ChannelId, priority.Priority, priority.RequestedAck, priority.PersistentNotifications)
if _, err := tx.ExecBuilder(insertQuery); err != nil {
return nil, errors.Wrap(err, "insert_priority")
}
// Handle persistent notifications - always delete first, then insert if enabled
deletePersistentQuery := s.getQueryBuilder().
Delete("PersistentNotifications").
Where(sq.Eq{"PostId": priority.PostId})
if _, err := tx.ExecBuilder(deletePersistentQuery); err != nil {
return nil, errors.Wrap(err, "delete_persistent_notification")
}
if priority.PersistentNotifications != nil && *priority.PersistentNotifications {
insertPersistentQuery := s.getQueryBuilder().
Insert("PersistentNotifications").
Columns("PostId", "CreateAt", "LastSentAt", "DeleteAt", "SentCount").
Values(priority.PostId, model.GetMillis(), 0, 0, 0)
if _, err := tx.ExecBuilder(insertPersistentQuery); err != nil {
return nil, errors.Wrap(err, "insert_persistent_notification")
}
}
// Clear acknowledgements if not requested
if priority.RequestedAck == nil || !*priority.RequestedAck {
clearAckQuery := s.getQueryBuilder().
Update("PostAcknowledgements").
Set("AcknowledgedAt", 0).
Where(sq.Eq{"PostId": priority.PostId})
if _, err := tx.ExecBuilder(clearAckQuery); err != nil {
return nil, errors.Wrap(err, "clear_acknowledgements")
}
}
// Update the post's UpdateAt to trigger clients to refresh
updatePostQuery := s.getQueryBuilder().
Update("Posts").
Set("UpdateAt", model.GetMillis()).
Where(sq.Eq{"Id": priority.PostId})
if _, err := tx.ExecBuilder(updatePostQuery); err != nil {
return nil, errors.Wrap(err, "update_post")
}
if err := tx.Commit(); err != nil {
return nil, errors.Wrap(err, "commit_transaction")
}
return priority, nil
}
func (s *SqlPostPriorityStore) Delete(postId string) error {
tx, err := s.GetMaster().Beginx()
if err != nil {
return errors.Wrap(err, "begin_transaction")
}
defer finalizeTransactionX(tx, &err)
// Delete from PostsPriority
deletePriorityQuery := s.getQueryBuilder().
Delete("PostsPriority").
Where(sq.Eq{"PostId": postId})
if _, err := tx.ExecBuilder(deletePriorityQuery); err != nil {
return errors.Wrap(err, "delete_priority")
}
// Delete from PersistentNotifications
deletePersistentQuery := s.getQueryBuilder().
Delete("PersistentNotifications").
Where(sq.Eq{"PostId": postId})
if _, err := tx.ExecBuilder(deletePersistentQuery); err != nil {
return errors.Wrap(err, "delete_persistent_notification")
}
// Clear acknowledgements
clearAckQuery := s.getQueryBuilder().
Update("PostAcknowledgements").
Set("AcknowledgedAt", 0).
Where(sq.Eq{"PostId": postId})
if _, err := tx.ExecBuilder(clearAckQuery); err != nil {
return errors.Wrap(err, "clear_acknowledgements")
}
// Update post
updatePostQuery := s.getQueryBuilder().
Update("Posts").
Set("UpdateAt", model.GetMillis()).
Where(sq.Eq{"Id": postId})
if _, err := tx.ExecBuilder(updatePostQuery); err != nil {
return errors.Wrap(err, "update_post")
}
if err := tx.Commit(); err != nil {
return errors.Wrap(err, "commit_transaction")
}
return nil
}

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

@@ -1035,6 +1035,8 @@ type SharedChannelStore interface {
type PostPriorityStore interface {
GetForPost(postID string) (*model.PostPriority, error)
GetForPosts(ids []string) ([]*model.PostPriority, error)
Save(priority *model.PostPriority) (*model.PostPriority, error)
Delete(postID string) error
}
type DraftStore interface {
@@ -1053,8 +1055,12 @@ type PostAcknowledgementStore interface {
Get(postID, userID string) (*model.PostAcknowledgement, error)
GetForPost(postID string) ([]*model.PostAcknowledgement, error)
GetForPosts(postIds []string) ([]*model.PostAcknowledgement, error)
Save(postID, userID string, acknowledgedAt int64) (*model.PostAcknowledgement, error)
GetForPostSince(postID string, since int64, excludeRemoteID string, inclDeleted bool) ([]*model.PostAcknowledgement, error)
GetSingle(userID, postID, remoteID string) (*model.PostAcknowledgement, error)
SaveWithModel(acknowledgement *model.PostAcknowledgement) (*model.PostAcknowledgement, error)
BatchSave(acknowledgements []*model.PostAcknowledgement) ([]*model.PostAcknowledgement, error)
Delete(acknowledgement *model.PostAcknowledgement) error
BatchDelete(acknowledgements []*model.PostAcknowledgement) error
}
type PostPersistentNotificationStore interface {

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

@@ -14,6 +14,54 @@ type PostAcknowledgementStore struct {
mock.Mock
}
// BatchDelete provides a mock function with given fields: acknowledgements
func (_m *PostAcknowledgementStore) BatchDelete(acknowledgements []*model.PostAcknowledgement) error {
ret := _m.Called(acknowledgements)
if len(ret) == 0 {
panic("no return value specified for BatchDelete")
}
var r0 error
if rf, ok := ret.Get(0).(func([]*model.PostAcknowledgement) error); ok {
r0 = rf(acknowledgements)
} else {
r0 = ret.Error(0)
}
return r0
}
// BatchSave provides a mock function with given fields: acknowledgements
func (_m *PostAcknowledgementStore) BatchSave(acknowledgements []*model.PostAcknowledgement) ([]*model.PostAcknowledgement, error) {
ret := _m.Called(acknowledgements)
if len(ret) == 0 {
panic("no return value specified for BatchSave")
}
var r0 []*model.PostAcknowledgement
var r1 error
if rf, ok := ret.Get(0).(func([]*model.PostAcknowledgement) ([]*model.PostAcknowledgement, error)); ok {
return rf(acknowledgements)
}
if rf, ok := ret.Get(0).(func([]*model.PostAcknowledgement) []*model.PostAcknowledgement); ok {
r0 = rf(acknowledgements)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*model.PostAcknowledgement)
}
}
if rf, ok := ret.Get(1).(func([]*model.PostAcknowledgement) error); ok {
r1 = rf(acknowledgements)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Delete provides a mock function with given fields: acknowledgement
func (_m *PostAcknowledgementStore) Delete(acknowledgement *model.PostAcknowledgement) error {
ret := _m.Called(acknowledgement)
@@ -92,6 +140,36 @@ func (_m *PostAcknowledgementStore) GetForPost(postID string) ([]*model.PostAckn
return r0, r1
}
// GetForPostSince provides a mock function with given fields: postID, since, excludeRemoteID, inclDeleted
func (_m *PostAcknowledgementStore) GetForPostSince(postID string, since int64, excludeRemoteID string, inclDeleted bool) ([]*model.PostAcknowledgement, error) {
ret := _m.Called(postID, since, excludeRemoteID, inclDeleted)
if len(ret) == 0 {
panic("no return value specified for GetForPostSince")
}
var r0 []*model.PostAcknowledgement
var r1 error
if rf, ok := ret.Get(0).(func(string, int64, string, bool) ([]*model.PostAcknowledgement, error)); ok {
return rf(postID, since, excludeRemoteID, inclDeleted)
}
if rf, ok := ret.Get(0).(func(string, int64, string, bool) []*model.PostAcknowledgement); ok {
r0 = rf(postID, since, excludeRemoteID, inclDeleted)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*model.PostAcknowledgement)
}
}
if rf, ok := ret.Get(1).(func(string, int64, string, bool) error); ok {
r1 = rf(postID, since, excludeRemoteID, inclDeleted)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetForPosts provides a mock function with given fields: postIds
func (_m *PostAcknowledgementStore) GetForPosts(postIds []string) ([]*model.PostAcknowledgement, error) {
ret := _m.Called(postIds)
@@ -122,29 +200,59 @@ func (_m *PostAcknowledgementStore) GetForPosts(postIds []string) ([]*model.Post
return r0, r1
}
// Save provides a mock function with given fields: postID, userID, acknowledgedAt
func (_m *PostAcknowledgementStore) Save(postID string, userID string, acknowledgedAt int64) (*model.PostAcknowledgement, error) {
ret := _m.Called(postID, userID, acknowledgedAt)
// GetSingle provides a mock function with given fields: userID, postID, remoteID
func (_m *PostAcknowledgementStore) GetSingle(userID string, postID string, remoteID string) (*model.PostAcknowledgement, error) {
ret := _m.Called(userID, postID, remoteID)
if len(ret) == 0 {
panic("no return value specified for Save")
panic("no return value specified for GetSingle")
}
var r0 *model.PostAcknowledgement
var r1 error
if rf, ok := ret.Get(0).(func(string, string, int64) (*model.PostAcknowledgement, error)); ok {
return rf(postID, userID, acknowledgedAt)
if rf, ok := ret.Get(0).(func(string, string, string) (*model.PostAcknowledgement, error)); ok {
return rf(userID, postID, remoteID)
}
if rf, ok := ret.Get(0).(func(string, string, int64) *model.PostAcknowledgement); ok {
r0 = rf(postID, userID, acknowledgedAt)
if rf, ok := ret.Get(0).(func(string, string, string) *model.PostAcknowledgement); ok {
r0 = rf(userID, postID, remoteID)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.PostAcknowledgement)
}
}
if rf, ok := ret.Get(1).(func(string, string, int64) error); ok {
r1 = rf(postID, userID, acknowledgedAt)
if rf, ok := ret.Get(1).(func(string, string, string) error); ok {
r1 = rf(userID, postID, remoteID)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// SaveWithModel provides a mock function with given fields: acknowledgement
func (_m *PostAcknowledgementStore) SaveWithModel(acknowledgement *model.PostAcknowledgement) (*model.PostAcknowledgement, error) {
ret := _m.Called(acknowledgement)
if len(ret) == 0 {
panic("no return value specified for SaveWithModel")
}
var r0 *model.PostAcknowledgement
var r1 error
if rf, ok := ret.Get(0).(func(*model.PostAcknowledgement) (*model.PostAcknowledgement, error)); ok {
return rf(acknowledgement)
}
if rf, ok := ret.Get(0).(func(*model.PostAcknowledgement) *model.PostAcknowledgement); ok {
r0 = rf(acknowledgement)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.PostAcknowledgement)
}
}
if rf, ok := ret.Get(1).(func(*model.PostAcknowledgement) error); ok {
r1 = rf(acknowledgement)
} else {
r1 = ret.Error(1)
}

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

@@ -14,6 +14,24 @@ type PostPriorityStore struct {
mock.Mock
}
// Delete provides a mock function with given fields: postID
func (_m *PostPriorityStore) Delete(postID string) error {
ret := _m.Called(postID)
if len(ret) == 0 {
panic("no return value specified for Delete")
}
var r0 error
if rf, ok := ret.Get(0).(func(string) error); ok {
r0 = rf(postID)
} else {
r0 = ret.Error(0)
}
return r0
}
// GetForPost provides a mock function with given fields: postID
func (_m *PostPriorityStore) GetForPost(postID string) (*model.PostPriority, error) {
ret := _m.Called(postID)
@@ -74,6 +92,36 @@ func (_m *PostPriorityStore) GetForPosts(ids []string) ([]*model.PostPriority, e
return r0, r1
}
// Save provides a mock function with given fields: priority
func (_m *PostPriorityStore) Save(priority *model.PostPriority) (*model.PostPriority, error) {
ret := _m.Called(priority)
if len(ret) == 0 {
panic("no return value specified for Save")
}
var r0 *model.PostPriority
var r1 error
if rf, ok := ret.Get(0).(func(*model.PostPriority) (*model.PostPriority, error)); ok {
return rf(priority)
}
if rf, ok := ret.Get(0).(func(*model.PostPriority) *model.PostPriority); ok {
r0 = rf(priority)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.PostPriority)
}
}
if rf, ok := ret.Get(1).(func(*model.PostPriority) error); ok {
r1 = rf(priority)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// NewPostPriorityStore creates a new instance of PostPriorityStore. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewPostPriorityStore(t interface {

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

@@ -17,6 +17,8 @@ func TestPostAcknowledgementsStore(t *testing.T, rctx request.CTX, ss store.Stor
t.Run("Save", func(t *testing.T) { testPostAcknowledgementsStoreSave(t, rctx, ss) })
t.Run("GetForPost", func(t *testing.T) { testPostAcknowledgementsStoreGetForPost(t, rctx, ss) })
t.Run("GetForPosts", func(t *testing.T) { testPostAcknowledgementsStoreGetForPosts(t, rctx, ss) })
t.Run("BatchSave", func(t *testing.T) { testPostAcknowledgementsStoreBatchSave(t, rctx, ss) })
t.Run("BatchDelete", func(t *testing.T) { testPostAcknowledgementsStoreBatchDelete(t, rctx, ss) })
}
func testPostAcknowledgementsStoreSave(t *testing.T, rctx request.CTX, ss store.Store) {
@@ -37,13 +39,16 @@ func testPostAcknowledgementsStoreSave(t *testing.T, rctx request.CTX, ss store.
require.NoError(t, err)
t.Run("consecutive saves should just update the acknowledged at", func(t *testing.T) {
_, err := ss.PostAcknowledgement().Save(post.Id, userID1, 0)
ack := &model.PostAcknowledgement{PostId: post.Id, UserId: userID1, AcknowledgedAt: 0, ChannelId: post.ChannelId}
_, err := ss.PostAcknowledgement().SaveWithModel(ack)
require.NoError(t, err)
_, err = ss.PostAcknowledgement().Save(post.Id, userID1, 0)
ack = &model.PostAcknowledgement{PostId: post.Id, UserId: userID1, AcknowledgedAt: 0, ChannelId: post.ChannelId}
_, err = ss.PostAcknowledgement().SaveWithModel(ack)
require.NoError(t, err)
ack1, err := ss.PostAcknowledgement().Save(post.Id, userID1, 0)
ack1 := &model.PostAcknowledgement{PostId: post.Id, UserId: userID1, AcknowledgedAt: 0, ChannelId: post.ChannelId}
ack1, err = ss.PostAcknowledgement().SaveWithModel(ack1)
require.NoError(t, err)
acknowledgements, err := ss.PostAcknowledgement().GetForPost(post.Id)
@@ -53,7 +58,8 @@ func testPostAcknowledgementsStoreSave(t *testing.T, rctx request.CTX, ss store.
t.Run("saving should update the update at of the post", func(t *testing.T) {
oldUpdateAt := post.UpdateAt
_, err := ss.PostAcknowledgement().Save(post.Id, userID1, 0)
ack := &model.PostAcknowledgement{PostId: post.Id, UserId: userID1, AcknowledgedAt: 0, ChannelId: post.ChannelId}
_, err := ss.PostAcknowledgement().SaveWithModel(ack)
require.NoError(t, err)
post, err = ss.Post().GetSingle(rctx, post.Id, false)
@@ -82,11 +88,14 @@ func testPostAcknowledgementsStoreGetForPost(t *testing.T, rctx request.CTX, ss
require.NoError(t, err)
t.Run("get acknowledgements for post", func(t *testing.T) {
ack1, err := ss.PostAcknowledgement().Save(p1.Id, userID1, 0)
ack1 := &model.PostAcknowledgement{PostId: p1.Id, UserId: userID1, AcknowledgedAt: 0, ChannelId: p1.ChannelId}
ack1, err := ss.PostAcknowledgement().SaveWithModel(ack1)
require.NoError(t, err)
ack2, err := ss.PostAcknowledgement().Save(p1.Id, userID2, 0)
ack2 := &model.PostAcknowledgement{PostId: p1.Id, UserId: userID2, AcknowledgedAt: 0, ChannelId: p1.ChannelId}
ack2, err = ss.PostAcknowledgement().SaveWithModel(ack2)
require.NoError(t, err)
ack3, err := ss.PostAcknowledgement().Save(p1.Id, userID3, 0)
ack3 := &model.PostAcknowledgement{PostId: p1.Id, UserId: userID3, AcknowledgedAt: 0, ChannelId: p1.ChannelId}
ack3, err = ss.PostAcknowledgement().SaveWithModel(ack3)
require.NoError(t, err)
acknowledgements, err := ss.PostAcknowledgement().GetForPost(p1.Id)
@@ -145,13 +154,17 @@ func testPostAcknowledgementsStoreGetForPosts(t *testing.T, rctx request.CTX, ss
require.Equal(t, -1, errIdx)
t.Run("get acknowledgements for post", func(t *testing.T) {
ack1, err := ss.PostAcknowledgement().Save(p1.Id, userID1, 0)
ack1 := &model.PostAcknowledgement{PostId: p1.Id, UserId: userID1, AcknowledgedAt: 0, ChannelId: p1.ChannelId}
ack1, err := ss.PostAcknowledgement().SaveWithModel(ack1)
require.NoError(t, err)
ack2, err := ss.PostAcknowledgement().Save(p1.Id, userID2, 0)
ack2 := &model.PostAcknowledgement{PostId: p1.Id, UserId: userID2, AcknowledgedAt: 0, ChannelId: p1.ChannelId}
ack2, err = ss.PostAcknowledgement().SaveWithModel(ack2)
require.NoError(t, err)
ack3, err := ss.PostAcknowledgement().Save(p2.Id, userID2, 0)
ack3 := &model.PostAcknowledgement{PostId: p2.Id, UserId: userID2, AcknowledgedAt: 0, ChannelId: p2.ChannelId}
ack3, err = ss.PostAcknowledgement().SaveWithModel(ack3)
require.NoError(t, err)
ack4, err := ss.PostAcknowledgement().Save(p2.Id, userID3, 0)
ack4 := &model.PostAcknowledgement{PostId: p2.Id, UserId: userID3, AcknowledgedAt: 0, ChannelId: p2.ChannelId}
ack4, err = ss.PostAcknowledgement().SaveWithModel(ack4)
require.NoError(t, err)
acknowledgements, err := ss.PostAcknowledgement().GetForPosts([]string{p1.Id})
@@ -191,3 +204,218 @@ func testPostAcknowledgementsStoreGetForPosts(t *testing.T, rctx request.CTX, ss
require.Empty(t, acknowledgements)
})
}
func testPostAcknowledgementsStoreBatchSave(t *testing.T, rctx request.CTX, ss store.Store) {
userID1 := model.NewId()
userID2 := model.NewId()
userID3 := model.NewId()
p1 := model.Post{}
p1.ChannelId = model.NewId()
p1.UserId = model.NewId()
p1.Message = NewTestID()
post, err := ss.Post().Save(rctx, &p1)
require.NoError(t, err)
t.Run("batch save acknowledgements for a post", func(t *testing.T) {
// Create a batch of acknowledgements
acks := []*model.PostAcknowledgement{
{
PostId: post.Id,
UserId: userID1,
AcknowledgedAt: model.GetMillis(),
},
{
PostId: post.Id,
UserId: userID2,
AcknowledgedAt: model.GetMillis(),
},
{
PostId: post.Id,
UserId: userID3,
AcknowledgedAt: model.GetMillis(),
},
}
// Save the batch
savedAcks, err := ss.PostAcknowledgement().BatchSave(acks)
require.NoError(t, err)
require.Len(t, savedAcks, 3)
// Verify all were saved correctly
retrievedAcks, err := ss.PostAcknowledgement().GetForPost(post.Id)
require.NoError(t, err)
require.Len(t, retrievedAcks, 3)
// Verify all users are in the saved acknowledgements
userIDMap := make(map[string]bool)
for _, ack := range retrievedAcks {
userIDMap[ack.UserId] = true
require.Equal(t, post.Id, ack.PostId)
require.Greater(t, ack.AcknowledgedAt, int64(0))
}
require.True(t, userIDMap[userID1])
require.True(t, userIDMap[userID2])
require.True(t, userIDMap[userID3])
})
t.Run("batch save empty list of acknowledgements", func(t *testing.T) {
// Create an empty batch of acknowledgements
acks := []*model.PostAcknowledgement{}
// Save the empty batch
savedAcks, err := ss.PostAcknowledgement().BatchSave(acks)
require.NoError(t, err)
require.Empty(t, savedAcks)
})
t.Run("batch save should update existing acknowledgements", func(t *testing.T) {
// First, delete all existing acknowledgements
acks, err := ss.PostAcknowledgement().GetForPost(post.Id)
require.NoError(t, err)
for _, ack := range acks {
err = ss.PostAcknowledgement().Delete(ack)
require.NoError(t, err)
}
// Create initial acknowledgement
ack := &model.PostAcknowledgement{PostId: post.Id, UserId: userID1, AcknowledgedAt: model.GetMillis(), ChannelId: post.ChannelId}
ack, err = ss.PostAcknowledgement().SaveWithModel(ack)
require.NoError(t, err)
initialAckTime := ack.AcknowledgedAt
// Create a batch with updated timestamp
newTimestamp := model.GetMillis() + 1000
updatedAcks := []*model.PostAcknowledgement{
{
PostId: post.Id,
UserId: userID1,
AcknowledgedAt: newTimestamp,
},
}
// Batch save should update the existing acknowledgement
savedAcks, err := ss.PostAcknowledgement().BatchSave(updatedAcks)
require.NoError(t, err)
require.Len(t, savedAcks, 1)
require.Equal(t, newTimestamp, savedAcks[0].AcknowledgedAt)
require.Greater(t, savedAcks[0].AcknowledgedAt, initialAckTime)
// Verify the acknowledgement was updated
retrievedAcks, err := ss.PostAcknowledgement().GetForPost(post.Id)
require.NoError(t, err)
require.Len(t, retrievedAcks, 1)
require.Equal(t, newTimestamp, retrievedAcks[0].AcknowledgedAt)
})
t.Run("batch save should update post's update_at", func(t *testing.T) {
// First, check the current post update timestamp
currentPost, err := ss.Post().GetSingle(rctx, post.Id, false)
require.NoError(t, err)
oldUpdateAt := currentPost.UpdateAt
// Create a batch of new acknowledgements
acks := []*model.PostAcknowledgement{
{
PostId: post.Id,
UserId: model.NewId(),
AcknowledgedAt: model.GetMillis(),
},
}
// Save the batch
_, err = ss.PostAcknowledgement().BatchSave(acks)
require.NoError(t, err)
// Verify post's update_at was updated
updatedPost, err := ss.Post().GetSingle(rctx, post.Id, false)
require.NoError(t, err)
require.Greater(t, updatedPost.UpdateAt, oldUpdateAt)
})
}
func testPostAcknowledgementsStoreBatchDelete(t *testing.T, rctx request.CTX, ss store.Store) {
userID1 := model.NewId()
userID2 := model.NewId()
userID3 := model.NewId()
p1 := model.Post{}
p1.ChannelId = model.NewId()
p1.UserId = model.NewId()
p1.Message = NewTestID()
post, err := ss.Post().Save(rctx, &p1)
require.NoError(t, err)
t.Run("batch delete all acknowledgements for a post", func(t *testing.T) {
// Create multiple acknowledgements
ack1 := &model.PostAcknowledgement{PostId: post.Id, UserId: userID1, AcknowledgedAt: 0, ChannelId: post.ChannelId}
ack1, err = ss.PostAcknowledgement().SaveWithModel(ack1)
require.NoError(t, err)
ack2 := &model.PostAcknowledgement{PostId: post.Id, UserId: userID2, AcknowledgedAt: 0, ChannelId: post.ChannelId}
ack2, err = ss.PostAcknowledgement().SaveWithModel(ack2)
require.NoError(t, err)
ack3 := &model.PostAcknowledgement{PostId: post.Id, UserId: userID3, AcknowledgedAt: 0, ChannelId: post.ChannelId}
ack3, err = ss.PostAcknowledgement().SaveWithModel(ack3)
require.NoError(t, err)
// Verify acknowledgements were created
acks, pErr := ss.PostAcknowledgement().GetForPost(post.Id)
require.NoError(t, pErr)
require.Len(t, acks, 3)
// Delete all acknowledgements in batch
err = ss.PostAcknowledgement().BatchDelete([]*model.PostAcknowledgement{ack1, ack2, ack3})
require.NoError(t, err)
// Verify all acknowledgements were deleted
acks, err = ss.PostAcknowledgement().GetForPost(post.Id)
require.NoError(t, err)
require.Empty(t, acks)
})
t.Run("batch delete should update post's update_at", func(t *testing.T) {
// Create acknowledgements
ack1 := &model.PostAcknowledgement{PostId: post.Id, UserId: userID1, AcknowledgedAt: 0, ChannelId: post.ChannelId}
ack1, err = ss.PostAcknowledgement().SaveWithModel(ack1)
require.NoError(t, err)
ack2 := &model.PostAcknowledgement{PostId: post.Id, UserId: userID2, AcknowledgedAt: 0, ChannelId: post.ChannelId}
ack2, err = ss.PostAcknowledgement().SaveWithModel(ack2)
require.NoError(t, err)
// Get current post update timestamp
currentPost, err := ss.Post().GetSingle(rctx, post.Id, false)
require.NoError(t, err)
oldUpdateAt := currentPost.UpdateAt
// Delete acknowledgements in batch
err = ss.PostAcknowledgement().BatchDelete([]*model.PostAcknowledgement{ack1, ack2})
require.NoError(t, err)
// Verify post's update_at was updated
updatedPost, err := ss.Post().GetSingle(rctx, post.Id, false)
require.NoError(t, err)
require.Greater(t, updatedPost.UpdateAt, oldUpdateAt)
})
t.Run("batch delete with empty list should not error", func(t *testing.T) {
// Delete with empty list should not error
err := ss.PostAcknowledgement().BatchDelete([]*model.PostAcknowledgement{})
require.NoError(t, err)
})
t.Run("batch delete with non-existent acknowledgements should not error", func(t *testing.T) {
// Create non-existent acknowledgements
nonExistentAck := &model.PostAcknowledgement{
PostId: model.NewId(),
UserId: model.NewId(),
AcknowledgedAt: model.GetMillis(),
}
// Delete non-existent acknowledgement should not error
err := ss.PostAcknowledgement().BatchDelete([]*model.PostAcknowledgement{nonExistentAck})
require.NoError(t, err)
})
}

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

@@ -6821,6 +6821,38 @@ func (s *TimerLayerPostStore) Update(rctx request.CTX, newPost *model.Post, oldP
return result, err
}
func (s *TimerLayerPostAcknowledgementStore) BatchDelete(acknowledgements []*model.PostAcknowledgement) error {
start := time.Now()
err := s.PostAcknowledgementStore.BatchDelete(acknowledgements)
elapsed := float64(time.Since(start)) / float64(time.Second)
if s.Root.Metrics != nil {
success := "false"
if err == nil {
success = "true"
}
s.Root.Metrics.ObserveStoreMethodDuration("PostAcknowledgementStore.BatchDelete", success, elapsed)
}
return err
}
func (s *TimerLayerPostAcknowledgementStore) BatchSave(acknowledgements []*model.PostAcknowledgement) ([]*model.PostAcknowledgement, error) {
start := time.Now()
result, err := s.PostAcknowledgementStore.BatchSave(acknowledgements)
elapsed := float64(time.Since(start)) / float64(time.Second)
if s.Root.Metrics != nil {
success := "false"
if err == nil {
success = "true"
}
s.Root.Metrics.ObserveStoreMethodDuration("PostAcknowledgementStore.BatchSave", success, elapsed)
}
return result, err
}
func (s *TimerLayerPostAcknowledgementStore) Delete(acknowledgement *model.PostAcknowledgement) error {
start := time.Now()
@@ -6869,6 +6901,22 @@ func (s *TimerLayerPostAcknowledgementStore) GetForPost(postID string) ([]*model
return result, err
}
func (s *TimerLayerPostAcknowledgementStore) GetForPostSince(postID string, since int64, excludeRemoteID string, inclDeleted bool) ([]*model.PostAcknowledgement, error) {
start := time.Now()
result, err := s.PostAcknowledgementStore.GetForPostSince(postID, since, excludeRemoteID, inclDeleted)
elapsed := float64(time.Since(start)) / float64(time.Second)
if s.Root.Metrics != nil {
success := "false"
if err == nil {
success = "true"
}
s.Root.Metrics.ObserveStoreMethodDuration("PostAcknowledgementStore.GetForPostSince", success, elapsed)
}
return result, err
}
func (s *TimerLayerPostAcknowledgementStore) GetForPosts(postIds []string) ([]*model.PostAcknowledgement, error) {
start := time.Now()
@@ -6885,10 +6933,10 @@ func (s *TimerLayerPostAcknowledgementStore) GetForPosts(postIds []string) ([]*m
return result, err
}
func (s *TimerLayerPostAcknowledgementStore) Save(postID string, userID string, acknowledgedAt int64) (*model.PostAcknowledgement, error) {
func (s *TimerLayerPostAcknowledgementStore) GetSingle(userID string, postID string, remoteID string) (*model.PostAcknowledgement, error) {
start := time.Now()
result, err := s.PostAcknowledgementStore.Save(postID, userID, acknowledgedAt)
result, err := s.PostAcknowledgementStore.GetSingle(userID, postID, remoteID)
elapsed := float64(time.Since(start)) / float64(time.Second)
if s.Root.Metrics != nil {
@@ -6896,7 +6944,23 @@ func (s *TimerLayerPostAcknowledgementStore) Save(postID string, userID string,
if err == nil {
success = "true"
}
s.Root.Metrics.ObserveStoreMethodDuration("PostAcknowledgementStore.Save", success, elapsed)
s.Root.Metrics.ObserveStoreMethodDuration("PostAcknowledgementStore.GetSingle", success, elapsed)
}
return result, err
}
func (s *TimerLayerPostAcknowledgementStore) SaveWithModel(acknowledgement *model.PostAcknowledgement) (*model.PostAcknowledgement, error) {
start := time.Now()
result, err := s.PostAcknowledgementStore.SaveWithModel(acknowledgement)
elapsed := float64(time.Since(start)) / float64(time.Second)
if s.Root.Metrics != nil {
success := "false"
if err == nil {
success = "true"
}
s.Root.Metrics.ObserveStoreMethodDuration("PostAcknowledgementStore.SaveWithModel", success, elapsed)
}
return result, err
}
@@ -7013,6 +7077,22 @@ func (s *TimerLayerPostPersistentNotificationStore) UpdateLastActivity(postIds [
return err
}
func (s *TimerLayerPostPriorityStore) Delete(postID string) error {
start := time.Now()
err := s.PostPriorityStore.Delete(postID)
elapsed := float64(time.Since(start)) / float64(time.Second)
if s.Root.Metrics != nil {
success := "false"
if err == nil {
success = "true"
}
s.Root.Metrics.ObserveStoreMethodDuration("PostPriorityStore.Delete", success, elapsed)
}
return err
}
func (s *TimerLayerPostPriorityStore) GetForPost(postID string) (*model.PostPriority, error) {
start := time.Now()
@@ -7045,6 +7125,22 @@ func (s *TimerLayerPostPriorityStore) GetForPosts(ids []string) ([]*model.PostPr
return result, err
}
func (s *TimerLayerPostPriorityStore) Save(priority *model.PostPriority) (*model.PostPriority, error) {
start := time.Now()
result, err := s.PostPriorityStore.Save(priority)
elapsed := float64(time.Since(start)) / float64(time.Second)
if s.Root.Metrics != nil {
success := "false"
if err == nil {
success = "true"
}
s.Root.Metrics.ObserveStoreMethodDuration("PostPriorityStore.Save", success, elapsed)
}
return result, err
}
func (s *TimerLayerPreferenceStore) CleanupFlagsBatch(limit int64) (int64, error) {
start := time.Now()