61 lines
2.7 KiB
JavaScript
61 lines
2.7 KiB
JavaScript
const { getRedisClient } = require('./redisClient')
|
|
|
|
// الگوی cache-aside عمومی: هر جایی یه کوئری/محاسبهی سنگین و پرتکرار داره، بهجای
|
|
// شمارندهی incremental (که نگهداشتنِ درستش نیاز به ردیابیِ دقیقِ *همهی* نقاطِ
|
|
// تغییر داره و راحت drift پیدا میکنه)، فقط نتیجهی همون کوئریِ فعلی رو با یه TTLِ
|
|
// کوتاه کش میکنه. اگه Redis در دسترس نباشه، هر بار مستقیم fetchFn اجرا میشه —
|
|
// دقیقاً همون رفتارِ قبل از اضافهشدنِ کش (کندتر، ولی همیشه درست)
|
|
async function getOrSetCache (key, ttlSeconds, fetchFn) {
|
|
const redis = getRedisClient()
|
|
if (!redis) return fetchFn()
|
|
|
|
try {
|
|
const cached = await redis.get(key)
|
|
if (cached != null) return JSON.parse(cached)
|
|
} catch (err) {
|
|
console.error(`[cache] read error for ${key}:`, err.message)
|
|
}
|
|
|
|
const fresh = await fetchFn()
|
|
|
|
try {
|
|
await redis.set(key, JSON.stringify(fresh), { EX: ttlSeconds })
|
|
} catch (err) {
|
|
console.error(`[cache] write error for ${key}:`, err.message)
|
|
}
|
|
|
|
return fresh
|
|
}
|
|
|
|
// موقعِ تغییرِ داده (پیام جدید/خوندهشدنِ پیام، لایک/آنلایک، پست جدید و...) باید
|
|
// صریحاً صدا زده بشه تا کشِ مرتبط بلافاصله باطل بشه، نه اینکه تا انقضای TTL کهنه بمونه
|
|
async function invalidateCache (key) {
|
|
const redis = getRedisClient()
|
|
if (!redis) return
|
|
try {
|
|
await redis.del(key)
|
|
} catch (err) {
|
|
console.error(`[cache] invalidate error for ${key}:`, err.message)
|
|
}
|
|
}
|
|
|
|
// برای وقتی چندین کلید همزمان باید باطل بشن (مثلاً کشِ فید همهی صفحههای اکسپلور)
|
|
async function invalidateCachePattern (pattern) {
|
|
const redis = getRedisClient()
|
|
if (!redis) return
|
|
try {
|
|
const keys = []
|
|
for await (const key of redis.scanIterator({ MATCH: pattern, COUNT: 100 })) {
|
|
keys.push(key)
|
|
}
|
|
// این کلاینتِ redis برای del چندکلیدی آرگومانهای جدا میخواد، نه یه آرایه —
|
|
// پاسدادنِ مستقیمِ آرایه با خطای "arguments[1] must be of type string|Buffer"
|
|
// silently fail میشد و کشِ باطلنشده میموند
|
|
if (keys.length) await redis.del(...keys)
|
|
} catch (err) {
|
|
console.error(`[cache] pattern invalidate error for ${pattern}:`, err.message)
|
|
}
|
|
}
|
|
|
|
module.exports = { getOrSetCache, invalidateCache, invalidateCachePattern }
|