github.com/masterhung0112/hk_server/v5@v5.0.0-20220302090640-ec71aef15e1c/store/localcachelayer/user_layer.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See LICENSE.txt for license information.
     3  
     4  package localcachelayer
     5  
     6  import (
     7  	"context"
     8  	"sort"
     9  	"sync"
    10  
    11  	"github.com/masterhung0112/hk_server/v5/model"
    12  	"github.com/masterhung0112/hk_server/v5/store"
    13  	"github.com/masterhung0112/hk_server/v5/store/sqlstore"
    14  )
    15  
    16  type LocalCacheUserStore struct {
    17  	store.UserStore
    18  	rootStore                     *LocalCacheStore
    19  	userProfileByIdsMut           sync.Mutex
    20  	userProfileByIdsInvalidations map[string]bool
    21  }
    22  
    23  func (s *LocalCacheUserStore) handleClusterInvalidateScheme(msg *model.ClusterMessage) {
    24  	if msg.Data == ClearCacheMessageData {
    25  		s.rootStore.userProfileByIdsCache.Purge()
    26  	} else {
    27  		s.userProfileByIdsMut.Lock()
    28  		s.userProfileByIdsInvalidations[msg.Data] = true
    29  		s.userProfileByIdsMut.Unlock()
    30  		s.rootStore.userProfileByIdsCache.Remove(msg.Data)
    31  	}
    32  }
    33  
    34  func (s *LocalCacheUserStore) handleClusterInvalidateProfilesInChannel(msg *model.ClusterMessage) {
    35  	if msg.Data == ClearCacheMessageData {
    36  		s.rootStore.profilesInChannelCache.Purge()
    37  	} else {
    38  		s.rootStore.profilesInChannelCache.Remove(msg.Data)
    39  	}
    40  }
    41  
    42  func (s *LocalCacheUserStore) ClearCaches() {
    43  	s.rootStore.userProfileByIdsCache.Purge()
    44  	s.rootStore.profilesInChannelCache.Purge()
    45  
    46  	if s.rootStore.metrics != nil {
    47  		s.rootStore.metrics.IncrementMemCacheInvalidationCounter("Profile By Ids - Purge")
    48  		s.rootStore.metrics.IncrementMemCacheInvalidationCounter("Profiles in Channel - Purge")
    49  	}
    50  }
    51  
    52  func (s *LocalCacheUserStore) InvalidateProfileCacheForUser(userId string) {
    53  	s.userProfileByIdsMut.Lock()
    54  	s.userProfileByIdsInvalidations[userId] = true
    55  	s.userProfileByIdsMut.Unlock()
    56  	s.rootStore.doInvalidateCacheCluster(s.rootStore.userProfileByIdsCache, userId)
    57  
    58  	if s.rootStore.metrics != nil {
    59  		s.rootStore.metrics.IncrementMemCacheInvalidationCounter("Profile By Ids - Remove")
    60  	}
    61  }
    62  
    63  func (s *LocalCacheUserStore) InvalidateProfilesInChannelCacheByUser(userId string) {
    64  	keys, err := s.rootStore.profilesInChannelCache.Keys()
    65  	if err == nil {
    66  		for _, key := range keys {
    67  			var userMap map[string]*model.User
    68  			if err = s.rootStore.profilesInChannelCache.Get(key, &userMap); err == nil {
    69  				if _, userInCache := userMap[userId]; userInCache {
    70  					s.rootStore.doInvalidateCacheCluster(s.rootStore.profilesInChannelCache, key)
    71  					if s.rootStore.metrics != nil {
    72  						s.rootStore.metrics.IncrementMemCacheInvalidationCounter("Profiles in Channel - Remove by User")
    73  					}
    74  				}
    75  			}
    76  		}
    77  	}
    78  }
    79  
    80  func (s *LocalCacheUserStore) InvalidateProfilesInChannelCache(channelID string) {
    81  	s.rootStore.doInvalidateCacheCluster(s.rootStore.profilesInChannelCache, channelID)
    82  	if s.rootStore.metrics != nil {
    83  		s.rootStore.metrics.IncrementMemCacheInvalidationCounter("Profiles in Channel - Remove by Channel")
    84  	}
    85  }
    86  
    87  func (s *LocalCacheUserStore) GetAllProfilesInChannel(ctx context.Context, channelId string, allowFromCache bool) (map[string]*model.User, error) {
    88  	if allowFromCache {
    89  		var cachedMap map[string]*model.User
    90  		if err := s.rootStore.doStandardReadCache(s.rootStore.profilesInChannelCache, channelId, &cachedMap); err == nil {
    91  			return cachedMap, nil
    92  		}
    93  	}
    94  
    95  	userMap, err := s.UserStore.GetAllProfilesInChannel(ctx, channelId, allowFromCache)
    96  	if err != nil {
    97  		return nil, err
    98  	}
    99  
   100  	if allowFromCache {
   101  		s.rootStore.doStandardAddToCache(s.rootStore.profilesInChannelCache, channelId, model.UserMap(userMap))
   102  	}
   103  
   104  	return userMap, nil
   105  }
   106  
   107  func (s *LocalCacheUserStore) GetProfileByIds(ctx context.Context, userIds []string, options *store.UserGetByIdsOpts, allowFromCache bool) ([]*model.User, error) {
   108  	if !allowFromCache {
   109  		return s.UserStore.GetProfileByIds(ctx, userIds, options, false)
   110  	}
   111  
   112  	if options == nil {
   113  		options = &store.UserGetByIdsOpts{}
   114  	}
   115  
   116  	users := []*model.User{}
   117  	remainingUserIds := make([]string, 0)
   118  
   119  	fromMaster := false
   120  	for _, userId := range userIds {
   121  		var cacheItem *model.User
   122  		if err := s.rootStore.doStandardReadCache(s.rootStore.userProfileByIdsCache, userId, &cacheItem); err == nil {
   123  			if options.Since == 0 || cacheItem.UpdateAt > options.Since {
   124  				users = append(users, cacheItem)
   125  			}
   126  		} else {
   127  			// If it was invalidated, then we need to query master.
   128  			s.userProfileByIdsMut.Lock()
   129  			if s.userProfileByIdsInvalidations[userId] {
   130  				fromMaster = true
   131  				// And then remove the key from the map.
   132  				delete(s.userProfileByIdsInvalidations, userId)
   133  			}
   134  			s.userProfileByIdsMut.Unlock()
   135  			remainingUserIds = append(remainingUserIds, userId)
   136  		}
   137  	}
   138  
   139  	if len(remainingUserIds) > 0 {
   140  		if fromMaster {
   141  			ctx = sqlstore.WithMaster(ctx)
   142  		}
   143  		remainingUsers, err := s.UserStore.GetProfileByIds(ctx, remainingUserIds, options, false)
   144  		if err != nil {
   145  			return nil, err
   146  		}
   147  		for _, user := range remainingUsers {
   148  			s.rootStore.doStandardAddToCache(s.rootStore.userProfileByIdsCache, user.Id, user)
   149  			users = append(users, user)
   150  		}
   151  	}
   152  
   153  	return users, nil
   154  }
   155  
   156  // Get is a cache wrapper around the SqlStore method to get a user profile by id.
   157  // It checks if the user entry is present in the cache, returning the entry from cache
   158  // if it is present. Otherwise, it fetches the entry from the store and stores it in the
   159  // cache.
   160  func (s *LocalCacheUserStore) Get(ctx context.Context, id string) (*model.User, error) {
   161  	var cacheItem *model.User
   162  	if err := s.rootStore.doStandardReadCache(s.rootStore.userProfileByIdsCache, id, &cacheItem); err == nil {
   163  		if s.rootStore.metrics != nil {
   164  			s.rootStore.metrics.AddMemCacheHitCounter("Profile By Id", float64(1))
   165  		}
   166  		return cacheItem, nil
   167  	}
   168  	if s.rootStore.metrics != nil {
   169  		s.rootStore.metrics.AddMemCacheMissCounter("Profile By Id", float64(1))
   170  	}
   171  
   172  	// If it was invalidated, then we need to query master.
   173  	s.userProfileByIdsMut.Lock()
   174  	if s.userProfileByIdsInvalidations[id] {
   175  		ctx = sqlstore.WithMaster(ctx)
   176  		// And then remove the key from the map.
   177  		delete(s.userProfileByIdsInvalidations, id)
   178  	}
   179  	s.userProfileByIdsMut.Unlock()
   180  
   181  	user, err := s.UserStore.Get(ctx, id)
   182  	if err != nil {
   183  		return nil, err
   184  	}
   185  	s.rootStore.doStandardAddToCache(s.rootStore.userProfileByIdsCache, id, user)
   186  	return user, nil
   187  }
   188  
   189  // GetMany is a cache wrapper around the SqlStore method to get a user profiles by ids.
   190  // It checks if the user entries are present in the cache, returning the entries from cache
   191  // if it is present. Otherwise, it fetches the entries from the store and stores it in the
   192  // cache.
   193  func (s *LocalCacheUserStore) GetMany(ctx context.Context, ids []string) ([]*model.User, error) {
   194  	// we are doing a loop instead of caching the full set in the cache because the number of permutations that we can have
   195  	// in this func is making caching of the total set not beneficial.
   196  	var cachedUsers []*model.User
   197  	var notCachedUserIds []string
   198  	uniqIDs := dedup(ids)
   199  
   200  	fromMaster := false
   201  	for _, id := range uniqIDs {
   202  		var cachedUser *model.User
   203  		if err := s.rootStore.doStandardReadCache(s.rootStore.userProfileByIdsCache, id, &cachedUser); err == nil {
   204  			if s.rootStore.metrics != nil {
   205  				s.rootStore.metrics.AddMemCacheHitCounter("Profile By Id", float64(1))
   206  			}
   207  			cachedUsers = append(cachedUsers, cachedUser)
   208  		} else {
   209  			if s.rootStore.metrics != nil {
   210  				s.rootStore.metrics.AddMemCacheMissCounter("Profile By Id", float64(1))
   211  			}
   212  			// If it was invalidated, then we need to query master.
   213  			s.userProfileByIdsMut.Lock()
   214  			if s.userProfileByIdsInvalidations[id] {
   215  				fromMaster = true
   216  				// And then remove the key from the map.
   217  				delete(s.userProfileByIdsInvalidations, id)
   218  			}
   219  			s.userProfileByIdsMut.Unlock()
   220  
   221  			notCachedUserIds = append(notCachedUserIds, id)
   222  		}
   223  	}
   224  
   225  	if len(notCachedUserIds) > 0 {
   226  		if fromMaster {
   227  			ctx = sqlstore.WithMaster(ctx)
   228  		}
   229  		dbUsers, err := s.UserStore.GetMany(ctx, notCachedUserIds)
   230  		if err != nil {
   231  			return nil, err
   232  		}
   233  		for _, user := range dbUsers {
   234  			s.rootStore.doStandardAddToCache(s.rootStore.userProfileByIdsCache, user.Id, user)
   235  			cachedUsers = append(cachedUsers, user)
   236  		}
   237  	}
   238  
   239  	return cachedUsers, nil
   240  }
   241  
   242  func dedup(elements []string) []string {
   243  	if len(elements) == 0 {
   244  		return elements
   245  	}
   246  
   247  	sort.Strings(elements)
   248  
   249  	j := 0
   250  	for i := 1; i < len(elements); i++ {
   251  		if elements[j] == elements[i] {
   252  			continue
   253  		}
   254  		j++
   255  		// preserve the original data
   256  		// in[i], in[j] = in[j], in[i]
   257  		// only set what is required
   258  		elements[j] = elements[i]
   259  	}
   260  
   261  	return elements[:j+1]
   262  }