@daiso-tech/core
Version:
The library offers flexible, framework-agnostic solutions for modern web applications, built on adaptable components that integrate seamlessly with popular frameworks like Next Js.
211 lines • 8.13 kB
JavaScript
/**
* @module SharedLock
*/
import {} from "../../../../shared-lock/contracts/_module.js";
import {} from "../../../../time-span/implementations/_module.js";
/**
* @internal
*/
export class DatabaseSharedLockAdapter {
adapter;
static async _forceReleaseWriter(trx, key) {
const readerSemaphore = await trx.reader.findSemaphore(key);
if (readerSemaphore !== null) {
return false;
}
const lockData = await trx.writer.remove(key);
if (lockData === null) {
return false;
}
if (lockData.expiration === null) {
return true;
}
return lockData.expiration > new Date();
}
static async _forceReleaseAllReaders(trx, key) {
const writerLock = await trx.writer.find(key);
if (writerLock !== null) {
return false;
}
const semapahoreSlotDataArray = await trx.reader.removeAllSlots(key);
return semapahoreSlotDataArray.some((semapahoreSlotData) => {
return (semapahoreSlotData.expiration === null ||
semapahoreSlotData.expiration > new Date());
});
}
static async getWriterState(trx, key) {
const lockData = await trx.writer.find(key);
if (lockData === null) {
return null;
}
if (lockData.expiration === null) {
return lockData;
}
if (lockData.expiration <= new Date()) {
return null;
}
return lockData;
}
static async getReaderState(trx, key) {
const semaphore = await trx.reader.findSemaphore(key);
if (semaphore === null) {
return null;
}
const slots = await trx.reader.findSlots(key);
const unexpiredSlots = slots.filter((slot) => slot.expiration === null || slot.expiration > new Date());
if (unexpiredSlots.length === 0) {
return null;
}
const unexpiredSlotsAsMap = new Map(unexpiredSlots.map((slot) => [slot.id, slot.expiration]));
return {
limit: semaphore.limit,
acquiredSlots: unexpiredSlotsAsMap,
};
}
constructor(adapter) {
this.adapter = adapter;
}
async acquireWriter(key, lockId, ttl) {
const expiration = ttl?.toEndDate() ?? null;
return await this.adapter.transaction(async (trx) => {
const readerSemaphore = await trx.reader.findSemaphore(key);
if (readerSemaphore !== null) {
return false;
}
const writerLock = await trx.writer.find(key);
if (writerLock === null) {
await trx.writer.upsert(key, lockId, expiration);
return true;
}
if (writerLock.owner === lockId) {
return true;
}
if (writerLock.expiration === null) {
return false;
}
if (writerLock.expiration <= new Date()) {
await trx.writer.upsert(key, lockId, expiration);
return true;
}
return writerLock.expiration <= new Date();
});
}
async releaseWriter(key, lockId) {
return await this.adapter.transaction(async (trx) => {
const readerSemaphore = await trx.reader.findSemaphore(key);
if (readerSemaphore !== null) {
return false;
}
const lockData = await trx.writer.removeIfOwner(key, lockId);
if (lockData === null) {
return false;
}
const { expiration } = lockData;
const hasNoExpiration = expiration === null;
if (hasNoExpiration) {
return true;
}
const { owner } = lockData;
const isNotExpired = expiration > new Date();
const isCurrentOwner = lockId === owner;
return isNotExpired && isCurrentOwner;
});
}
async forceReleaseWriter(key) {
return await this.adapter.transaction(async (trx) => {
return DatabaseSharedLockAdapter._forceReleaseWriter(trx, key);
});
}
async refreshWriter(key, lockId, ttl) {
return await this.adapter.transaction(async (trx) => {
const readerSemaphore = await trx.reader.findSemaphore(key);
if (readerSemaphore !== null) {
return false;
}
const updateCount = await trx.writer.updateExpiration(key, lockId, ttl.toEndDate());
return Number(updateCount) > 0;
});
}
async acquireReader(settings) {
const expiration = settings.ttl?.toEndDate() ?? null;
return await this.adapter.transaction(async (trx) => {
const writerLock = await trx.writer.find(settings.key);
if (writerLock !== null) {
return false;
}
const semaphoreData = await trx.reader.findSemaphore(settings.key);
let limit = semaphoreData?.limit;
if (limit === undefined) {
limit = settings.limit;
await trx.reader.upsertSemaphore(settings.key, limit);
}
const slots = await trx.reader.findSlots(settings.key);
const unexpiredSlots = slots.filter((slot) => slot.expiration === null || slot.expiration > new Date());
if (unexpiredSlots.length === 0) {
await trx.reader.upsertSemaphore(settings.key, settings.limit);
}
if (unexpiredSlots.length >= limit) {
return false;
}
const hasNotSlot = unexpiredSlots.every((slot) => slot.id !== settings.lockId);
if (hasNotSlot) {
await trx.reader.upsertSlot(settings.key, settings.lockId, expiration);
}
return true;
});
}
async releaseReader(key, lockId) {
return await this.adapter.transaction(async (trx) => {
const writerLock = await trx.writer.find(key);
if (writerLock !== null) {
return false;
}
const semapahoreSlotData = await trx.reader.removeSlot(key, lockId);
if (semapahoreSlotData === null) {
return false;
}
return (semapahoreSlotData.expiration === null ||
semapahoreSlotData.expiration > new Date());
});
}
async forceReleaseAllReaders(key) {
return await this.adapter.transaction(async (trx) => {
return DatabaseSharedLockAdapter._forceReleaseAllReaders(trx, key);
});
}
async refreshReader(key, slotId, ttl) {
return await this.adapter.transaction(async (trx) => {
const writerLock = await trx.writer.find(key);
if (writerLock !== null) {
return false;
}
const updateCount = await trx.reader.updateExpiration(key, slotId, ttl.toEndDate());
return Number(updateCount) > 0;
});
}
async forceRelease(key) {
return await this.adapter.transaction(async (trx) => {
const [hasForceReleasedWriter, hasForceReleasedAllReaders] = await Promise.all([
await DatabaseSharedLockAdapter._forceReleaseWriter(trx, key),
await DatabaseSharedLockAdapter._forceReleaseAllReaders(trx, key),
]);
return hasForceReleasedWriter || hasForceReleasedAllReaders;
});
}
async getState(key) {
return await this.adapter.transaction(async (trx) => {
const [writerState, readerState] = await Promise.all([
DatabaseSharedLockAdapter.getWriterState(trx, key),
DatabaseSharedLockAdapter.getReaderState(trx, key),
]);
if (writerState === null && readerState === null) {
return null;
}
return {
writer: writerState,
reader: readerState,
};
});
}
}
//# sourceMappingURL=database-shared-lock-adapter.js.map