UNPKG

@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.

171 lines (155 loc) 5.54 kB
/** * @module Lock */ import {} from "ioredis"; import {} from "../../../../execution-context/contracts/_module.js"; import {} from "../../../../lock/contracts/_module.js"; import {} from "../../../../time-span/implementations/_module.js"; /** * To utilize the `RedisLockAdapter`, you must install the [`"ioredis"`](https://www.npmjs.com/package/ioredis) package. * * Note in order to use `RedisLockAdapter` correctly, ensure you use a single, consistent database across all server instances. * * IMPORT_PATH: `"@daiso-tech/core/lock/redis-lock-adapter"` * @group Adapters */ export class RedisLockAdapter { database; /** * @example * ```ts * import { RedisLockAdapter } from "@daiso-tech/core/lock/redis-lock-adapter"; * import Redis from "ioredis"; * * const database = new Redis("YOUR_REDIS_CONNECTION_STRING"); * const lockAdapter = new RedisLockAdapter(database); * ``` */ constructor(database) { this.database = database; this.initAquireCommand(); this.initReleaseCommand(); this.initRefreshComand(); this.initGetStateComand(); } initAquireCommand() { if (typeof this.database.daiso_lock_acquire === "function") { return; } this.database.defineCommand("daiso_lock_acquire", { numberOfKeys: 1, lua: ` local key = KEYS[1]; local lockId = ARGV[1]; -- Expiration time as unix timestamp in ms local expiration = tonumber(ARGV[2]); if redis.call("exists", key) == 1 then return redis.call("get", key) == lockId; end if expiration == nil then redis.call("set", key, lockId, "nx"); else redis.call("set", key, lockId, "pxat", expiration, "nx"); end return 1; `, }); } initReleaseCommand() { if (typeof this.database.daiso_lock_release === "function") { return; } this.database.defineCommand("daiso_lock_release", { numberOfKeys: 1, lua: ` local key = KEYS[1]; local lockId = ARGV[1]; if redis.call("exists", key) == 0 then return 0 end local isNotCurrentOwner = redis.call("get", key) ~= lockId if isNotCurrentOwner then return 0 end redis.call("del", key) return 1 `, }); } initRefreshComand() { if (typeof this.database.daiso_lock_refresh === "function") { return; } this.database.defineCommand("daiso_lock_refresh", { numberOfKeys: 1, lua: ` -- Arguments local key = KEYS[1]; local lockId = ARGV[1]; -- Expiration time as unix timestamp in ms local expiration = tonumber(ARGV[2]); if redis.call("exists", key) == 0 then return 0 end local isNotCurrentOwner = redis.call("get", key) ~= lockId if redis.call("get", key) ~= lockId then return 0 end local currentExpiration = redis.call("pttl", key) local isUnexpireable = currentExpiration == -1 if isUnexpireable then return 0 end redis.call("pexpireat", key, expiration) return 1 `, }); } initGetStateComand() { if (typeof this.database.daiso_lock_get_state === "function") { return; } this.database.defineCommand("daiso_lock_get_state", { numberOfKeys: 1, lua: ` -- Arguments local key = KEYS[1]; if tonumber(redis.call("exists", key)) == 0 then return cjson.encode(nil); end local state = { owner = redis.call("get", key), expiration = tonumber(redis.call("pexpiretime", key)) }; return cjson.encode(state); `, }); } async acquire(_context, key, lockId, ttl) { const result = await this.database.daiso_lock_acquire(key, lockId, ttl?.toEndDate().getTime() ?? null); return result === 1; } async release(_context, key, lockId) { const result = await this.database.daiso_lock_release(key, lockId); return result === 1; } async forceRelease(_context, key) { const result = await this.database.del(key); return result > 0; } async refresh(_context, key, lockId, ttl) { const result = await this.database.daiso_lock_refresh(key, lockId, ttl.toEndDate().getTime()); return result === 1; } async getState(_context, key) { const json = JSON.parse(await this.database.daiso_lock_get_state(key)); if (json === null) { return null; } return { owner: json.owner, expiration: json.expiration === -1 ? null : new Date(json.expiration), }; } } //# sourceMappingURL=redis-lock-adapter.js.map