71 lines
1.9 KiB
JavaScript
71 lines
1.9 KiB
JavaScript
/**
|
|
* Менеджер soft-locks для ресурсов
|
|
* Предотвращает одновременное редактирование одного ресурса
|
|
*/
|
|
|
|
// In-memory locks: key -> { owner, expiresAt }
|
|
const locks = new Map();
|
|
|
|
/**
|
|
* Очистить истекшие блокировки
|
|
*/
|
|
function cleanupExpiredLocks() {
|
|
const now = Date.now();
|
|
for (const [k, v] of locks.entries()) {
|
|
if (!v || typeof v.expiresAt !== 'number' || v.expiresAt <= now) {
|
|
locks.delete(k);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Автоматическая очистка каждые 30 секунд
|
|
setInterval(cleanupExpiredLocks, 30_000);
|
|
|
|
/**
|
|
* Получить статус блокировки ресурса
|
|
*/
|
|
function getLockStatus(resource) {
|
|
cleanupExpiredLocks();
|
|
const info = locks.get(resource);
|
|
if (!info) return { locked: false };
|
|
return { locked: true, owner: info.owner, expiresAt: info.expiresAt };
|
|
}
|
|
|
|
/**
|
|
* Получить или обновить блокировку ресурса
|
|
*/
|
|
function acquireLock(resource, owner = 'anonymous', ttlSeconds = 120) {
|
|
cleanupExpiredLocks();
|
|
const now = Date.now();
|
|
const existing = locks.get(resource);
|
|
|
|
if (existing && existing.expiresAt > now && existing.owner !== owner) {
|
|
return {
|
|
success: false,
|
|
locked: true,
|
|
owner: existing.owner,
|
|
expiresAt: existing.expiresAt
|
|
};
|
|
}
|
|
|
|
const expiresAt = now + Math.max(30, Math.min(600, Number(ttlSeconds) || 120)) * 1000;
|
|
locks.set(resource, { owner, expiresAt });
|
|
return { success: true, locked: true, owner, expiresAt };
|
|
}
|
|
|
|
/**
|
|
* Освободить блокировку ресурса
|
|
*/
|
|
function releaseLock(resource) {
|
|
locks.delete(resource);
|
|
return { released: true };
|
|
}
|
|
|
|
module.exports = {
|
|
getLockStatus,
|
|
acquireLock,
|
|
releaseLock,
|
|
cleanupExpiredLocks,
|
|
};
|
|
|