From 4fcf025611aae32a7b957d79a893c3d7b57de7a3 Mon Sep 17 00:00:00 2001 From: Hongbo Wu Date: Wed, 14 Aug 2024 12:24:35 +0800 Subject: [PATCH 1/4] update cache when user is updated --- packages/api/src/resolvers/user/index.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/api/src/resolvers/user/index.ts b/packages/api/src/resolvers/user/index.ts index 4dc6b5921..de65cea35 100644 --- a/packages/api/src/resolvers/user/index.ts +++ b/packages/api/src/resolvers/user/index.ts @@ -82,6 +82,8 @@ export const updateUserResolver = authorized< }) ) + await cacheUser(updatedUser) + return { user: updatedUser } }) @@ -139,6 +141,8 @@ export const updateUserProfileResolver = authorized< }) ) + await cacheUser(updatedUser) + return { user: updatedUser } }) From 4bf3e4ba0fbbd97e8da76c07d31aa127cbea67de Mon Sep 17 00:00:00 2001 From: Hongbo Wu Date: Wed, 14 Aug 2024 12:59:52 +0800 Subject: [PATCH 2/4] delete count cache after bulk action or empty trash --- packages/api/src/resolvers/article/index.ts | 7 +++++++ packages/api/src/services/library_item.ts | 18 +++++++++++++++--- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/packages/api/src/resolvers/article/index.ts b/packages/api/src/resolvers/article/index.ts index a5fdfe7e4..4043b657c 100644 --- a/packages/api/src/resolvers/article/index.ts +++ b/packages/api/src/resolvers/article/index.ts @@ -79,6 +79,7 @@ import { batchUpdateLibraryItems, countLibraryItems, createOrUpdateLibraryItem, + deleteCachedTotalCount, findLibraryItemsByPrefix, SearchArgs, searchLibraryItems, @@ -771,6 +772,8 @@ export const bulkActionResolver = authorized< // if there are less than batchSize items, update them synchronously await batchUpdateLibraryItems(action, searchArgs, uid, labelIds, args) + await deleteCachedTotalCount(uid) + return { success: true } } @@ -790,6 +793,8 @@ export const bulkActionResolver = authorized< return { errorCodes: [BulkActionErrorCode.BadRequest] } } + await deleteCachedTotalCount(uid) + return { success: true } } catch (error) { log.error('bulkActionResolver error', error) @@ -986,6 +991,8 @@ export const emptyTrashResolver = authorized< state: LibraryItemState.Deleted, }) + await deleteCachedTotalCount(uid) + return { success: true, } diff --git a/packages/api/src/services/library_item.ts b/packages/api/src/services/library_item.ts index 84886511c..90bd8c3c0 100644 --- a/packages/api/src/services/library_item.ts +++ b/packages/api/src/services/library_item.ts @@ -1717,9 +1717,10 @@ export const filterItemEvents = ( } const totalCountCacheKey = (userId: string, args: SearchArgs) => { - return `cache:library_items_count:${userId}:${stringToHash( - JSON.stringify(args) - )}` + // sort the args to make sure the cache key is consistent + const sortedArgs = JSON.stringify(args, Object.keys(args).sort()) + + return `cache:library_items_count:${userId}:${stringToHash(sortedArgs)}` } export const getCachedTotalCount = async (userId: string, args: SearchArgs) => { @@ -1740,3 +1741,14 @@ export const setCachedTotalCount = async ( const cacheKey = totalCountCacheKey(userId, args) await redisDataSource.redisClient?.set(cacheKey, count, 'EX', 600) } + +export const deleteCachedTotalCount = async (userId: string) => { + const keyPattern = `cache:library_items_count:${userId}:*` + const keys = await redisDataSource.redisClient?.keys(keyPattern) + if (!keys || keys.length === 0) { + return + } + + console.log('Deleting keys:', keys) + await redisDataSource.redisClient?.del(keys) +} From 8b9ab287d70fbe664c5ac5f79b248d80d427d1d7 Mon Sep 17 00:00:00 2001 From: Hongbo Wu Date: Wed, 14 Aug 2024 13:00:53 +0800 Subject: [PATCH 3/4] add debugging logs --- packages/api/src/services/library_item.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/api/src/services/library_item.ts b/packages/api/src/services/library_item.ts index 90bd8c3c0..d1d1673e0 100644 --- a/packages/api/src/services/library_item.ts +++ b/packages/api/src/services/library_item.ts @@ -1749,6 +1749,6 @@ export const deleteCachedTotalCount = async (userId: string) => { return } - console.log('Deleting keys:', keys) + logger.debug('Deleting keys:', keys) await redisDataSource.redisClient?.del(keys) } From c9aa90b3aed89d1b1d1cebda8daf018cdbb00806 Mon Sep 17 00:00:00 2001 From: Hongbo Wu Date: Wed, 14 Aug 2024 13:04:29 +0800 Subject: [PATCH 4/4] more debugging logs --- packages/api/src/services/features.ts | 5 +++++ packages/api/src/services/library_item.ts | 17 ++++++++++++++++- packages/api/src/services/user.ts | 4 ++++ .../api/src/services/user_personalization.ts | 5 +++++ 4 files changed, 30 insertions(+), 1 deletion(-) diff --git a/packages/api/src/services/features.ts b/packages/api/src/services/features.ts index 28d097ec2..307d3cc59 100644 --- a/packages/api/src/services/features.ts +++ b/packages/api/src/services/features.ts @@ -206,6 +206,8 @@ export const userDigestEligible = async (uid: string): Promise => { const featuresCacheKey = (userId: string) => `cache:features:${userId}` export const getFeaturesCache = async (userId: string) => { + logger.debug('getFeaturesCache', { userId }) + const cachedFeatures = await redisDataSource.redisClient?.get( featuresCacheKey(userId) ) @@ -218,6 +220,9 @@ export const getFeaturesCache = async (userId: string) => { export const setFeaturesCache = async (userId: string, features: Feature[]) => { const value = JSON.stringify(features) + + logger.debug('setFeaturesCache', { userId, value }) + return redisDataSource.redisClient?.set( featuresCacheKey(userId), value, diff --git a/packages/api/src/services/library_item.ts b/packages/api/src/services/library_item.ts index d1d1673e0..9292284fc 100644 --- a/packages/api/src/services/library_item.ts +++ b/packages/api/src/services/library_item.ts @@ -1724,6 +1724,11 @@ const totalCountCacheKey = (userId: string, args: SearchArgs) => { } export const getCachedTotalCount = async (userId: string, args: SearchArgs) => { + logger.debug('Getting cached total count:', { + userId, + args, + }) + const cacheKey = totalCountCacheKey(userId, args) const cachedCount = await redisDataSource.redisClient?.get(cacheKey) if (!cachedCount) { @@ -1739,6 +1744,12 @@ export const setCachedTotalCount = async ( count: number ) => { const cacheKey = totalCountCacheKey(userId, args) + + logger.debug('Setting cached total count:', { + cacheKey, + count, + }) + await redisDataSource.redisClient?.set(cacheKey, count, 'EX', 600) } @@ -1749,6 +1760,10 @@ export const deleteCachedTotalCount = async (userId: string) => { return } - logger.debug('Deleting keys:', keys) + logger.debug('Deleting keys:', { + keys, + userId, + }) + await redisDataSource.redisClient?.del(keys) } diff --git a/packages/api/src/services/user.ts b/packages/api/src/services/user.ts index 3f7ed7d6c..c5dc336b9 100644 --- a/packages/api/src/services/user.ts +++ b/packages/api/src/services/user.ts @@ -161,6 +161,8 @@ export const findUserAndPersonalization = async (id: string) => { const userCacheKey = (id: string) => `cache:user:${id}` export const getCachedUser = async (id: string) => { + logger.debug(`Getting user from cache: ${id}`) + const user = await redisDataSource.redisClient?.get(userCacheKey(id)) if (!user) { return undefined @@ -170,6 +172,8 @@ export const getCachedUser = async (id: string) => { } export const cacheUser = async (user: User) => { + logger.debug(`Caching user: ${user.id}`) + await redisDataSource.redisClient?.set( userCacheKey(user.id), JSON.stringify(user), diff --git a/packages/api/src/services/user_personalization.ts b/packages/api/src/services/user_personalization.ts index 5661d960a..f20afa156 100644 --- a/packages/api/src/services/user_personalization.ts +++ b/packages/api/src/services/user_personalization.ts @@ -4,6 +4,7 @@ import { Subscription, SubscriptionStatus } from '../entity/subscription' import { Shortcut, UserPersonalization } from '../entity/user_personalization' import { redisDataSource } from '../redis_data_source' import { authTrx } from '../repository' +import { logger } from '../utils/logger' import { findLabelsByUserId } from './labels' export const findUserPersonalization = async (userId: string) => { @@ -177,6 +178,8 @@ const userDefaultShortcuts = async (userId: string): Promise => { const shortcutsCacheKey = (userId: string) => `cache:shortcuts:${userId}` export const getShortcutsCache = async (userId: string) => { + logger.debug(`Getting shortcuts from cache: ${userId}`) + const cachedShortcuts = await redisDataSource.redisClient?.get( shortcutsCacheKey(userId) ) @@ -187,6 +190,8 @@ export const getShortcutsCache = async (userId: string) => { } export const cacheShortcuts = async (userId: string, shortcuts: Shortcut[]) => { + logger.debug(`Caching shortcuts: ${userId}`) + await redisDataSource.redisClient?.set( shortcutsCacheKey(userId), JSON.stringify(shortcuts),