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/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 } }) 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 84886511c..9292284fc 100644 --- a/packages/api/src/services/library_item.ts +++ b/packages/api/src/services/library_item.ts @@ -1717,12 +1717,18 @@ 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) => { + logger.debug('Getting cached total count:', { + userId, + args, + }) + const cacheKey = totalCountCacheKey(userId, args) const cachedCount = await redisDataSource.redisClient?.get(cacheKey) if (!cachedCount) { @@ -1738,5 +1744,26 @@ 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) } + +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 + } + + 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),