@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
JavaScript
/**
* @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