-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #200 from boostcampwm-2024/dev-back
[BE] Merge to main
- Loading branch information
Showing
12 changed files
with
228 additions
and
25 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
export const CACHE_REFRESH_THRESHOLD_METADATA = 'CACHE_REFRESH_THRESHOLD'; | ||
|
||
export const FIFTEEN_SECONDS = 15 * 1000; | ||
export const THIRTY_SECONDS = 30 * 1000; | ||
export const ONE_MINUTE = 60 * 1000; | ||
export const ONE_MINUTE_HALF = 3 * 30 * 1000; | ||
export const THREE_MINUTES = 3 * 60 * 1000; | ||
export const FIVE_MINUTES = 5 * 60 * 1000; | ||
export const TEN_MINUTES = 10 * 60 * 1000; | ||
export const HALF_HOUR = 30 * 60 * 1000; | ||
export const ONE_HOUR = 60 * 60 * 1000; |
23 changes: 23 additions & 0 deletions
23
backend/console-server/src/common/cache/cache.decorator.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import type { ExecutionContext } from '@nestjs/common'; | ||
import { SetMetadata } from '@nestjs/common'; | ||
import { CacheTTL } from '@nestjs/cache-manager'; | ||
import { CACHE_REFRESH_THRESHOLD_METADATA } from './cache.constant'; | ||
|
||
const calculateMillisecondsUntilMidnight = () => { | ||
const now = new Date(); | ||
const midnight = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1); | ||
return midnight.getTime() - now.getTime(); | ||
}; | ||
|
||
export const CacheTTLUntilMidnight = () => { | ||
return CacheTTL((_ctx: ExecutionContext) => calculateMillisecondsUntilMidnight()); | ||
}; | ||
|
||
type CacheRefreshThresholdFactory = (ctx: ExecutionContext) => Promise<number> | number; | ||
|
||
export const CacheRefreshThreshold = (threshold: number | CacheRefreshThresholdFactory) => | ||
SetMetadata(CACHE_REFRESH_THRESHOLD_METADATA, threshold); | ||
|
||
export const CacheRefreshAtMidnight = () => { | ||
return CacheRefreshThreshold((_ctx: ExecutionContext) => calculateMillisecondsUntilMidnight()); | ||
}; |
111 changes: 111 additions & 0 deletions
111
backend/console-server/src/common/cache/cache.interceptor.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
import { | ||
CallHandler, | ||
ExecutionContext, | ||
Inject, | ||
Injectable, | ||
Logger, | ||
NestInterceptor, | ||
Optional, | ||
} from '@nestjs/common'; | ||
import { Observable, of } from 'rxjs'; | ||
import { Cache } from 'cache-manager'; | ||
import { CACHE_MANAGER, CACHE_KEY_METADATA, CACHE_TTL_METADATA } from '@nestjs/cache-manager'; | ||
import { Reflector, HttpAdapterHost } from '@nestjs/core'; | ||
import { firstValueFrom } from 'rxjs'; | ||
import { CACHE_REFRESH_THRESHOLD_METADATA } from './cache.constant'; | ||
import { isNil } from '@nestjs/common/utils/shared.utils'; | ||
|
||
@Injectable() | ||
export class CustomCacheInterceptor<T> implements NestInterceptor<T, T> { | ||
@Optional() | ||
@Inject() | ||
protected readonly httpAdapterHost: HttpAdapterHost; | ||
|
||
protected allowedMethods = ['GET']; | ||
|
||
constructor( | ||
@Inject(CACHE_MANAGER) private readonly cacheManager: Cache, | ||
private readonly reflector: Reflector, | ||
) {} | ||
|
||
async intercept(context: ExecutionContext, next: CallHandler<T>): Promise<Observable<T>> { | ||
const cacheKey = this.trackBy(context); | ||
|
||
const ttlValueOrFactory = | ||
this.reflector.get(CACHE_TTL_METADATA, context.getHandler()) ?? | ||
this.reflector.get(CACHE_TTL_METADATA, context.getClass()) ?? | ||
null; | ||
|
||
const refreshThresholdValueOrFactory = | ||
this.reflector.get(CACHE_REFRESH_THRESHOLD_METADATA, context.getHandler()) ?? | ||
this.reflector.get(CACHE_REFRESH_THRESHOLD_METADATA, context.getClass()) ?? | ||
null; | ||
|
||
if (!cacheKey) return next.handle(); | ||
|
||
try { | ||
const ttl = | ||
typeof ttlValueOrFactory === 'function' | ||
? await ttlValueOrFactory(context) | ||
: ttlValueOrFactory; | ||
|
||
const refreshThreshold = | ||
typeof refreshThresholdValueOrFactory === 'function' | ||
? await refreshThresholdValueOrFactory(context) | ||
: refreshThresholdValueOrFactory; | ||
|
||
const args: [string, () => Promise<T>, number?, number?] = [ | ||
cacheKey, | ||
() => firstValueFrom(next.handle()), | ||
]; | ||
if (!isNil(ttl)) args.push(ttl); | ||
if (!isNil(refreshThreshold)) args.push(refreshThreshold); | ||
|
||
const cachedResponse = await this.cacheManager.wrap<T>(...args); | ||
|
||
this.setHeadersWhenHttp(context, cachedResponse); | ||
|
||
return of(cachedResponse); | ||
} catch (err) { | ||
Logger.error( | ||
`CacheInterceptor Error: ${err.message}`, | ||
err.stack, | ||
'CustomCacheInterceptor', | ||
); | ||
return next.handle(); | ||
} | ||
} | ||
|
||
protected trackBy(context: ExecutionContext): string | undefined { | ||
const httpAdapter = this.httpAdapterHost.httpAdapter; | ||
const isHttpApp = httpAdapter && !!httpAdapter.getRequestMethod; | ||
const cacheMetadata = this.reflector.get(CACHE_KEY_METADATA, context.getHandler()); | ||
|
||
if (!isHttpApp || cacheMetadata) { | ||
return cacheMetadata; | ||
} | ||
|
||
const request = context.getArgByIndex(0); | ||
if (!this.isRequestCacheable(context)) { | ||
return undefined; | ||
} | ||
return httpAdapter.getRequestUrl(request); | ||
} | ||
|
||
protected isRequestCacheable(context: ExecutionContext): boolean { | ||
const req = context.switchToHttp().getRequest(); | ||
return this.allowedMethods.includes(req.method); | ||
} | ||
|
||
protected setHeadersWhenHttp(context: ExecutionContext, value: unknown): void { | ||
if (!this.httpAdapterHost) { | ||
return; | ||
} | ||
const { httpAdapter } = this.httpAdapterHost; | ||
if (!httpAdapter) { | ||
return; | ||
} | ||
const response = context.switchToHttp().getResponse(); | ||
httpAdapter.setHeader(response, 'X-Cache', !isNil(value) ? 'HIT' : 'MISS'); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export * from './cache.constant'; | ||
export * from './cache.decorator'; | ||
export * from './cache.interceptor'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.