PLT-4982 Adding caching to user profiles (#4782)

This commit is contained in:
Corey Hulen
2016-12-13 19:23:36 -08:00
committed by enahum
parent 51b794501e
commit aacbe99548
5 changed files with 92 additions and 8 deletions

View File

@@ -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 {

View File

@@ -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)

View File

@@ -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)
for index, userId := range userIds {
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
}
// 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

View File

@@ -453,7 +453,33 @@ 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)
@@ -466,7 +492,33 @@ func TestUserStoreGetProfilesByIds(t *testing.T) {
}
}
if r1 := <-store.User().GetProfileByIds([]string{u1.Id}); r1.Err != nil {
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)
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}, false); r1.Err != nil {
t.Fatal(r1.Err)
} else {
users := r1.Data.(map[string]*model.User)

View File

@@ -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