MM-55295: Improve DB performance of some (Take 2) (#25865)

* Revert "Revert "MM-55295: Improve DB performance of some APIs (#25318)" (#25852)"

This reverts commit 077221a940.

* Fix the issue

```release-note
NONE
```

* lint fix

```release-note
NONE
```
Этот коммит содержится в:
Agniva De Sarker
2024-01-11 10:18:36 +05:30
коммит произвёл GitHub
родитель 43cca04f04
Коммит 1d879ed0f4
12 изменённых файлов: 232 добавлений и 56 удалений

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

@@ -83,7 +83,6 @@ func (a *App) SessionHasPermissionToChannel(c request.CTX, session model.Session
} }
ids, err := a.Srv().Store().Channel().GetAllChannelMembersForUser(session.UserId, true, true) ids, err := a.Srv().Store().Channel().GetAllChannelMembersForUser(session.UserId, true, true)
var channelRoles []string var channelRoles []string
if err == nil { if err == nil {
if roles, ok := ids[channelID]; ok { if roles, ok := ids[channelID]; ok {
@@ -260,17 +259,22 @@ func (a *App) HasPermissionToChannel(c request.CTX, askingUserId string, channel
return false return false
} }
channelMember, err := a.GetChannelMember(c, channelID, askingUserId) // We call GetAllChannelMembersForUser instead of just getting
// a single member from the DB, because it's cache backed
// and this is a very frequent call.
ids, err := a.Srv().Store().Channel().GetAllChannelMembersForUser(askingUserId, true, true)
var channelRoles []string
if err == nil { if err == nil {
roles := channelMember.GetRoles() if roles, ok := ids[channelID]; ok {
if a.RolesGrantPermission(roles, permission.Id) { channelRoles = strings.Fields(roles)
return true if a.RolesGrantPermission(channelRoles, permission.Id) {
return true
}
} }
} }
var channel *model.Channel channel, appErr := a.GetChannel(c, channelID)
channel, err = a.GetChannel(c, channelID) if appErr == nil {
if err == nil {
return a.HasPermissionToTeam(c, askingUserId, channel.TeamId, permission) return a.HasPermissionToTeam(c, askingUserId, channel.TeamId, permission)
} }

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

@@ -2063,6 +2063,21 @@ func (s *Server) getChannelMember(c request.CTX, channelID string, userID string
return channelMember, nil return channelMember, nil
} }
func (s *Server) getChannelMemberLastViewedAt(c request.CTX, channelID string, userID string) (int64, *model.AppError) {
lastViewedAt, err := s.Store().Channel().GetMemberLastViewedAt(c.Context(), channelID, userID)
if err != nil {
var nfErr *store.ErrNotFound
switch {
case errors.As(err, &nfErr):
return 0, model.NewAppError("getChannelMemberLastViewedAt", MissingChannelMemberError, nil, "", http.StatusNotFound).Wrap(err)
default:
return 0, model.NewAppError("getChannelMemberLastViewedAt", "app.channel.get_member.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
}
return lastViewedAt, nil
}
func (a *App) GetChannelMembersPage(c request.CTX, channelID string, page, perPage int) (model.ChannelMembers, *model.AppError) { func (a *App) GetChannelMembersPage(c request.CTX, channelID string, page, perPage int) (model.ChannelMembers, *model.AppError) {
channelMembers, err := a.Srv().Store().Channel().GetMembers(channelID, page*perPage, perPage) channelMembers, err := a.Srv().Store().Channel().GetMembers(channelID, page*perPage, perPage)
if err != nil { if err != nil {

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

@@ -810,9 +810,9 @@ func (a *App) publishWebsocketEventForPermalinkPost(c request.CTX, post *model.P
return false, err return false, err
} }
channelMembers, err := a.GetChannelMembersPage(c, post.ChannelId, 0, 10000000) userIDs, nErr := a.Srv().Store().Channel().GetAllChannelMemberIdsByChannelId(post.ChannelId)
if err != nil { if nErr != nil {
return false, err return false, model.NewAppError("publishWebsocketEventForPermalinkPost", "app.channel.get_members.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
} }
permalinkPreviewedChannel, err := a.GetChannel(c, previewedPost.ChannelId) permalinkPreviewedChannel, err := a.GetChannel(c, previewedPost.ChannelId)
@@ -827,19 +827,19 @@ func (a *App) publishWebsocketEventForPermalinkPost(c request.CTX, post *model.P
originalEmbeds := post.Metadata.Embeds originalEmbeds := post.Metadata.Embeds
originalProps := post.GetProps() originalProps := post.GetProps()
permalinkPreviewedPost := post.GetPreviewPost() permalinkPreviewedPost := post.GetPreviewPost()
for _, cm := range channelMembers { for _, userID := range userIDs {
if permalinkPreviewedPost != nil { if permalinkPreviewedPost != nil {
post.Metadata.Embeds = originalEmbeds post.Metadata.Embeds = originalEmbeds
post.SetProps(originalProps) post.SetProps(originalProps)
} }
postForUser := a.sanitizePostMetadataForUserAndChannel(c, post, permalinkPreviewedPost, permalinkPreviewedChannel, cm.UserId) postForUser := a.sanitizePostMetadataForUserAndChannel(c, post, permalinkPreviewedPost, permalinkPreviewedChannel, userID)
// Using DeepCopy here to avoid a race condition // Using DeepCopy here to avoid a race condition
// between publishing the event and setting the "post" data value below. // between publishing the event and setting the "post" data value below.
messageCopy := message.DeepCopy() messageCopy := message.DeepCopy()
broadcastCopy := messageCopy.GetBroadcast() broadcastCopy := messageCopy.GetBroadcast()
broadcastCopy.UserId = cm.UserId broadcastCopy.UserId = userID
messageCopy.SetBroadcast(broadcastCopy) messageCopy.SetBroadcast(broadcastCopy)
postJSON, jsonErr := postForUser.ToJSON() postJSON, jsonErr := postForUser.ToJSON()
@@ -1292,15 +1292,15 @@ func (a *App) AddCursorIdsForPostList(originalList *model.PostList, afterPost, b
originalList.PrevPostId = prevPostId originalList.PrevPostId = prevPostId
} }
func (a *App) GetPostsForChannelAroundLastUnread(c request.CTX, channelID, userID string, limitBefore, limitAfter int, skipFetchThreads bool, collapsedThreads, collapsedThreadsExtended bool) (*model.PostList, *model.AppError) { func (a *App) GetPostsForChannelAroundLastUnread(c request.CTX, channelID, userID string, limitBefore, limitAfter int, skipFetchThreads bool, collapsedThreads, collapsedThreadsExtended bool) (*model.PostList, *model.AppError) {
var member *model.ChannelMember var lastViewedAt int64
var err *model.AppError var err *model.AppError
if member, err = a.GetChannelMember(c, channelID, userID); err != nil { if lastViewedAt, err = a.Srv().getChannelMemberLastViewedAt(c, channelID, userID); err != nil {
return nil, err return nil, err
} else if member.LastViewedAt == 0 { } else if lastViewedAt == 0 {
return model.NewPostList(), nil return model.NewPostList(), nil
} }
lastUnreadPostId, err := a.GetPostIdAfterTime(channelID, member.LastViewedAt, collapsedThreads) lastUnreadPostId, err := a.GetPostIdAfterTime(channelID, lastViewedAt, collapsedThreads)
if err != nil { if err != nil {
return nil, err return nil, err
} else if lastUnreadPostId == "" { } else if lastUnreadPostId == "" {
@@ -1870,9 +1870,9 @@ func (a *App) countThreadMentions(c request.CTX, user *model.User, post *model.P
// countMentionsFromPost returns the number of posts in the post's channel that mention the user after and including the // countMentionsFromPost returns the number of posts in the post's channel that mention the user after and including the
// given post. // given post.
func (a *App) countMentionsFromPost(c request.CTX, user *model.User, post *model.Post) (int, int, int, *model.AppError) { func (a *App) countMentionsFromPost(c request.CTX, user *model.User, post *model.Post) (int, int, int, *model.AppError) {
channel, err := a.GetChannel(c, post.ChannelId) channel, appErr := a.GetChannel(c, post.ChannelId)
if err != nil { if appErr != nil {
return 0, 0, 0, err return 0, 0, 0, appErr
} }
if channel.Type == model.ChannelTypeDirect || channel.Type == model.ChannelTypeGroup { if channel.Type == model.ChannelTypeDirect || channel.Type == model.ChannelTypeGroup {
@@ -1893,15 +1893,15 @@ func (a *App) countMentionsFromPost(c request.CTX, user *model.User, post *model
return count, countRoot, urgentCount, nil return count, countRoot, urgentCount, nil
} }
channelMember, err := a.GetChannelMember(c, channel.Id, user.Id) members, err := a.Srv().Store().Channel().GetAllChannelMembersNotifyPropsForChannel(channel.Id, true)
if err != nil { if err != nil {
return 0, 0, 0, err return 0, 0, 0, model.NewAppError("countMentionsFromPost", "app.channel.count_posts_since.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
} }
keywords := MentionKeywords{} keywords := MentionKeywords{}
keywords.AddUser( keywords.AddUser(
user, user,
channelMember.NotifyProps, members[user.Id],
&model.Status{Status: model.StatusOnline}, // Assume the user is online since they would've triggered this &model.Status{Status: model.StatusOnline}, // Assume the user is online since they would've triggered this
true, // Assume channel mentions are always allowed for simplicity true, // Assume channel mentions are always allowed for simplicity
) )
@@ -1911,9 +1911,9 @@ func (a *App) countMentionsFromPost(c request.CTX, user *model.User, post *model
// A mapping of thread root IDs to whether or not a post in that thread mentions the user // A mapping of thread root IDs to whether or not a post in that thread mentions the user
mentionedByThread := make(map[string]bool) mentionedByThread := make(map[string]bool)
thread, err := a.GetPostThread(post.Id, model.GetPostsOptions{}, user.Id) thread, appErr := a.GetPostThread(post.Id, model.GetPostsOptions{}, user.Id)
if err != nil { if appErr != nil {
return 0, 0, 0, err return 0, 0, 0, appErr
} }
count := 0 count := 0

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

@@ -965,16 +965,16 @@ func (s *OpenTracingLayerChannelStore) GetAll(teamID string) ([]*model.Channel,
return result, err return result, err
} }
func (s *OpenTracingLayerChannelStore) GetAllChannelMembersById(id string) ([]string, error) { func (s *OpenTracingLayerChannelStore) GetAllChannelMemberIdsByChannelId(id string) ([]string, error) {
origCtx := s.Root.Store.Context() origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.GetAllChannelMembersById") span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.GetAllChannelMemberIdsByChannelId")
s.Root.Store.SetContext(newCtx) s.Root.Store.SetContext(newCtx)
defer func() { defer func() {
s.Root.Store.SetContext(origCtx) s.Root.Store.SetContext(origCtx)
}() }()
defer span.Finish() defer span.Finish()
result, err := s.ChannelStore.GetAllChannelMembersById(id) result, err := s.ChannelStore.GetAllChannelMemberIdsByChannelId(id)
if err != nil { if err != nil {
span.LogFields(spanlog.Error(err)) span.LogFields(spanlog.Error(err))
ext.Error.Set(span, true) ext.Error.Set(span, true)
@@ -1572,6 +1572,24 @@ func (s *OpenTracingLayerChannelStore) GetMemberForPost(postID string, userID st
return result, err return result, err
} }
func (s *OpenTracingLayerChannelStore) GetMemberLastViewedAt(ctx context.Context, channelID string, userID string) (int64, error) {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.GetMemberLastViewedAt")
s.Root.Store.SetContext(newCtx)
defer func() {
s.Root.Store.SetContext(origCtx)
}()
defer span.Finish()
result, err := s.ChannelStore.GetMemberLastViewedAt(ctx, channelID, userID)
if err != nil {
span.LogFields(spanlog.Error(err))
ext.Error.Set(span, true)
}
return result, err
}
func (s *OpenTracingLayerChannelStore) GetMembers(channelID string, offset int, limit int) (model.ChannelMembers, error) { func (s *OpenTracingLayerChannelStore) GetMembers(channelID string, offset int, limit int) (model.ChannelMembers, error) {
origCtx := s.Root.Store.Context() origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.GetMembers") span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.GetMembers")

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

@@ -1049,11 +1049,11 @@ func (s *RetryLayerChannelStore) GetAll(teamID string) ([]*model.Channel, error)
} }
func (s *RetryLayerChannelStore) GetAllChannelMembersById(id string) ([]string, error) { func (s *RetryLayerChannelStore) GetAllChannelMemberIdsByChannelId(id string) ([]string, error) {
tries := 0 tries := 0
for { for {
result, err := s.ChannelStore.GetAllChannelMembersById(id) result, err := s.ChannelStore.GetAllChannelMemberIdsByChannelId(id)
if err == nil { if err == nil {
return result, nil return result, nil
} }
@@ -1748,6 +1748,27 @@ func (s *RetryLayerChannelStore) GetMemberForPost(postID string, userID string,
} }
func (s *RetryLayerChannelStore) GetMemberLastViewedAt(ctx context.Context, channelID string, userID string) (int64, error) {
tries := 0
for {
result, err := s.ChannelStore.GetMemberLastViewedAt(ctx, channelID, userID)
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 *RetryLayerChannelStore) GetMembers(channelID string, offset int, limit int) (model.ChannelMembers, error) { func (s *RetryLayerChannelStore) GetMembers(channelID string, offset int, limit int) (model.ChannelMembers, error) {
tries := 0 tries := 0

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

@@ -40,7 +40,7 @@ func (c *SearchChannelStore) indexChannel(rctx request.CTX, channel *model.Chann
var userIDs, teamMemberIDs []string var userIDs, teamMemberIDs []string
var err error var err error
if channel.Type == model.ChannelTypePrivate { if channel.Type == model.ChannelTypePrivate {
userIDs, err = c.GetAllChannelMembersById(channel.Id) userIDs, err = c.GetAllChannelMemberIdsByChannelId(channel.Id)
if err != nil { if err != nil {
rctx.Logger().Warn("Encountered error while indexing channel", mlog.String("channel_id", channel.Id), mlog.Err(err)) rctx.Logger().Warn("Encountered error while indexing channel", mlog.String("channel_id", channel.Id), mlog.Err(err))
return return

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

@@ -1126,26 +1126,16 @@ func (s SqlChannelStore) GetChannelsByUser(userId string, includeDeleted bool, l
return channels, nil return channels, nil
} }
func (s SqlChannelStore) GetAllChannelMembersById(channelID string) ([]string, error) { func (s SqlChannelStore) GetAllChannelMemberIdsByChannelId(channelID string) ([]string, error) {
sql, args, err := s.channelMembersForTeamWithSchemeSelectQuery.Where(sq.Eq{ userIDs := []string{}
"ChannelId": channelID, err := s.GetReplicaX().Select(&userIDs, `SELECT UserId
}).ToSql() FROM ChannelMembers
if err != nil { WHERE ChannelId=?`, channelID)
return nil, errors.Wrap(err, "GetAllChannelMembersById_ToSql")
}
dbMembers := channelMemberWithSchemeRolesList{}
err = s.GetReplicaX().Select(&dbMembers, sql, args...)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "failed to get ChannelMembers with channelID=%s", channelID) return nil, errors.Wrapf(err, "failed to get ChannelMembers with channelID=%s", channelID)
} }
res := make([]string, len(dbMembers)) return userIDs, nil
for i, member := range dbMembers.ToModel() {
res[i] = member.UserId
}
return res, nil
} }
func (s SqlChannelStore) GetAllChannels(offset, limit int, opts store.ChannelSearchOpts) (model.ChannelListWithTeamData, error) { func (s SqlChannelStore) GetAllChannels(offset, limit int, opts store.ChannelSearchOpts) (model.ChannelListWithTeamData, error) {
@@ -2070,6 +2060,20 @@ func (s SqlChannelStore) GetMember(ctx context.Context, channelID string, userID
return dbMember.ToModel(), nil return dbMember.ToModel(), nil
} }
func (s SqlChannelStore) GetMemberLastViewedAt(ctx context.Context, channelID string, userID string) (int64, error) {
var lastViewedAt int64
if err := s.DBXFromContext(ctx).Get(&lastViewedAt, `SELECT COALESCE(LastViewedAt, 0) AS LastViewedAt
FROM ChannelMembers
WHERE ChannelId=? AND UserId=?`, channelID, userID); err != nil {
if err == sql.ErrNoRows {
return 0, store.NewErrNotFound("LastViewedAt", fmt.Sprintf("channelId=%s, userId=%s", channelID, userID))
}
return 0, errors.Wrapf(err, "failed to get lastViewedAt with channelId=%s and userId=%s", channelID, userID)
}
return lastViewedAt, nil
}
func (s SqlChannelStore) InvalidateAllChannelMembersForUser(userId string) { func (s SqlChannelStore) InvalidateAllChannelMembersForUser(userId string) {
allChannelMembersForUserCache.Remove(userId) allChannelMembersForUserCache.Remove(userId)
allChannelMembersForUserCache.Remove(userId + "_deleted") allChannelMembersForUserCache.Remove(userId + "_deleted")

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

@@ -205,7 +205,7 @@ type ChannelStore interface {
GetDeleted(team_id string, offset int, limit int, userID string) (model.ChannelList, error) GetDeleted(team_id string, offset int, limit int, userID string) (model.ChannelList, error)
GetChannels(teamID, userID string, opts *model.ChannelSearchOpts) (model.ChannelList, error) GetChannels(teamID, userID string, opts *model.ChannelSearchOpts) (model.ChannelList, error)
GetChannelsByUser(userID string, includeDeleted bool, lastDeleteAt, pageSize int, fromChannelID string) (model.ChannelList, error) GetChannelsByUser(userID string, includeDeleted bool, lastDeleteAt, pageSize int, fromChannelID string) (model.ChannelList, error)
GetAllChannelMembersById(id string) ([]string, error) GetAllChannelMemberIdsByChannelId(id string) ([]string, error)
GetAllChannels(page, perPage int, opts ChannelSearchOpts) (model.ChannelListWithTeamData, error) GetAllChannels(page, perPage int, opts ChannelSearchOpts) (model.ChannelListWithTeamData, error)
GetAllChannelsCount(opts ChannelSearchOpts) (int64, error) GetAllChannelsCount(opts ChannelSearchOpts) (int64, error)
GetMoreChannels(teamID string, userID string, offset int, limit int) (model.ChannelList, error) GetMoreChannels(teamID string, userID string, offset int, limit int) (model.ChannelList, error)
@@ -227,6 +227,7 @@ type ChannelStore interface {
UpdateMemberNotifyProps(channelID, userID string, props map[string]string) (*model.ChannelMember, error) UpdateMemberNotifyProps(channelID, userID string, props map[string]string) (*model.ChannelMember, error)
GetMembers(channelID string, offset, limit int) (model.ChannelMembers, error) GetMembers(channelID string, offset, limit int) (model.ChannelMembers, error)
GetMember(ctx context.Context, channelID string, userID string) (*model.ChannelMember, error) GetMember(ctx context.Context, channelID string, userID string) (*model.ChannelMember, error)
GetMemberLastViewedAt(ctx context.Context, channelID string, userID string) (int64, error)
GetChannelMembersTimezones(channelID string) ([]model.StringMap, error) GetChannelMembersTimezones(channelID string) ([]model.StringMap, error)
GetAllChannelMembersForUser(userID string, allowFromCache bool, includeDeleted bool) (map[string]string, error) GetAllChannelMembersForUser(userID string, allowFromCache bool, includeDeleted bool) (map[string]string, error)
GetChannelsMemberCount(channelIDs []string) (map[string]int64, error) GetChannelsMemberCount(channelIDs []string) (map[string]int64, error)

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

@@ -110,6 +110,7 @@ func TestChannelStore(t *testing.T, rctx request.CTX, ss store.Store, s SqlStore
t.Run("IncrementMentionCount", func(t *testing.T) { testChannelStoreIncrementMentionCount(t, rctx, ss) }) t.Run("IncrementMentionCount", func(t *testing.T) { testChannelStoreIncrementMentionCount(t, rctx, ss) })
t.Run("UpdateChannelMember", func(t *testing.T) { testUpdateChannelMember(t, rctx, ss) }) t.Run("UpdateChannelMember", func(t *testing.T) { testUpdateChannelMember(t, rctx, ss) })
t.Run("GetMember", func(t *testing.T) { testGetMember(t, rctx, ss) }) t.Run("GetMember", func(t *testing.T) { testGetMember(t, rctx, ss) })
t.Run("GetMemberLastViewedAt", func(t *testing.T) { testGetMemberLastViewedAt(t, rctx, ss) })
t.Run("GetMemberForPost", func(t *testing.T) { testChannelStoreGetMemberForPost(t, rctx, ss) }) t.Run("GetMemberForPost", func(t *testing.T) { testChannelStoreGetMemberForPost(t, rctx, ss) })
t.Run("GetMemberCount", func(t *testing.T) { testGetMemberCount(t, rctx, ss) }) t.Run("GetMemberCount", func(t *testing.T) { testGetMemberCount(t, rctx, ss) })
t.Run("GetMemberCountsByGroup", func(t *testing.T) { testGetMemberCountsByGroup(t, rctx, ss) }) t.Run("GetMemberCountsByGroup", func(t *testing.T) { testGetMemberCountsByGroup(t, rctx, ss) })
@@ -246,6 +247,10 @@ func testChannelStoreSaveDirectChannel(t *testing.T, rctx request.CTX, ss store.
require.NoError(t, nErr) require.NoError(t, nErr)
require.Len(t, members, 2, "should have saved 2 members") require.Len(t, members, 2, "should have saved 2 members")
userIDs, nErr := ss.Channel().GetAllChannelMemberIdsByChannelId(o1.Id)
require.NoError(t, nErr)
require.ElementsMatch(t, []string{u1.Id, u2.Id}, userIDs)
_, nErr = ss.Channel().SaveDirectChannel(rctx, &o1, &m1, &m2) _, nErr = ss.Channel().SaveDirectChannel(rctx, &o1, &m1, &m2)
require.Error(t, nErr, "shouldn't be a able to update from save") require.Error(t, nErr, "shouldn't be a able to update from save")
@@ -282,6 +287,10 @@ func testChannelStoreSaveDirectChannel(t *testing.T, rctx request.CTX, ss store.
require.NoError(t, nErr) require.NoError(t, nErr)
require.Len(t, members, 1, "should have saved just 1 member") require.Len(t, members, 1, "should have saved just 1 member")
userIDs, nErr = ss.Channel().GetAllChannelMemberIdsByChannelId(o1.Id)
require.NoError(t, nErr)
require.ElementsMatch(t, []string{u1.Id}, userIDs)
// Manually truncate Channels table until testlib can handle cleanups // Manually truncate Channels table until testlib can handle cleanups
s.GetMasterX().Exec("TRUNCATE Channels") s.GetMasterX().Exec("TRUNCATE Channels")
} }
@@ -4892,6 +4901,62 @@ func testGetMember(t *testing.T, rctx request.CTX, ss store.Store) {
ss.Channel().InvalidateCacheForChannelMembersNotifyProps(c2.Id) ss.Channel().InvalidateCacheForChannelMembersNotifyProps(c2.Id)
} }
func testGetMemberLastViewedAt(t *testing.T, rctx request.CTX, ss store.Store) {
userId := model.NewId()
c1 := &model.Channel{
TeamId: model.NewId(),
DisplayName: model.NewId(),
Name: model.NewId(),
Type: model.ChannelTypeOpen,
}
_, nErr := ss.Channel().Save(c1, -1)
require.NoError(t, nErr)
c2 := &model.Channel{
TeamId: c1.TeamId,
DisplayName: model.NewId(),
Name: model.NewId(),
Type: model.ChannelTypeOpen,
}
_, nErr = ss.Channel().Save(c2, -1)
require.NoError(t, nErr)
m1 := &model.ChannelMember{
ChannelId: c1.Id,
UserId: userId,
NotifyProps: model.GetDefaultChannelNotifyProps(),
LastViewedAt: int64(100),
}
_, err := ss.Channel().SaveMember(m1)
require.NoError(t, err)
m2 := &model.ChannelMember{
ChannelId: c2.Id,
UserId: userId,
NotifyProps: model.GetDefaultChannelNotifyProps(),
LastViewedAt: int64(200),
}
_, err = ss.Channel().SaveMember(m2)
require.NoError(t, err)
_, err = ss.Channel().GetMemberLastViewedAt(context.Background(), model.NewId(), userId)
require.Error(t, err, "should've failed to get member for non-existent channel")
_, err = ss.Channel().GetMemberLastViewedAt(context.Background(), c1.Id, model.NewId())
require.Error(t, err, "should've failed to get member for non-existent user")
lvAt, err := ss.Channel().GetMemberLastViewedAt(context.Background(), c1.Id, userId)
require.NoError(t, err, "shouldn't have errored when getting member", err)
require.Equal(t, m1.LastViewedAt, lvAt, "should've gotten LastViewedAt of channel 1")
lvAt, err = ss.Channel().GetMemberLastViewedAt(context.Background(), c2.Id, userId)
require.NoError(t, err, "shouldn't have errored when getting member", err)
require.Equal(t, m2.LastViewedAt, lvAt, "should've gotten gotten LastViewedAt of channel 2")
ss.Channel().InvalidateCacheForChannelMembersNotifyProps(c2.Id)
}
func testChannelStoreGetMemberForPost(t *testing.T, rctx request.CTX, ss store.Store) { func testChannelStoreGetMemberForPost(t *testing.T, rctx request.CTX, ss store.Store) {
ch := &model.Channel{ ch := &model.Channel{
TeamId: model.NewId(), TeamId: model.NewId(),
@@ -7477,6 +7542,10 @@ func testChannelStoreRemoveAllDeactivatedMembers(t *testing.T, rctx request.CTX,
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, d1, 3) assert.Len(t, d1, 3)
userIDs, nErr := ss.Channel().GetAllChannelMemberIdsByChannelId(c1.Id)
require.NoError(t, nErr)
require.ElementsMatch(t, []string{u1.Id, u2.Id, u3.Id}, userIDs)
// Deactivate users 1 & 2. // Deactivate users 1 & 2.
u1.DeleteAt = model.GetMillis() u1.DeleteAt = model.GetMillis()
u2.DeleteAt = model.GetMillis() u2.DeleteAt = model.GetMillis()
@@ -7494,6 +7563,10 @@ func testChannelStoreRemoveAllDeactivatedMembers(t *testing.T, rctx request.CTX,
assert.Len(t, d2, 1) assert.Len(t, d2, 1)
assert.Equal(t, u3.Id, d2[0].UserId) assert.Equal(t, u3.Id, d2[0].UserId)
userIDs, nErr = ss.Channel().GetAllChannelMemberIdsByChannelId(c1.Id)
require.NoError(t, nErr)
require.ElementsMatch(t, []string{u3.Id}, userIDs)
// Manually truncate Channels table until testlib can handle cleanups // Manually truncate Channels table until testlib can handle cleanups
s.GetMasterX().Exec("TRUNCATE Channels") s.GetMasterX().Exec("TRUNCATE Channels")
} }

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

@@ -432,8 +432,8 @@ func (_m *ChannelStore) GetAll(teamID string) ([]*model.Channel, error) {
return r0, r1 return r0, r1
} }
// GetAllChannelMembersById provides a mock function with given fields: id // GetAllChannelMemberIdsByChannelId provides a mock function with given fields: id
func (_m *ChannelStore) GetAllChannelMembersById(id string) ([]string, error) { func (_m *ChannelStore) GetAllChannelMemberIdsByChannelId(id string) ([]string, error) {
ret := _m.Called(id) ret := _m.Called(id)
var r0 []string var r0 []string
@@ -1314,6 +1314,30 @@ func (_m *ChannelStore) GetMemberForPost(postID string, userID string, includeAr
return r0, r1 return r0, r1
} }
// GetMemberLastViewedAt provides a mock function with given fields: ctx, channelID, userID
func (_m *ChannelStore) GetMemberLastViewedAt(ctx context.Context, channelID string, userID string) (int64, error) {
ret := _m.Called(ctx, channelID, userID)
var r0 int64
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, string, string) (int64, error)); ok {
return rf(ctx, channelID, userID)
}
if rf, ok := ret.Get(0).(func(context.Context, string, string) int64); ok {
r0 = rf(ctx, channelID, userID)
} else {
r0 = ret.Get(0).(int64)
}
if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok {
r1 = rf(ctx, channelID, userID)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetMembers provides a mock function with given fields: channelID, offset, limit // GetMembers provides a mock function with given fields: channelID, offset, limit
func (_m *ChannelStore) GetMembers(channelID string, offset int, limit int) (model.ChannelMembers, error) { func (_m *ChannelStore) GetMembers(channelID string, offset int, limit int) (model.ChannelMembers, error) {
ret := _m.Called(channelID, offset, limit) ret := _m.Called(channelID, offset, limit)

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

@@ -917,10 +917,10 @@ func (s *TimerLayerChannelStore) GetAll(teamID string) ([]*model.Channel, error)
return result, err return result, err
} }
func (s *TimerLayerChannelStore) GetAllChannelMembersById(id string) ([]string, error) { func (s *TimerLayerChannelStore) GetAllChannelMemberIdsByChannelId(id string) ([]string, error) {
start := time.Now() start := time.Now()
result, err := s.ChannelStore.GetAllChannelMembersById(id) result, err := s.ChannelStore.GetAllChannelMemberIdsByChannelId(id)
elapsed := float64(time.Since(start)) / float64(time.Second) elapsed := float64(time.Since(start)) / float64(time.Second)
if s.Root.Metrics != nil { if s.Root.Metrics != nil {
@@ -928,7 +928,7 @@ func (s *TimerLayerChannelStore) GetAllChannelMembersById(id string) ([]string,
if err == nil { if err == nil {
success = "true" success = "true"
} }
s.Root.Metrics.ObserveStoreMethodDuration("ChannelStore.GetAllChannelMembersById", success, elapsed) s.Root.Metrics.ObserveStoreMethodDuration("ChannelStore.GetAllChannelMemberIdsByChannelId", success, elapsed)
} }
return result, err return result, err
} }
@@ -1461,6 +1461,22 @@ func (s *TimerLayerChannelStore) GetMemberForPost(postID string, userID string,
return result, err return result, err
} }
func (s *TimerLayerChannelStore) GetMemberLastViewedAt(ctx context.Context, channelID string, userID string) (int64, error) {
start := time.Now()
result, err := s.ChannelStore.GetMemberLastViewedAt(ctx, channelID, userID)
elapsed := float64(time.Since(start)) / float64(time.Second)
if s.Root.Metrics != nil {
success := "false"
if err == nil {
success = "true"
}
s.Root.Metrics.ObserveStoreMethodDuration("ChannelStore.GetMemberLastViewedAt", success, elapsed)
}
return result, err
}
func (s *TimerLayerChannelStore) GetMembers(channelID string, offset int, limit int) (model.ChannelMembers, error) { func (s *TimerLayerChannelStore) GetMembers(channelID string, offset int, limit int) (model.ChannelMembers, error) {
start := time.Now() start := time.Now()

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

@@ -526,7 +526,7 @@ func (worker *BleveIndexerWorker) BulkIndexChannels(logger mlog.LoggerIFace, cha
var userIDs []string var userIDs []string
var err error var err error
if channel.Type == model.ChannelTypePrivate { if channel.Type == model.ChannelTypePrivate {
userIDs, err = worker.jobServer.Store.Channel().GetAllChannelMembersById(channel.Id) userIDs, err = worker.jobServer.Store.Channel().GetAllChannelMemberIdsByChannelId(channel.Id)
if err != nil { if err != nil {
return nil, model.NewAppError("BleveIndexerWorker.BulkIndexChannels", "bleveengine.indexer.do_job.bulk_index_channels.batch_error", nil, "", http.StatusInternalServerError).Wrap(err) return nil, model.NewAppError("BleveIndexerWorker.BulkIndexChannels", "bleveengine.indexer.do_job.bulk_index_channels.batch_error", nil, "", http.StatusInternalServerError).Wrap(err)
} }