apilink / utils /requestDedup.js
franyer24's picture
grand update
92a83a5 verified
Raw
History Blame Contribute Delete
2.74 kB
// Contenido con más de estos meses de antigüedad se considera "estable"
// y se cachea por más tiempo. Lo reciente no se cachea para evitar
// servir links rotos o resultados incompletos de providers que aún indexan.
const STABLE_CONTENT_MONTHS = 3
const SHORT_TTL_MS = 20_000 // contenido reciente: 20s (solo dedup de vuelo)
const LONG_TTL_MS = 30 * 60_000 // contenido estable: 30 minutos
const MAX_ENTRIES = 500
const resultsCache = new Map()
const inflight = new Map()
function pruneExpired() {
const now = Date.now()
for (const [key, entry] of resultsCache.entries()) {
if (entry.expires <= now) {
resultsCache.delete(key)
}
}
while (resultsCache.size > MAX_ENTRIES) {
const oldestKey = resultsCache.keys().next().value
if (oldestKey !== undefined) resultsCache.delete(oldestKey)
}
}
/**
* Determina si el contenido es suficientemente antiguo para cachearlo largo.
* @param {string|null} releaseDate - Fecha ISO "YYYY-MM-DD" o null
*/
function isStableContent(releaseDate) {
if (!releaseDate) return false
const release = new Date(releaseDate)
if (isNaN(release.getTime())) return false
const threshold = new Date()
threshold.setMonth(threshold.getMonth() - STABLE_CONTENT_MONTHS)
return release < threshold
}
/**
* @param {string} key
* @param {() => Promise<any>} factory
* @param {{ releaseDate?: string|null, cacheErrors?: boolean }} [opts]
*/
async function dedupeRequest(key, factory, { releaseDate = null, cacheErrors = false } = {}) {
if (!key || typeof key !== 'string') {
return factory()
}
pruneExpired()
const cached = resultsCache.get(key)
if (cached && cached.expires > Date.now()) {
return cached.value
}
if (inflight.has(key)) {
return inflight.get(key)
}
const promise = (async () => factory())()
inflight.set(key, promise)
try {
const value = await promise
// Solo cachear largo si el contenido es estable Y hay resultados reales
const hasResults = Array.isArray(value) && value.length > 0
const stable = isStableContent(releaseDate)
const ttl = (stable && hasResults) ? LONG_TTL_MS : SHORT_TTL_MS
resultsCache.set(key, {
value,
expires: Date.now() + ttl,
})
pruneExpired()
return value
} catch (error) {
if (cacheErrors) {
resultsCache.set(key, {
value: { error: error.message || String(error) },
expires: Date.now() + Math.min(SHORT_TTL_MS, 5_000),
})
}
throw error
} finally {
inflight.delete(key)
}
}
function clearRequestDedupCache() {
resultsCache.clear()
inflight.clear()
}
module.exports = {
dedupeRequest,
clearRequestDedupCache,
isStableContent,
STABLE_CONTENT_MONTHS,
}