Skip to content

Commit

Permalink
Added sidebar category coverage (#486)
Browse files Browse the repository at this point in the history
GetSidebarCategories are called for every team load.

Create/Update SidebarCategories are called at the same rate
as delete posts. Verified from Community Grafana.
  • Loading branch information
agnivade authored Apr 30, 2022
1 parent 60db357 commit b6a3610
Show file tree
Hide file tree
Showing 8 changed files with 171 additions and 3 deletions.
81 changes: 78 additions & 3 deletions loadtest/control/simulcontroller/actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,11 +223,14 @@ func loadTeam(u user.User, team *model.Team) control.UserActionResponse {
return control.UserActionResponse{Err: control.NewUserError(err)}
}

_, err := u.GetUserThreads(team.Id, &model.GetUserThreadsOpts{
if _, err := u.GetUserThreads(team.Id, &model.GetUserThreadsOpts{
TotalsOnly: true,
ThreadsOnly: false,
})
if err != nil {
}); err != nil {
return control.UserActionResponse{Err: control.NewUserError(err)}
}

if err := u.GetSidebarCategories(u.Store().Id(), team.Id); err != nil {
return control.UserActionResponse{Err: control.NewUserError(err)}
}

Expand Down Expand Up @@ -556,6 +559,78 @@ func deletePost(u user.User) control.UserActionResponse {
return control.UserActionResponse{Info: fmt.Sprintf("post deleted, id %v", post.Id)}
}

func (c *SimulController) createSidebarCategory(u user.User) control.UserActionResponse {
team, err := u.Store().CurrentTeam()
if err != nil {
return control.UserActionResponse{Err: control.NewUserError(err)}
} else if team == nil {
return control.UserActionResponse{Err: control.NewUserError(errors.New("current team should be set"))}
}

category := &model.SidebarCategoryWithChannels{
SidebarCategory: model.SidebarCategory{
UserId: u.Store().Id(),
TeamId: team.Id,
DisplayName: "category" + control.PickRandomWord(),
},
}

sidebarCategory, err := u.CreateSidebarCategory(u.Store().Id(), team.Id, category)
if err != nil {
return control.UserActionResponse{Err: control.NewUserError(err)}
}

return control.UserActionResponse{Info: fmt.Sprintf("created sidebar category, id %s", sidebarCategory.Id)}
}

func (c *SimulController) updateSidebarCategory(u user.User) control.UserActionResponse {
team, err := u.Store().CurrentTeam()
if err != nil {
return control.UserActionResponse{Err: control.NewUserError(err)}
} else if team == nil {
return control.UserActionResponse{Err: control.NewUserError(errors.New("current team should be set"))}
}

cat1, err := u.Store().RandomCategory(team.Id)
if err != nil {
return control.UserActionResponse{Err: control.NewUserError(err)}
}

cat2, err := u.Store().RandomCategory(team.Id)
if err != nil {
return control.UserActionResponse{Err: control.NewUserError(err)}
}

// Not repeatedly looping until we get a different category because there have been edge-cases before
// ending in infinite loop.s
if cat1.Id == cat2.Id {
return control.UserActionResponse{Info: "same categories returned. Skipping."}
}
if len(cat1.Channels) <= 1 {
return control.UserActionResponse{Info: "Not enough categories to remove. Skipping."}
}

// We pick a random channel from first category and move to second category.
channelToMove := control.PickRandomString(cat1.Channels)

// Find index
i := findIndex(cat1.Channels, channelToMove)
// Defense in depth
if i == -1 {
return control.UserActionResponse{Info: fmt.Sprintf("Channel %s not found in the category", channelToMove)}
}

// Move from the first, and add to second.
cat1.Channels = append(cat1.Channels[:i], cat1.Channels[i+1:]...)
cat2.Channels = append(cat2.Channels, channelToMove)

if err := u.UpdateSidebarCategory(u.Store().Id(), team.Id, []*model.SidebarCategoryWithChannels{&cat1, &cat2}); err != nil {
return control.UserActionResponse{Err: control.NewUserError(err)}
}

return control.UserActionResponse{Info: fmt.Sprintf("updated sidebar categories, ids [%s, %s]", cat1.Id, cat2.Id)}
}

func editPost(u user.User) control.UserActionResponse {
channel, err := u.Store().CurrentChannel()
if err != nil {
Expand Down
8 changes: 8 additions & 0 deletions loadtest/control/simulcontroller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,14 @@ func (c *SimulController) Run() {
run: deletePost,
frequency: 0.06,
},
{
run: c.createSidebarCategory,
frequency: 0.06,
},
{
run: c.updateSidebarCategory,
frequency: 0.06,
},
{
run: searchGroupChannels,
frequency: 0.1,
Expand Down
10 changes: 10 additions & 0 deletions loadtest/control/simulcontroller/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,3 +154,13 @@ func extractMentionFromMessage(msg string) string {
}
return mention[1:]
}

// findIndex returns the index of needle in a haystack.
func findIndex(haystack []string, needle string) int {
for i := range haystack {
if haystack[i] == needle {
return i
}
}
return -1
}
18 changes: 18 additions & 0 deletions loadtest/store/memstore/random.go
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,24 @@ func (s *MemStore) RandomTeamMember(teamId string) (model.TeamMember, error) {
return *teamMemberMap[key.(string)], nil
}

func (s *MemStore) RandomCategory(teamID string) (model.SidebarCategoryWithChannels, error) {
s.lock.RLock()
defer s.lock.RUnlock()

teamCat := s.sidebarCategories[teamID]

key, err := pickRandomKeyFromMap(teamCat)
if err != nil {
return model.SidebarCategoryWithChannels{}, err
}

category := *teamCat[key.(string)]
tmp := make([]string, len(category.Channels))
copy(tmp, category.Channels)
category.Channels = tmp
return category, nil
}

func pickRandomKeyFromMap(m interface{}) (interface{}, error) {
val := reflect.ValueOf(m)
if val.Kind() != reflect.Map {
Expand Down
14 changes: 14 additions & 0 deletions loadtest/store/memstore/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ type MemStore struct {
serverVersion string
threads map[string]*model.ThreadResponse
threadsQueue *CQueue
sidebarCategories map[string]map[string]*model.SidebarCategoryWithChannels
}

// New returns a new instance of MemStore with the given config.
Expand Down Expand Up @@ -100,6 +101,7 @@ func (s *MemStore) Clear() {
s.channelViews = map[string]int64{}
s.threads = map[string]*model.ThreadResponse{}
s.threadsQueue.Reset()
s.sidebarCategories = map[string]map[string]*model.SidebarCategoryWithChannels{}
}

func (s *MemStore) setupQueues(config *Config) error {
Expand Down Expand Up @@ -1020,6 +1022,18 @@ func (s *MemStore) SetThreads(trs []*model.ThreadResponse) error {
return nil
}

func (s *MemStore) SetCategories(teamID string, sidebarCategories *model.OrderedSidebarCategories) error {
s.lock.Lock()
defer s.lock.Unlock()

teamCat := make(map[string]*model.SidebarCategoryWithChannels)
for _, cat := range sidebarCategories.Categories {
teamCat[cat.SidebarCategory.Id] = cat
}
s.sidebarCategories[teamID] = teamCat
return nil
}

func (s *MemStore) getThreads(unreadOnly bool) ([]*model.ThreadResponse, error) {
var threads []*model.ThreadResponse
for _, thread := range s.threads {
Expand Down
5 changes: 5 additions & 0 deletions loadtest/store/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ type UserStore interface {
RandomTeamMember(teamId string) (model.TeamMember, error)
// RandomThread returns a random thread.
RandomThread() (model.ThreadResponse, error)
// RandomCategory returns a random category from a team
RandomCategory(teamID string) (model.SidebarCategoryWithChannels, error)

// profile
// ProfileImage returns whether the profile image for the given user has been
Expand Down Expand Up @@ -238,4 +240,7 @@ type MutableUserStore interface {
SetThreads(threads []*model.ThreadResponse) error
// MarkAllThreadsInTeamAsRead marks all threads in the given team as read
MarkAllThreadsInTeamAsRead(teamId string) error

// SidebarCategories
SetCategories(teamID string, sidebarCategories *model.OrderedSidebarCategories) error
}
5 changes: 5 additions & 0 deletions loadtest/user/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -283,4 +283,9 @@ type User interface {
MarkAllThreadsInTeamAsRead(teamId string) error
// UpdateThreadRead updates the read timestamp of the thread
UpdateThreadRead(teamId, threadId string, timestamp int64) error

// SidebarCategories
GetSidebarCategories(userID, teamID string) error
CreateSidebarCategory(userID, teamID string, category *model.SidebarCategoryWithChannels) (*model.SidebarCategoryWithChannels, error)
UpdateSidebarCategory(userID, teamID string, categories []*model.SidebarCategoryWithChannels) error
}
33 changes: 33 additions & 0 deletions loadtest/user/userentity/actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -1212,3 +1212,36 @@ func (ue *UserEntity) UpdateThreadRead(teamId, threadId string, timestamp int64)
}
return ue.store.SetThreads([]*model.ThreadResponse{thread})
}

// GetSidebarCategories fetches and stores the sidebar categories for an user.
func (ue *UserEntity) GetSidebarCategories(userID, teamID string) error {
categories, _, err := ue.client.GetSidebarCategoriesForTeamForUser(userID, teamID, "")
if err != nil {
return err
}

return ue.store.SetCategories(teamID, categories)
}

func (ue *UserEntity) CreateSidebarCategory(userID, teamID string, category *model.SidebarCategoryWithChannels) (*model.SidebarCategoryWithChannels, error) {
cat, _, err := ue.client.CreateSidebarCategoryForTeamForUser(userID, teamID, category)
if err != nil {
return nil, err
}

// The client fetches and stores all categories again.
if err := ue.GetSidebarCategories(userID, teamID); err != nil {
return nil, err
}
return cat, nil
}

func (ue *UserEntity) UpdateSidebarCategory(userID, teamID string, categories []*model.SidebarCategoryWithChannels) error {
_, _, err := ue.client.UpdateSidebarCategoriesForTeamForUser(userID, teamID, categories)
if err != nil {
return err
}

// The client fetches and stores all categories again.
return ue.GetSidebarCategories(userID, teamID)
}

0 comments on commit b6a3610

Please sign in to comment.