[server] Cache count improvement (#2972)

## Description
This does not handle the case where the server might get multiple
request for same user to get the count, and the file count is not cached
yet.

## Tests
This commit is contained in:
Manav Rathi 2024-08-27 12:15:09 +05:30 committed by GitHub
commit 46e1552f1e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 50 additions and 32 deletions

View File

@ -188,7 +188,9 @@ func main() {
}
userCache := cache2.NewUserCache()
userCacheCtrl := &usercache.Controller{UserCache: userCache, FileRepo: fileRepo, StoreBonusRepo: storagBonusRepo}
userCacheCtrl := &usercache.Controller{UserCache: userCache, FileRepo: fileRepo,
UsageRepo: usageRepo, TrashRepo: trashRepo,
StoreBonusRepo: storagBonusRepo}
offerController := offer.NewOfferController(*userRepo, discordController, storagBonusRepo, userCacheCtrl)
plans := billing.GetPlans()
defaultPlan := billing.GetDefaultPlans(plans)

View File

@ -10,20 +10,26 @@ import (
// UserCache struct holds can be used to fileCount various entities for user.
type UserCache struct {
mu sync.Mutex
fileCache map[string]int64
fileCache map[string]*FileCountCache
bonusCache map[int64]*storagebonus.ActiveStorageBonus
}
type FileCountCache struct {
Count int64
TrashUpdatedAt int64
Usage int64
}
// NewUserCache creates a new instance of the UserCache struct.
func NewUserCache() *UserCache {
return &UserCache{
fileCache: make(map[string]int64),
fileCache: make(map[string]*FileCountCache),
bonusCache: make(map[int64]*storagebonus.ActiveStorageBonus),
}
}
// SetFileCount updates the fileCount with the given userID and fileCount.
func (c *UserCache) SetFileCount(userID, fileCount int64, app ente.App) {
func (c *UserCache) SetFileCount(userID int64, fileCount *FileCountCache, app ente.App) {
c.mu.Lock()
defer c.mu.Unlock()
c.fileCache[cacheKey(userID, app)] = fileCount
@ -44,7 +50,7 @@ func (c *UserCache) GetBonus(userID int64) (*storagebonus.ActiveStorageBonus, bo
// GetFileCount retrieves the file count from the fileCount for the given userID.
// It returns the file count and a boolean indicating if the value was found.
func (c *UserCache) GetFileCount(userID int64, app ente.App) (int64, bool) {
func (c *UserCache) GetFileCount(userID int64, app ente.App) (*FileCountCache, bool) {
c.mu.Lock()
defer c.mu.Unlock()
count, ok := c.fileCache[cacheKey(userID, app)]

View File

@ -11,27 +11,6 @@ import (
"golang.org/x/sync/errgroup"
)
func (c *UserController) getUserFileCountWithCache(userID int64, app ente.App) (int64, error) {
// Check if the value is present in the cache
if count, ok := c.UserCache.GetFileCount(userID, app); ok {
// Cache hit, update the cache asynchronously
go func() {
_, _ = c.getUserCountAndUpdateCache(userID, app)
}()
return count, nil
}
return c.getUserCountAndUpdateCache(userID, app)
}
func (c *UserController) getUserCountAndUpdateCache(userID int64, app ente.App) (int64, error) {
count, err := c.FileRepo.GetFileCountForUser(userID, app)
if err != nil {
return 0, stacktrace.Propagate(err, "")
}
c.UserCache.SetFileCount(userID, count, app)
return count, nil
}
func (c *UserController) GetDetailsV2(ctx *gin.Context, userID int64, fetchMemoryCount bool, app ente.App) (details.UserDetailsResponse, error) {
g := new(errgroup.Group)
@ -86,7 +65,7 @@ func (c *UserController) GetDetailsV2(ctx *gin.Context, userID int64, fetchMemor
if fetchMemoryCount {
g.Go(func() error {
fCount, err := c.getUserFileCountWithCache(userID, app)
fCount, err := c.UserCacheController.GetUserFileCountWithCache(userID, app)
if err == nil {
fileCount = fCount
}

View File

@ -14,6 +14,8 @@ import (
// Avoid adding any direct dependencies to the other controller.
type Controller struct {
FileRepo *repo.FileRepository
UsageRepo *repo.UsageRepository
TrashRepo *repo.TrashRepository
StoreBonusRepo *storagebonus.Repository
UserCache *cache.UserCache
}

View File

@ -2,7 +2,9 @@ package usercache
import (
"github.com/ente-io/museum/ente"
"github.com/ente-io/museum/ente/cache"
"github.com/ente-io/stacktrace"
"github.com/sirupsen/logrus"
)
func (c *Controller) GetUserFileCountWithCache(userID int64, app ente.App) (int64, error) {
@ -10,18 +12,35 @@ func (c *Controller) GetUserFileCountWithCache(userID int64, app ente.App) (int6
if count, ok := c.UserCache.GetFileCount(userID, app); ok {
// Cache hit, update the cache asynchronously
go func() {
_, _ = c.getUserCountAndUpdateCache(userID, app)
_, _ = c.getUserCountAndUpdateCache(userID, app, count)
}()
return count, nil
return count.Count, nil
}
return c.getUserCountAndUpdateCache(userID, app)
return c.getUserCountAndUpdateCache(userID, app, nil)
}
func (c *Controller) getUserCountAndUpdateCache(userID int64, app ente.App) (int64, error) {
func (c *Controller) getUserCountAndUpdateCache(userID int64, app ente.App, oldCache *cache.FileCountCache) (int64, error) {
usage, err := c.UsageRepo.GetUsage(userID)
if err != nil {
return 0, stacktrace.Propagate(err, "")
}
trashUpdatedAt, err := c.TrashRepo.GetTrashUpdatedAt(userID)
if err != nil {
return 0, stacktrace.Propagate(err, "")
}
if oldCache != nil && oldCache.Usage == usage && oldCache.TrashUpdatedAt == trashUpdatedAt {
logrus.Debugf("Cache hit for user %d", userID)
return oldCache.Count, nil
}
count, err := c.FileRepo.GetFileCountForUser(userID, app)
if err != nil {
return 0, stacktrace.Propagate(err, "")
}
c.UserCache.SetFileCount(userID, count, app)
cntCache := &cache.FileCountCache{
Count: count,
Usage: usage,
TrashUpdatedAt: trashUpdatedAt,
}
c.UserCache.SetFileCount(userID, cntCache, app)
return count, nil
}

View File

@ -422,6 +422,16 @@ func (t *TrashRepository) EmptyTrash(ctx context.Context, userID int64, lastUpda
return t.QueueRepo.InsertItem(ctx, TrashEmptyQueue, itemID)
}
func (t *TrashRepository) GetTrashUpdatedAt(userID int64) (int64, error) {
row := t.DB.QueryRow(`SELECT max(updated_at) FROM trash WHERE user_id = $1`, userID)
var updatedAt int64
err := row.Scan(&updatedAt)
if errors.Is(err, sql.ErrNoRows) {
return 0, nil
}
return updatedAt, stacktrace.Propagate(err, "")
}
func convertRowsToTrash(rows *sql.Rows) ([]ente.Trash, error) {
defer rows.Close()
trashFiles := make([]ente.Trash, 0)