From b3517eaf2f7ce51898dcdf9130d75197cd0c1fa9 Mon Sep 17 00:00:00 2001 From: Eli Yukelzon Date: Tue, 17 Sep 2019 14:37:10 +0100 Subject: [PATCH] MM-17468 - Improving performance of fetching threads (#11980) fetchThreads parameter support in the API --- api4/post.go | 24 ++- app/auto_posts.go | 6 + app/channel_test.go | 2 +- app/command_loadtest.go | 35 +++++ app/file.go | 2 +- app/plugin_api.go | 8 +- app/post.go | 39 +++-- model/post.go | 18 ++- store/sqlstore/post_store.go | 199 ++++++++++++++----------- store/store.go | 10 +- store/storetest/mocks/PostStore.go | 70 ++++----- store/storetest/post_store.go | 228 +++++++++++++++++++++-------- store/storetest/reaction_store.go | 14 +- store/timer_layer.go | 37 +++-- 14 files changed, 457 insertions(+), 235 deletions(-) diff --git a/api4/post.go b/api4/post.go index 6d314350e9..2cb20ec040 100644 --- a/api4/post.go +++ b/api4/post.go @@ -133,6 +133,10 @@ func getPostsForChannel(c *Context, w http.ResponseWriter, r *http.Request) { return } } + skipFetchThreads := false + if r.URL.Query().Get("fetchThreads") == "false" { + skipFetchThreads = true + } channelId := c.Params.ChannelId page := c.Params.Page @@ -148,7 +152,7 @@ func getPostsForChannel(c *Context, w http.ResponseWriter, r *http.Request) { etag := "" if since > 0 { - list, err = c.App.GetPostsSince(channelId, since) + list, err = c.App.GetPostsSince(model.GetPostsSinceOptions{ChannelId: channelId, Time: since, SkipFetchThreads: skipFetchThreads}) } else if len(afterPost) > 0 { etag = c.App.GetPostsEtag(channelId) @@ -156,7 +160,7 @@ func getPostsForChannel(c *Context, w http.ResponseWriter, r *http.Request) { return } - list, err = c.App.GetPostsAfterPost(channelId, afterPost, page, perPage) + list, err = c.App.GetPostsAfterPost(model.GetPostsOptions{ChannelId: channelId, PostId: afterPost, Page: page, PerPage: perPage, SkipFetchThreads: skipFetchThreads}) } else if len(beforePost) > 0 { etag = c.App.GetPostsEtag(channelId) @@ -164,7 +168,7 @@ func getPostsForChannel(c *Context, w http.ResponseWriter, r *http.Request) { return } - list, err = c.App.GetPostsBeforePost(channelId, beforePost, page, perPage) + list, err = c.App.GetPostsBeforePost(model.GetPostsOptions{ChannelId: channelId, PostId: beforePost, Page: page, PerPage: perPage, SkipFetchThreads: skipFetchThreads}) } else { etag = c.App.GetPostsEtag(channelId) @@ -172,7 +176,7 @@ func getPostsForChannel(c *Context, w http.ResponseWriter, r *http.Request) { return } - list, err = c.App.GetPostsPage(channelId, page, perPage) + list, err = c.App.GetPostsPage(model.GetPostsOptions{ChannelId: channelId, Page: page, PerPage: perPage, SkipFetchThreads: skipFetchThreads}) } if err != nil { @@ -208,7 +212,11 @@ func getPostsForChannelAroundLastUnread(c *Context, w http.ResponseWriter, r *ht return } - postList, err := c.App.GetPostsForChannelAroundLastUnread(channelId, userId, c.Params.LimitBefore, c.Params.LimitAfter) + skipFetchThreads := false + if r.URL.Query().Get("fetchThreads") == "false" { + skipFetchThreads = true + } + postList, err := c.App.GetPostsForChannelAroundLastUnread(channelId, userId, c.Params.LimitBefore, c.Params.LimitAfter, skipFetchThreads) if err != nil { c.Err = err return @@ -222,7 +230,11 @@ func getPostsForChannelAroundLastUnread(c *Context, w http.ResponseWriter, r *ht return } - postList, err = c.App.GetPostsPage(channelId, app.PAGE_DEFAULT, c.Params.LimitBefore) + postList, err = c.App.GetPostsPage(model.GetPostsOptions{ChannelId: channelId, Page: app.PAGE_DEFAULT, PerPage: c.Params.LimitBefore, SkipFetchThreads: skipFetchThreads}) + if err != nil { + c.Err = err + return + } } postList.NextPostId = c.App.GetNextPostIdFromPostList(postList) diff --git a/app/auto_posts.go b/app/auto_posts.go index 0eda016678..b8837b4c9f 100644 --- a/app/auto_posts.go +++ b/app/auto_posts.go @@ -66,6 +66,10 @@ func (cfg *AutoPostCreator) UploadTestFile() ([]string, bool) { } func (cfg *AutoPostCreator) CreateRandomPost() (*model.Post, bool) { + return cfg.CreateRandomPostNested("", "") +} + +func (cfg *AutoPostCreator) CreateRandomPostNested(parentId, rootId string) (*model.Post, bool) { var fileIds []string if cfg.HasImage { var err1 bool @@ -84,6 +88,8 @@ func (cfg *AutoPostCreator) CreateRandomPost() (*model.Post, bool) { post := &model.Post{ ChannelId: cfg.channelid, + ParentId: parentId, + RootId: rootId, Message: postText, FileIds: fileIds} rpost, err2 := cfg.client.CreatePost(post) diff --git a/app/channel_test.go b/app/channel_test.go index bc2cd33b37..2e3eb40ff3 100644 --- a/app/channel_test.go +++ b/app/channel_test.go @@ -526,7 +526,7 @@ func TestAddChannelMemberNoUserRequestor(t *testing.T) { } assert.Equal(t, groupUserIds, channelMemberHistoryUserIds) - postList, err := th.App.Srv.Store.Post().GetPosts(channel.Id, 0, 1, false) + postList, err := th.App.Srv.Store.Post().GetPosts(model.GetPostsOptions{ChannelId: channel.Id, Page: 0, PerPage: 1}, false) require.Nil(t, err) if assert.Len(t, postList.Order, 1) { diff --git a/app/command_loadtest.go b/app/command_loadtest.go index 455d94ed74..01f5a1b27c 100644 --- a/app/command_loadtest.go +++ b/app/command_loadtest.go @@ -39,6 +39,9 @@ var usage = `Mattermost testing commands to help configure the system Example: /test channels fuzz 5 10 + ThreadedPost - create a large threaded post + /test threaded_post + Posts - Add some random posts with fuzz text to current channel. /test posts [fuzz] @@ -127,6 +130,10 @@ func (me *LoadTestProvider) DoCommand(a *App, args *model.CommandArgs, message s return me.PostCommand(a, args, message) } + if strings.HasPrefix(message, "threaded_post") { + return me.ThreadedPostCommand(a, args, message) + } + if strings.HasPrefix(message, "url") { return me.UrlCommand(a, args, message) } @@ -277,6 +284,34 @@ func (me *LoadTestProvider) ChannelsCommand(a *App, args *model.CommandArgs, mes return &model.CommandResponse{Text: "Added channels", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} } +func (me *LoadTestProvider) ThreadedPostCommand(a *App, args *model.CommandArgs, message string) *model.CommandResponse { + var usernames []string + options := &model.UserGetOptions{InTeamId: args.TeamId, Page: 0, PerPage: 1000} + if profileUsers, err := a.Srv.Store.User().GetProfiles(options); err == nil { + usernames = make([]string, len(profileUsers)) + i := 0 + for _, userprof := range profileUsers { + usernames[i] = userprof.Username + i++ + } + } + + client := model.NewAPIv4Client(args.SiteURL) + client.MockSession(args.Session.Token) + testPoster := NewAutoPostCreator(client, args.ChannelId) + testPoster.Fuzzy = true + testPoster.Users = usernames + rpost, ok := testPoster.CreateRandomPost() + if !ok { + return &model.CommandResponse{Text: "Cannot create a post", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} + } + for i := 0; i < 1000; i++ { + testPoster.CreateRandomPostNested(rpost.Id, rpost.Id) + } + + return &model.CommandResponse{Text: "Added threaded post", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} +} + func (me *LoadTestProvider) PostsCommand(a *App, args *model.CommandArgs, message string) *model.CommandResponse { cmd := strings.TrimSpace(strings.TrimPrefix(message, "posts")) diff --git a/app/file.go b/app/file.go index 3ae1641560..2d9b71f5b2 100644 --- a/app/file.go +++ b/app/file.go @@ -279,7 +279,7 @@ func (a *App) MigrateFilenamesToFileInfos(post *model.Post) []*model.FileInfo { fileMigrationLock.Lock() defer fileMigrationLock.Unlock() - result, err := a.Srv.Store.Post().Get(post.Id) + result, err := a.Srv.Store.Post().Get(post.Id, false) if err != nil { mlog.Error(fmt.Sprintf("Unable to get post when migrating post to use FileInfos, err=%v", err), mlog.String("post_id", post.Id)) return []*model.FileInfo{} diff --git a/app/plugin_api.go b/app/plugin_api.go index 05ca69ab8b..867e2f587d 100644 --- a/app/plugin_api.go +++ b/app/plugin_api.go @@ -474,19 +474,19 @@ func (api *PluginAPI) GetPost(postId string) (*model.Post, *model.AppError) { } func (api *PluginAPI) GetPostsSince(channelId string, time int64) (*model.PostList, *model.AppError) { - return api.app.GetPostsSince(channelId, time) + return api.app.GetPostsSince(model.GetPostsSinceOptions{ChannelId: channelId, Time: time}) } func (api *PluginAPI) GetPostsAfter(channelId, postId string, page, perPage int) (*model.PostList, *model.AppError) { - return api.app.GetPostsAfterPost(channelId, postId, page, perPage) + return api.app.GetPostsAfterPost(model.GetPostsOptions{ChannelId: channelId, PostId: postId, Page: page, PerPage: perPage}) } func (api *PluginAPI) GetPostsBefore(channelId, postId string, page, perPage int) (*model.PostList, *model.AppError) { - return api.app.GetPostsBeforePost(channelId, postId, page, perPage) + return api.app.GetPostsBeforePost(model.GetPostsOptions{ChannelId: channelId, PostId: postId, Page: page, PerPage: perPage}) } func (api *PluginAPI) GetPostsForChannel(channelId string, page, perPage int) (*model.PostList, *model.AppError) { - return api.app.GetPostsPage(channelId, page, perPage) + return api.app.GetPostsPage(model.GetPostsOptions{ChannelId: channelId, Page: perPage, PerPage: page}) } func (api *PluginAPI) UpdatePost(post *model.Post) (*model.Post, *model.AppError) { diff --git a/app/post.go b/app/post.go index b54de9e1cc..d6f5e1d6a8 100644 --- a/app/post.go +++ b/app/post.go @@ -167,7 +167,7 @@ func (a *App) CreatePost(post *model.Post, channel *model.Channel, triggerWebhoo if len(post.RootId) > 0 { pchan = make(chan store.StoreResult, 1) go func() { - r, pErr := a.Srv.Store.Post().Get(post.RootId) + r, pErr := a.Srv.Store.Post().Get(post.RootId, true) pchan <- store.StoreResult{Data: r, Err: pErr} close(pchan) }() @@ -475,7 +475,7 @@ func (a *App) DeleteEphemeralPost(userId, postId string) { func (a *App) UpdatePost(post *model.Post, safeUpdate bool) (*model.Post, *model.AppError) { post.SanitizeProps() - postLists, err := a.Srv.Store.Post().Get(post.Id) + postLists, err := a.Srv.Store.Post().Get(post.Id, true) if err != nil { return nil, err } @@ -614,20 +614,20 @@ func (a *App) PatchPost(postId string, patch *model.PostPatch) (*model.Post, *mo return updatedPost, nil } -func (a *App) GetPostsPage(channelId string, page int, perPage int) (*model.PostList, *model.AppError) { - return a.Srv.Store.Post().GetPosts(channelId, page*perPage, perPage, true) +func (a *App) GetPostsPage(options model.GetPostsOptions) (*model.PostList, *model.AppError) { + return a.Srv.Store.Post().GetPosts(options, false) } func (a *App) GetPosts(channelId string, offset int, limit int) (*model.PostList, *model.AppError) { - return a.Srv.Store.Post().GetPosts(channelId, offset, limit, true) + return a.Srv.Store.Post().GetPosts(model.GetPostsOptions{ChannelId: channelId, Page: offset, PerPage: limit}, true) } func (a *App) GetPostsEtag(channelId string) string { return a.Srv.Store.Post().GetEtag(channelId, true) } -func (a *App) GetPostsSince(channelId string, time int64) (*model.PostList, *model.AppError) { - return a.Srv.Store.Post().GetPostsSince(channelId, time, true) +func (a *App) GetPostsSince(options model.GetPostsSinceOptions) (*model.PostList, *model.AppError) { + return a.Srv.Store.Post().GetPostsSince(options, true) } func (a *App) GetSinglePost(postId string) (*model.Post, *model.AppError) { @@ -635,7 +635,7 @@ func (a *App) GetSinglePost(postId string) (*model.Post, *model.AppError) { } func (a *App) GetPostThread(postId string) (*model.PostList, *model.AppError) { - return a.Srv.Store.Post().Get(postId) + return a.Srv.Store.Post().Get(postId, false) } func (a *App) GetFlaggedPosts(userId string, offset int, limit int) (*model.PostList, *model.AppError) { @@ -651,7 +651,7 @@ func (a *App) GetFlaggedPostsForChannel(userId, channelId string, offset int, li } func (a *App) GetPermalinkPost(postId string, userId string) (*model.PostList, *model.AppError) { - list, err := a.Srv.Store.Post().Get(postId) + list, err := a.Srv.Store.Post().Get(postId, false) if err != nil { return nil, err } @@ -673,19 +673,19 @@ func (a *App) GetPermalinkPost(postId string, userId string) (*model.PostList, * return list, nil } -func (a *App) GetPostsBeforePost(channelId, postId string, page, perPage int) (*model.PostList, *model.AppError) { - return a.Srv.Store.Post().GetPostsBefore(channelId, postId, perPage, page*perPage) +func (a *App) GetPostsBeforePost(options model.GetPostsOptions) (*model.PostList, *model.AppError) { + return a.Srv.Store.Post().GetPostsBefore(options) } -func (a *App) GetPostsAfterPost(channelId, postId string, page, perPage int) (*model.PostList, *model.AppError) { - return a.Srv.Store.Post().GetPostsAfter(channelId, postId, perPage, page*perPage) +func (a *App) GetPostsAfterPost(options model.GetPostsOptions) (*model.PostList, *model.AppError) { + return a.Srv.Store.Post().GetPostsAfter(options) } -func (a *App) GetPostsAroundPost(postId, channelId string, offset, limit int, before bool) (*model.PostList, *model.AppError) { +func (a *App) GetPostsAroundPost(before bool, options model.GetPostsOptions) (*model.PostList, *model.AppError) { if before { - return a.Srv.Store.Post().GetPostsBefore(channelId, postId, limit, offset) + return a.Srv.Store.Post().GetPostsBefore(options) } - return a.Srv.Store.Post().GetPostsAfter(channelId, postId, limit, offset) + return a.Srv.Store.Post().GetPostsAfter(options) } func (a *App) GetPostAfterTime(channelId string, time int64) (*model.Post, *model.AppError) { @@ -773,8 +773,7 @@ func (a *App) AddCursorIdsForPostList(originalList *model.PostList, afterPost, b originalList.NextPostId = nextPostId originalList.PrevPostId = prevPostId } - -func (a *App) GetPostsForChannelAroundLastUnread(channelId, userId string, limitBefore, limitAfter int) (*model.PostList, *model.AppError) { +func (a *App) GetPostsForChannelAroundLastUnread(channelId, userId string, limitBefore, limitAfter int, skipFetchThreads bool) (*model.PostList, *model.AppError) { var member *model.ChannelMember var err *model.AppError if member, err = a.GetChannelMember(channelId, userId); err != nil { @@ -798,13 +797,13 @@ func (a *App) GetPostsForChannelAroundLastUnread(channelId, userId string, limit // channel organically, those replies will be added below. postList.Order = []string{lastUnreadPostId} - if postListBefore, err := a.GetPostsBeforePost(channelId, lastUnreadPostId, PAGE_DEFAULT, limitBefore); err != nil { + if postListBefore, err := a.GetPostsBeforePost(model.GetPostsOptions{ChannelId: channelId, PostId: lastUnreadPostId, Page: PAGE_DEFAULT, PerPage: limitBefore, SkipFetchThreads: skipFetchThreads}); err != nil { return nil, err } else if postListBefore != nil { postList.Extend(postListBefore) } - if postListAfter, err := a.GetPostsAfterPost(channelId, lastUnreadPostId, PAGE_DEFAULT, limitAfter-1); err != nil { + if postListAfter, err := a.GetPostsAfterPost(model.GetPostsOptions{ChannelId: channelId, PostId: lastUnreadPostId, Page: PAGE_DEFAULT, PerPage: limitAfter - 1, SkipFetchThreads: skipFetchThreads}); err != nil { return nil, err } else if postListAfter != nil { postList.Extend(postListAfter) diff --git a/model/post.go b/model/post.go index c2dad69163..93241af09e 100644 --- a/model/post.go +++ b/model/post.go @@ -73,7 +73,6 @@ type Post struct { OriginalId string `json:"original_id"` Message string `json:"message"` - // MessageSource will contain the message as submitted by the user if Message has been modified // by Mattermost for presentation (e.g if an image proxy is being used). It should be used to // populate edit boxes if present. @@ -88,7 +87,8 @@ type Post struct { HasReactions bool `json:"has_reactions,omitempty"` // Transient data populated before sending a post to the client - Metadata *PostMetadata `json:"metadata,omitempty" db:"-"` + ReplyCount int64 `json:"reply_count" db:"-"` + Metadata *PostMetadata `json:"metadata,omitempty" db:"-"` } type PostEphemeral struct { @@ -170,6 +170,20 @@ func (o *Post) ToUnsanitizedJson() string { return string(b) } +type GetPostsSinceOptions struct { + ChannelId string + Time int64 + SkipFetchThreads bool +} + +type GetPostsOptions struct { + ChannelId string + PostId string + Page int + PerPage int + SkipFetchThreads bool +} + func PostFromJson(data io.Reader) *Post { var o *Post json.NewDecoder(data).Decode(&o) diff --git a/store/sqlstore/post_store.go b/store/sqlstore/post_store.go index fa994a1aaa..4c8698cdbd 100644 --- a/store/sqlstore/post_store.go +++ b/store/sqlstore/post_store.go @@ -128,6 +128,12 @@ func (s *SqlPostStore) Save(post *model.Post) (*model.Post, *model.AppError) { if _, err := s.GetMaster().Exec("UPDATE Posts SET UpdateAt = :UpdateAt WHERE Id = :RootId", map[string]interface{}{"UpdateAt": time, "RootId": post.RootId}); err != nil { mlog.Error("Error updating Post UpdateAt.", mlog.Err(err)) } + } else { + if count, err := s.GetMaster().SelectInt("SELECT COUNT(*) FROM Posts WHERE RootId = :Id", map[string]interface{}{"Id": post.Id}); err != nil { + mlog.Error(fmt.Sprintf("Error fetching post's thread: %v", err.Error())) + } else { + post.ReplyCount = count + } } return post, nil @@ -266,7 +272,7 @@ func (s *SqlPostStore) GetFlaggedPostsForChannel(userId, channelId string, offse return pl, nil } -func (s *SqlPostStore) Get(id string) (*model.PostList, *model.AppError) { +func (s *SqlPostStore) Get(id string, skipFetchThreads bool) (*model.PostList, *model.AppError) { pl := model.NewPostList() if len(id) == 0 { @@ -274,35 +280,40 @@ func (s *SqlPostStore) Get(id string) (*model.PostList, *model.AppError) { } var post model.Post - err := s.GetReplica().SelectOne(&post, "SELECT * FROM Posts WHERE Id = :Id AND DeleteAt = 0", map[string]interface{}{"Id": id}) + var postFetchQuery string + if skipFetchThreads { + postFetchQuery = "SELECT p.*, (SELECT count(Posts.Id) FROM Posts WHERE Posts.RootId = p.RootId) FROM Posts p WHERE p.Id = :Id AND p.DeleteAt = 0" + } else { + postFetchQuery = "SELECT * FROM Posts WHERE Id = :Id AND DeleteAt = 0" + } + err := s.GetReplica().SelectOne(&post, postFetchQuery, map[string]interface{}{"Id": id}) if err != nil { return nil, model.NewAppError("SqlPostStore.GetPost", "store.sql_post.get.app_error", nil, "id="+id+err.Error(), http.StatusNotFound) } - pl.AddPost(&post) pl.AddOrder(id) + if !skipFetchThreads { + rootId := post.RootId - rootId := post.RootId + if rootId == "" { + rootId = post.Id + } - if rootId == "" { - rootId = post.Id + if len(rootId) == 0 { + return nil, model.NewAppError("SqlPostStore.GetPost", "store.sql_post.get.app_error", nil, "root_id="+rootId, http.StatusInternalServerError) + } + + var posts []*model.Post + _, err = s.GetReplica().Select(&posts, "SELECT * FROM Posts WHERE (Id = :Id OR RootId = :RootId) AND DeleteAt = 0", map[string]interface{}{"Id": rootId, "RootId": rootId}) + if err != nil { + return nil, model.NewAppError("SqlPostStore.GetPost", "store.sql_post.get.app_error", nil, "root_id="+rootId+err.Error(), http.StatusInternalServerError) + } + + for _, p := range posts { + pl.AddPost(p) + pl.AddOrder(p.Id) + } } - - if len(rootId) == 0 { - return nil, model.NewAppError("SqlPostStore.GetPost", "store.sql_post.get.app_error", nil, "root_id="+rootId, http.StatusInternalServerError) - } - - var posts []*model.Post - _, err = s.GetReplica().Select(&posts, "SELECT * FROM Posts WHERE (Id = :Id OR RootId = :RootId) AND DeleteAt = 0", map[string]interface{}{"Id": rootId, "RootId": rootId}) - if err != nil { - return nil, model.NewAppError("SqlPostStore.GetPost", "store.sql_post.get.app_error", nil, "root_id="+rootId+err.Error(), http.StatusInternalServerError) - } - - for _, p := range posts { - pl.AddPost(p) - pl.AddOrder(p.Id) - } - return pl, nil } @@ -445,14 +456,14 @@ func (s *SqlPostStore) PermanentDeleteByChannel(channelId string) *model.AppErro return nil } -func (s *SqlPostStore) GetPosts(channelId string, offset int, limit int, allowFromCache bool) (*model.PostList, *model.AppError) { - if limit > 1000 { - return nil, model.NewAppError("SqlPostStore.GetLinearPosts", "store.sql_post.get_posts.app_error", nil, "channelId="+channelId, http.StatusBadRequest) +func (s *SqlPostStore) GetPosts(options model.GetPostsOptions, allowFromCache bool) (*model.PostList, *model.AppError) { + if options.PerPage > 1000 { + return nil, model.NewAppError("SqlPostStore.GetLinearPosts", "store.sql_post.get_posts.app_error", nil, "channelId="+options.ChannelId, http.StatusBadRequest) } - + offset := options.PerPage * options.Page // Caching only occurs on limits of 30 and 60, the common limits requested by MM clients - if allowFromCache && offset == 0 && (limit == 60 || limit == 30) { - if cacheItem, ok := s.lastPostsCache.Get(fmt.Sprintf("%s%v", channelId, limit)); ok { + if allowFromCache && offset == 0 && (options.PerPage == 60 || options.PerPage == 30) { + if cacheItem, ok := s.lastPostsCache.Get(fmt.Sprintf("%s%v", options.ChannelId, options.PerPage)); ok { if s.metrics != nil { s.metrics.IncrementMemCacheHitCounter("Last Posts Cache") } @@ -466,13 +477,13 @@ func (s *SqlPostStore) GetPosts(channelId string, offset int, limit int, allowFr rpc := make(chan store.StoreResult, 1) go func() { - posts, err := s.getRootPosts(channelId, offset, limit) + posts, err := s.getRootPosts(options.ChannelId, offset, options.PerPage, options.SkipFetchThreads) rpc <- store.StoreResult{Data: posts, Err: err} close(rpc) }() cpc := make(chan store.StoreResult, 1) go func() { - posts, err := s.getParentsPosts(channelId, offset, limit) + posts, err := s.getParentsPosts(options.ChannelId, offset, options.PerPage, options.SkipFetchThreads) cpc <- store.StoreResult{Data: posts, Err: err} close(cpc) }() @@ -505,18 +516,18 @@ func (s *SqlPostStore) GetPosts(channelId string, offset int, limit int, allowFr list.MakeNonNil() // Caching only occurs on limits of 30 and 60, the common limits requested by MM clients - if offset == 0 && (limit == 60 || limit == 30) { - s.lastPostsCache.AddWithExpiresInSecs(fmt.Sprintf("%s%v", channelId, limit), list, LAST_POSTS_CACHE_SEC) + if offset == 0 && (options.PerPage == 60 || options.PerPage == 30) { + s.lastPostsCache.AddWithExpiresInSecs(fmt.Sprintf("%s%v", options.ChannelId, options.PerPage), list, LAST_POSTS_CACHE_SEC) } return list, err } -func (s *SqlPostStore) GetPostsSince(channelId string, time int64, allowFromCache bool) (*model.PostList, *model.AppError) { +func (s *SqlPostStore) GetPostsSince(options model.GetPostsSinceOptions, allowFromCache bool) (*model.PostList, *model.AppError) { if allowFromCache { // If the last post in the channel's time is less than or equal to the time we are getting posts since, // we can safely return no posts. - if cacheItem, ok := s.lastPostTimeCache.Get(channelId); ok && cacheItem.(int64) <= time { + if cacheItem, ok := s.lastPostTimeCache.Get(options.ChannelId); ok && cacheItem.(int64) <= options.Time { if s.metrics != nil { s.metrics.IncrementMemCacheHitCounter("Last Post Time") } @@ -556,19 +567,19 @@ func (s *SqlPostStore) GetPostsSince(channelId string, time int64, allowFromCach AND ChannelId = :ChannelId LIMIT 1000) temp_tab)) ORDER BY CreateAt DESC`, - map[string]interface{}{"ChannelId": channelId, "Time": time}) + map[string]interface{}{"ChannelId": options.ChannelId, "Time": options.Time}) if err != nil { - return nil, model.NewAppError("SqlPostStore.GetPostsSince", "store.sql_post.get_posts_since.app_error", nil, "channelId="+channelId+err.Error(), http.StatusInternalServerError) + return nil, model.NewAppError("SqlPostStore.GetPostsSince", "store.sql_post.get_posts_since.app_error", nil, "channelId="+options.ChannelId+err.Error(), http.StatusInternalServerError) } list := model.NewPostList() - latestUpdate := time + latestUpdate := options.Time for _, p := range posts { list.AddPost(p) - if p.UpdateAt > time { + if p.UpdateAt > options.Time { list.AddOrder(p.Id) } if latestUpdate < p.UpdateAt { @@ -576,21 +587,25 @@ func (s *SqlPostStore) GetPostsSince(channelId string, time int64, allowFromCach } } - s.lastPostTimeCache.AddWithExpiresInSecs(channelId, latestUpdate, LAST_POST_TIME_CACHE_SEC) + s.lastPostTimeCache.AddWithExpiresInSecs(options.ChannelId, latestUpdate, LAST_POST_TIME_CACHE_SEC) return list, nil } -func (s *SqlPostStore) GetPostsBefore(channelId string, postId string, limit int, offset int) (*model.PostList, *model.AppError) { - return s.getPostsAround(channelId, postId, limit, offset, true) +func (s *SqlPostStore) GetPostsBefore(options model.GetPostsOptions) (*model.PostList, *model.AppError) { + return s.getPostsAround(true, options) } -func (s *SqlPostStore) GetPostsAfter(channelId string, postId string, limit int, offset int) (*model.PostList, *model.AppError) { - return s.getPostsAround(channelId, postId, limit, offset, false) +func (s *SqlPostStore) GetPostsAfter(options model.GetPostsOptions) (*model.PostList, *model.AppError) { + return s.getPostsAround(false, options) } -func (s *SqlPostStore) getPostsAround(channelId string, postId string, limit int, offset int, before bool) (*model.PostList, *model.AppError) { - var direction, sort string +func (s *SqlPostStore) getPostsAround(before bool, options model.GetPostsOptions) (*model.PostList, *model.AppError) { + offset := options.Page * options.PerPage + var posts, parents []*model.Post + + var direction string + var sort string if before { direction = "<" sort = "DESC" @@ -598,23 +613,29 @@ func (s *SqlPostStore) getPostsAround(channelId string, postId string, limit int direction = ">" sort = "ASC" } + replyCountSubQuery := s.getQueryBuilder().Select("COUNT(Posts.Id)").From("Posts").Where(sq.Expr("p.RootId = '' AND RootId = p.Id")) + query := s.getQueryBuilder().Select("p.*") + if options.SkipFetchThreads { + query = query.Column(sq.Alias(replyCountSubQuery, "ReplyCount")) + } + query = query.From("Posts p"). + Where(sq.And{ + sq.Expr(`CreateAt `+direction+` (SELECT CreateAt FROM Posts WHERE Id = ?)`, options.PostId), + sq.Eq{"ChannelId": options.ChannelId}, + sq.Eq{"DeleteAt": int(0)}, + }). + OrderBy("CreateAt " + sort). + Limit(uint64(options.PerPage)). + Offset(uint64(offset)) + + queryString, args, err := query.ToSql() - var posts, parents []*model.Post - _, err := s.GetReplica().Select(&posts, - `SELECT - * - FROM - Posts - WHERE - CreateAt `+direction+` (SELECT CreateAt FROM Posts WHERE Id = :PostId) - AND ChannelId = :ChannelId - AND DeleteAt = 0 - ORDER BY CreateAt `+sort+` - LIMIT :Limit - OFFSET :Offset`, - map[string]interface{}{"ChannelId": channelId, "PostId": postId, "Limit": limit, "Offset": offset}) if err != nil { - return nil, model.NewAppError("SqlPostStore.GetPostContext", "store.sql_post.get_posts_around.get.app_error", nil, "channelId="+channelId+err.Error(), http.StatusInternalServerError) + return nil, model.NewAppError("SqlPostStore.GetPostContext", "store.sql_post.get_posts_around.get.app_error", nil, "channelId="+options.ChannelId+err.Error(), http.StatusInternalServerError) + } + _, err = s.GetMaster().Select(&posts, queryString, args...) + if err != nil { + return nil, model.NewAppError("SqlPostStore.GetPostContext", "store.sql_post.get_posts_around.get.app_error", nil, "channelId="+options.ChannelId+err.Error(), http.StatusInternalServerError) } if len(posts) > 0 { @@ -625,28 +646,29 @@ func (s *SqlPostStore) getPostsAround(channelId string, postId string, limit int rootIds = append(rootIds, post.RootId) } } + rootQuery := s.getQueryBuilder().Select("p.*") + if options.SkipFetchThreads { + rootQuery = rootQuery.Column(sq.Alias(replyCountSubQuery, "ReplyCount")) + } + rootQuery = rootQuery.From("Posts p"). + Where(sq.And{ + sq.Or{ + sq.Eq{"RootId": rootIds}, + sq.Eq{"Id": rootIds}, + }, + sq.Eq{"ChannelId": options.ChannelId}, + sq.Eq{"DeleteAt": 0}, + }). + OrderBy("CreateAt DESC") - keys, params := MapStringsToQueryParams(rootIds, "PostId") - - params["ChannelId"] = channelId - params["PostId"] = postId - params["Limit"] = limit - params["Offset"] = offset - - _, err = s.GetReplica().Select(&parents, - `SELECT - * - FROM - Posts - WHERE - (Id IN `+keys+` OR RootId IN `+keys+`) - AND ChannelId = :ChannelId - AND DeleteAt = 0 - ORDER BY CreateAt DESC`, - params) + rootQueryString, rootArgs, err := rootQuery.ToSql() if err != nil { - return nil, model.NewAppError("SqlPostStore.GetPostContext", "store.sql_post.get_posts_around.get_parent.app_error", nil, "channelId="+channelId+err.Error(), http.StatusInternalServerError) + return nil, model.NewAppError("SqlPostStore.GetPostContext", "store.sql_post.get_posts_around.get_parent.app_error", nil, "channelId="+options.ChannelId+err.Error(), http.StatusInternalServerError) + } + _, err = s.GetMaster().Select(&parents, rootQueryString, rootArgs...) + if err != nil { + return nil, model.NewAppError("SqlPostStore.GetPostContext", "store.sql_post.get_posts_around.get_parent.app_error", nil, "channelId="+options.ChannelId+err.Error(), http.StatusInternalServerError) } } @@ -745,20 +767,29 @@ func (s *SqlPostStore) GetPostAfterTime(channelId string, time int64) (*model.Po return post, nil } -func (s *SqlPostStore) getRootPosts(channelId string, offset int, limit int) ([]*model.Post, *model.AppError) { +func (s *SqlPostStore) getRootPosts(channelId string, offset int, limit int, skipFetchThreads bool) ([]*model.Post, *model.AppError) { var posts []*model.Post - _, err := s.GetReplica().Select(&posts, "SELECT * FROM Posts WHERE ChannelId = :ChannelId AND DeleteAt = 0 ORDER BY CreateAt DESC LIMIT :Limit OFFSET :Offset", map[string]interface{}{"ChannelId": channelId, "Offset": offset, "Limit": limit}) + var fetchQuery string + if skipFetchThreads { + fetchQuery = "SELECT p.*, (SELECT COUNT(Posts.Id) FROM Posts WHERE p.RootId = '' AND Posts.RootId = p.Id) as ReplyCount FROM Posts p WHERE ChannelId = :ChannelId AND DeleteAt = 0 ORDER BY CreateAt DESC LIMIT :Limit OFFSET :Offset" + } else { + fetchQuery = "SELECT * FROM Posts WHERE ChannelId = :ChannelId AND DeleteAt = 0 ORDER BY CreateAt DESC LIMIT :Limit OFFSET :Offset" + } + _, err := s.GetReplica().Select(&posts, fetchQuery, map[string]interface{}{"ChannelId": channelId, "Offset": offset, "Limit": limit}) if err != nil { return nil, model.NewAppError("SqlPostStore.GetLinearPosts", "store.sql_post.get_root_posts.app_error", nil, "channelId="+channelId+err.Error(), http.StatusInternalServerError) } return posts, nil } -func (s *SqlPostStore) getParentsPosts(channelId string, offset int, limit int) ([]*model.Post, *model.AppError) { +func (s *SqlPostStore) getParentsPosts(channelId string, offset int, limit int, skipFetchThreads bool) ([]*model.Post, *model.AppError) { var posts []*model.Post + replyCountQuery := "" + if skipFetchThreads { + replyCountQuery = ` ,(SELECT COUNT(Posts.Id) FROM Posts WHERE q2.RootId = '' AND Posts.RootId = q2.Id) as ReplyCount` + } _, err := s.GetReplica().Select(&posts, - `SELECT - q2.* + `SELECT q2.*`+replyCountQuery+` FROM Posts q2 INNER JOIN diff --git a/store/store.go b/store/store.go index c6fe2b1d5b..cc96278eee 100644 --- a/store/store.go +++ b/store/store.go @@ -200,18 +200,18 @@ type ChannelMemberHistoryStore interface { type PostStore interface { Save(post *model.Post) (*model.Post, *model.AppError) Update(newPost *model.Post, oldPost *model.Post) (*model.Post, *model.AppError) - Get(id string) (*model.PostList, *model.AppError) + Get(id string, skipFetchThreads bool) (*model.PostList, *model.AppError) GetSingle(id string) (*model.Post, *model.AppError) Delete(postId string, time int64, deleteByID string) *model.AppError PermanentDeleteByUser(userId string) *model.AppError PermanentDeleteByChannel(channelId string) *model.AppError - GetPosts(channelId string, offset int, limit int, allowFromCache bool) (*model.PostList, *model.AppError) + GetPosts(options model.GetPostsOptions, allowFromCache bool) (*model.PostList, *model.AppError) GetFlaggedPosts(userId string, offset int, limit int) (*model.PostList, *model.AppError) GetFlaggedPostsForTeam(userId, teamId string, offset int, limit int) (*model.PostList, *model.AppError) GetFlaggedPostsForChannel(userId, channelId string, offset int, limit int) (*model.PostList, *model.AppError) - GetPostsBefore(channelId string, postId string, numPosts int, offset int) (*model.PostList, *model.AppError) - GetPostsAfter(channelId string, postId string, numPosts int, offset int) (*model.PostList, *model.AppError) - GetPostsSince(channelId string, time int64, allowFromCache bool) (*model.PostList, *model.AppError) + GetPostsBefore(options model.GetPostsOptions) (*model.PostList, *model.AppError) + GetPostsAfter(options model.GetPostsOptions) (*model.PostList, *model.AppError) + GetPostsSince(options model.GetPostsSinceOptions, allowFromCache bool) (*model.PostList, *model.AppError) GetPostAfterTime(channelId string, time int64) (*model.Post, *model.AppError) GetPostIdAfterTime(channelId string, time int64) (string, *model.AppError) GetPostIdBeforeTime(channelId string, time int64) (string, *model.AppError) diff --git a/store/storetest/mocks/PostStore.go b/store/storetest/mocks/PostStore.go index acb8adfe7f..a023a9c13c 100644 --- a/store/storetest/mocks/PostStore.go +++ b/store/storetest/mocks/PostStore.go @@ -108,13 +108,13 @@ func (_m *PostStore) Delete(postId string, time int64, deleteByID string) *model return r0 } -// Get provides a mock function with given fields: id -func (_m *PostStore) Get(id string) (*model.PostList, *model.AppError) { - ret := _m.Called(id) +// Get provides a mock function with given fields: id, skipFetchThreads +func (_m *PostStore) Get(id string, skipFetchThreads bool) (*model.PostList, *model.AppError) { + ret := _m.Called(id, skipFetchThreads) var r0 *model.PostList - if rf, ok := ret.Get(0).(func(string) *model.PostList); ok { - r0 = rf(id) + if rf, ok := ret.Get(0).(func(string, bool) *model.PostList); ok { + r0 = rf(id, skipFetchThreads) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*model.PostList) @@ -122,8 +122,8 @@ func (_m *PostStore) Get(id string) (*model.PostList, *model.AppError) { } var r1 *model.AppError - if rf, ok := ret.Get(1).(func(string) *model.AppError); ok { - r1 = rf(id) + if rf, ok := ret.Get(1).(func(string, bool) *model.AppError); ok { + r1 = rf(id, skipFetchThreads) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*model.AppError) @@ -382,13 +382,13 @@ func (_m *PostStore) GetPostIdBeforeTime(channelId string, time int64) (string, return r0, r1 } -// GetPosts provides a mock function with given fields: channelId, offset, limit, allowFromCache -func (_m *PostStore) GetPosts(channelId string, offset int, limit int, allowFromCache bool) (*model.PostList, *model.AppError) { - ret := _m.Called(channelId, offset, limit, allowFromCache) +// GetPosts provides a mock function with given fields: options, allowFromCache +func (_m *PostStore) GetPosts(options model.GetPostsOptions, allowFromCache bool) (*model.PostList, *model.AppError) { + ret := _m.Called(options, allowFromCache) var r0 *model.PostList - if rf, ok := ret.Get(0).(func(string, int, int, bool) *model.PostList); ok { - r0 = rf(channelId, offset, limit, allowFromCache) + if rf, ok := ret.Get(0).(func(model.GetPostsOptions, bool) *model.PostList); ok { + r0 = rf(options, allowFromCache) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*model.PostList) @@ -396,8 +396,8 @@ func (_m *PostStore) GetPosts(channelId string, offset int, limit int, allowFrom } var r1 *model.AppError - if rf, ok := ret.Get(1).(func(string, int, int, bool) *model.AppError); ok { - r1 = rf(channelId, offset, limit, allowFromCache) + if rf, ok := ret.Get(1).(func(model.GetPostsOptions, bool) *model.AppError); ok { + r1 = rf(options, allowFromCache) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*model.AppError) @@ -407,13 +407,13 @@ func (_m *PostStore) GetPosts(channelId string, offset int, limit int, allowFrom return r0, r1 } -// GetPostsAfter provides a mock function with given fields: channelId, postId, numPosts, offset -func (_m *PostStore) GetPostsAfter(channelId string, postId string, numPosts int, offset int) (*model.PostList, *model.AppError) { - ret := _m.Called(channelId, postId, numPosts, offset) +// GetPostsAfter provides a mock function with given fields: options +func (_m *PostStore) GetPostsAfter(options model.GetPostsOptions) (*model.PostList, *model.AppError) { + ret := _m.Called(options) var r0 *model.PostList - if rf, ok := ret.Get(0).(func(string, string, int, int) *model.PostList); ok { - r0 = rf(channelId, postId, numPosts, offset) + if rf, ok := ret.Get(0).(func(model.GetPostsOptions) *model.PostList); ok { + r0 = rf(options) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*model.PostList) @@ -421,8 +421,8 @@ func (_m *PostStore) GetPostsAfter(channelId string, postId string, numPosts int } var r1 *model.AppError - if rf, ok := ret.Get(1).(func(string, string, int, int) *model.AppError); ok { - r1 = rf(channelId, postId, numPosts, offset) + if rf, ok := ret.Get(1).(func(model.GetPostsOptions) *model.AppError); ok { + r1 = rf(options) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*model.AppError) @@ -457,13 +457,13 @@ func (_m *PostStore) GetPostsBatchForIndexing(startTime int64, endTime int64, li return r0, r1 } -// GetPostsBefore provides a mock function with given fields: channelId, postId, numPosts, offset -func (_m *PostStore) GetPostsBefore(channelId string, postId string, numPosts int, offset int) (*model.PostList, *model.AppError) { - ret := _m.Called(channelId, postId, numPosts, offset) +// GetPostsBefore provides a mock function with given fields: options +func (_m *PostStore) GetPostsBefore(options model.GetPostsOptions) (*model.PostList, *model.AppError) { + ret := _m.Called(options) var r0 *model.PostList - if rf, ok := ret.Get(0).(func(string, string, int, int) *model.PostList); ok { - r0 = rf(channelId, postId, numPosts, offset) + if rf, ok := ret.Get(0).(func(model.GetPostsOptions) *model.PostList); ok { + r0 = rf(options) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*model.PostList) @@ -471,8 +471,8 @@ func (_m *PostStore) GetPostsBefore(channelId string, postId string, numPosts in } var r1 *model.AppError - if rf, ok := ret.Get(1).(func(string, string, int, int) *model.AppError); ok { - r1 = rf(channelId, postId, numPosts, offset) + if rf, ok := ret.Get(1).(func(model.GetPostsOptions) *model.AppError); ok { + r1 = rf(options) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*model.AppError) @@ -532,13 +532,13 @@ func (_m *PostStore) GetPostsCreatedAt(channelId string, time int64) ([]*model.P return r0, r1 } -// GetPostsSince provides a mock function with given fields: channelId, time, allowFromCache -func (_m *PostStore) GetPostsSince(channelId string, time int64, allowFromCache bool) (*model.PostList, *model.AppError) { - ret := _m.Called(channelId, time, allowFromCache) +// GetPostsSince provides a mock function with given fields: options, allowFromCache +func (_m *PostStore) GetPostsSince(options model.GetPostsSinceOptions, allowFromCache bool) (*model.PostList, *model.AppError) { + ret := _m.Called(options, allowFromCache) var r0 *model.PostList - if rf, ok := ret.Get(0).(func(string, int64, bool) *model.PostList); ok { - r0 = rf(channelId, time, allowFromCache) + if rf, ok := ret.Get(0).(func(model.GetPostsSinceOptions, bool) *model.PostList); ok { + r0 = rf(options, allowFromCache) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*model.PostList) @@ -546,8 +546,8 @@ func (_m *PostStore) GetPostsSince(channelId string, time int64, allowFromCache } var r1 *model.AppError - if rf, ok := ret.Get(1).(func(string, int64, bool) *model.AppError); ok { - r1 = rf(channelId, time, allowFromCache) + if rf, ok := ret.Get(1).(func(model.GetPostsSinceOptions, bool) *model.AppError); ok { + r1 = rf(options, allowFromCache) } else { if ret.Get(1) != nil { r1 = ret.Get(1).(*model.AppError) diff --git a/store/storetest/post_store.go b/store/storetest/post_store.go index ceb9d56f21..e7379941c8 100644 --- a/store/storetest/post_store.go +++ b/store/storetest/post_store.go @@ -134,7 +134,7 @@ func testPostStoreGet(t *testing.T, ss store.Store) { t.Fatal("Invalid Etag") } - r1, err := ss.Post().Get(o1.Id) + r1, err := ss.Post().Get(o1.Id, false) if err != nil { t.Fatal(err) } @@ -142,11 +142,11 @@ func testPostStoreGet(t *testing.T, ss store.Store) { t.Fatal("invalid returned post") } - if _, err = ss.Post().Get("123"); err == nil { + if _, err = ss.Post().Get("123", false); err == nil { t.Fatal("Missing id should have failed") } - if _, err = ss.Post().Get(""); err == nil { + if _, err = ss.Post().Get("", false); err == nil { t.Fatal("should fail for blank post ids") } } @@ -232,17 +232,17 @@ func testPostStoreUpdate(t *testing.T, ss store.Store) { o3, err = ss.Post().Save(o3) require.Nil(t, err) - r1, err := ss.Post().Get(o1.Id) + r1, err := ss.Post().Get(o1.Id, false) if err != nil { t.Fatal(err) } ro1 := r1.Posts[o1.Id] - r2, err := ss.Post().Get(o1.Id) + r2, err := ss.Post().Get(o1.Id, false) if err != nil { t.Fatal(err) } ro2 := r2.Posts[o2.Id] - r3, err := ss.Post().Get(o3.Id) + r3, err := ss.Post().Get(o3.Id, false) if err != nil { t.Fatal(err) } @@ -259,7 +259,7 @@ func testPostStoreUpdate(t *testing.T, ss store.Store) { t.Fatal(err) } - r1, err = ss.Post().Get(o1.Id) + r1, err = ss.Post().Get(o1.Id, false) if err != nil { t.Fatal(err) } @@ -276,7 +276,7 @@ func testPostStoreUpdate(t *testing.T, ss store.Store) { t.Fatal(err) } - r2, err = ss.Post().Get(o1.Id) + r2, err = ss.Post().Get(o1.Id, false) if err != nil { t.Fatal(err) } @@ -293,7 +293,7 @@ func testPostStoreUpdate(t *testing.T, ss store.Store) { t.Fatal(err) } - r3, err = ss.Post().Get(o3.Id) + r3, err = ss.Post().Get(o3.Id, false) if err != nil { t.Fatal(err) } @@ -311,7 +311,7 @@ func testPostStoreUpdate(t *testing.T, ss store.Store) { }) require.Nil(t, err) - r4, err := ss.Post().Get(o4.Id) + r4, err := ss.Post().Get(o4.Id, false) if err != nil { t.Fatal(err) } @@ -325,7 +325,7 @@ func testPostStoreUpdate(t *testing.T, ss store.Store) { t.Fatal(err) } - r4, err = ss.Post().Get(o4.Id) + r4, err = ss.Post().Get(o4.Id, false) if err != nil { t.Fatal(err) } @@ -352,7 +352,7 @@ func testPostStoreDelete(t *testing.T, ss store.Store) { o1, err := ss.Post().Save(o1) require.Nil(t, err) - if r1, err := ss.Post().Get(o1.Id); err != nil { + if r1, err := ss.Post().Get(o1.Id, false); err != nil { t.Fatal(err) } else { if r1.Posts[o1.Id].CreateAt != o1.CreateAt { @@ -371,7 +371,7 @@ func testPostStoreDelete(t *testing.T, ss store.Store) { t.Errorf("Expected (*Post).Props[model.POST_PROPS_DELETE_BY] to be %v but got %v.", deleteByID, actual) } - if r3, err := ss.Post().Get(o1.Id); err == nil { + if r3, err := ss.Post().Get(o1.Id, false); err == nil { t.Log(r3) t.Fatal("Missing id should have failed") } @@ -403,11 +403,11 @@ func testPostStoreDelete1Level(t *testing.T, ss store.Store) { t.Fatal(err) } - if _, err := ss.Post().Get(o1.Id); err == nil { + if _, err := ss.Post().Get(o1.Id, false); err == nil { t.Fatal("Deleted id should have failed") } - if _, err := ss.Post().Get(o2.Id); err == nil { + if _, err := ss.Post().Get(o2.Id, false); err == nil { t.Fatal("Deleted id should have failed") } } @@ -449,19 +449,19 @@ func testPostStoreDelete2Level(t *testing.T, ss store.Store) { t.Fatal(err) } - if _, err := ss.Post().Get(o1.Id); err == nil { + if _, err := ss.Post().Get(o1.Id, false); err == nil { t.Fatal("Deleted id should have failed") } - if _, err := ss.Post().Get(o2.Id); err == nil { + if _, err := ss.Post().Get(o2.Id, false); err == nil { t.Fatal("Deleted id should have failed") } - if _, err := ss.Post().Get(o3.Id); err == nil { + if _, err := ss.Post().Get(o3.Id, false); err == nil { t.Fatal("Deleted id should have failed") } - if _, err := ss.Post().Get(o4.Id); err != nil { + if _, err := ss.Post().Get(o4.Id, false); err != nil { t.Fatal(err) } } @@ -494,11 +494,11 @@ func testPostStorePermDelete1Level(t *testing.T, ss store.Store) { t.Fatal(err2) } - if _, err := ss.Post().Get(o1.Id); err != nil { + if _, err := ss.Post().Get(o1.Id, false); err != nil { t.Fatal("Deleted id shouldn't have failed") } - if _, err := ss.Post().Get(o2.Id); err == nil { + if _, err := ss.Post().Get(o2.Id, false); err == nil { t.Fatal("Deleted id should have failed") } @@ -506,7 +506,7 @@ func testPostStorePermDelete1Level(t *testing.T, ss store.Store) { t.Fatal(err) } - if _, err := ss.Post().Get(o3.Id); err == nil { + if _, err := ss.Post().Get(o3.Id, false); err == nil { t.Fatal("Deleted id should have failed") } } @@ -539,15 +539,15 @@ func testPostStorePermDelete1Level2(t *testing.T, ss store.Store) { t.Fatal(err2) } - if _, err := ss.Post().Get(o1.Id); err == nil { + if _, err := ss.Post().Get(o1.Id, false); err == nil { t.Fatal("Deleted id should have failed") } - if _, err := ss.Post().Get(o2.Id); err == nil { + if _, err := ss.Post().Get(o2.Id, false); err == nil { t.Fatal("Deleted id should have failed") } - if _, err := ss.Post().Get(o3.Id); err != nil { + if _, err := ss.Post().Get(o3.Id, false); err != nil { t.Fatal("Deleted id shouldn't have failed") } } @@ -578,7 +578,7 @@ func testPostStoreGetWithChildren(t *testing.T, ss store.Store) { o3, err = ss.Post().Save(o3) require.Nil(t, err) - pl, err := ss.Post().Get(o1.Id) + pl, err := ss.Post().Get(o1.Id, false) if err != nil { t.Fatal(err) } @@ -591,7 +591,7 @@ func testPostStoreGetWithChildren(t *testing.T, ss store.Store) { t.Fatal(dErr) } - pl, err = ss.Post().Get(o1.Id) + pl, err = ss.Post().Get(o1.Id, false) if err != nil { t.Fatal(err) } @@ -604,7 +604,7 @@ func testPostStoreGetWithChildren(t *testing.T, ss store.Store) { t.Fatal(dErr) } - pl, err = ss.Post().Get(o1.Id) + pl, err = ss.Post().Get(o1.Id, false) if err != nil { t.Fatal(err) } @@ -670,7 +670,7 @@ func testPostStoreGetPostsWithDetails(t *testing.T, ss store.Store) { o5, err = ss.Post().Save(o5) require.Nil(t, err) - r1, err := ss.Post().GetPosts(o1.ChannelId, 0, 4, false) + r1, err := ss.Post().GetPosts(model.GetPostsOptions{ChannelId: o1.ChannelId, Page: 0, PerPage: 4}, false) require.Nil(t, err) if r1.Order[0] != o5.Id { @@ -697,7 +697,7 @@ func testPostStoreGetPostsWithDetails(t *testing.T, ss store.Store) { t.Fatal("Missing parent") } - r2, err := ss.Post().GetPosts(o1.ChannelId, 0, 4, true) + r2, err := ss.Post().GetPosts(model.GetPostsOptions{ChannelId: o1.ChannelId, Page: 0, PerPage: 4}, true) require.Nil(t, err) if r2.Order[0] != o5.Id { @@ -725,7 +725,7 @@ func testPostStoreGetPostsWithDetails(t *testing.T, ss store.Store) { } // Run once to fill cache - _, err = ss.Post().GetPosts(o1.ChannelId, 0, 30, true) + _, err = ss.Post().GetPosts(model.GetPostsOptions{ChannelId: o1.ChannelId, Page: 0, PerPage: 30}, true) require.Nil(t, err) o6 := &model.Post{} @@ -736,14 +736,14 @@ func testPostStoreGetPostsWithDetails(t *testing.T, ss store.Store) { require.Nil(t, err) // Should only be 6 since we hit the cache - r3, err := ss.Post().GetPosts(o1.ChannelId, 0, 30, true) + r3, err := ss.Post().GetPosts(model.GetPostsOptions{ChannelId: o1.ChannelId, Page: 0, PerPage: 30}, true) require.Nil(t, err) assert.Equal(t, 6, len(r3.Order)) ss.Post().InvalidateLastPostTimeCache(o1.ChannelId) // Cache was invalidated, we should get all the posts - r4, err := ss.Post().GetPosts(o1.ChannelId, 0, 30, true) + r4, err := ss.Post().GetPosts(model.GetPostsOptions{ChannelId: o1.ChannelId, Page: 0, PerPage: 30}, true) require.Nil(t, err) assert.Equal(t, 7, len(r4.Order)) } @@ -768,7 +768,7 @@ func testPostStoreGetPostsBeforeAfter(t *testing.T, ss store.Store) { } t.Run("should not return anything before the first post", func(t *testing.T) { - postList, err := ss.Post().GetPostsBefore(channelId, posts[0].Id, 10, 0) + postList, err := ss.Post().GetPostsBefore(model.GetPostsOptions{ChannelId: channelId, PostId: posts[0].Id, Page: 0, PerPage: 10}) assert.Nil(t, err) assert.Equal(t, []string{}, postList.Order) @@ -776,7 +776,7 @@ func testPostStoreGetPostsBeforeAfter(t *testing.T, ss store.Store) { }) t.Run("should return posts before a post", func(t *testing.T) { - postList, err := ss.Post().GetPostsBefore(channelId, posts[5].Id, 10, 0) + postList, err := ss.Post().GetPostsBefore(model.GetPostsOptions{ChannelId: channelId, PostId: posts[5].Id, Page: 0, PerPage: 10}) assert.Nil(t, err) assert.Equal(t, []string{posts[4].Id, posts[3].Id, posts[2].Id, posts[1].Id, posts[0].Id}, postList.Order) @@ -790,7 +790,7 @@ func testPostStoreGetPostsBeforeAfter(t *testing.T, ss store.Store) { }) t.Run("should limit posts before", func(t *testing.T) { - postList, err := ss.Post().GetPostsBefore(channelId, posts[5].Id, 2, 0) + postList, err := ss.Post().GetPostsBefore(model.GetPostsOptions{ChannelId: channelId, PostId: posts[5].Id, PerPage: 2}) assert.Nil(t, err) assert.Equal(t, []string{posts[4].Id, posts[3].Id}, postList.Order) @@ -801,7 +801,7 @@ func testPostStoreGetPostsBeforeAfter(t *testing.T, ss store.Store) { }) t.Run("should not return anything after the last post", func(t *testing.T) { - postList, err := ss.Post().GetPostsAfter(channelId, posts[len(posts)-1].Id, 10, 0) + postList, err := ss.Post().GetPostsAfter(model.GetPostsOptions{ChannelId: channelId, PostId: posts[len(posts)-1].Id, PerPage: 10}) assert.Nil(t, err) assert.Equal(t, []string{}, postList.Order) @@ -809,7 +809,7 @@ func testPostStoreGetPostsBeforeAfter(t *testing.T, ss store.Store) { }) t.Run("should return posts after a post", func(t *testing.T) { - postList, err := ss.Post().GetPostsAfter(channelId, posts[5].Id, 10, 0) + postList, err := ss.Post().GetPostsAfter(model.GetPostsOptions{ChannelId: channelId, PostId: posts[5].Id, PerPage: 10}) assert.Nil(t, err) assert.Equal(t, []string{posts[9].Id, posts[8].Id, posts[7].Id, posts[6].Id}, postList.Order) @@ -822,7 +822,7 @@ func testPostStoreGetPostsBeforeAfter(t *testing.T, ss store.Store) { }) t.Run("should limit posts after", func(t *testing.T) { - postList, err := ss.Post().GetPostsAfter(channelId, posts[5].Id, 2, 0) + postList, err := ss.Post().GetPostsAfter(model.GetPostsOptions{ChannelId: channelId, PostId: posts[5].Id, PerPage: 2}) assert.Nil(t, err) assert.Equal(t, []string{posts[7].Id, posts[6].Id}, postList.Order) @@ -832,7 +832,6 @@ func testPostStoreGetPostsBeforeAfter(t *testing.T, ss store.Store) { }, postList.Posts) }) }) - t.Run("with threads", func(t *testing.T) { channelId := model.NewId() userId := model.NewId() @@ -903,7 +902,7 @@ func testPostStoreGetPostsBeforeAfter(t *testing.T, ss store.Store) { post2.UpdateAt = post6.UpdateAt t.Run("should return each post and thread before a post", func(t *testing.T) { - postList, err := ss.Post().GetPostsBefore(channelId, post4.Id, 2, 0) + postList, err := ss.Post().GetPostsBefore(model.GetPostsOptions{ChannelId: channelId, PostId: post4.Id, PerPage: 2}) assert.Nil(t, err) assert.Equal(t, []string{post3.Id, post2.Id}, postList.Order) @@ -917,7 +916,116 @@ func testPostStoreGetPostsBeforeAfter(t *testing.T, ss store.Store) { }) t.Run("should return each post and the root of each thread after a post", func(t *testing.T) { - postList, err := ss.Post().GetPostsAfter(channelId, post4.Id, 2, 0) + postList, err := ss.Post().GetPostsAfter(model.GetPostsOptions{ChannelId: channelId, PostId: post4.Id, PerPage: 2}) + assert.Nil(t, err) + + assert.Equal(t, []string{post6.Id, post5.Id}, postList.Order) + assert.Equal(t, map[string]*model.Post{ + post2.Id: post2, + post4.Id: post4, + post5.Id: post5, + post6.Id: post6, + }, postList.Posts) + }) + }) + t.Run("with threads (skipFetchThreads)", func(t *testing.T) { + channelId := model.NewId() + userId := model.NewId() + + // This creates a series of posts that looks like: + // post1 + // post2 + // post3 (in response to post1) + // post4 (in response to post2) + // post5 + // post6 (in response to post2) + + post1, err := ss.Post().Save(&model.Post{ + ChannelId: channelId, + UserId: userId, + Message: "post1", + }) + require.Nil(t, err) + post1.ReplyCount = 1 + time.Sleep(time.Millisecond) + + post2, err := ss.Post().Save(&model.Post{ + ChannelId: channelId, + UserId: userId, + Message: "post2", + }) + require.Nil(t, err) + post2.ReplyCount = 2 + time.Sleep(time.Millisecond) + + post3, err := ss.Post().Save(&model.Post{ + ChannelId: channelId, + UserId: userId, + ParentId: post1.Id, + RootId: post1.Id, + Message: "post3", + }) + require.Nil(t, err) + time.Sleep(time.Millisecond) + + post4, err := ss.Post().Save(&model.Post{ + ChannelId: channelId, + UserId: userId, + RootId: post2.Id, + ParentId: post2.Id, + Message: "post4", + }) + require.Nil(t, err) + time.Sleep(time.Millisecond) + + post5, err := ss.Post().Save(&model.Post{ + ChannelId: channelId, + UserId: userId, + Message: "post5", + }) + require.Nil(t, err) + time.Sleep(time.Millisecond) + + post6, err := ss.Post().Save(&model.Post{ + ChannelId: channelId, + UserId: userId, + ParentId: post2.Id, + RootId: post2.Id, + Message: "post6", + }) + require.Nil(t, err) + + // Adding a post to a thread changes the UpdateAt timestamp of the parent post + post1.UpdateAt = post3.UpdateAt + post2.UpdateAt = post6.UpdateAt + + t.Run("should return each post and thread before a post", func(t *testing.T) { + postList, err := ss.Post().GetPostsBefore(model.GetPostsOptions{ChannelId: channelId, PostId: post4.Id, PerPage: 2, SkipFetchThreads: true}) + assert.Nil(t, err) + + assert.Equal(t, []string{post3.Id, post2.Id}, postList.Order) + assert.Equal(t, map[string]*model.Post{ + post1.Id: post1, + post2.Id: post2, + post3.Id: post3, + post4.Id: post4, + post6.Id: post6, + }, postList.Posts) + }) + + t.Run("should return each post and thread before a post with limit", func(t *testing.T) { + postList, err := ss.Post().GetPostsBefore(model.GetPostsOptions{ChannelId: channelId, PostId: post4.Id, PerPage: 1, SkipFetchThreads: true}) + assert.Nil(t, err) + + assert.Equal(t, []string{post3.Id}, postList.Order) + assert.Equal(t, map[string]*model.Post{ + post1.Id: post1, + post3.Id: post3, + }, postList.Posts) + }) + + t.Run("should return each post and the root of each thread after a post", func(t *testing.T) { + postList, err := ss.Post().GetPostsAfter(model.GetPostsOptions{ChannelId: channelId, PostId: post4.Id, PerPage: 2, SkipFetchThreads: true}) assert.Nil(t, err) assert.Equal(t, []string{post6.Id, post5.Id}, postList.Order) @@ -986,7 +1094,7 @@ func testPostStoreGetPostsSince(t *testing.T, ss store.Store) { require.Nil(t, err) time.Sleep(time.Millisecond) - postList, err := ss.Post().GetPostsSince(channelId, post3.CreateAt, false) + postList, err := ss.Post().GetPostsSince(model.GetPostsSinceOptions{ChannelId: channelId, Time: post3.CreateAt}, false) assert.Nil(t, err) assert.Equal(t, []string{ @@ -1017,7 +1125,7 @@ func testPostStoreGetPostsSince(t *testing.T, ss store.Store) { require.Nil(t, err) time.Sleep(time.Millisecond) - postList, err := ss.Post().GetPostsSince(channelId, post1.CreateAt, false) + postList, err := ss.Post().GetPostsSince(model.GetPostsSinceOptions{ChannelId: channelId, Time: post1.CreateAt}, false) assert.Nil(t, err) assert.Equal(t, []string{}, postList.Order) @@ -1039,12 +1147,12 @@ func testPostStoreGetPostsSince(t *testing.T, ss store.Store) { time.Sleep(time.Millisecond) // Make a request that returns no results - postList, err := ss.Post().GetPostsSince(channelId, post1.CreateAt, true) + postList, err := ss.Post().GetPostsSince(model.GetPostsSinceOptions{ChannelId: channelId, Time: post1.CreateAt}, true) require.Nil(t, err) require.Equal(t, model.NewPostList(), postList) // And then ensure that it doesn't cause future requests to also return no results - postList, err = ss.Post().GetPostsSince(channelId, post1.CreateAt-1, true) + postList, err = ss.Post().GetPostsSince(model.GetPostsSinceOptions{ChannelId: channelId, Time: post1.CreateAt - 1}, true) assert.Nil(t, err) assert.Equal(t, []string{post1.Id}, postList.Order) @@ -2173,17 +2281,17 @@ func testPostStoreOverwrite(t *testing.T, ss store.Store) { o3.Message = "zz" + model.NewId() + "QQQQQQQQQQ" o3, err = ss.Post().Save(o3) - r1, err := ss.Post().Get(o1.Id) + r1, err := ss.Post().Get(o1.Id, false) if err != nil { t.Fatal(err) } ro1 := r1.Posts[o1.Id] - r2, err := ss.Post().Get(o1.Id) + r2, err := ss.Post().Get(o1.Id, false) if err != nil { t.Fatal(err) } ro2 := r2.Posts[o2.Id] - r3, err := ss.Post().Get(o3.Id) + r3, err := ss.Post().Get(o3.Id, false) if err != nil { t.Fatal(err) } @@ -2201,7 +2309,7 @@ func testPostStoreOverwrite(t *testing.T, ss store.Store) { t.Fatal(err) } - r1, err = ss.Post().Get(o1.Id) + r1, err = ss.Post().Get(o1.Id, false) if err != nil { t.Fatal(err) } @@ -2219,7 +2327,7 @@ func testPostStoreOverwrite(t *testing.T, ss store.Store) { t.Fatal(err) } - r2, err = ss.Post().Get(o1.Id) + r2, err = ss.Post().Get(o1.Id, false) if err != nil { t.Fatal(err) } @@ -2237,7 +2345,7 @@ func testPostStoreOverwrite(t *testing.T, ss store.Store) { t.Fatal(err) } - r3, err = ss.Post().Get(o3.Id) + r3, err = ss.Post().Get(o3.Id, false) if err != nil { t.Fatal(err) } @@ -2255,7 +2363,7 @@ func testPostStoreOverwrite(t *testing.T, ss store.Store) { }) require.Nil(t, err) - r4, err := ss.Post().Get(o4.Id) + r4, err := ss.Post().Get(o4.Id, false) if err != nil { t.Fatal(err) } @@ -2270,7 +2378,7 @@ func testPostStoreOverwrite(t *testing.T, ss store.Store) { t.Fatal(err) } - r4, err = ss.Post().Get(o4.Id) + r4, err = ss.Post().Get(o4.Id, false) if err != nil { t.Fatal(err) } @@ -2306,17 +2414,17 @@ func testPostStoreGetPostsByIds(t *testing.T, ss store.Store) { o3, err = ss.Post().Save(o3) require.Nil(t, err) - r1, err := ss.Post().Get(o1.Id) + r1, err := ss.Post().Get(o1.Id, false) if err != nil { t.Fatal(err) } ro1 := r1.Posts[o1.Id] - r2, err := ss.Post().Get(o2.Id) + r2, err := ss.Post().Get(o2.Id, false) if err != nil { t.Fatal(err) } ro2 := r2.Posts[o2.Id] - r3, err := ss.Post().Get(o3.Id) + r3, err := ss.Post().Get(o3.Id, false) if err != nil { t.Fatal(err) } @@ -2444,15 +2552,15 @@ func testPostStorePermanentDeleteBatch(t *testing.T, ss store.Store) { _, err = ss.Post().PermanentDeleteBatch(2000, 1000) require.Nil(t, err) - if _, err := ss.Post().Get(o1.Id); err == nil { + if _, err := ss.Post().Get(o1.Id, false); err == nil { t.Fatalf("Should have not found post 1 after purge") } - if _, err := ss.Post().Get(o2.Id); err == nil { + if _, err := ss.Post().Get(o2.Id, false); err == nil { t.Fatalf("Should have not found post 2 after purge") } - if _, err := ss.Post().Get(o3.Id); err != nil { + if _, err := ss.Post().Get(o3.Id, false); err != nil { t.Fatalf("Should have not found post 3 after purge") } } diff --git a/store/storetest/reaction_store.go b/store/storetest/reaction_store.go index 83860c583c..06cf857d05 100644 --- a/store/storetest/reaction_store.go +++ b/store/storetest/reaction_store.go @@ -42,7 +42,7 @@ func testReactionSave(t *testing.T, ss store.Store) { } var secondUpdateAt int64 - postList, err := ss.Post().Get(reaction1.PostId) + postList, err := ss.Post().Get(reaction1.PostId, false) if err != nil { t.Fatal(err) } @@ -69,7 +69,7 @@ func testReactionSave(t *testing.T, ss store.Store) { t.Fatal(err) } - postList, err = ss.Post().Get(reaction2.PostId) + postList, err = ss.Post().Get(reaction2.PostId, false) if err != nil { t.Fatal(err) } @@ -123,7 +123,7 @@ func testReactionDelete(t *testing.T, ss store.Store) { _, err = ss.Reaction().Save(reaction) require.Nil(t, err) - result, err := ss.Post().Get(reaction.PostId) + result, err := ss.Post().Get(reaction.PostId, false) if err != nil { t.Fatal(err) } @@ -138,7 +138,7 @@ func testReactionDelete(t *testing.T, ss store.Store) { } else if len(reactions) != 0 { t.Fatal("should've deleted reaction") } - postList, err := ss.Post().Get(post.Id) + postList, err := ss.Post().Get(post.Id, false) if err != nil { t.Fatal(err) } @@ -316,7 +316,7 @@ func testReactionDeleteAllWithEmojiName(t *testing.T, ss store.Store) { } // check that the posts are updated - postList, err := ss.Post().Get(post.Id) + postList, err := ss.Post().Get(post.Id, false) if err != nil { t.Fatal(err) } @@ -324,7 +324,7 @@ func testReactionDeleteAllWithEmojiName(t *testing.T, ss store.Store) { t.Fatal("post should still have reactions") } - postList, err = ss.Post().Get(post2.Id) + postList, err = ss.Post().Get(post2.Id, false) if err != nil { t.Fatal(err) } @@ -332,7 +332,7 @@ func testReactionDeleteAllWithEmojiName(t *testing.T, ss store.Store) { t.Fatal("post should still have reactions") } - postList, err = ss.Post().Get(post3.Id) + postList, err = ss.Post().Get(post3.Id, false) if err != nil { t.Fatal(err) } diff --git a/store/timer_layer.go b/store/timer_layer.go index da3bcb5a8c..577852408e 100644 --- a/store/timer_layer.go +++ b/store/timer_layer.go @@ -3728,6 +3728,23 @@ func (s *TimerLayerOAuthStore) UpdateApp(app *model.OAuthApp) (*model.OAuthApp, return resultVar0, resultVar1 } +func (s *TimerLayerPluginStore) CompareAndDelete(keyVal *model.PluginKeyValue, oldValue []byte) (bool, *model.AppError) { + start := timemodule.Now() + + resultVar0, resultVar1 := s.PluginStore.CompareAndDelete(keyVal, oldValue) + + t := timemodule.Now() + elapsed := t.Sub(start) + if s.Root.Metrics != nil { + success := "false" + if resultVar1 == nil { + success = "true" + } + s.Root.Metrics.ObserveStoreMethodDuration("PluginStore.CompareAndDelete", success, float64(elapsed)) + } + return resultVar0, resultVar1 +} + func (s *TimerLayerPluginStore) CompareAndSet(keyVal *model.PluginKeyValue, oldValue []byte) (bool, *model.AppError) { start := timemodule.Now() @@ -3932,10 +3949,10 @@ func (s *TimerLayerPostStore) Delete(postId string, time int64, deleteByID strin return resultVar0 } -func (s *TimerLayerPostStore) Get(id string) (*model.PostList, *model.AppError) { +func (s *TimerLayerPostStore) Get(id string, skipFetchThreads bool) (*model.PostList, *model.AppError) { start := timemodule.Now() - resultVar0, resultVar1 := s.PostStore.Get(id) + resultVar0, resultVar1 := s.PostStore.Get(id, skipFetchThreads) t := timemodule.Now() elapsed := t.Sub(start) @@ -4136,10 +4153,10 @@ func (s *TimerLayerPostStore) GetPostIdBeforeTime(channelId string, time int64) return resultVar0, resultVar1 } -func (s *TimerLayerPostStore) GetPosts(channelId string, offset int, limit int, allowFromCache bool) (*model.PostList, *model.AppError) { +func (s *TimerLayerPostStore) GetPosts(options model.GetPostsOptions, allowFromCache bool) (*model.PostList, *model.AppError) { start := timemodule.Now() - resultVar0, resultVar1 := s.PostStore.GetPosts(channelId, offset, limit, allowFromCache) + resultVar0, resultVar1 := s.PostStore.GetPosts(options, allowFromCache) t := timemodule.Now() elapsed := t.Sub(start) @@ -4153,10 +4170,10 @@ func (s *TimerLayerPostStore) GetPosts(channelId string, offset int, limit int, return resultVar0, resultVar1 } -func (s *TimerLayerPostStore) GetPostsAfter(channelId string, postId string, numPosts int, offset int) (*model.PostList, *model.AppError) { +func (s *TimerLayerPostStore) GetPostsAfter(options model.GetPostsOptions) (*model.PostList, *model.AppError) { start := timemodule.Now() - resultVar0, resultVar1 := s.PostStore.GetPostsAfter(channelId, postId, numPosts, offset) + resultVar0, resultVar1 := s.PostStore.GetPostsAfter(options) t := timemodule.Now() elapsed := t.Sub(start) @@ -4187,10 +4204,10 @@ func (s *TimerLayerPostStore) GetPostsBatchForIndexing(startTime int64, endTime return resultVar0, resultVar1 } -func (s *TimerLayerPostStore) GetPostsBefore(channelId string, postId string, numPosts int, offset int) (*model.PostList, *model.AppError) { +func (s *TimerLayerPostStore) GetPostsBefore(options model.GetPostsOptions) (*model.PostList, *model.AppError) { start := timemodule.Now() - resultVar0, resultVar1 := s.PostStore.GetPostsBefore(channelId, postId, numPosts, offset) + resultVar0, resultVar1 := s.PostStore.GetPostsBefore(options) t := timemodule.Now() elapsed := t.Sub(start) @@ -4238,10 +4255,10 @@ func (s *TimerLayerPostStore) GetPostsCreatedAt(channelId string, time int64) ([ return resultVar0, resultVar1 } -func (s *TimerLayerPostStore) GetPostsSince(channelId string, time int64, allowFromCache bool) (*model.PostList, *model.AppError) { +func (s *TimerLayerPostStore) GetPostsSince(options model.GetPostsSinceOptions, allowFromCache bool) (*model.PostList, *model.AppError) { start := timemodule.Now() - resultVar0, resultVar1 := s.PostStore.GetPostsSince(channelId, time, allowFromCache) + resultVar0, resultVar1 := s.PostStore.GetPostsSince(options, allowFromCache) t := timemodule.Now() elapsed := t.Sub(start)