namirasoft-node-redis 1.4.39 → 1.4.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/RedisInstance.d.ts +4 -0
- package/dist/RedisInstance.js +14 -6
- package/dist/RedisInstance.js.map +1 -1
- package/dist/RedisScopeTotalCount.js +16 -16
- package/package.json +25 -25
- package/src/RedisInstance.ts +61 -55
- package/src/RedisScope.ts +54 -54
- package/src/RedisScopeDelay.ts +55 -55
- package/src/RedisScopeHashCounter.ts +55 -55
- package/src/RedisScopeLimit.ts +60 -60
- package/src/RedisScopeLock.ts +32 -32
- package/src/RedisScopeShardCoordinator.ts +38 -38
- package/src/RedisScopeTotalCount.ts +45 -45
- package/src/RedisScopeUniqueness.ts +29 -29
- package/src/index.ts +8 -8
package/dist/RedisInstance.d.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import * as Redis from "ioredis";
|
|
2
2
|
import { IDatabase } from "namirasoft-node";
|
|
3
3
|
export declare abstract class RedisInstance implements IDatabase {
|
|
4
|
+
private host;
|
|
5
|
+
private port;
|
|
6
|
+
private password;
|
|
7
|
+
private db;
|
|
4
8
|
client: Redis.Redis;
|
|
5
9
|
constructor(host: string, port: number, password: string, db?: number);
|
|
6
10
|
getType(): string;
|
package/dist/RedisInstance.js
CHANGED
|
@@ -47,10 +47,10 @@ const Redis = __importStar(require("ioredis"));
|
|
|
47
47
|
const namirasoft_log_1 = require("namirasoft-log");
|
|
48
48
|
class RedisInstance {
|
|
49
49
|
constructor(host, port, password, db = 0) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
50
|
+
this.host = host;
|
|
51
|
+
this.port = port;
|
|
52
|
+
this.password = password;
|
|
53
|
+
this.db = db;
|
|
54
54
|
this.close = this.close.bind(this);
|
|
55
55
|
}
|
|
56
56
|
getType() {
|
|
@@ -65,6 +65,11 @@ class RedisInstance {
|
|
|
65
65
|
}
|
|
66
66
|
connect() {
|
|
67
67
|
return __awaiter(this, void 0, void 0, function* () {
|
|
68
|
+
var _a;
|
|
69
|
+
if (!process.env.NAMIRASOFT_MUTE) {
|
|
70
|
+
this.client = new Redis.Redis({ host: this.host, port: this.port, password: this.password, db: this.db });
|
|
71
|
+
(_a = namirasoft_log_1.Logger.main) === null || _a === void 0 ? void 0 : _a.success(`Redis - ${this.host}:${this.port} - was connected.`);
|
|
72
|
+
}
|
|
68
73
|
});
|
|
69
74
|
}
|
|
70
75
|
prepare() {
|
|
@@ -96,8 +101,11 @@ class RedisInstance {
|
|
|
96
101
|
}
|
|
97
102
|
close() {
|
|
98
103
|
return __awaiter(this, void 0, void 0, function* () {
|
|
99
|
-
var _a;
|
|
100
|
-
(
|
|
104
|
+
var _a, _b;
|
|
105
|
+
if (!process.env.NAMIRASOFT_MUTE) {
|
|
106
|
+
(_a = this.client) === null || _a === void 0 ? void 0 : _a.disconnect();
|
|
107
|
+
(_b = namirasoft_log_1.Logger.main) === null || _b === void 0 ? void 0 : _b.success(`Redis - ${this.host}:${this.port} - was closed.`);
|
|
108
|
+
}
|
|
101
109
|
});
|
|
102
110
|
}
|
|
103
111
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RedisInstance.js","sourceRoot":"","sources":["../src/RedisInstance.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+CAAiC;AACjC,mDAAwC;AAGxC,MAAsB,aAAa;IAG/B,
|
|
1
|
+
{"version":3,"file":"RedisInstance.js","sourceRoot":"","sources":["../src/RedisInstance.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+CAAiC;AACjC,mDAAwC;AAGxC,MAAsB,aAAa;IAG/B,YAAoB,IAAY,EAAU,IAAY,EAAU,QAAgB,EAAU,KAAa,CAAC;QAApF,SAAI,GAAJ,IAAI,CAAQ;QAAU,SAAI,GAAJ,IAAI,CAAQ;QAAU,aAAQ,GAAR,QAAQ,CAAQ;QAAU,OAAE,GAAF,EAAE,CAAY;QAEpG,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACvC,CAAC;IACD,OAAO;QAEH,OAAO,OAAO,CAAC;IACnB,CAAC;IACD,OAAO;QAEH,OAAO,EAAE,CAAC;IACd,CAAC;IACK,IAAI;;QAEV,CAAC;KAAA;IACK,OAAO;;;YAET,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,EAChC,CAAC;gBACG,IAAI,CAAC,MAAM,GAAG,IAAI,KAAK,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;gBAC1G,MAAA,uBAAM,CAAC,IAAI,0CAAE,OAAO,CAAC,WAAW,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,mBAAmB,CAAC,CAAC;YAC/E,CAAC;QACL,CAAC;KAAA;IACK,OAAO;;QAEb,CAAC;KAAA;IACK,UAAU;;QAEhB,CAAC;KAAA;IACK,SAAS;;QAEf,CAAC;KAAA;IACK,IAAI;;QAEV,CAAC;KAAA;IACK,SAAS;;YAEX,IACA,CAAC;gBACG,IAAI,GAAG,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;gBACnC,OAAO,GAAG,KAAK,MAAM,CAAA;YACzB,CAAC;YAAC,OAAO,KAAK,EACd,CAAC;YACD,CAAC;YACD,OAAO,KAAK,CAAC;QACjB,CAAC;KAAA;IACK,KAAK;;;YAEP,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,EAChC,CAAC;gBACG,MAAA,IAAI,CAAC,MAAM,0CAAE,UAAU,EAAE,CAAC;gBAC1B,MAAA,uBAAM,CAAC,IAAI,0CAAE,OAAO,CAAC,WAAW,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,gBAAgB,CAAC,CAAC;YAC5E,CAAC;QACL,CAAC;KAAA;CACJ;AAzDD,sCAyDC;AAAA,CAAC"}
|
|
@@ -27,22 +27,22 @@ class RedisScopeTotalCount extends RedisScope_1.RedisScope {
|
|
|
27
27
|
let total_field = this.total.field(field_parameter);
|
|
28
28
|
let count_key = this.count.key(key_parameter);
|
|
29
29
|
let count_field = this.count.field(field_parameter);
|
|
30
|
-
const script = `
|
|
31
|
-
local total = tonumber(redis.call("HGET", KEYS[1], ARGV[1]) or "0")
|
|
32
|
-
local count = tonumber(redis.call("HGET", KEYS[2], ARGV[2]) or "0")
|
|
33
|
-
local step = tonumber(ARGV[3])
|
|
34
|
-
local threshold = tonumber(ARGV[4])
|
|
35
|
-
|
|
36
|
-
if threshold >= 0 then
|
|
37
|
-
if total + step > threshold then
|
|
38
|
-
return {0, total, count}
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
total = redis.call("HINCRBY", KEYS[1], ARGV[1], step)
|
|
43
|
-
count = redis.call("HINCRBY", KEYS[2], ARGV[2], step)
|
|
44
|
-
|
|
45
|
-
return {1, total, count}
|
|
30
|
+
const script = `
|
|
31
|
+
local total = tonumber(redis.call("HGET", KEYS[1], ARGV[1]) or "0")
|
|
32
|
+
local count = tonumber(redis.call("HGET", KEYS[2], ARGV[2]) or "0")
|
|
33
|
+
local step = tonumber(ARGV[3])
|
|
34
|
+
local threshold = tonumber(ARGV[4])
|
|
35
|
+
|
|
36
|
+
if threshold >= 0 then
|
|
37
|
+
if total + step > threshold then
|
|
38
|
+
return {0, total, count}
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
total = redis.call("HINCRBY", KEYS[1], ARGV[1], step)
|
|
43
|
+
count = redis.call("HINCRBY", KEYS[2], ARGV[2], step)
|
|
44
|
+
|
|
45
|
+
return {1, total, count}
|
|
46
46
|
`;
|
|
47
47
|
let res = yield this.instance.client.eval(script, 2, total_key, count_key, total_field, count_field, step, threshold !== null && threshold !== void 0 ? threshold : -1);
|
|
48
48
|
let [success, total, count] = res;
|
package/package.json
CHANGED
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "namirasoft-node-redis",
|
|
3
|
-
"title": "Namirasoft Node Redis NPM Package",
|
|
4
|
-
"description": "Namira Software Corporation Node Redis NPM Package",
|
|
5
|
-
"icon": "logo.png",
|
|
6
|
-
"logo": "https://static.namirasoft.com/image/application/redis/logo/base.png",
|
|
7
|
-
"language": "ts",
|
|
8
|
-
"framework": "npm",
|
|
9
|
-
"application": "package",
|
|
10
|
-
"private": false,
|
|
11
|
-
"version": "1.4.
|
|
12
|
-
"author": "Amir Abolhasani",
|
|
13
|
-
"license": "MIT",
|
|
14
|
-
"main": "./dist/index.js",
|
|
15
|
-
"types": "./dist/index.d.ts",
|
|
16
|
-
"scripts": {
|
|
17
|
-
"build": ""
|
|
18
|
-
},
|
|
19
|
-
"dependencies": {
|
|
20
|
-
"@types/node": "^
|
|
21
|
-
"ioredis": "^5.
|
|
22
|
-
"namirasoft-core": "^1.4.
|
|
23
|
-
"namirasoft-log": "^1.4.41",
|
|
24
|
-
"namirasoft-node": "^1.4.
|
|
25
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "namirasoft-node-redis",
|
|
3
|
+
"title": "Namirasoft Node Redis NPM Package",
|
|
4
|
+
"description": "Namira Software Corporation Node Redis NPM Package",
|
|
5
|
+
"icon": "logo.png",
|
|
6
|
+
"logo": "https://static.namirasoft.com/image/application/redis/logo/base.png",
|
|
7
|
+
"language": "ts",
|
|
8
|
+
"framework": "npm",
|
|
9
|
+
"application": "package",
|
|
10
|
+
"private": false,
|
|
11
|
+
"version": "1.4.41",
|
|
12
|
+
"author": "Amir Abolhasani",
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"main": "./dist/index.js",
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": ""
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"@types/node": "^25.2.3",
|
|
21
|
+
"ioredis": "^5.9.3",
|
|
22
|
+
"namirasoft-core": "^1.4.111",
|
|
23
|
+
"namirasoft-log": "^1.4.41",
|
|
24
|
+
"namirasoft-node": "^1.4.135"
|
|
25
|
+
}
|
|
26
26
|
}
|
package/src/RedisInstance.ts
CHANGED
|
@@ -1,56 +1,62 @@
|
|
|
1
|
-
import * as Redis from "ioredis";
|
|
2
|
-
import { Logger } from "namirasoft-log";
|
|
3
|
-
import { IDatabase } from "namirasoft-node";
|
|
4
|
-
|
|
5
|
-
export abstract class RedisInstance implements IDatabase
|
|
6
|
-
{
|
|
7
|
-
public client!: Redis.Redis;
|
|
8
|
-
constructor(host: string, port: number, password: string, db: number = 0)
|
|
9
|
-
{
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
async
|
|
24
|
-
{
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
1
|
+
import * as Redis from "ioredis";
|
|
2
|
+
import { Logger } from "namirasoft-log";
|
|
3
|
+
import { IDatabase } from "namirasoft-node";
|
|
4
|
+
|
|
5
|
+
export abstract class RedisInstance implements IDatabase
|
|
6
|
+
{
|
|
7
|
+
public client!: Redis.Redis;
|
|
8
|
+
constructor(private host: string, private port: number, private password: string, private db: number = 0)
|
|
9
|
+
{
|
|
10
|
+
this.close = this.close.bind(this);
|
|
11
|
+
}
|
|
12
|
+
getType()
|
|
13
|
+
{
|
|
14
|
+
return "Redis";
|
|
15
|
+
}
|
|
16
|
+
getName()
|
|
17
|
+
{
|
|
18
|
+
return "";
|
|
19
|
+
}
|
|
20
|
+
async init()
|
|
21
|
+
{
|
|
22
|
+
}
|
|
23
|
+
async connect()
|
|
24
|
+
{
|
|
25
|
+
if (!process.env.NAMIRASOFT_MUTE)
|
|
26
|
+
{
|
|
27
|
+
this.client = new Redis.Redis({ host: this.host, port: this.port, password: this.password, db: this.db });
|
|
28
|
+
Logger.main?.success(`Redis - ${this.host}:${this.port} - was connected.`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
async prepare()
|
|
32
|
+
{
|
|
33
|
+
}
|
|
34
|
+
async seedBefore()
|
|
35
|
+
{
|
|
36
|
+
}
|
|
37
|
+
async seedAfter()
|
|
38
|
+
{
|
|
39
|
+
}
|
|
40
|
+
async sync()
|
|
41
|
+
{
|
|
42
|
+
}
|
|
43
|
+
async isHealthy()
|
|
44
|
+
{
|
|
45
|
+
try
|
|
46
|
+
{
|
|
47
|
+
let res = await this.client.ping();
|
|
48
|
+
return res === "PONG"
|
|
49
|
+
} catch (error)
|
|
50
|
+
{
|
|
51
|
+
}
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
async close()
|
|
55
|
+
{
|
|
56
|
+
if (!process.env.NAMIRASOFT_MUTE)
|
|
57
|
+
{
|
|
58
|
+
this.client?.disconnect();
|
|
59
|
+
Logger.main?.success(`Redis - ${this.host}:${this.port} - was closed.`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
56
62
|
};
|
package/src/RedisScope.ts
CHANGED
|
@@ -1,55 +1,55 @@
|
|
|
1
|
-
import { EncodingOperation } from "namirasoft-core";
|
|
2
|
-
import { RedisInstance } from "./RedisInstance";
|
|
3
|
-
|
|
4
|
-
export abstract class RedisScope<KeyParameters, ValueType>
|
|
5
|
-
{
|
|
6
|
-
protected instance: RedisInstance;
|
|
7
|
-
constructor(instance: RedisInstance)
|
|
8
|
-
{
|
|
9
|
-
this.instance = instance;
|
|
10
|
-
this.key = this.key.bind(this);
|
|
11
|
-
this._get = this._get.bind(this);
|
|
12
|
-
this._set = this._set.bind(this);
|
|
13
|
-
this._del = this._del.bind(this);
|
|
14
|
-
}
|
|
15
|
-
public abstract key(key_parameter: KeyParameters): string;
|
|
16
|
-
protected async _get(key: string, base64: boolean): Promise<ValueType | null>
|
|
17
|
-
{
|
|
18
|
-
if (process.env.NAMIRASOFT_MUTE)
|
|
19
|
-
return null;
|
|
20
|
-
let data = await this.instance.client.get(key);
|
|
21
|
-
if (data)
|
|
22
|
-
{
|
|
23
|
-
if (base64)
|
|
24
|
-
data = EncodingOperation.Base64Decode(data);
|
|
25
|
-
return JSON.parse(data) as ValueType;
|
|
26
|
-
}
|
|
27
|
-
return null;
|
|
28
|
-
}
|
|
29
|
-
protected async _set(key: string, value: ValueType, base64: boolean, ttl_seconds?: number, NotExists: boolean = false)
|
|
30
|
-
{
|
|
31
|
-
if (process.env.NAMIRASOFT_MUTE)
|
|
32
|
-
return null;
|
|
33
|
-
let data = JSON.stringify(value);
|
|
34
|
-
if (base64)
|
|
35
|
-
data = EncodingOperation.Base64Encode(data);
|
|
36
|
-
if (NotExists)
|
|
37
|
-
{
|
|
38
|
-
if (ttl_seconds)
|
|
39
|
-
return await this.instance.client.set(key, data, "EX", ttl_seconds, "NX");
|
|
40
|
-
return await this.instance.client.set(key, data, "NX");
|
|
41
|
-
}
|
|
42
|
-
else
|
|
43
|
-
{
|
|
44
|
-
if (ttl_seconds)
|
|
45
|
-
return await this.instance.client.set(key, data, "EX", ttl_seconds);
|
|
46
|
-
return await this.instance.client.set(key, data);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
protected async _del(key: string)
|
|
50
|
-
{
|
|
51
|
-
if (process.env.NAMIRASOFT_MUTE)
|
|
52
|
-
return;
|
|
53
|
-
await this.instance.client.del(key);
|
|
54
|
-
}
|
|
1
|
+
import { EncodingOperation } from "namirasoft-core";
|
|
2
|
+
import { RedisInstance } from "./RedisInstance";
|
|
3
|
+
|
|
4
|
+
export abstract class RedisScope<KeyParameters, ValueType>
|
|
5
|
+
{
|
|
6
|
+
protected instance: RedisInstance;
|
|
7
|
+
constructor(instance: RedisInstance)
|
|
8
|
+
{
|
|
9
|
+
this.instance = instance;
|
|
10
|
+
this.key = this.key.bind(this);
|
|
11
|
+
this._get = this._get.bind(this);
|
|
12
|
+
this._set = this._set.bind(this);
|
|
13
|
+
this._del = this._del.bind(this);
|
|
14
|
+
}
|
|
15
|
+
public abstract key(key_parameter: KeyParameters): string;
|
|
16
|
+
protected async _get(key: string, base64: boolean): Promise<ValueType | null>
|
|
17
|
+
{
|
|
18
|
+
if (process.env.NAMIRASOFT_MUTE)
|
|
19
|
+
return null;
|
|
20
|
+
let data = await this.instance.client.get(key);
|
|
21
|
+
if (data)
|
|
22
|
+
{
|
|
23
|
+
if (base64)
|
|
24
|
+
data = EncodingOperation.Base64Decode(data);
|
|
25
|
+
return JSON.parse(data) as ValueType;
|
|
26
|
+
}
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
protected async _set(key: string, value: ValueType, base64: boolean, ttl_seconds?: number, NotExists: boolean = false)
|
|
30
|
+
{
|
|
31
|
+
if (process.env.NAMIRASOFT_MUTE)
|
|
32
|
+
return null;
|
|
33
|
+
let data = JSON.stringify(value);
|
|
34
|
+
if (base64)
|
|
35
|
+
data = EncodingOperation.Base64Encode(data);
|
|
36
|
+
if (NotExists)
|
|
37
|
+
{
|
|
38
|
+
if (ttl_seconds)
|
|
39
|
+
return await this.instance.client.set(key, data, "EX", ttl_seconds, "NX");
|
|
40
|
+
return await this.instance.client.set(key, data, "NX");
|
|
41
|
+
}
|
|
42
|
+
else
|
|
43
|
+
{
|
|
44
|
+
if (ttl_seconds)
|
|
45
|
+
return await this.instance.client.set(key, data, "EX", ttl_seconds);
|
|
46
|
+
return await this.instance.client.set(key, data);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
protected async _del(key: string)
|
|
50
|
+
{
|
|
51
|
+
if (process.env.NAMIRASOFT_MUTE)
|
|
52
|
+
return;
|
|
53
|
+
await this.instance.client.del(key);
|
|
54
|
+
}
|
|
55
55
|
};
|
package/src/RedisScopeDelay.ts
CHANGED
|
@@ -1,56 +1,56 @@
|
|
|
1
|
-
import { DurationUnit, TimeUnitOperation } from "namirasoft-core";
|
|
2
|
-
import { RedisInstance } from "./RedisInstance";
|
|
3
|
-
import { RedisScope } from "./RedisScope";
|
|
4
|
-
import { RedisScopeLock } from "./RedisScopeLock";
|
|
5
|
-
|
|
6
|
-
export type RedisScopeDelayParameter = {
|
|
7
|
-
id: string;
|
|
8
|
-
delay: boolean;
|
|
9
|
-
delay_value: number | null;
|
|
10
|
-
delay_unit: DurationUnit | null;
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
export abstract class RedisScopeDelay<KeyParameters> extends RedisScope<KeyParameters, Date>
|
|
14
|
-
{
|
|
15
|
-
private locker: RedisScopeLock<KeyParameters>;
|
|
16
|
-
private max_processing_time_in_seconds: number;
|
|
17
|
-
constructor(instance: RedisInstance, locker: RedisScopeLock<KeyParameters>, max_processing_time_in_seconds: number = 60)
|
|
18
|
-
{
|
|
19
|
-
super(instance);
|
|
20
|
-
this.locker = locker;
|
|
21
|
-
this.max_processing_time_in_seconds = max_processing_time_in_seconds;
|
|
22
|
-
this.wait = this.wait.bind(this);
|
|
23
|
-
this.release = this.release.bind(this);
|
|
24
|
-
}
|
|
25
|
-
async wait(key_parameter: KeyParameters, parameter: RedisScopeDelayParameter): Promise<void>
|
|
26
|
-
{
|
|
27
|
-
if (!parameter.delay || !parameter.delay_value || !parameter.delay_unit)
|
|
28
|
-
return;
|
|
29
|
-
|
|
30
|
-
let key = this.key(key_parameter);
|
|
31
|
-
|
|
32
|
-
let delay_requested = TimeUnitOperation.toMilliseconds(parameter.delay_value, parameter.delay_unit);
|
|
33
|
-
let ttl_seconds = Math.ceil(delay_requested / 1000) + this.max_processing_time_in_seconds;
|
|
34
|
-
await this.locker.waitForLock(key_parameter, ttl_seconds);
|
|
35
|
-
|
|
36
|
-
let LastRun = await this._get(key, false);
|
|
37
|
-
if (!LastRun)
|
|
38
|
-
return;
|
|
39
|
-
LastRun = new Date(LastRun);
|
|
40
|
-
|
|
41
|
-
let difference = new Date().getTime() - LastRun.getTime();
|
|
42
|
-
let delay_required = Math.max(delay_requested - difference, 0);
|
|
43
|
-
|
|
44
|
-
if (delay_required > 0)
|
|
45
|
-
await new Promise(resolve => setTimeout(resolve, delay_required));
|
|
46
|
-
}
|
|
47
|
-
async release(key_parameter: KeyParameters, parameter: RedisScopeDelayParameter)
|
|
48
|
-
{
|
|
49
|
-
if (!parameter.delay || !parameter.delay_value || !parameter.delay_unit)
|
|
50
|
-
return;
|
|
51
|
-
|
|
52
|
-
let key = this.key(key_parameter);
|
|
53
|
-
await this._set(key, new Date(), false);
|
|
54
|
-
await this.locker.unlock(key_parameter);
|
|
55
|
-
}
|
|
1
|
+
import { DurationUnit, TimeUnitOperation } from "namirasoft-core";
|
|
2
|
+
import { RedisInstance } from "./RedisInstance";
|
|
3
|
+
import { RedisScope } from "./RedisScope";
|
|
4
|
+
import { RedisScopeLock } from "./RedisScopeLock";
|
|
5
|
+
|
|
6
|
+
export type RedisScopeDelayParameter = {
|
|
7
|
+
id: string;
|
|
8
|
+
delay: boolean;
|
|
9
|
+
delay_value: number | null;
|
|
10
|
+
delay_unit: DurationUnit | null;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export abstract class RedisScopeDelay<KeyParameters> extends RedisScope<KeyParameters, Date>
|
|
14
|
+
{
|
|
15
|
+
private locker: RedisScopeLock<KeyParameters>;
|
|
16
|
+
private max_processing_time_in_seconds: number;
|
|
17
|
+
constructor(instance: RedisInstance, locker: RedisScopeLock<KeyParameters>, max_processing_time_in_seconds: number = 60)
|
|
18
|
+
{
|
|
19
|
+
super(instance);
|
|
20
|
+
this.locker = locker;
|
|
21
|
+
this.max_processing_time_in_seconds = max_processing_time_in_seconds;
|
|
22
|
+
this.wait = this.wait.bind(this);
|
|
23
|
+
this.release = this.release.bind(this);
|
|
24
|
+
}
|
|
25
|
+
async wait(key_parameter: KeyParameters, parameter: RedisScopeDelayParameter): Promise<void>
|
|
26
|
+
{
|
|
27
|
+
if (!parameter.delay || !parameter.delay_value || !parameter.delay_unit)
|
|
28
|
+
return;
|
|
29
|
+
|
|
30
|
+
let key = this.key(key_parameter);
|
|
31
|
+
|
|
32
|
+
let delay_requested = TimeUnitOperation.toMilliseconds(parameter.delay_value, parameter.delay_unit);
|
|
33
|
+
let ttl_seconds = Math.ceil(delay_requested / 1000) + this.max_processing_time_in_seconds;
|
|
34
|
+
await this.locker.waitForLock(key_parameter, ttl_seconds);
|
|
35
|
+
|
|
36
|
+
let LastRun = await this._get(key, false);
|
|
37
|
+
if (!LastRun)
|
|
38
|
+
return;
|
|
39
|
+
LastRun = new Date(LastRun);
|
|
40
|
+
|
|
41
|
+
let difference = new Date().getTime() - LastRun.getTime();
|
|
42
|
+
let delay_required = Math.max(delay_requested - difference, 0);
|
|
43
|
+
|
|
44
|
+
if (delay_required > 0)
|
|
45
|
+
await new Promise(resolve => setTimeout(resolve, delay_required));
|
|
46
|
+
}
|
|
47
|
+
async release(key_parameter: KeyParameters, parameter: RedisScopeDelayParameter)
|
|
48
|
+
{
|
|
49
|
+
if (!parameter.delay || !parameter.delay_value || !parameter.delay_unit)
|
|
50
|
+
return;
|
|
51
|
+
|
|
52
|
+
let key = this.key(key_parameter);
|
|
53
|
+
await this._set(key, new Date(), false);
|
|
54
|
+
await this.locker.unlock(key_parameter);
|
|
55
|
+
}
|
|
56
56
|
};
|
|
@@ -1,56 +1,56 @@
|
|
|
1
|
-
import { RedisInstance } from "./RedisInstance";
|
|
2
|
-
import { RedisScope } from "./RedisScope";
|
|
3
|
-
|
|
4
|
-
export abstract class RedisScopeHashCounter<KeyParameters, FieldParameters> extends RedisScope<KeyParameters | null, Date>
|
|
5
|
-
{
|
|
6
|
-
constructor(instance: RedisInstance)
|
|
7
|
-
{
|
|
8
|
-
super(instance);
|
|
9
|
-
this.increase = this.increase.bind(this);
|
|
10
|
-
this.listKeys = this.listKeys.bind(this);
|
|
11
|
-
this.listFields = this.listFields.bind(this);
|
|
12
|
-
this.del = this.del.bind(this);
|
|
13
|
-
}
|
|
14
|
-
public abstract field(field_parameter: FieldParameters): string;
|
|
15
|
-
async valid(key_parameter: KeyParameters, field_parameter: FieldParameters, threshold: number, step: number): Promise<boolean>
|
|
16
|
-
{
|
|
17
|
-
let count = await this.hget(key_parameter, field_parameter);
|
|
18
|
-
return count + step <= threshold;
|
|
19
|
-
}
|
|
20
|
-
async increase(key_parameter: KeyParameters, field_parameter: FieldParameters, step: number): Promise<number>
|
|
21
|
-
{
|
|
22
|
-
let key = this.key(key_parameter);
|
|
23
|
-
let field = this.field(field_parameter);
|
|
24
|
-
return await this.instance.client.hincrby(key, field, step);
|
|
25
|
-
}
|
|
26
|
-
async hget(key_parameter: KeyParameters, field_parameter: FieldParameters): Promise<number>
|
|
27
|
-
{
|
|
28
|
-
let key = this.key(key_parameter);
|
|
29
|
-
let field = this.field(field_parameter);
|
|
30
|
-
let res = await this.instance.client.hget(key, field) ?? "0";
|
|
31
|
-
return parseInt(res);
|
|
32
|
-
}
|
|
33
|
-
async listKeys(count: number = 1, cursor?: string | number): Promise<{ cursor: string | number, keys: string[] }>
|
|
34
|
-
{
|
|
35
|
-
if (cursor == null)
|
|
36
|
-
cursor = "0";
|
|
37
|
-
let [next, keys] = await this.instance.client.scan(cursor, "MATCH", this.key(null), "COUNT", count,);
|
|
38
|
-
return { keys, cursor: next };
|
|
39
|
-
}
|
|
40
|
-
async listFields(key_parameter: KeyParameters)
|
|
41
|
-
{
|
|
42
|
-
let key = this.key(key_parameter);
|
|
43
|
-
return await this.instance.client.hgetall(key);
|
|
44
|
-
}
|
|
45
|
-
async delField(key_parameter: KeyParameters, field_parameter: FieldParameters)
|
|
46
|
-
{
|
|
47
|
-
let key = this.key(key_parameter);
|
|
48
|
-
let field = this.field(field_parameter);
|
|
49
|
-
return await this.instance.client.hdel(key, field);
|
|
50
|
-
}
|
|
51
|
-
async del(key_parameter: KeyParameters)
|
|
52
|
-
{
|
|
53
|
-
let key = this.key(key_parameter);
|
|
54
|
-
return await this._del(key);
|
|
55
|
-
}
|
|
1
|
+
import { RedisInstance } from "./RedisInstance";
|
|
2
|
+
import { RedisScope } from "./RedisScope";
|
|
3
|
+
|
|
4
|
+
export abstract class RedisScopeHashCounter<KeyParameters, FieldParameters> extends RedisScope<KeyParameters | null, Date>
|
|
5
|
+
{
|
|
6
|
+
constructor(instance: RedisInstance)
|
|
7
|
+
{
|
|
8
|
+
super(instance);
|
|
9
|
+
this.increase = this.increase.bind(this);
|
|
10
|
+
this.listKeys = this.listKeys.bind(this);
|
|
11
|
+
this.listFields = this.listFields.bind(this);
|
|
12
|
+
this.del = this.del.bind(this);
|
|
13
|
+
}
|
|
14
|
+
public abstract field(field_parameter: FieldParameters): string;
|
|
15
|
+
async valid(key_parameter: KeyParameters, field_parameter: FieldParameters, threshold: number, step: number): Promise<boolean>
|
|
16
|
+
{
|
|
17
|
+
let count = await this.hget(key_parameter, field_parameter);
|
|
18
|
+
return count + step <= threshold;
|
|
19
|
+
}
|
|
20
|
+
async increase(key_parameter: KeyParameters, field_parameter: FieldParameters, step: number): Promise<number>
|
|
21
|
+
{
|
|
22
|
+
let key = this.key(key_parameter);
|
|
23
|
+
let field = this.field(field_parameter);
|
|
24
|
+
return await this.instance.client.hincrby(key, field, step);
|
|
25
|
+
}
|
|
26
|
+
async hget(key_parameter: KeyParameters, field_parameter: FieldParameters): Promise<number>
|
|
27
|
+
{
|
|
28
|
+
let key = this.key(key_parameter);
|
|
29
|
+
let field = this.field(field_parameter);
|
|
30
|
+
let res = await this.instance.client.hget(key, field) ?? "0";
|
|
31
|
+
return parseInt(res);
|
|
32
|
+
}
|
|
33
|
+
async listKeys(count: number = 1, cursor?: string | number): Promise<{ cursor: string | number, keys: string[] }>
|
|
34
|
+
{
|
|
35
|
+
if (cursor == null)
|
|
36
|
+
cursor = "0";
|
|
37
|
+
let [next, keys] = await this.instance.client.scan(cursor, "MATCH", this.key(null), "COUNT", count,);
|
|
38
|
+
return { keys, cursor: next };
|
|
39
|
+
}
|
|
40
|
+
async listFields(key_parameter: KeyParameters)
|
|
41
|
+
{
|
|
42
|
+
let key = this.key(key_parameter);
|
|
43
|
+
return await this.instance.client.hgetall(key);
|
|
44
|
+
}
|
|
45
|
+
async delField(key_parameter: KeyParameters, field_parameter: FieldParameters)
|
|
46
|
+
{
|
|
47
|
+
let key = this.key(key_parameter);
|
|
48
|
+
let field = this.field(field_parameter);
|
|
49
|
+
return await this.instance.client.hdel(key, field);
|
|
50
|
+
}
|
|
51
|
+
async del(key_parameter: KeyParameters)
|
|
52
|
+
{
|
|
53
|
+
let key = this.key(key_parameter);
|
|
54
|
+
return await this._del(key);
|
|
55
|
+
}
|
|
56
56
|
};
|
package/src/RedisScopeLimit.ts
CHANGED
|
@@ -1,61 +1,61 @@
|
|
|
1
|
-
import { DurationUnit, TimeOperation, TimeUnitOperation } from "namirasoft-core";
|
|
2
|
-
import { RedisInstance } from "./RedisInstance";
|
|
3
|
-
import { RedisScope } from "./RedisScope";
|
|
4
|
-
|
|
5
|
-
export type RedisScopeLimitParameter = {
|
|
6
|
-
id: string;
|
|
7
|
-
name: string;
|
|
8
|
-
limit: boolean;
|
|
9
|
-
limit_count: number | null;
|
|
10
|
-
limit_per_value: number | null;
|
|
11
|
-
limit_per_unit: DurationUnit | null;
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
export abstract class RedisScopeLimit<KeyParameters> extends RedisScope<KeyParameters, number>
|
|
15
|
-
{
|
|
16
|
-
constructor(instance: RedisInstance)
|
|
17
|
-
{
|
|
18
|
-
super(instance);
|
|
19
|
-
this.getCount = this.getCount.bind(this);
|
|
20
|
-
this.add = this.add.bind(this);
|
|
21
|
-
this.del = this.del.bind(this);
|
|
22
|
-
}
|
|
23
|
-
async getCount(key_parameter: KeyParameters, limit_per_value: number, limit_per_unit: DurationUnit): Promise<number>
|
|
24
|
-
{
|
|
25
|
-
let key = this.key(key_parameter);
|
|
26
|
-
|
|
27
|
-
let milliseconds = TimeUnitOperation.toMilliseconds(limit_per_value, limit_per_unit);
|
|
28
|
-
let date_finish = new Date();
|
|
29
|
-
let date_start = TimeOperation.millisecondsAgo(milliseconds, date_finish);
|
|
30
|
-
|
|
31
|
-
return await this.instance.client.zcount(key, date_start.getTime(), date_finish.getTime());
|
|
32
|
-
}
|
|
33
|
-
async add(key_parameter: KeyParameters, limit_per_value: number, limit_per_unit: DurationUnit)
|
|
34
|
-
{
|
|
35
|
-
let key = this.key(key_parameter);
|
|
36
|
-
|
|
37
|
-
let milliseconds = TimeUnitOperation.toMilliseconds(limit_per_value, limit_per_unit);
|
|
38
|
-
let date_finish = new Date();
|
|
39
|
-
let date_start = TimeOperation.millisecondsAgo(milliseconds, date_finish)
|
|
40
|
-
|
|
41
|
-
await this.instance.client.zadd(key, date_finish.getTime(), date_finish.getTime());
|
|
42
|
-
await this.instance.client.zremrangebyscore(key, 0, date_start.getTime());
|
|
43
|
-
}
|
|
44
|
-
async del(key_parameter: KeyParameters)
|
|
45
|
-
{
|
|
46
|
-
let key = this.key(key_parameter);
|
|
47
|
-
await this._del(key);
|
|
48
|
-
}
|
|
49
|
-
async check(key_parameter: KeyParameters, parameter: RedisScopeLimitParameter)
|
|
50
|
-
{
|
|
51
|
-
if (parameter.limit)
|
|
52
|
-
if (parameter.limit_count && parameter.limit_per_value && parameter.limit_per_unit)
|
|
53
|
-
{
|
|
54
|
-
let count = await this.getCount(key_parameter, parameter.limit_per_value, parameter.limit_per_unit);
|
|
55
|
-
if (count < parameter.limit_count)
|
|
56
|
-
await this.add(key_parameter, parameter.limit_per_value, parameter.limit_per_unit);
|
|
57
|
-
else
|
|
58
|
-
throw new Error(`Limit has reached for '${parameter.name}' '${parameter.id}' for ${parameter.limit_count} messages every ${parameter.limit_per_value} ${parameter.limit_per_unit}`);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
1
|
+
import { DurationUnit, TimeOperation, TimeUnitOperation } from "namirasoft-core";
|
|
2
|
+
import { RedisInstance } from "./RedisInstance";
|
|
3
|
+
import { RedisScope } from "./RedisScope";
|
|
4
|
+
|
|
5
|
+
export type RedisScopeLimitParameter = {
|
|
6
|
+
id: string;
|
|
7
|
+
name: string;
|
|
8
|
+
limit: boolean;
|
|
9
|
+
limit_count: number | null;
|
|
10
|
+
limit_per_value: number | null;
|
|
11
|
+
limit_per_unit: DurationUnit | null;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export abstract class RedisScopeLimit<KeyParameters> extends RedisScope<KeyParameters, number>
|
|
15
|
+
{
|
|
16
|
+
constructor(instance: RedisInstance)
|
|
17
|
+
{
|
|
18
|
+
super(instance);
|
|
19
|
+
this.getCount = this.getCount.bind(this);
|
|
20
|
+
this.add = this.add.bind(this);
|
|
21
|
+
this.del = this.del.bind(this);
|
|
22
|
+
}
|
|
23
|
+
async getCount(key_parameter: KeyParameters, limit_per_value: number, limit_per_unit: DurationUnit): Promise<number>
|
|
24
|
+
{
|
|
25
|
+
let key = this.key(key_parameter);
|
|
26
|
+
|
|
27
|
+
let milliseconds = TimeUnitOperation.toMilliseconds(limit_per_value, limit_per_unit);
|
|
28
|
+
let date_finish = new Date();
|
|
29
|
+
let date_start = TimeOperation.millisecondsAgo(milliseconds, date_finish);
|
|
30
|
+
|
|
31
|
+
return await this.instance.client.zcount(key, date_start.getTime(), date_finish.getTime());
|
|
32
|
+
}
|
|
33
|
+
async add(key_parameter: KeyParameters, limit_per_value: number, limit_per_unit: DurationUnit)
|
|
34
|
+
{
|
|
35
|
+
let key = this.key(key_parameter);
|
|
36
|
+
|
|
37
|
+
let milliseconds = TimeUnitOperation.toMilliseconds(limit_per_value, limit_per_unit);
|
|
38
|
+
let date_finish = new Date();
|
|
39
|
+
let date_start = TimeOperation.millisecondsAgo(milliseconds, date_finish)
|
|
40
|
+
|
|
41
|
+
await this.instance.client.zadd(key, date_finish.getTime(), date_finish.getTime());
|
|
42
|
+
await this.instance.client.zremrangebyscore(key, 0, date_start.getTime());
|
|
43
|
+
}
|
|
44
|
+
async del(key_parameter: KeyParameters)
|
|
45
|
+
{
|
|
46
|
+
let key = this.key(key_parameter);
|
|
47
|
+
await this._del(key);
|
|
48
|
+
}
|
|
49
|
+
async check(key_parameter: KeyParameters, parameter: RedisScopeLimitParameter)
|
|
50
|
+
{
|
|
51
|
+
if (parameter.limit)
|
|
52
|
+
if (parameter.limit_count && parameter.limit_per_value && parameter.limit_per_unit)
|
|
53
|
+
{
|
|
54
|
+
let count = await this.getCount(key_parameter, parameter.limit_per_value, parameter.limit_per_unit);
|
|
55
|
+
if (count < parameter.limit_count)
|
|
56
|
+
await this.add(key_parameter, parameter.limit_per_value, parameter.limit_per_unit);
|
|
57
|
+
else
|
|
58
|
+
throw new Error(`Limit has reached for '${parameter.name}' '${parameter.id}' for ${parameter.limit_count} messages every ${parameter.limit_per_value} ${parameter.limit_per_unit}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
61
|
};
|
package/src/RedisScopeLock.ts
CHANGED
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
import { RedisInstance } from "./RedisInstance";
|
|
2
|
-
import { RedisScope } from "./RedisScope";
|
|
3
|
-
|
|
4
|
-
export abstract class RedisScopeLock<KeyParameters> extends RedisScope<KeyParameters, number>
|
|
5
|
-
{
|
|
6
|
-
constructor(instance: RedisInstance, private ttl_seconds: number)
|
|
7
|
-
{
|
|
8
|
-
super(instance);
|
|
9
|
-
this.lock = this.lock.bind(this);
|
|
10
|
-
this.waitForLock = this.waitForLock.bind(this);
|
|
11
|
-
this.unlock = this.unlock.bind(this);
|
|
12
|
-
}
|
|
13
|
-
async lock(key_parameter: KeyParameters, ttl_seconds?: number): Promise<boolean>
|
|
14
|
-
{
|
|
15
|
-
let time = ttl_seconds ?? this.ttl_seconds;
|
|
16
|
-
let ans = await this._set(this.key(key_parameter), Math.random(), true, time, true);
|
|
17
|
-
return ans === "OK";
|
|
18
|
-
}
|
|
19
|
-
async waitForLock(key_parameter: KeyParameters, ttl_seconds?: number): Promise<void>
|
|
20
|
-
{
|
|
21
|
-
while (true)
|
|
22
|
-
{
|
|
23
|
-
let ans = await this.lock(key_parameter, ttl_seconds);
|
|
24
|
-
if (ans)
|
|
25
|
-
break;
|
|
26
|
-
await new Promise(res => setTimeout(res, 10));
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
async unlock(key_parameter: KeyParameters)
|
|
30
|
-
{
|
|
31
|
-
await this._del(this.key(key_parameter));
|
|
32
|
-
}
|
|
1
|
+
import { RedisInstance } from "./RedisInstance";
|
|
2
|
+
import { RedisScope } from "./RedisScope";
|
|
3
|
+
|
|
4
|
+
export abstract class RedisScopeLock<KeyParameters> extends RedisScope<KeyParameters, number>
|
|
5
|
+
{
|
|
6
|
+
constructor(instance: RedisInstance, private ttl_seconds: number)
|
|
7
|
+
{
|
|
8
|
+
super(instance);
|
|
9
|
+
this.lock = this.lock.bind(this);
|
|
10
|
+
this.waitForLock = this.waitForLock.bind(this);
|
|
11
|
+
this.unlock = this.unlock.bind(this);
|
|
12
|
+
}
|
|
13
|
+
async lock(key_parameter: KeyParameters, ttl_seconds?: number): Promise<boolean>
|
|
14
|
+
{
|
|
15
|
+
let time = ttl_seconds ?? this.ttl_seconds;
|
|
16
|
+
let ans = await this._set(this.key(key_parameter), Math.random(), true, time, true);
|
|
17
|
+
return ans === "OK";
|
|
18
|
+
}
|
|
19
|
+
async waitForLock(key_parameter: KeyParameters, ttl_seconds?: number): Promise<void>
|
|
20
|
+
{
|
|
21
|
+
while (true)
|
|
22
|
+
{
|
|
23
|
+
let ans = await this.lock(key_parameter, ttl_seconds);
|
|
24
|
+
if (ans)
|
|
25
|
+
break;
|
|
26
|
+
await new Promise(res => setTimeout(res, 10));
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
async unlock(key_parameter: KeyParameters)
|
|
30
|
+
{
|
|
31
|
+
await this._del(this.key(key_parameter));
|
|
32
|
+
}
|
|
33
33
|
};
|
|
@@ -1,39 +1,39 @@
|
|
|
1
|
-
import { BaseUUID } from "namirasoft-core";
|
|
2
|
-
import { RedisInstance } from "./RedisInstance";
|
|
3
|
-
import { RedisScope } from "./RedisScope";
|
|
4
|
-
|
|
5
|
-
export abstract class RedisScopeShardCoordinator extends RedisScope<void, number>
|
|
6
|
-
{
|
|
7
|
-
protected id: string;
|
|
8
|
-
constructor(instance: RedisInstance, private ttl_seconds: number)
|
|
9
|
-
{
|
|
10
|
-
super(instance);
|
|
11
|
-
this.id = `${new Date().getTime()}-${BaseUUID.uuid(5)}`;
|
|
12
|
-
}
|
|
13
|
-
async heartbeat(ttl_seconds?: number): Promise<void>
|
|
14
|
-
{
|
|
15
|
-
const ttl = ttl_seconds ?? this.ttl_seconds;
|
|
16
|
-
const key = this.key();
|
|
17
|
-
const key_instance = `${key}:${this.id}`;
|
|
18
|
-
await this.instance.client.set(key_instance, "1", "EX", ttl);
|
|
19
|
-
}
|
|
20
|
-
async get(ttl_seconds?: number): Promise<{
|
|
21
|
-
id: string,
|
|
22
|
-
index: number,
|
|
23
|
-
total: number
|
|
24
|
-
}>
|
|
25
|
-
{
|
|
26
|
-
await this.heartbeat(ttl_seconds);
|
|
27
|
-
|
|
28
|
-
const key = this.key();
|
|
29
|
-
const pattern = `${key}:*`;
|
|
30
|
-
|
|
31
|
-
const keys = await this.instance.client.keys(pattern);
|
|
32
|
-
keys.sort();
|
|
33
|
-
|
|
34
|
-
const index = keys.indexOf(`${key}:${this.id}`);
|
|
35
|
-
const total = keys.length;
|
|
36
|
-
|
|
37
|
-
return { id: this.id, index, total };
|
|
38
|
-
}
|
|
1
|
+
import { BaseUUID } from "namirasoft-core";
|
|
2
|
+
import { RedisInstance } from "./RedisInstance";
|
|
3
|
+
import { RedisScope } from "./RedisScope";
|
|
4
|
+
|
|
5
|
+
export abstract class RedisScopeShardCoordinator extends RedisScope<void, number>
|
|
6
|
+
{
|
|
7
|
+
protected id: string;
|
|
8
|
+
constructor(instance: RedisInstance, private ttl_seconds: number)
|
|
9
|
+
{
|
|
10
|
+
super(instance);
|
|
11
|
+
this.id = `${new Date().getTime()}-${BaseUUID.uuid(5)}`;
|
|
12
|
+
}
|
|
13
|
+
async heartbeat(ttl_seconds?: number): Promise<void>
|
|
14
|
+
{
|
|
15
|
+
const ttl = ttl_seconds ?? this.ttl_seconds;
|
|
16
|
+
const key = this.key();
|
|
17
|
+
const key_instance = `${key}:${this.id}`;
|
|
18
|
+
await this.instance.client.set(key_instance, "1", "EX", ttl);
|
|
19
|
+
}
|
|
20
|
+
async get(ttl_seconds?: number): Promise<{
|
|
21
|
+
id: string,
|
|
22
|
+
index: number,
|
|
23
|
+
total: number
|
|
24
|
+
}>
|
|
25
|
+
{
|
|
26
|
+
await this.heartbeat(ttl_seconds);
|
|
27
|
+
|
|
28
|
+
const key = this.key();
|
|
29
|
+
const pattern = `${key}:*`;
|
|
30
|
+
|
|
31
|
+
const keys = await this.instance.client.keys(pattern);
|
|
32
|
+
keys.sort();
|
|
33
|
+
|
|
34
|
+
const index = keys.indexOf(`${key}:${this.id}`);
|
|
35
|
+
const total = keys.length;
|
|
36
|
+
|
|
37
|
+
return { id: this.id, index, total };
|
|
38
|
+
}
|
|
39
39
|
};
|
|
@@ -1,46 +1,46 @@
|
|
|
1
|
-
import { RedisInstance } from "./RedisInstance";
|
|
2
|
-
import { RedisScope } from "./RedisScope";
|
|
3
|
-
import { RedisScopeHashCounter } from "./RedisScopeHashCounter";
|
|
4
|
-
|
|
5
|
-
export class RedisScopeTotalCount<KeyParameters, FieldParameters> extends RedisScope<null, Date>
|
|
6
|
-
{
|
|
7
|
-
constructor(instance: RedisInstance,
|
|
8
|
-
public total: RedisScopeHashCounter<KeyParameters, FieldParameters>,
|
|
9
|
-
public count: RedisScopeHashCounter<KeyParameters, FieldParameters>)
|
|
10
|
-
{
|
|
11
|
-
super(instance);
|
|
12
|
-
this.increase = this.increase.bind(this);
|
|
13
|
-
}
|
|
14
|
-
override key(): string
|
|
15
|
-
{
|
|
16
|
-
return "";
|
|
17
|
-
}
|
|
18
|
-
async increase(key_parameter: KeyParameters, field_parameter: FieldParameters, step: number, threshold: number | null): Promise<{ success: boolean, total: number, count: number }>
|
|
19
|
-
{
|
|
20
|
-
let total_key = this.total.key(key_parameter);
|
|
21
|
-
let total_field = this.total.field(field_parameter);
|
|
22
|
-
let count_key = this.count.key(key_parameter);
|
|
23
|
-
let count_field = this.count.field(field_parameter);
|
|
24
|
-
|
|
25
|
-
const script = `
|
|
26
|
-
local total = tonumber(redis.call("HGET", KEYS[1], ARGV[1]) or "0")
|
|
27
|
-
local count = tonumber(redis.call("HGET", KEYS[2], ARGV[2]) or "0")
|
|
28
|
-
local step = tonumber(ARGV[3])
|
|
29
|
-
local threshold = tonumber(ARGV[4])
|
|
30
|
-
|
|
31
|
-
if threshold >= 0 then
|
|
32
|
-
if total + step > threshold then
|
|
33
|
-
return {0, total, count}
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
total = redis.call("HINCRBY", KEYS[1], ARGV[1], step)
|
|
38
|
-
count = redis.call("HINCRBY", KEYS[2], ARGV[2], step)
|
|
39
|
-
|
|
40
|
-
return {1, total, count}
|
|
41
|
-
`;
|
|
42
|
-
let res = await this.instance.client.eval(script, 2, total_key, count_key, total_field, count_field, step, threshold ?? -1);
|
|
43
|
-
let [success, total, count] = res as number[];
|
|
44
|
-
return { success: success == 1, total, count };
|
|
45
|
-
}
|
|
1
|
+
import { RedisInstance } from "./RedisInstance";
|
|
2
|
+
import { RedisScope } from "./RedisScope";
|
|
3
|
+
import { RedisScopeHashCounter } from "./RedisScopeHashCounter";
|
|
4
|
+
|
|
5
|
+
export class RedisScopeTotalCount<KeyParameters, FieldParameters> extends RedisScope<null, Date>
|
|
6
|
+
{
|
|
7
|
+
constructor(instance: RedisInstance,
|
|
8
|
+
public total: RedisScopeHashCounter<KeyParameters, FieldParameters>,
|
|
9
|
+
public count: RedisScopeHashCounter<KeyParameters, FieldParameters>)
|
|
10
|
+
{
|
|
11
|
+
super(instance);
|
|
12
|
+
this.increase = this.increase.bind(this);
|
|
13
|
+
}
|
|
14
|
+
override key(): string
|
|
15
|
+
{
|
|
16
|
+
return "";
|
|
17
|
+
}
|
|
18
|
+
async increase(key_parameter: KeyParameters, field_parameter: FieldParameters, step: number, threshold: number | null): Promise<{ success: boolean, total: number, count: number }>
|
|
19
|
+
{
|
|
20
|
+
let total_key = this.total.key(key_parameter);
|
|
21
|
+
let total_field = this.total.field(field_parameter);
|
|
22
|
+
let count_key = this.count.key(key_parameter);
|
|
23
|
+
let count_field = this.count.field(field_parameter);
|
|
24
|
+
|
|
25
|
+
const script = `
|
|
26
|
+
local total = tonumber(redis.call("HGET", KEYS[1], ARGV[1]) or "0")
|
|
27
|
+
local count = tonumber(redis.call("HGET", KEYS[2], ARGV[2]) or "0")
|
|
28
|
+
local step = tonumber(ARGV[3])
|
|
29
|
+
local threshold = tonumber(ARGV[4])
|
|
30
|
+
|
|
31
|
+
if threshold >= 0 then
|
|
32
|
+
if total + step > threshold then
|
|
33
|
+
return {0, total, count}
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
total = redis.call("HINCRBY", KEYS[1], ARGV[1], step)
|
|
38
|
+
count = redis.call("HINCRBY", KEYS[2], ARGV[2], step)
|
|
39
|
+
|
|
40
|
+
return {1, total, count}
|
|
41
|
+
`;
|
|
42
|
+
let res = await this.instance.client.eval(script, 2, total_key, count_key, total_field, count_field, step, threshold ?? -1);
|
|
43
|
+
let [success, total, count] = res as number[];
|
|
44
|
+
return { success: success == 1, total, count };
|
|
45
|
+
}
|
|
46
46
|
};
|
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
import { RedisInstance } from "./RedisInstance";
|
|
2
|
-
import { RedisScope } from "./RedisScope";
|
|
3
|
-
|
|
4
|
-
export abstract class RedisScopeUniqueness<KeyParameters> extends RedisScope<KeyParameters, number>
|
|
5
|
-
{
|
|
6
|
-
constructor(instance: RedisInstance, private ttl_seconds: number)
|
|
7
|
-
{
|
|
8
|
-
super(instance);
|
|
9
|
-
this.set = this.set.bind(this);
|
|
10
|
-
this.exists = this.exists.bind(this);
|
|
11
|
-
}
|
|
12
|
-
async set(key_parameter: KeyParameters, ttl_seconds?: number): Promise<boolean>
|
|
13
|
-
{
|
|
14
|
-
const ttl = ttl_seconds ?? this.ttl_seconds;
|
|
15
|
-
const ans = await this._set(this.key(key_parameter), Math.random(), true, ttl, true);
|
|
16
|
-
return ans === "OK";
|
|
17
|
-
}
|
|
18
|
-
async exists(key_parameter: KeyParameters): Promise<boolean>
|
|
19
|
-
{
|
|
20
|
-
const key = this.key(key_parameter);
|
|
21
|
-
const exists = await this.instance.client.exists(key);
|
|
22
|
-
|
|
23
|
-
return exists != 0;
|
|
24
|
-
}
|
|
25
|
-
async del(key_parameter: KeyParameters): Promise<void>
|
|
26
|
-
{
|
|
27
|
-
const key = this.key(key_parameter);
|
|
28
|
-
this._del(key);
|
|
29
|
-
}
|
|
1
|
+
import { RedisInstance } from "./RedisInstance";
|
|
2
|
+
import { RedisScope } from "./RedisScope";
|
|
3
|
+
|
|
4
|
+
export abstract class RedisScopeUniqueness<KeyParameters> extends RedisScope<KeyParameters, number>
|
|
5
|
+
{
|
|
6
|
+
constructor(instance: RedisInstance, private ttl_seconds: number)
|
|
7
|
+
{
|
|
8
|
+
super(instance);
|
|
9
|
+
this.set = this.set.bind(this);
|
|
10
|
+
this.exists = this.exists.bind(this);
|
|
11
|
+
}
|
|
12
|
+
async set(key_parameter: KeyParameters, ttl_seconds?: number): Promise<boolean>
|
|
13
|
+
{
|
|
14
|
+
const ttl = ttl_seconds ?? this.ttl_seconds;
|
|
15
|
+
const ans = await this._set(this.key(key_parameter), Math.random(), true, ttl, true);
|
|
16
|
+
return ans === "OK";
|
|
17
|
+
}
|
|
18
|
+
async exists(key_parameter: KeyParameters): Promise<boolean>
|
|
19
|
+
{
|
|
20
|
+
const key = this.key(key_parameter);
|
|
21
|
+
const exists = await this.instance.client.exists(key);
|
|
22
|
+
|
|
23
|
+
return exists != 0;
|
|
24
|
+
}
|
|
25
|
+
async del(key_parameter: KeyParameters): Promise<void>
|
|
26
|
+
{
|
|
27
|
+
const key = this.key(key_parameter);
|
|
28
|
+
this._del(key);
|
|
29
|
+
}
|
|
30
30
|
};
|
package/src/index.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
export * from "./RedisInstance";
|
|
2
|
-
export * from "./RedisScope";
|
|
3
|
-
export * from "./RedisScopeDelay";
|
|
4
|
-
export * from "./RedisScopeHashCounter";
|
|
5
|
-
export * from "./RedisScopeLimit";
|
|
6
|
-
export * from "./RedisScopeLock";
|
|
7
|
-
export * from "./RedisScopeShardCoordinator";
|
|
8
|
-
export * from "./RedisScopeTotalCount";
|
|
1
|
+
export * from "./RedisInstance";
|
|
2
|
+
export * from "./RedisScope";
|
|
3
|
+
export * from "./RedisScopeDelay";
|
|
4
|
+
export * from "./RedisScopeHashCounter";
|
|
5
|
+
export * from "./RedisScopeLimit";
|
|
6
|
+
export * from "./RedisScopeLock";
|
|
7
|
+
export * from "./RedisScopeShardCoordinator";
|
|
8
|
+
export * from "./RedisScopeTotalCount";
|
|
9
9
|
export * from "./RedisScopeUniqueness";
|