-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
140 additions
and
9 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
Binary file not shown.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import { OboCacheInterface } from '@app/auth/cache/interface'; | ||
import { oboMemoryCache } from '@app/auth/cache/memory-cache'; | ||
import { OboRedisCache } from '@app/auth/cache/redis-cache'; | ||
import { optionalEnvString } from '@app/config/env-var'; | ||
|
||
const REDIS_URI = optionalEnvString('REDIS_URI_OBO_CACHE'); | ||
const REDIS_USERNAME = optionalEnvString('REDIS_USERNAME_OBO_CACHE'); | ||
const REDIS_PASSWORD = optionalEnvString('REDIS_PASSWORD_OBO_CACHE'); | ||
|
||
class OboTieredCache implements OboCacheInterface { | ||
private oboRedisCache: OboRedisCache; | ||
|
||
constructor(redisUri: string, redisUsername: string, redisPassword: string) { | ||
this.oboRedisCache = new OboRedisCache(redisUri, redisUsername, redisPassword); | ||
} | ||
|
||
public async get(key: string): Promise<string | null> { | ||
const memoryHit = await oboMemoryCache.get(key); | ||
|
||
if (memoryHit !== null) { | ||
return memoryHit.token; | ||
} | ||
|
||
const redisHit = await this.oboRedisCache.get(key); | ||
|
||
if (redisHit !== null) { | ||
oboMemoryCache.set(key, redisHit.token, redisHit.expiresAt); | ||
|
||
return redisHit.token; | ||
} | ||
|
||
return null; | ||
} | ||
|
||
public async set(key: string, token: string, expiresAt: number): Promise<void> { | ||
await Promise.all([oboMemoryCache.set(key, token, expiresAt), this.oboRedisCache.set(key, token, expiresAt)]); | ||
} | ||
} | ||
|
||
class OboSimpleCache { | ||
public async get(key: string): Promise<string | null> { | ||
const memoryHit = await oboMemoryCache.get(key); | ||
|
||
return memoryHit?.token ?? null; | ||
} | ||
|
||
public async set(key: string, token: string, expiresAt: number): Promise<void> { | ||
await oboMemoryCache.set(key, token, expiresAt); | ||
} | ||
} | ||
|
||
const hasRedis = REDIS_URI !== undefined && REDIS_USERNAME !== undefined && REDIS_PASSWORD !== undefined; | ||
|
||
export const oboCache = hasRedis ? new OboTieredCache(REDIS_URI, REDIS_USERNAME, REDIS_PASSWORD) : new OboSimpleCache(); |
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,9 @@ | ||
export interface OboCacheTierInterface { | ||
get(key: string): Promise<{ token: string; expiresAt: number } | null>; | ||
set(key: string, token: string, expiresAt: number): Promise<void>; | ||
} | ||
|
||
export interface OboCacheInterface { | ||
get(key: string): Promise<string | null>; | ||
set(key: string, token: string, expiresAt: number): Promise<void>; | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import { RedisClientType, createClient } from 'redis'; | ||
import { getLogger } from '@app/logger'; | ||
import { OboCacheTierInterface } from '@app/auth/cache/interface'; | ||
import { cacheGauge, cacheRedisGauge } from '@app/auth/cache/cache-gauge'; | ||
|
||
const log = getLogger('redis-obo-cache'); | ||
|
||
export class OboRedisCache implements OboCacheTierInterface { | ||
private client: RedisClientType; | ||
|
||
constructor(url: string, username: string, password: string) { | ||
this.client = createClient({ url, username, password }); | ||
this.client.on('error', (error) => log.error({ msg: 'Redis Client Error', error })); | ||
this.client.connect(); | ||
} | ||
|
||
public async get(key: string) { | ||
/** | ||
* ttl() gets remaining time to live in seconds. | ||
* Returns -2 if the key does not exist. | ||
* Returns -1 if the key exists but has no associated expire. | ||
* @see https://redis.io/docs/latest/commands/ttl/ | ||
*/ | ||
const [token, ttl] = await Promise.all([this.client.get(key), this.client.ttl(key)]); | ||
|
||
if (token === null || ttl === -2) { | ||
cacheRedisGauge.inc({ hit: 'miss' }); | ||
|
||
return null; | ||
} | ||
|
||
if (ttl === -1) { | ||
cacheRedisGauge.inc({ hit: 'invalid' }); | ||
this.client.del(key); | ||
|
||
return null; | ||
} | ||
|
||
if (ttl === 0) { | ||
cacheRedisGauge.inc({ hit: 'expired' }); | ||
|
||
return null; | ||
} | ||
|
||
cacheRedisGauge.inc({ hit: 'hit' }); | ||
cacheGauge.inc({ hit: 'redis' }); | ||
|
||
return { token, expiresAt: now() + ttl }; | ||
} | ||
|
||
public async set(key: string, token: string, expiresAt: number) { | ||
await this.client.set(key, token, { EXAT: expiresAt }); | ||
} | ||
} | ||
|
||
const now = () => Math.floor(Date.now() / 1_000); |
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