From bfc13b9647898b117190a3a4aa64edbb91d2e5e8 Mon Sep 17 00:00:00 2001 From: shubham yadav <126192924+yadavshubham01@users.noreply.github.com> Date: Thu, 10 Oct 2024 19:35:09 +0530 Subject: [PATCH] Add cache-flush step in Twenty upgrade command #7521 (#7553) Flush Specific Redis Keys After Upgrade Command :---- Changes Made :---- Updated the FlushCacheCommand to allow for selective flushing of keys that match the pattern engine:*, rather than flushing the entire cache. Added a new optional parameter to specify the cache keys pattern. Ensured that the cache is flushed at the end of the upgrade command. Code Changes :---- Modified FlushCacheCommand to include a method for flushing keys by pattern. Added a new function in CacheStorageService to handle the pattern-based flushing. --------- Co-authored-by: Weiko --- .../commands/flush-cache.command.ts | 30 +++++++++++++++---- .../services/cache-storage.service.ts | 27 ++++++++++++++++- 2 files changed, 50 insertions(+), 7 deletions(-) diff --git a/packages/twenty-server/src/engine/core-modules/cache-storage/commands/flush-cache.command.ts b/packages/twenty-server/src/engine/core-modules/cache-storage/commands/flush-cache.command.ts index c07565936a..0a4d513570 100644 --- a/packages/twenty-server/src/engine/core-modules/cache-storage/commands/flush-cache.command.ts +++ b/packages/twenty-server/src/engine/core-modules/cache-storage/commands/flush-cache.command.ts @@ -1,15 +1,14 @@ import { Logger } from '@nestjs/common'; -import { Command, CommandRunner } from 'nest-commander'; +import { Command, CommandRunner, Option } from 'nest-commander'; import { InjectCacheStorage } from 'src/engine/core-modules/cache-storage/decorators/cache-storage.decorator'; import { CacheStorageService } from 'src/engine/core-modules/cache-storage/services/cache-storage.service'; import { CacheStorageNamespace } from 'src/engine/core-modules/cache-storage/types/cache-storage-namespace.enum'; -// TODO: implement dry-run @Command({ name: 'cache:flush', - description: 'Completely flush cache', + description: 'Flush cache for specific keys matching the pattern', }) export class FlushCacheCommand extends CommandRunner { private readonly logger = new Logger(FlushCacheCommand.name); @@ -21,9 +20,28 @@ export class FlushCacheCommand extends CommandRunner { super(); } - async run(): Promise { - this.logger.log('Flushing cache...'); - await this.cacheStorage.flush(); + async run( + passedParams: string[], + options?: Record, + ): Promise { + const pattern = options?.pattern || '*'; + + this.logger.log(`Flushing cache for pattern: ${pattern}...`); + + if (pattern === '*') { + await this.cacheStorage.flush(); + } else { + await this.cacheStorage.flushByPattern(pattern); + } + this.logger.log('Cache flushed'); } + + @Option({ + flags: '-p, --pattern ', + description: 'Pattern to flush specific cache keys (e.g., engine:*)', + }) + parsePattern(val: string): string { + return val; + } } diff --git a/packages/twenty-server/src/engine/core-modules/cache-storage/services/cache-storage.service.ts b/packages/twenty-server/src/engine/core-modules/cache-storage/services/cache-storage.service.ts index e5195c18ad..7b132c7fbd 100644 --- a/packages/twenty-server/src/engine/core-modules/cache-storage/services/cache-storage.service.ts +++ b/packages/twenty-server/src/engine/core-modules/cache-storage/services/cache-storage.service.ts @@ -1,5 +1,5 @@ -import { Inject, Injectable } from '@nestjs/common'; import { CACHE_MANAGER, Cache } from '@nestjs/cache-manager'; +import { Inject, Injectable } from '@nestjs/common'; import { RedisCache } from 'cache-manager-redis-yet'; @@ -67,6 +67,31 @@ export class CacheStorageService { return this.cache.reset(); } + async flushByPattern(scanPattern: string): Promise { + if (!this.isRedisCache()) { + throw new Error('flushByPattern is only supported with Redis cache'); + } + + const redisClient = (this.cache as RedisCache).store.client; + let cursor = 0; + + do { + const result = await redisClient.scan(cursor, { + MATCH: scanPattern, + COUNT: 100, + }); + + const nextCursor = result.cursor; + const keys = result.keys; + + if (keys.length > 0) { + await redisClient.del(keys); + } + + cursor = nextCursor; + } while (cursor !== 0); + } + private isRedisCache() { return (this.cache.store as any)?.name === 'redis'; }