UNPKG

wise-json-db

Version:

Blazing fast, crash-proof embedded JSON database for Node.js with batch operations, TTL, indexes, and segmented checkpointing.

106 lines (96 loc) 5.52 kB
// wise-json/collection/ttl.js const logger = require('../logger'); /** * Проверяет, жив ли документ (учитывая expireAt или ttl). * ttl — это время жизни в ms с момента createdAt. * expireAt — абсолютная дата (ms или ISO string). */ function isAlive(doc) { if (!doc || typeof doc !== 'object') { return false; } // Абсолютный срок жизни (expireAt) if (doc.hasOwnProperty('expireAt')) { // Используем hasOwnProperty для явного указания на поле if (doc.expireAt === null || doc.expireAt === undefined) { // Если expireAt явно null или undefined, считаем его бессрочным по этому критерию // (или можно интерпретировать как ошибку, но для isAlive лучше вернуть true) } else { const exp = typeof doc.expireAt === 'string' ? Date.parse(doc.expireAt) : Number(doc.expireAt); if (isNaN(exp)) { // Если expireAt не может быть преобразован в валидную дату (например, "not-a-date"), // считаем такой документ "живым", чтобы избежать случайного удаления из-за неверных данных. // Администратор должен будет исправить такие данные вручную. return true; } // Если exp - валидное число, проверяем, не истек ли срок return Date.now() < exp; } } // TTL — относительный срок жизни (ms от createdAt) if (doc.hasOwnProperty('ttl')) { // Используем hasOwnProperty if (doc.ttl === null || doc.ttl === undefined) { // Если ttl явно null или undefined, этот критерий не применяется } else { const createdAtStr = doc.createdAt; if (!createdAtStr) { // Если нет createdAt, а ttl задан, невозможно рассчитать срок жизни. // Считаем "живым", чтобы не удалить по ошибке. return true; } const createdAtMs = Date.parse(createdAtStr); if (isNaN(createdAtMs)) { // Если createdAt невалиден, невозможно рассчитать срок жизни. // Считаем "живым". return true; } const ttlMs = Number(doc.ttl); if (isNaN(ttlMs)) { // Если ttl не число, невозможно рассчитать. Считаем "живым". return true; } // Date.now() должен быть строго меньше, чем createdAtMs + ttlMs // Если ttlMs равен 0, то Date.now() < createdAtMs будет false (или true, если часы сильно рассинхронизированы), // что корректно сделает документ "неживым" практически сразу. return Date.now() < (createdAtMs + ttlMs); } } // Если нет ни expireAt, ни ttl, документ считается бессрочным (живым) return true; } exports.isAlive = isAlive; /** * Удаляет все expired-документы из Map документов и обновляет индексы, если indexManager предоставлен. * Возвращает количество удалённых документов. * @param {Map<string, object>} documents - Карта документов коллекции. * @param {object} [indexManager] - Опциональный менеджер индексов для обновления. * @returns {number} - Количество удаленных документов. */ function cleanupExpiredDocs(documents, indexManager) { let removedCount = 0; if (!(documents instanceof Map)) { // logger.warn('[TTL Cleanup] Provided documents is not a Map. Skipping cleanup.'); return removedCount; // Защита, если передан не Map } const idsToRemove = []; for (const [id, doc] of documents.entries()) { if (!isAlive(doc)) { idsToRemove.push(id); } } if (idsToRemove.length > 0) { for (const id of idsToRemove) { const docToRemove = documents.get(id); // Получаем документ перед удалением для indexManager if (docToRemove) { // Убедимся, что он все еще там (маловероятно, но для безопасности) documents.delete(id); if (indexManager && typeof indexManager.afterRemove === 'function') { indexManager.afterRemove(docToRemove); } removedCount++; } } } return removedCount; } exports.cleanupExpiredDocs = cleanupExpiredDocs;