@shagital/atomic-lock
Version:
Universal atomic locking with pluggable drivers (Redis, SQLite, File, Memory)
140 lines (139 loc) • 4.08 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MemoryLockDriver = void 0;
/**
* In-memory driver implementation (for testing and single-process scenarios)
*/
class MemoryLockDriver {
constructor(config = {}) {
this.locks = new Map();
if (config.cleanupInterval) {
this.cleanupInterval = setInterval(() => {
this.cleanup().catch(console.error);
}, config.cleanupInterval);
}
}
async tryAcquire(key, lockValue, expiryInSeconds) {
const now = Date.now();
const existing = this.locks.get(key);
// Check if existing lock is expired
if (existing && existing.expiresAt < now) {
this.locks.delete(key);
}
// Check if lock exists and is not expired
if (this.locks.has(key)) {
return false;
}
// Acquire lock
this.locks.set(key, {
value: lockValue,
expiresAt: now + (expiryInSeconds * 1000),
createdAt: now
});
return true;
}
async tryAcquireMultiple(keys, lockValue, expiryInSeconds) {
const now = Date.now();
// Clean up expired locks for all keys
for (const key of keys) {
const existing = this.locks.get(key);
if (existing && existing.expiresAt < now) {
this.locks.delete(key);
}
}
// Check if any locks exist
for (const key of keys) {
if (this.locks.has(key)) {
return false;
}
}
// Acquire all locks
const lockData = {
value: lockValue,
expiresAt: now + (expiryInSeconds * 1000),
createdAt: now
};
for (const key of keys) {
this.locks.set(key, lockData);
}
return true;
}
async release(key, lockValue) {
const existing = this.locks.get(key);
if (existing && existing.value === lockValue) {
this.locks.delete(key);
return true;
}
return false;
}
async releaseMultiple(keys, lockValue) {
let released = 0;
for (const key of keys) {
const success = await this.release(key, lockValue);
if (success)
released++;
}
return released;
}
async exists(key) {
const existing = this.locks.get(key);
if (!existing)
return false;
// Check if expired
if (existing.expiresAt < Date.now()) {
this.locks.delete(key);
return false;
}
return true;
}
async getLockInfo(key) {
const existing = this.locks.get(key);
if (!existing)
return null;
// Check if expired
if (existing.expiresAt < Date.now()) {
this.locks.delete(key);
return null;
}
return {
key,
value: existing.value,
expiresAt: existing.expiresAt,
createdAt: existing.createdAt
};
}
async cleanup() {
const now = Date.now();
for (const [key, lock] of this.locks.entries()) {
if (lock.expiresAt < now) {
this.locks.delete(key);
}
}
}
async close() {
if (this.cleanupInterval) {
clearInterval(this.cleanupInterval);
}
this.locks.clear();
}
// For testing purposes
getLockCount() {
return this.locks.size;
}
getAllLocks() {
const result = new Map();
const now = Date.now();
for (const [key, lock] of this.locks.entries()) {
if (lock.expiresAt >= now) {
result.set(key, {
key,
value: lock.value,
expiresAt: lock.expiresAt,
createdAt: lock.createdAt
});
}
}
return result;
}
}
exports.MemoryLockDriver = MemoryLockDriver;