summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCorey Hulen <corey@hulen.com>2016-12-13 19:23:36 -0800
committerenahum <nahumhbl@gmail.com>2016-12-14 00:23:36 -0300
commitaacbe995483b694e169acdc63136171dff3725d9 (patch)
treee60e67a1378ce929f42e41a49c2215a0289eae78
parent51b794501e21e1d19a58bb6dc273a5425c03a107 (diff)
downloadchat-aacbe995483b694e169acdc63136171dff3725d9.tar.gz
chat-aacbe995483b694e169acdc63136171dff3725d9.tar.bz2
chat-aacbe995483b694e169acdc63136171dff3725d9.zip
PLT-4982 Adding caching to user profiles (#4782)
-rw-r--r--api/user.go2
-rw-r--r--api/web_hub.go1
-rw-r--r--store/sql_user_store.go38
-rw-r--r--store/sql_user_store_test.go56
-rw-r--r--store/store.go3
5 files changed, 92 insertions, 8 deletions
diff --git a/api/user.go b/api/user.go
index 0e74a577c..84d3641f2 100644
--- a/api/user.go
+++ b/api/user.go
@@ -2735,7 +2735,7 @@ func getProfilesByIds(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
- if result := <-Srv.Store.User().GetProfileByIds(userIds); result.Err != nil {
+ if result := <-Srv.Store.User().GetProfileByIds(userIds, true); result.Err != nil {
c.Err = result.Err
return
} else {
diff --git a/api/web_hub.go b/api/web_hub.go
index cad77395b..ce11d26b0 100644
--- a/api/web_hub.go
+++ b/api/web_hub.go
@@ -118,6 +118,7 @@ func InvalidateCacheForUser(userId string) {
func InvalidateCacheForUserSkipClusterSend(userId string) {
Srv.Store.Channel().InvalidateAllChannelMembersForUser(userId)
Srv.Store.User().InvalidateProfilesInChannelCacheByUser(userId)
+ Srv.Store.User().InvalidatProfileCacheForUser(userId)
if len(hubs) != 0 {
GetHubForUserId(userId).InvalidateUser(userId)
diff --git a/store/sql_user_store.go b/store/sql_user_store.go
index 286b6551a..5882ed454 100644
--- a/store/sql_user_store.go
+++ b/store/sql_user_store.go
@@ -19,6 +19,8 @@ const (
MISSING_AUTH_ACCOUNT_ERROR = "store.sql_user.get_by_auth.missing_account.app_error"
PROFILES_IN_CHANNEL_CACHE_SIZE = 5000
PROFILES_IN_CHANNEL_CACHE_SEC = 900 // 15 mins
+ PROFILE_BY_IDS_CACHE_SIZE = 20000
+ PROFILE_BY_IDS_CACHE_SEC = 900 // 15 mins
USER_SEARCH_OPTION_NAMES_ONLY = "names_only"
USER_SEARCH_OPTION_NAMES_ONLY_NO_FULL_NAME = "names_only_no_full_name"
USER_SEARCH_OPTION_ALL_NO_FULL_NAME = "all_no_full_name"
@@ -34,9 +36,15 @@ type SqlUserStore struct {
}
var profilesInChannelCache *utils.Cache = utils.NewLru(PROFILES_IN_CHANNEL_CACHE_SIZE)
+var profileByIdsCache *utils.Cache = utils.NewLru(PROFILE_BY_IDS_CACHE_SIZE)
func ClearUserCaches() {
profilesInChannelCache.Purge()
+ profileByIdsCache.Purge()
+}
+
+func (us SqlUserStore) InvalidatProfileCacheForUser(userId string) {
+ profileByIdsCache.Remove(userId)
}
func NewSqlUserStore(sqlStore *SqlStore) UserStore {
@@ -776,7 +784,7 @@ func (us SqlUserStore) GetRecentlyActiveUsersForTeam(teamId string) StoreChannel
return storeChannel
}
-func (us SqlUserStore) GetProfileByIds(userIds []string) StoreChannel {
+func (us SqlUserStore) GetProfileByIds(userIds []string, allowFromCache bool) StoreChannel {
storeChannel := make(StoreChannel, 1)
@@ -784,10 +792,33 @@ func (us SqlUserStore) GetProfileByIds(userIds []string) StoreChannel {
result := StoreResult{}
var users []*model.User
+ userMap := make(map[string]*model.User)
props := make(map[string]interface{})
idQuery := ""
+ remainingUserIds := make([]string, 0)
+
+ if allowFromCache {
+ for _, userId := range userIds {
+ if cacheItem, ok := profileByIdsCache.Get(userId); ok {
+ u := cacheItem.(*model.User)
+ userMap[u.Id] = u
+ } else {
+ remainingUserIds = append(remainingUserIds, userId)
+ }
+ }
+ } else {
+ remainingUserIds = userIds
+ }
- for index, userId := range userIds {
+ // If everything came from the cache then just return
+ if len(remainingUserIds) == 0 {
+ result.Data = userMap
+ storeChannel <- result
+ close(storeChannel)
+ return
+ }
+
+ for index, userId := range remainingUserIds {
if len(idQuery) > 0 {
idQuery += ", "
}
@@ -800,13 +831,12 @@ func (us SqlUserStore) GetProfileByIds(userIds []string) StoreChannel {
result.Err = model.NewLocAppError("SqlUserStore.GetProfileByIds", "store.sql_user.get_profiles.app_error", nil, err.Error())
} else {
- userMap := make(map[string]*model.User)
-
for _, u := range users {
u.Password = ""
u.AuthData = new(string)
*u.AuthData = ""
userMap[u.Id] = u
+ profileByIdsCache.AddWithExpiresInSecs(u.Id, u, PROFILE_BY_IDS_CACHE_SEC)
}
result.Data = userMap
diff --git a/store/sql_user_store_test.go b/store/sql_user_store_test.go
index 56d9c0a6a..765a164e3 100644
--- a/store/sql_user_store_test.go
+++ b/store/sql_user_store_test.go
@@ -453,7 +453,59 @@ func TestUserStoreGetProfilesByIds(t *testing.T) {
Must(store.User().Save(u2))
Must(store.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u2.Id}))
- if r1 := <-store.User().GetProfileByIds([]string{u1.Id, u2.Id}); r1.Err != nil {
+ if r1 := <-store.User().GetProfileByIds([]string{u1.Id}, false); r1.Err != nil {
+ t.Fatal(r1.Err)
+ } else {
+ users := r1.Data.(map[string]*model.User)
+ if len(users) != 1 {
+ t.Fatal("invalid returned users")
+ }
+
+ if users[u1.Id].Id != u1.Id {
+ t.Fatal("invalid returned user")
+ }
+ }
+
+ if r1 := <-store.User().GetProfileByIds([]string{u1.Id}, true); r1.Err != nil {
+ t.Fatal(r1.Err)
+ } else {
+ users := r1.Data.(map[string]*model.User)
+ if len(users) != 1 {
+ t.Fatal("invalid returned users")
+ }
+
+ if users[u1.Id].Id != u1.Id {
+ t.Fatal("invalid returned user")
+ }
+ }
+
+ if r1 := <-store.User().GetProfileByIds([]string{u1.Id, u2.Id}, true); r1.Err != nil {
+ t.Fatal(r1.Err)
+ } else {
+ users := r1.Data.(map[string]*model.User)
+ if len(users) != 2 {
+ t.Fatal("invalid returned users")
+ }
+
+ if users[u1.Id].Id != u1.Id {
+ t.Fatal("invalid returned user")
+ }
+ }
+
+ if r1 := <-store.User().GetProfileByIds([]string{u1.Id, u2.Id}, true); r1.Err != nil {
+ t.Fatal(r1.Err)
+ } else {
+ users := r1.Data.(map[string]*model.User)
+ if len(users) != 2 {
+ t.Fatal("invalid returned users")
+ }
+
+ if users[u1.Id].Id != u1.Id {
+ t.Fatal("invalid returned user")
+ }
+ }
+
+ if r1 := <-store.User().GetProfileByIds([]string{u1.Id, u2.Id}, false); r1.Err != nil {
t.Fatal(r1.Err)
} else {
users := r1.Data.(map[string]*model.User)
@@ -466,7 +518,7 @@ func TestUserStoreGetProfilesByIds(t *testing.T) {
}
}
- if r1 := <-store.User().GetProfileByIds([]string{u1.Id}); r1.Err != nil {
+ if r1 := <-store.User().GetProfileByIds([]string{u1.Id}, false); r1.Err != nil {
t.Fatal(r1.Err)
} else {
users := r1.Data.(map[string]*model.User)
diff --git a/store/store.go b/store/store.go
index ffc325eea..236fbbc13 100644
--- a/store/store.go
+++ b/store/store.go
@@ -155,7 +155,8 @@ type UserStore interface {
GetProfilesByUsernames(usernames []string, teamId string) StoreChannel
GetAllProfiles(offset int, limit int) StoreChannel
GetProfiles(teamId string, offset int, limit int) StoreChannel
- GetProfileByIds(userId []string) StoreChannel
+ GetProfileByIds(userId []string, allowFromCache bool) StoreChannel
+ InvalidatProfileCacheForUser(userId string)
GetByEmail(email string) StoreChannel
GetByAuth(authData *string, authService string) StoreChannel
GetAllUsingAuthService(authService string) StoreChannel