perf(session): ttl
This commit is contained in:
96
components/login/libs/session-cache.ts
Normal file
96
components/login/libs/session-cache.ts
Normal file
@@ -0,0 +1,96 @@
|
||||
const SESSION_CACHE_TTL_MS = 5000
|
||||
|
||||
type SessionCacheSource = "memory" | "shared" | "redis"
|
||||
|
||||
interface SessionCacheEntry<T> {
|
||||
data: T
|
||||
expiresAt: number
|
||||
}
|
||||
|
||||
const sessionCache = new Map<string, SessionCacheEntry<any>>()
|
||||
const inflightSessionLoads = new Map<string, Promise<any | null>>()
|
||||
|
||||
const getNow = () => Date.now()
|
||||
|
||||
const getValidCachedSession = <T>(redisKey: string) => {
|
||||
const cacheEntry = sessionCache.get(redisKey)
|
||||
|
||||
if (!cacheEntry) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (cacheEntry.expiresAt <= getNow()) {
|
||||
sessionCache.delete(redisKey)
|
||||
return null
|
||||
}
|
||||
|
||||
return cacheEntry.data as T
|
||||
}
|
||||
|
||||
export const getCachedSession = <T>(redisKey: string) =>
|
||||
getValidCachedSession<T>(redisKey)
|
||||
|
||||
export const setCachedSession = <T>(redisKey: string, data: T) => {
|
||||
sessionCache.set(redisKey, {
|
||||
data,
|
||||
expiresAt: getNow() + SESSION_CACHE_TTL_MS,
|
||||
})
|
||||
}
|
||||
|
||||
export const deleteCachedSession = (redisKey: string) => {
|
||||
sessionCache.delete(redisKey)
|
||||
inflightSessionLoads.delete(redisKey)
|
||||
}
|
||||
|
||||
export const getSessionCacheTtlMs = () => SESSION_CACHE_TTL_MS
|
||||
|
||||
export const getOrLoadSession = async <T>(
|
||||
redisKey: string,
|
||||
loader: () => Promise<T | null>
|
||||
): Promise<{
|
||||
data: T | null
|
||||
source: SessionCacheSource
|
||||
}> => {
|
||||
const cachedData = getValidCachedSession<T>(redisKey)
|
||||
|
||||
if (cachedData) {
|
||||
return {
|
||||
data: cachedData,
|
||||
source: "memory",
|
||||
}
|
||||
}
|
||||
|
||||
const inflightLoad = inflightSessionLoads.get(redisKey)
|
||||
|
||||
if (inflightLoad) {
|
||||
return {
|
||||
data: (await inflightLoad) as T | null,
|
||||
source: "shared",
|
||||
}
|
||||
}
|
||||
|
||||
const loadPromise = (async () => {
|
||||
const loadedData = await loader()
|
||||
|
||||
if (loadedData) {
|
||||
setCachedSession(redisKey, loadedData)
|
||||
} else {
|
||||
deleteCachedSession(redisKey)
|
||||
}
|
||||
|
||||
return loadedData
|
||||
})()
|
||||
|
||||
inflightSessionLoads.set(redisKey, loadPromise)
|
||||
|
||||
try {
|
||||
return {
|
||||
data: await loadPromise,
|
||||
source: "redis",
|
||||
}
|
||||
} finally {
|
||||
if (inflightSessionLoads.get(redisKey) === loadPromise) {
|
||||
inflightSessionLoads.delete(redisKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user