MM-44088: Add teamID filter to channelMembers (#20176)

We add 2 new params to channel members query.
1. Filter by teamId.
2. Negate that filter.

We include some more optimizations like:
- Moved the team role checks inside the dataloader.
- Moved the channel pretty name computation inside the loader.

Now that we load less data on initial load, we can reduce
the concurrency requirement to be a bit on the safer side.

```release-note
NONE
```
Этот коммит содержится в:
Agniva De Sarker
2022-05-11 13:54:12 +05:30
коммит произвёл GitHub
родитель 5ac3dbf058
Коммит a6d8e45297
14 изменённых файлов: 225 добавлений и 75 удалений

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

@@ -284,8 +284,8 @@ func generateLayer(name, templateFile string) ([]byte, error) {
switch param.Type {
case "ChannelSearchOpts", "UserGetByIdsOpts", "ThreadMembershipOpts":
paramsWithType = append(paramsWithType, fmt.Sprintf("%s store.%s", param.Name, param.Type))
case "*UserGetByIdsOpts":
paramsWithType = append(paramsWithType, fmt.Sprintf("%s *store.UserGetByIdsOpts", param.Name))
case "*UserGetByIdsOpts", "*ChannelMemberGraphQLSearchOpts":
paramsWithType = append(paramsWithType, fmt.Sprintf("%s *store.%s", param.Name, strings.TrimPrefix(param.Type, "*")))
default:
paramsWithType = append(paramsWithType, fmt.Sprintf("%s %s", param.Name, param.Type))
}
@@ -298,8 +298,8 @@ func generateLayer(name, templateFile string) ([]byte, error) {
switch param.Type {
case "ChannelSearchOpts", "UserGetByIdsOpts", "ThreadMembershipOpts":
paramsWithType = append(paramsWithType, fmt.Sprintf("%s store.%s", param.Name, param.Type))
case "*UserGetByIdsOpts":
paramsWithType = append(paramsWithType, fmt.Sprintf("%s *store.UserGetByIdsOpts", param.Name))
case "*UserGetByIdsOpts", "*ChannelMemberGraphQLSearchOpts":
paramsWithType = append(paramsWithType, fmt.Sprintf("%s *store.%s", param.Name, strings.TrimPrefix(param.Type, "*")))
default:
paramsWithType = append(paramsWithType, fmt.Sprintf("%s %s", param.Name, param.Type))
}

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

@@ -1478,7 +1478,7 @@ func (s *OpenTracingLayerChannelStore) GetMembersForUser(teamID string, userID s
return result, err
}
func (s *OpenTracingLayerChannelStore) GetMembersForUserWithCursor(userID string, afterChannel string, afterUser string, limit int, lastUpdateAt int) (model.ChannelMembers, error) {
func (s *OpenTracingLayerChannelStore) GetMembersForUserWithCursor(userID string, teamID string, opts *store.ChannelMemberGraphQLSearchOpts) (model.ChannelMembers, error) {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.GetMembersForUserWithCursor")
s.Root.Store.SetContext(newCtx)
@@ -1487,7 +1487,7 @@ func (s *OpenTracingLayerChannelStore) GetMembersForUserWithCursor(userID string
}()
defer span.Finish()
result, err := s.ChannelStore.GetMembersForUserWithCursor(userID, afterChannel, afterUser, limit, lastUpdateAt)
result, err := s.ChannelStore.GetMembersForUserWithCursor(userID, teamID, opts)
if err != nil {
span.LogFields(spanlog.Error(err))
ext.Error.Set(span, true)

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

@@ -1660,11 +1660,11 @@ func (s *RetryLayerChannelStore) GetMembersForUser(teamID string, userID string)
}
func (s *RetryLayerChannelStore) GetMembersForUserWithCursor(userID string, afterChannel string, afterUser string, limit int, lastUpdateAt int) (model.ChannelMembers, error) {
func (s *RetryLayerChannelStore) GetMembersForUserWithCursor(userID string, teamID string, opts *store.ChannelMemberGraphQLSearchOpts) (model.ChannelMembers, error) {
tries := 0
for {
result, err := s.ChannelStore.GetMembersForUserWithCursor(userID, afterChannel, afterUser, limit, lastUpdateAt)
result, err := s.ChannelStore.GetMembersForUserWithCursor(userID, teamID, opts)
if err == nil {
return result, nil
}

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

@@ -2814,7 +2814,7 @@ func (s SqlChannelStore) GetMembersForUser(teamID string, userID string) (model.
return dbMembers.ToModel(), nil
}
func (s SqlChannelStore) GetMembersForUserWithCursor(userID, afterChannel, afterUser string, limit, lastUpdateAt int) (model.ChannelMembers, error) {
func (s SqlChannelStore) GetMembersForUserWithCursor(userID, teamID string, opts *store.ChannelMemberGraphQLSearchOpts) (model.ChannelMembers, error) {
query := s.getQueryBuilder().
Select("ChannelMembers.*",
"TeamScheme.DefaultChannelGuestRole TeamSchemeDefaultGuestRole",
@@ -2834,20 +2834,36 @@ func (s SqlChannelStore) GetMembersForUserWithCursor(userID, afterChannel, after
}).
OrderBy("ChannelId, UserId ASC").
// The limit is verified at the GraphQL layer.
Limit(uint64(limit))
Limit(uint64(opts.Limit))
if afterChannel != "" && afterUser != "" {
if teamID != "" {
if opts.ExcludeTeam {
// Exclude this team and DM/GMs
query = query.Where(sq.And{
sq.NotEq{"Channels.TeamId": teamID},
sq.NotEq{"Channels.TeamId": ""},
})
} else {
// Include this team and DM/GMs
query = query.Where(sq.Or{
sq.Eq{"Channels.TeamId": teamID},
sq.Eq{"Channels.TeamId": ""},
})
}
}
if opts.AfterChannel != "" && opts.AfterUser != "" {
query = query.Where(sq.Or{
sq.Gt{"ChannelMembers.ChannelId": afterChannel},
sq.Gt{"ChannelMembers.ChannelId": opts.AfterChannel},
sq.And{
sq.Eq{"ChannelMembers.ChannelId": afterChannel},
sq.Gt{"ChannelMembers.UserId": afterUser},
sq.Eq{"ChannelMembers.ChannelId": opts.AfterChannel},
sq.Gt{"ChannelMembers.UserId": opts.AfterUser},
},
})
}
if lastUpdateAt != 0 {
query = query.Where(sq.GtOrEq{"ChannelMembers.LastUpdateAt": lastUpdateAt})
if opts.LastUpdateAt != 0 {
query = query.Where(sq.GtOrEq{"ChannelMembers.LastUpdateAt": opts.LastUpdateAt})
}
queryString, args, err := query.ToSql()

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

@@ -234,7 +234,7 @@ type ChannelStore interface {
GetMembersForUser(teamID string, userID string) (model.ChannelMembers, error)
GetTeamMembersForChannel(channelID string) ([]string, error)
GetMembersForUserWithPagination(userID string, page, perPage int) (model.ChannelMembersWithTeamData, error)
GetMembersForUserWithCursor(userID, afterChannel, afterUser string, limit, lastUpdateAt int) (model.ChannelMembers, error)
GetMembersForUserWithCursor(userID, teamID string, opts *ChannelMemberGraphQLSearchOpts) (model.ChannelMembers, error)
Autocomplete(userID, term string, includeDeleted bool) (model.ChannelListWithTeamData, error)
AutocompleteInTeam(teamID, userID, term string, includeDeleted bool) (model.ChannelList, error)
AutocompleteInTeamForSearch(teamID string, userID string, term string, includeDeleted bool) (model.ChannelList, error)
@@ -983,3 +983,13 @@ type ThreadMembershipOpts struct {
// should be updated.
UpdateParticipants bool
}
// ChannelMemberGraphQLSearchOpts contains the options for a graphQL query
// to get the channel members.
type ChannelMemberGraphQLSearchOpts struct {
AfterChannel string
AfterUser string
Limit int
LastUpdateAt int
ExcludeTeam bool
}

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

@@ -4438,6 +4438,14 @@ func testChannelStoreGetMembersForUserWithCursor(t *testing.T, ss store.Store) {
_, err := ss.Team().Save(&t1)
require.NoError(t, err)
t2 := model.Team{}
t2.DisplayName = "Team2"
t2.Name = NewTestId()
t2.Email = MakeEmail()
t2.Type = model.TeamOpen
_, err = ss.Team().Save(&t2)
require.NoError(t, err)
o1 := model.Channel{}
o1.TeamId = t1.Id
o1.DisplayName = "Channel1"
@@ -4454,6 +4462,14 @@ func testChannelStoreGetMembersForUserWithCursor(t *testing.T, ss store.Store) {
_, nErr = ss.Channel().Save(&o2, -1)
require.NoError(t, nErr)
o3 := model.Channel{}
o3.TeamId = t2.Id
o3.DisplayName = "Channel3"
o3.Name = NewTestId()
o3.Type = model.ChannelTypeOpen
_, nErr = ss.Channel().Save(&o3, -1)
require.NoError(t, nErr)
m1 := model.ChannelMember{}
m1.ChannelId = o1.Id
m1.UserId = model.NewId()
@@ -4468,15 +4484,29 @@ func testChannelStoreGetMembersForUserWithCursor(t *testing.T, ss store.Store) {
_, err = ss.Channel().SaveMember(&m2)
require.NoError(t, err)
m3 := model.ChannelMember{}
m3.ChannelId = o3.Id
m3.UserId = m1.UserId
m3.NotifyProps = model.GetDefaultChannelNotifyProps()
_, err = ss.Channel().SaveMember(&m3)
require.NoError(t, err)
t.Run("with channels", func(t *testing.T) {
var members model.ChannelMembers
members, err = ss.Channel().GetMembersForUserWithCursor(m1.UserId, "", "", 1, 0)
opts := &store.ChannelMemberGraphQLSearchOpts{
Limit: 1,
}
members, err = ss.Channel().GetMembersForUserWithCursor(m1.UserId, "", opts)
require.NoError(t, err)
assert.Len(t, members, 1)
members, err = ss.Channel().GetMembersForUserWithCursor(m1.UserId, "", "", 3, 0)
opts.Limit = 3
members, err = ss.Channel().GetMembersForUserWithCursor(m1.UserId, "", opts)
require.NoError(t, err)
assert.Len(t, members, 2)
members, err = ss.Channel().GetMembersForUserWithCursor(m1.UserId, members[0].ChannelId, m1.UserId, 1, 0)
assert.Len(t, members, 3)
opts.AfterChannel = members[0].ChannelId
opts.AfterUser = m1.UserId
opts.Limit = 1
members, err = ss.Channel().GetMembersForUserWithCursor(m1.UserId, "", opts)
require.NoError(t, err)
assert.Len(t, members, 1)
})
@@ -4495,15 +4525,41 @@ func testChannelStoreGetMembersForUserWithCursor(t *testing.T, ss store.Store) {
_, nErr = ss.Channel().CreateDirectChannel(&u3, &u4)
require.NoError(t, nErr)
members, err2 := ss.Channel().GetMembersForUserWithCursor(m1.UserId, "", "", 10, 0)
opts := &store.ChannelMemberGraphQLSearchOpts{
Limit: 10,
}
members, err2 := ss.Channel().GetMembersForUserWithCursor(m1.UserId, "", opts)
require.NoError(t, err2)
assert.Len(t, members, 4)
assert.Len(t, members, 5)
members, err2 = ss.Channel().GetMembersForUserWithCursor(m1.UserId, "", "", 2, 0)
opts.Limit = 2
members, err2 = ss.Channel().GetMembersForUserWithCursor(m1.UserId, "", opts)
require.NoError(t, err2)
assert.Len(t, members, 2)
members, err2 = ss.Channel().GetMembersForUserWithCursor(m1.UserId, members[1].ChannelId, m1.UserId, 2, 0)
opts.AfterChannel = members[1].ChannelId
opts.AfterUser = m1.UserId
opts.Limit = 2
members, err2 = ss.Channel().GetMembersForUserWithCursor(m1.UserId, "", opts)
require.NoError(t, err2)
assert.Len(t, members, 2)
})
t.Run("for a specific team", func(t *testing.T) {
opts := &store.ChannelMemberGraphQLSearchOpts{
Limit: 10,
}
members, err2 := ss.Channel().GetMembersForUserWithCursor(m1.UserId, t2.Id, opts)
require.NoError(t, err2)
assert.Len(t, members, 3)
})
t.Run("excluding a team", func(t *testing.T) {
opts := &store.ChannelMemberGraphQLSearchOpts{
Limit: 10,
ExcludeTeam: true,
}
members, err2 := ss.Channel().GetMembersForUserWithCursor(m1.UserId, t2.Id, opts)
require.NoError(t, err2)
assert.Len(t, members, 2)
})
@@ -4529,17 +4585,24 @@ func testChannelStoreGetMembersForUserWithCursor(t *testing.T, ss store.Store) {
_, err = ss.Channel().SaveMember(cm)
require.NoError(t, err)
}
members, err := ss.Channel().GetMembersForUserWithCursor(m1.UserId, "", "", 10, 0)
opts := &store.ChannelMemberGraphQLSearchOpts{
Limit: 10,
}
members, err := ss.Channel().GetMembersForUserWithCursor(m1.UserId, "", opts)
require.NoError(t, err)
assert.Len(t, members, 5)
assert.Len(t, members, 6)
members, err = ss.Channel().GetMembersForUserWithCursor(m1.UserId, "", "", 2, 0)
opts.Limit = 2
members, err = ss.Channel().GetMembersForUserWithCursor(m1.UserId, "", opts)
require.NoError(t, err)
assert.Len(t, members, 2)
members, err = ss.Channel().GetMembersForUserWithCursor(m1.UserId, members[1].ChannelId, m1.UserId, 10, 0)
opts.AfterChannel = members[1].ChannelId
opts.AfterUser = m1.UserId
opts.Limit = 10
members, err = ss.Channel().GetMembersForUserWithCursor(m1.UserId, "", opts)
require.NoError(t, err)
assert.Len(t, members, 3)
assert.Len(t, members, 4)
})
}

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

@@ -1165,13 +1165,13 @@ func (_m *ChannelStore) GetMembersForUser(teamID string, userID string) (model.C
return r0, r1
}
// GetMembersForUserWithCursor provides a mock function with given fields: userID, afterChannel, afterUser, limit, lastUpdateAt
func (_m *ChannelStore) GetMembersForUserWithCursor(userID string, afterChannel string, afterUser string, limit int, lastUpdateAt int) (model.ChannelMembers, error) {
ret := _m.Called(userID, afterChannel, afterUser, limit, lastUpdateAt)
// GetMembersForUserWithCursor provides a mock function with given fields: userID, teamID, opts
func (_m *ChannelStore) GetMembersForUserWithCursor(userID string, teamID string, opts *store.ChannelMemberGraphQLSearchOpts) (model.ChannelMembers, error) {
ret := _m.Called(userID, teamID, opts)
var r0 model.ChannelMembers
if rf, ok := ret.Get(0).(func(string, string, string, int, int) model.ChannelMembers); ok {
r0 = rf(userID, afterChannel, afterUser, limit, lastUpdateAt)
if rf, ok := ret.Get(0).(func(string, string, *store.ChannelMemberGraphQLSearchOpts) model.ChannelMembers); ok {
r0 = rf(userID, teamID, opts)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(model.ChannelMembers)
@@ -1179,8 +1179,8 @@ func (_m *ChannelStore) GetMembersForUserWithCursor(userID string, afterChannel
}
var r1 error
if rf, ok := ret.Get(1).(func(string, string, string, int, int) error); ok {
r1 = rf(userID, afterChannel, afterUser, limit, lastUpdateAt)
if rf, ok := ret.Get(1).(func(string, string, *store.ChannelMemberGraphQLSearchOpts) error); ok {
r1 = rf(userID, teamID, opts)
} else {
r1 = ret.Error(1)
}

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

@@ -1365,10 +1365,10 @@ func (s *TimerLayerChannelStore) GetMembersForUser(teamID string, userID string)
return result, err
}
func (s *TimerLayerChannelStore) GetMembersForUserWithCursor(userID string, afterChannel string, afterUser string, limit int, lastUpdateAt int) (model.ChannelMembers, error) {
func (s *TimerLayerChannelStore) GetMembersForUserWithCursor(userID string, teamID string, opts *store.ChannelMemberGraphQLSearchOpts) (model.ChannelMembers, error) {
start := timemodule.Now()
result, err := s.ChannelStore.GetMembersForUserWithCursor(userID, afterChannel, afterUser, limit, lastUpdateAt)
result, err := s.ChannelStore.GetMembersForUserWithCursor(userID, teamID, opts)
elapsed := float64(timemodule.Since(start)) / float64(timemodule.Second)
if s.Root.Metrics != nil {