diff options
-rw-r--r-- | api/authorization.go | 2 | ||||
-rw-r--r-- | api/channel.go | 24 | ||||
-rw-r--r-- | api/command.go | 2 | ||||
-rw-r--r-- | api/email_batching.go | 2 | ||||
-rw-r--r-- | api/file.go | 2 | ||||
-rw-r--r-- | api/post.go | 5 | ||||
-rw-r--r-- | api/web_hub.go | 1 | ||||
-rw-r--r-- | api/webhook.go | 6 | ||||
-rw-r--r-- | cmd/platform/channelargs.go | 2 | ||||
-rw-r--r-- | store/sql_channel_store.go | 41 | ||||
-rw-r--r-- | store/sql_channel_store_test.go | 28 | ||||
-rw-r--r-- | store/store.go | 3 |
12 files changed, 82 insertions, 36 deletions
diff --git a/api/authorization.go b/api/authorization.go index 8b3140b0f..e931c4b33 100644 --- a/api/authorization.go +++ b/api/authorization.go @@ -79,7 +79,7 @@ func HasPermissionToChannelContext(c *Context, channelId string, permission *mod } } - cc := Srv.Store.Channel().Get(channelId) + cc := Srv.Store.Channel().Get(channelId, true) if ccresult := <-cc; ccresult.Err == nil { channel := ccresult.Data.(*model.Channel) diff --git a/api/channel.go b/api/channel.go index 91b7ad565..a14987c16 100644 --- a/api/channel.go +++ b/api/channel.go @@ -216,7 +216,7 @@ func updateChannel(c *Context, w http.ResponseWriter, r *http.Request) { return } - sc := Srv.Store.Channel().Get(channel.Id) + sc := Srv.Store.Channel().Get(channel.Id, true) cmc := Srv.Store.Channel().GetMember(channel.Id, c.Session.UserId) if cresult := <-sc; cresult.Err != nil { @@ -264,6 +264,7 @@ func updateChannel(c *Context, w http.ResponseWriter, r *http.Request) { oldChannel.Type = channel.Type } + InvalidateCacheForChannel(oldChannel.Id) if ucresult := <-Srv.Store.Channel().Update(oldChannel); ucresult.Err != nil { c.Err = ucresult.Err return @@ -292,7 +293,7 @@ func updateChannelHeader(c *Context, w http.ResponseWriter, r *http.Request) { return } - sc := Srv.Store.Channel().Get(channelId) + sc := Srv.Store.Channel().Get(channelId, true) cmc := Srv.Store.Channel().GetMember(channelId, c.Session.UserId) if cresult := <-sc; cresult.Err != nil { @@ -312,6 +313,7 @@ func updateChannelHeader(c *Context, w http.ResponseWriter, r *http.Request) { oldChannelHeader := channel.Header channel.Header = channelHeader + InvalidateCacheForChannel(channel.Id) if ucresult := <-Srv.Store.Channel().Update(channel); ucresult.Err != nil { c.Err = ucresult.Err return @@ -400,7 +402,7 @@ func updateChannelPurpose(c *Context, w http.ResponseWriter, r *http.Request) { return } - sc := Srv.Store.Channel().Get(channelId) + sc := Srv.Store.Channel().Get(channelId, true) cmc := Srv.Store.Channel().GetMember(channelId, c.Session.UserId) if cresult := <-sc; cresult.Err != nil { @@ -419,6 +421,7 @@ func updateChannelPurpose(c *Context, w http.ResponseWriter, r *http.Request) { channel.Purpose = channelPurpose + InvalidateCacheForChannel(channel.Id) if ucresult := <-Srv.Store.Channel().Update(channel); ucresult.Err != nil { c.Err = ucresult.Err return @@ -542,7 +545,7 @@ func JoinChannelByName(c *Context, userId string, teamId string, channelName str } func JoinChannelById(c *Context, userId string, channelId string) (*model.AppError, *model.Channel) { - channelChannel := Srv.Store.Channel().Get(channelId) + channelChannel := Srv.Store.Channel().Get(channelId, true) userChannel := Srv.Store.User().Get(userId) return joinChannel(c, channelChannel, userChannel) @@ -713,7 +716,7 @@ func leave(c *Context, w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) id := params["channel_id"] - sc := Srv.Store.Channel().Get(id) + sc := Srv.Store.Channel().Get(id, true) uc := Srv.Store.User().Get(c.Session.UserId) ccm := Srv.Store.Channel().GetMemberCount(id, false) @@ -769,7 +772,7 @@ func deleteChannel(c *Context, w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) id := params["channel_id"] - sc := Srv.Store.Channel().Get(id) + sc := Srv.Store.Channel().Get(id, true) scm := Srv.Store.Channel().GetMember(id, c.Session.UserId) cmc := Srv.Store.Channel().GetMemberCount(id, false) uc := Srv.Store.User().Get(c.Session.UserId) @@ -842,6 +845,7 @@ func deleteChannel(c *Context, w http.ResponseWriter, r *http.Request) { }() } + InvalidateCacheForChannel(channel.Id) if dresult := <-Srv.Store.Channel().Delete(channel.Id, model.GetMillis()); dresult.Err != nil { c.Err = dresult.Err return @@ -876,7 +880,7 @@ func getChannel(c *Context, w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) id := params["channel_id"] - cchan := Srv.Store.Channel().Get(id) + cchan := Srv.Store.Channel().Get(id, true) cmchan := Srv.Store.Channel().GetMember(id, c.Session.UserId) if cresult := <-cchan; cresult.Err != nil { @@ -956,7 +960,7 @@ func getChannelStats(c *Context, w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) id := params["channel_id"] - sc := Srv.Store.Channel().Get(id) + sc := Srv.Store.Channel().Get(id, true) var channel *model.Channel if result := <-sc; result.Err != nil { c.Err = result.Err @@ -1026,7 +1030,7 @@ func addMember(c *Context, w http.ResponseWriter, r *http.Request) { return } - sc := Srv.Store.Channel().Get(id) + sc := Srv.Store.Channel().Get(id, true) ouc := Srv.Store.User().Get(c.Session.UserId) nuc := Srv.Store.User().Get(userId) if nresult := <-nuc; nresult.Err != nil { @@ -1081,7 +1085,7 @@ func removeMember(c *Context, w http.ResponseWriter, r *http.Request) { return } - sc := Srv.Store.Channel().Get(channelId) + sc := Srv.Store.Channel().Get(channelId, true) cmc := Srv.Store.Channel().GetMember(channelId, c.Session.UserId) ouc := Srv.Store.User().Get(userIdToRemove) diff --git a/api/command.go b/api/command.go index a6c3ea201..842d67843 100644 --- a/api/command.go +++ b/api/command.go @@ -119,7 +119,7 @@ func executeCommand(c *Context, w http.ResponseWriter, r *http.Request) { return } - chanChan := Srv.Store.Channel().Get(commandArgs.ChannelId) + chanChan := Srv.Store.Channel().Get(commandArgs.ChannelId, true) teamChan := Srv.Store.Team().Get(c.TeamId) userChan := Srv.Store.User().Get(c.Session.UserId) diff --git a/api/email_batching.go b/api/email_batching.go index aa2836570..608d839da 100644 --- a/api/email_batching.go +++ b/api/email_batching.go @@ -214,7 +214,7 @@ func sendBatchedEmailNotification(userId string, notifications []*batchedNotific func renderBatchedPost(template *utils.HTMLTemplate, post *model.Post, teamName string, displayNameFormat string, translateFunc i18n.TranslateFunc) string { schan := Srv.Store.User().Get(post.UserId) - cchan := Srv.Store.Channel().Get(post.ChannelId) + cchan := Srv.Store.Channel().Get(post.ChannelId, true) template.Props["Button"] = translateFunc("api.email_batching.render_batched_post.go_to_post") template.Props["PostMessage"] = getMessageForNotification(post, translateFunc) diff --git a/api/file.go b/api/file.go index b91ea7d4e..ede04e2d8 100644 --- a/api/file.go +++ b/api/file.go @@ -584,7 +584,7 @@ func migrateFilenamesToFileInfos(post *model.Post) []*model.FileInfo { return []*model.FileInfo{} } - cchan := Srv.Store.Channel().Get(post.ChannelId) + cchan := Srv.Store.Channel().Get(post.ChannelId, true) // There's a weird bug that rarely happens where a post ends up with duplicate Filenames so remove those filenames := utils.RemoveDuplicatesFromStringArray(post.Filenames) diff --git a/api/post.go b/api/post.go index 5cce766c0..354fe35db 100644 --- a/api/post.go +++ b/api/post.go @@ -61,7 +61,7 @@ func createPost(c *Context, w http.ResponseWriter, r *http.Request) { } post.UserId = c.Session.UserId - cchan := Srv.Store.Channel().Get(post.ChannelId) + cchan := Srv.Store.Channel().Get(post.ChannelId, true) if !HasPermissionToChannelContext(c, post.ChannelId, model.PERMISSION_CREATE_POST) { return @@ -166,6 +166,7 @@ func CreatePost(c *Context, post *model.Post, triggerWebhooks bool) (*model.Post } } + InvalidateCacheForChannel(rpost.ChannelId) InvalidateCacheForChannelPosts(rpost.ChannelId) handlePostEvents(c, rpost, triggerWebhooks) @@ -245,7 +246,7 @@ func CreateWebhookPost(c *Context, channelId, text, overrideUsername, overrideIc func handlePostEvents(c *Context, post *model.Post, triggerWebhooks bool) { tchan := Srv.Store.Team().Get(c.TeamId) - cchan := Srv.Store.Channel().Get(post.ChannelId) + cchan := Srv.Store.Channel().Get(post.ChannelId, true) uchan := Srv.Store.User().Get(post.UserId) var team *model.Team diff --git a/api/web_hub.go b/api/web_hub.go index 64903ea59..b66925004 100644 --- a/api/web_hub.go +++ b/api/web_hub.go @@ -113,6 +113,7 @@ func InvalidateCacheForChannel(channelId string) { func InvalidateCacheForChannelSkipClusterSend(channelId string) { Srv.Store.User().InvalidateProfilesInChannelCache(channelId) Srv.Store.Channel().InvalidateMemberCount(channelId) + Srv.Store.Channel().InvalidateChannel(channelId) } func InvalidateCacheForChannelPosts(channelId string) { diff --git a/api/webhook.go b/api/webhook.go index b164d0ae7..8a4263533 100644 --- a/api/webhook.go +++ b/api/webhook.go @@ -55,7 +55,7 @@ func createIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) { return } - cchan := Srv.Store.Channel().Get(hook.ChannelId) + cchan := Srv.Store.Channel().Get(hook.ChannelId, true) hook.UserId = c.Session.UserId hook.TeamId = c.TeamId @@ -174,7 +174,7 @@ func createOutgoingHook(c *Context, w http.ResponseWriter, r *http.Request) { hook.TeamId = c.TeamId if len(hook.ChannelId) != 0 { - cchan := Srv.Store.Channel().Get(hook.ChannelId) + cchan := Srv.Store.Channel().Get(hook.ChannelId, true) var channel *model.Channel if result := <-cchan; result.Err != nil { @@ -447,7 +447,7 @@ func incomingWebhook(c *Context, w http.ResponseWriter, r *http.Request) { cchan = Srv.Store.Channel().GetByName(hook.TeamId, channelName) } else { - cchan = Srv.Store.Channel().Get(hook.ChannelId) + cchan = Srv.Store.Channel().Get(hook.ChannelId, true) } overrideUsername := parsedRequest.Username diff --git a/cmd/platform/channelargs.go b/cmd/platform/channelargs.go index 136d73fd2..b94bb6b70 100644 --- a/cmd/platform/channelargs.go +++ b/cmd/platform/channelargs.go @@ -50,7 +50,7 @@ func getChannelFromChannelArg(channelArg string) *model.Channel { } if channel == nil { - if result := <-api.Srv.Store.Channel().Get(channelPart); result.Err == nil { + if result := <-api.Srv.Store.Channel().Get(channelPart, true); result.Err == nil { channel = result.Data.(*model.Channel) } } diff --git a/store/sql_channel_store.go b/store/sql_channel_store.go index 47c172f77..7cdebba8a 100644 --- a/store/sql_channel_store.go +++ b/store/sql_channel_store.go @@ -26,6 +26,9 @@ const ( CHANNEL_MEMBERS_COUNTS_CACHE_SIZE = 20000 CHANNEL_MEMBERS_COUNTS_CACHE_SEC = 900 // 15 mins + + CHANNEL_CACHE_SIZE = 5000 + CHANNEL_CACHE_SEC = 900 // 15 mins ) type SqlChannelStore struct { @@ -34,10 +37,12 @@ type SqlChannelStore struct { var channelMemberCountsCache = utils.NewLru(CHANNEL_MEMBERS_COUNTS_CACHE_SIZE) var allChannelMembersForUserCache = utils.NewLru(ALL_CHANNEL_MEMBERS_FOR_USER_CACHE_SIZE) +var channelCache = utils.NewLru(CHANNEL_CACHE_SIZE) func ClearChannelCaches() { channelMemberCountsCache.Purge() allChannelMembersForUserCache.Purge() + channelCache.Purge() } func NewSqlChannelStore(sqlStore *SqlStore) ChannelStore { @@ -297,19 +302,24 @@ func (s SqlChannelStore) extraUpdated(channel *model.Channel) StoreChannel { return storeChannel } -func (s SqlChannelStore) Get(id string) StoreChannel { - return s.get(id, false) +func (us SqlChannelStore) InvalidateChannel(id string) { + channelCache.Remove(id) +} + +func (s SqlChannelStore) Get(id string, allowFromCache bool) StoreChannel { + return s.get(id, false, allowFromCache) } func (s SqlChannelStore) GetFromMaster(id string) StoreChannel { - return s.get(id, true) + return s.get(id, true, false) } -func (s SqlChannelStore) get(id string, master bool) StoreChannel { +func (s SqlChannelStore) get(id string, master bool, allowFromCache bool) StoreChannel { storeChannel := make(StoreChannel, 1) go func() { result := StoreResult{} + metrics := einterfaces.GetMetricsInterface() var db *gorp.DbMap if master { @@ -318,12 +328,33 @@ func (s SqlChannelStore) get(id string, master bool) StoreChannel { db = s.GetReplica() } + if allowFromCache { + if cacheItem, ok := channelCache.Get(id); ok { + if metrics != nil { + metrics.IncrementMemCacheHitCounter("Channel") + } + result.Data = cacheItem.(*model.Channel) + storeChannel <- result + close(storeChannel) + return + } else { + if metrics != nil { + metrics.IncrementMemCacheMissCounter("Channel") + } + } + } else { + if metrics != nil { + metrics.IncrementMemCacheMissCounter("Channel") + } + } + if obj, err := db.Get(model.Channel{}, id); err != nil { result.Err = model.NewLocAppError("SqlChannelStore.Get", "store.sql_channel.get.find.app_error", nil, "id="+id+", "+err.Error()) } else if obj == nil { result.Err = model.NewLocAppError("SqlChannelStore.Get", "store.sql_channel.get.existing.app_error", nil, "id="+id) } else { result.Data = obj.(*model.Channel) + channelCache.AddWithExpiresInSecs(id, obj.(*model.Channel), CHANNEL_MEMBERS_COUNTS_CACHE_SEC) } storeChannel <- result @@ -869,7 +900,7 @@ func (s SqlChannelStore) RemoveMember(channelId string, userId string) StoreChan result := StoreResult{} // Grab the channel we are saving this member to - if cr := <-s.Get(channelId); cr.Err != nil { + if cr := <-s.Get(channelId, true); cr.Err != nil { result.Err = cr.Err } else { channel := cr.Data.(*model.Channel) diff --git a/store/sql_channel_store_test.go b/store/sql_channel_store_test.go index 64e44cbb3..7aeef98cc 100644 --- a/store/sql_channel_store_test.go +++ b/store/sql_channel_store_test.go @@ -181,7 +181,7 @@ func TestChannelStoreGet(t *testing.T) { o1.Type = model.CHANNEL_OPEN Must(store.Channel().Save(&o1)) - if r1 := <-store.Channel().Get(o1.Id); r1.Err != nil { + if r1 := <-store.Channel().Get(o1.Id, false); r1.Err != nil { t.Fatal(r1.Err) } else { if r1.Data.(*model.Channel).ToJson() != o1.ToJson() { @@ -189,7 +189,7 @@ func TestChannelStoreGet(t *testing.T) { } } - if err := (<-store.Channel().Get("")).Err; err == nil { + if err := (<-store.Channel().Get("", false)).Err; err == nil { t.Fatal("Missing id should have failed") } @@ -223,7 +223,7 @@ func TestChannelStoreGet(t *testing.T) { Must(store.Channel().SaveDirectChannel(&o2, &m1, &m2)) - if r2 := <-store.Channel().Get(o2.Id); r2.Err != nil { + if r2 := <-store.Channel().Get(o2.Id, false); r2.Err != nil { t.Fatal(r2.Err) } else { if r2.Data.(*model.Channel).ToJson() != o2.ToJson() { @@ -231,6 +231,14 @@ func TestChannelStoreGet(t *testing.T) { } } + if r4 := <-store.Channel().Get(o2.Id, true); r4.Err != nil { + t.Fatal(r4.Err) + } else { + if r4.Data.(*model.Channel).ToJson() != o2.ToJson() { + t.Fatal("invalid returned channel") + } + } + if r3 := <-store.Channel().GetAll(o1.TeamId); r3.Err != nil { t.Fatal(r3.Err) } else { @@ -311,7 +319,7 @@ func TestChannelStoreDelete(t *testing.T) { t.Fatal(r.Err) } - if r := <-store.Channel().Get(o1.Id); r.Data.(*model.Channel).DeleteAt == 0 { + if r := <-store.Channel().Get(o1.Id, false); r.Data.(*model.Channel).DeleteAt == 0 { t.Fatal("should have been deleted") } @@ -367,7 +375,7 @@ func TestChannelMemberStore(t *testing.T) { c1.Type = model.CHANNEL_OPEN c1 = *Must(store.Channel().Save(&c1)).(*model.Channel) - c1t1 := (<-store.Channel().Get(c1.Id)).Data.(*model.Channel) + c1t1 := (<-store.Channel().Get(c1.Id, false)).Data.(*model.Channel) t1 := c1t1.ExtraUpdateAt u1 := model.User{} @@ -394,7 +402,7 @@ func TestChannelMemberStore(t *testing.T) { o2.NotifyProps = model.GetDefaultChannelNotifyProps() Must(store.Channel().SaveMember(&o2)) - c1t2 := (<-store.Channel().Get(c1.Id)).Data.(*model.Channel) + c1t2 := (<-store.Channel().Get(c1.Id, false)).Data.(*model.Channel) t2 := c1t2.ExtraUpdateAt if t2 <= t1 { @@ -423,7 +431,7 @@ func TestChannelMemberStore(t *testing.T) { t.Fatal("should have removed 1 member") } - c1t3 := (<-store.Channel().Get(c1.Id)).Data.(*model.Channel) + c1t3 := (<-store.Channel().Get(c1.Id, false)).Data.(*model.Channel) t3 := c1t3.ExtraUpdateAt if t3 <= t2 || t3 <= t1 { @@ -439,7 +447,7 @@ func TestChannelMemberStore(t *testing.T) { t.Fatal("Should have been a duplicate") } - c1t4 := (<-store.Channel().Get(c1.Id)).Data.(*model.Channel) + c1t4 := (<-store.Channel().Get(c1.Id, false)).Data.(*model.Channel) t4 := c1t4.ExtraUpdateAt if t4 != t3 { t.Fatal("Should not update time upon failure") @@ -456,7 +464,7 @@ func TestChannelDeleteMemberStore(t *testing.T) { c1.Type = model.CHANNEL_OPEN c1 = *Must(store.Channel().Save(&c1)).(*model.Channel) - c1t1 := (<-store.Channel().Get(c1.Id)).Data.(*model.Channel) + c1t1 := (<-store.Channel().Get(c1.Id, false)).Data.(*model.Channel) t1 := c1t1.ExtraUpdateAt u1 := model.User{} @@ -483,7 +491,7 @@ func TestChannelDeleteMemberStore(t *testing.T) { o2.NotifyProps = model.GetDefaultChannelNotifyProps() Must(store.Channel().SaveMember(&o2)) - c1t2 := (<-store.Channel().Get(c1.Id)).Data.(*model.Channel) + c1t2 := (<-store.Channel().Get(c1.Id, false)).Data.(*model.Channel) t2 := c1t2.ExtraUpdateAt if t2 <= t1 { diff --git a/store/store.go b/store/store.go index 2f5065b88..9fe566844 100644 --- a/store/store.go +++ b/store/store.go @@ -85,7 +85,8 @@ type ChannelStore interface { CreateDirectChannel(userId string, otherUserId string) StoreChannel SaveDirectChannel(channel *model.Channel, member1 *model.ChannelMember, member2 *model.ChannelMember) StoreChannel Update(channel *model.Channel) StoreChannel - Get(id string) StoreChannel + Get(id string, allowFromCache bool) StoreChannel + InvalidateChannel(id string) GetFromMaster(id string) StoreChannel Delete(channelId string, time int64) StoreChannel SetDeleteAt(channelId string, deleteAt int64, updateAt int64) StoreChannel |