Files
modstagram-back/utils/redisClient.js
payacom 3f416a7a60 full
2026-08-31 20:45:41 +03:30

81 lines
4.0 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const { createClient } = require('redis')
// یک کلاینتِ Redis مشترک برای کل اپ (presence، کش، rate-limit، session) — Redis
// اختیاریه: اگه سرورش در دسترس نباشه (چه موقع boot، چه بعداً قطع بشه)، هیچ‌جای اپ
// نباید کرش کنه یا هنگ کنه. برای همین getRedisClient() به‌جای خودِ کلاینت، فقط وقتی
// واقعاً "ready"ه اون رو برمی‌گردونه؛ صدازننده‌ها همیشه باید مسیرِ fallback
// (مستقیم Mongo/memory) رو هم داشته باشن، دقیقاً عین چیزی که توی lastOnline.js و
// middlewares/index.js پیاده شده
let client = null
let isReady = false
function buildRedisUrl () {
if (process.env.REDIS_URL) return process.env.REDIS_URL
const host = process.env.REDIS_HOST || '127.0.0.1'
const port = process.env.REDIS_PORT || '6379'
const password = process.env.REDIS_PASSWORD || ''
const db = process.env.REDIS_DB || '0'
const auth = password ? `:${encodeURIComponent(password)}@` : ''
return `redis://${auth}${host}:${port}/${db}`
}
function getClient () {
if (client) return client
client = createClient({
url: buildRedisUrl(),
socket: {
// بک‌آف نمایی با سقف ۱۰ ثانیه — تا ابد retry می‌کنه (وقتی سرورِ Redis بعداً بالا
// اومد خودش وصل می‌شه)، ولی هیچ‌وقت کل اپ رو برای اتصال بلاک نمی‌کنه چون
// connectRedis() پایین await نمی‌شه
reconnectStrategy: (retries) => Math.min(retries * 200, 10000),
// بدون این، وقتی REDIS_HOST به یه آدرسِ اشتباه/غیرقابل‌دسترس (نه صرفاً «رد شد»،
// بلکه ساکت هیچ پاسخی نمی‌ده) اشاره کنه، تلاشِ اتصال می‌تونه تا timeoutِ پیش‌فرضِ
// TCP سطحِ سیستم‌عامل (روی بعضی سیستم‌ها ۱۰۰+ ثانیه) طول بکشه — و هر درخواستی که
// منتظرِ همون تلاشِ اتصاله (مثلاً session store) به همون اندازه کند می‌شه
connectTimeout: 5000
}
})
// کتابخونه‌ی redis v4+: بدون این listener، هر خطای اتصال کل پروسه‌ی Node رو
// کرش می‌کنه (unhandled 'error' event) — این مهم‌ترین خطِ این فایله
client.on('error', (err) => {
isReady = false
console.error('[redis] connection error:', err.message)
})
client.on('ready', () => {
isReady = true
console.log('[redis] connected')
})
client.on('end', () => {
isReady = false
})
return client
}
// عمداً await نمی‌شه توی boot — اگه Redis در دسترس نباشه، بوت‌شدنِ کل سرور نباید
// معطلِ چندین‌بار retry بمونه؛ اتصال توی بک‌گراند انجام می‌شه و هر وقت آماده شد
// isReady=true می‌شه
function connectRedis () {
const c = getClient()
c.connect().catch((err) => {
console.error('[redis] initial connection failed — app continues without Redis:', err.message)
})
}
// همه‌جای اپ باید از همین تابع استفاده کنن، نه مستقیم از getRawClient() — چون فقط
// وقتی واقعاً متصله چیزی برمی‌گردونه
function getRedisClient () {
return isReady ? client : null
}
// فقط برای jaهایی مثل connect-redis که خودشون client رو نگه می‌دارن و باید همون
// اول (حتی قبل از وصل‌شدن) بهش رفرنس بدیم — چون کتابخونه‌ی redis v4 دستورهای
// قبل از اتصال رو صف می‌کنه (offline queue)، نه اینکه فوراً خطا بده
function getRawClient () {
return getClient()
}
module.exports = { connectRedis, getRedisClient, getRawClient }