@digitalwalletcorp/redis-pooling 1.0.0 → 1.1.0
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/README.md +46 -26
- package/lib/redis-pooling.d.ts +18 -14
- package/lib/redis-pooling.js +100 -98
- package/lib/redis-pooling.js.map +1 -1
- package/package.json +1 -1
- package/src/redis-pooling.ts +106 -106
package/README.md
CHANGED
|
@@ -10,10 +10,10 @@ Designed for both server-side Node.js applications and cron-style background job
|
|
|
10
10
|
* **Connection Pooling**: Efficiently manage multiple Redis connections with `min` and `max` pool size configuration.
|
|
11
11
|
* **Safe Acquire/Release**: Guaranteed handling of Redis client lifecycle with optional database selection (`SELECT dbIndex`).
|
|
12
12
|
* **Custom Utilities**: Built-in helper methods like `getKeys(pattern)` and `deleteKeys(pattern)` for convenient Redis key operations.
|
|
13
|
-
* **Connection Validation**: PING-based validation before borrowing from the pool.
|
|
13
|
+
* **Connection Validation**: PING-based validation with timeout before borrowing from the pool.
|
|
14
14
|
* **TLS Support**: Optional TLS configuration for secure Redis connections.
|
|
15
15
|
|
|
16
|
-
#### 📦
|
|
16
|
+
#### 📦 Installation
|
|
17
17
|
|
|
18
18
|
```bash
|
|
19
19
|
npm install @digitalwalletcorp/redis-pooling
|
|
@@ -28,19 +28,17 @@ yarn add @digitalwalletcorp/redis-pooling
|
|
|
28
28
|
This pattern is ideal for short-lived Node.js scripts, such as cron jobs, where a pool is created, used, and destroyed within a single run.
|
|
29
29
|
|
|
30
30
|
```typescript
|
|
31
|
-
import {
|
|
31
|
+
import { RedisPool } from '@digitalwalletcorp/redis-pooling';
|
|
32
32
|
|
|
33
|
-
const redisPool =
|
|
33
|
+
const redisPool = new RedisPool({
|
|
34
34
|
url: 'redis://localhost:6379',
|
|
35
|
-
|
|
35
|
+
dbIndex: 0,
|
|
36
36
|
max: 10,
|
|
37
|
-
min: 2
|
|
38
|
-
connectTimeout: 5000,
|
|
39
|
-
enableTls: false
|
|
37
|
+
min: 2
|
|
40
38
|
});
|
|
41
39
|
|
|
42
40
|
async function main() {
|
|
43
|
-
const client
|
|
41
|
+
const client = await redisPool.acquire();
|
|
44
42
|
try {
|
|
45
43
|
await client.set('key', 'value');
|
|
46
44
|
const value = await client.get('key');
|
|
@@ -60,15 +58,13 @@ For long-running web applications, a singleton Redis pool ensures efficient reus
|
|
|
60
58
|
|
|
61
59
|
`@/server/singleton/redis-pooling`
|
|
62
60
|
```typescript
|
|
63
|
-
import {
|
|
61
|
+
import { RedisPool } from '@digitalwalletcorp/redis-pooling';
|
|
64
62
|
|
|
65
|
-
export const redisPool =
|
|
63
|
+
export const redisPool = new RedisPool({
|
|
66
64
|
url: 'redis://localhost:6379',
|
|
67
|
-
|
|
65
|
+
dbIndex: 0,
|
|
68
66
|
max: 20,
|
|
69
|
-
min: 5
|
|
70
|
-
connectTimeout: 5000,
|
|
71
|
-
enableTls: false
|
|
67
|
+
min: 5
|
|
72
68
|
});
|
|
73
69
|
```
|
|
74
70
|
|
|
@@ -77,7 +73,7 @@ export const redisPool = createRedisPool({
|
|
|
77
73
|
import { redisPool } from '@/server/singleton/redis-pooling';
|
|
78
74
|
|
|
79
75
|
async function handleRequest() {
|
|
80
|
-
const client
|
|
76
|
+
const client = await redisPool.acquire();
|
|
81
77
|
try {
|
|
82
78
|
const value = await client.get('session:1234');
|
|
83
79
|
console.log('Session value:', value);
|
|
@@ -89,7 +85,7 @@ async function handleRequest() {
|
|
|
89
85
|
|
|
90
86
|
##### Example 3: Using Custom Methods (`getKeys`, `deleteKeys`)
|
|
91
87
|
|
|
92
|
-
`
|
|
88
|
+
`RedisClient` extends the standard `ioredis` client with convenient helper methods.
|
|
93
89
|
`getKeys` and `deleteKeys` use `SCAN` and `UNLINK` internally to avoid blocking Redis with large datasets (unlike `KEYS`).
|
|
94
90
|
All other standard Redis commands remain available.
|
|
95
91
|
|
|
@@ -115,28 +111,29 @@ async function manageCache() {
|
|
|
115
111
|
|
|
116
112
|
#### 📚 API Reference
|
|
117
113
|
|
|
118
|
-
##### `
|
|
114
|
+
##### `new RedisPool(config: RedisConfig)`
|
|
119
115
|
|
|
120
|
-
Creates a
|
|
116
|
+
Creates a Redis connection pool.
|
|
121
117
|
|
|
122
118
|
| Property | Type | Default | Description |
|
|
123
119
|
| ---------------- | ------- | -------- | -------------------------------------- |
|
|
124
120
|
| `url` | string | required | Redis connection URL. |
|
|
125
|
-
| `
|
|
126
|
-
| `connectTimeout` | number | 5000 | Connection timeout in milliseconds. |
|
|
121
|
+
| `dbIndex` | number | 0 | Default Redis database index. |
|
|
127
122
|
| `max` | number | 10 | Maximum number of clients in the pool. |
|
|
128
123
|
| `min` | number | 0 | Minimum number of clients in the pool. |
|
|
124
|
+
| `connectTimeout` | number | 5000 | Connection timeout in milliseconds. |
|
|
125
|
+
| `testOnBorrow` | boolean | true | Enable connection validate on borrow. |
|
|
129
126
|
| `enableTls` | boolean | false | Enable TLS for Redis connection. |
|
|
130
127
|
|
|
131
|
-
##### `
|
|
128
|
+
##### `RedisPool` Methods
|
|
132
129
|
|
|
133
130
|
| Method | Signature | Description |
|
|
134
131
|
| ------------------------------------- | ----------------------------- | -------------------------------------------------------------------------------------- |
|
|
135
|
-
| `acquire(dbIndex?: number)` | `Promise<
|
|
136
|
-
| `release(client:
|
|
137
|
-
| `destroy()` | `Promise<void>`
|
|
132
|
+
| `acquire(dbIndex?: number)` | `Promise<RedisClient>` | Acquire a Redis client from the pool. Optionally switch to a different database index. |
|
|
133
|
+
| `release(client: RedisClient)` | `Promise<void>` | Release the Redis client back to the pool. |
|
|
134
|
+
| `destroy()` | `Promise<void>` | Drain and clear all connections in the pool. |
|
|
138
135
|
|
|
139
|
-
##### `
|
|
136
|
+
##### `RedisClient` Methods
|
|
140
137
|
|
|
141
138
|
Extends the standard `ioredis` `Redis` client with additional helpers:
|
|
142
139
|
|
|
@@ -145,6 +142,29 @@ Extends the standard `ioredis` `Redis` client with additional helpers:
|
|
|
145
142
|
| `getKeys(pattern: string)` | `Promise<string[]>` | Scan and return all keys matching a pattern. |
|
|
146
143
|
| `deleteKeys(pattern: string)` | `Promise<number>` | Scan and delete all keys matching a pattern using `UNLINK`. Returns the number of deleted keys. |
|
|
147
144
|
|
|
145
|
+
#### 🗄 Database Index Handling
|
|
146
|
+
|
|
147
|
+
This library provides a Redis connection pool capable of managing Redis clients
|
|
148
|
+
connected to different database indexes (`SELECT db`).
|
|
149
|
+
|
|
150
|
+
**Calling `acquire(dbIndex)`:**
|
|
151
|
+
|
|
152
|
+
> When `dbIndex` is specified, the acquired client will be connected to the specified database index.
|
|
153
|
+
|
|
154
|
+
**Calling `acquire()` without `dbIndex`:**
|
|
155
|
+
> When `dbIndex` is not specified, the acquired client will be connected to the default database index configured when the pool was created via **createRedisPool({ dbIndex })**.
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
// The default db index: 2
|
|
159
|
+
const pool = new RedisPool({ url, dbIndex: 2 });
|
|
160
|
+
|
|
161
|
+
// With dbIndex: connected to DB 1
|
|
162
|
+
const client1 = await pool.acquire(1);
|
|
163
|
+
|
|
164
|
+
// Without dbIndex: connected to DB 2 (default)
|
|
165
|
+
const client2 = await pool.acquire();
|
|
166
|
+
```
|
|
167
|
+
|
|
148
168
|
---
|
|
149
169
|
#### 💡 Notes
|
|
150
170
|
|
package/lib/redis-pooling.d.ts
CHANGED
|
@@ -1,27 +1,31 @@
|
|
|
1
1
|
import { Redis } from 'ioredis';
|
|
2
2
|
export interface RedisConfig {
|
|
3
3
|
url: string;
|
|
4
|
-
|
|
4
|
+
dbIndex?: number;
|
|
5
5
|
connectTimeout?: number;
|
|
6
6
|
max?: number;
|
|
7
7
|
min?: number;
|
|
8
8
|
testOnBorrow?: boolean;
|
|
9
9
|
enableTls?: boolean;
|
|
10
10
|
}
|
|
11
|
-
export interface
|
|
12
|
-
acquire(dbIndex?: number): Promise<Redis>;
|
|
13
|
-
release(client?: Redis): Promise<void>;
|
|
14
|
-
destroy(timeoutMs?: number): Promise<void>;
|
|
15
|
-
}
|
|
16
|
-
export interface ManagedRedisClient extends Redis {
|
|
11
|
+
export interface RedisClient extends Redis {
|
|
17
12
|
getKeys(pattern: string): Promise<string[]>;
|
|
18
13
|
deleteKeys(pattern: string): Promise<number>;
|
|
19
14
|
_originalDbIndex?: number;
|
|
20
15
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
16
|
+
export declare class RedisPool {
|
|
17
|
+
private readonly url;
|
|
18
|
+
private readonly db;
|
|
19
|
+
private readonly connectTimeout;
|
|
20
|
+
private readonly max;
|
|
21
|
+
private readonly min;
|
|
22
|
+
private readonly testOnBorrow;
|
|
23
|
+
private readonly enableTls?;
|
|
24
|
+
private readonly poolMap;
|
|
25
|
+
constructor(config: RedisConfig);
|
|
26
|
+
acquire(dbIndex?: number): Promise<RedisClient>;
|
|
27
|
+
release(client?: RedisClient): Promise<void>;
|
|
28
|
+
destroy(timeoutMs?: number): Promise<void>;
|
|
29
|
+
private getPool;
|
|
30
|
+
private createSingleDbPool;
|
|
31
|
+
}
|
package/lib/redis-pooling.js
CHANGED
|
@@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.
|
|
36
|
+
exports.RedisPool = void 0;
|
|
37
37
|
const ioredis_1 = require("ioredis");
|
|
38
38
|
const genericPool = __importStar(require("generic-pool"));
|
|
39
39
|
const logHeader = '[RedisPooling]';
|
|
@@ -41,35 +41,107 @@ const DEFAULT_CONNECT_TIMEOUT = 5000;
|
|
|
41
41
|
const DEFAULT_MAX_POOLING_SIZE = 10;
|
|
42
42
|
const DEFAULT_MIN_POOLING_SIZE = 0;
|
|
43
43
|
const REDIS_PING_TIMEOUT_MS = 3000; // 3秒
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
44
|
+
class RedisPool {
|
|
45
|
+
url;
|
|
46
|
+
db;
|
|
47
|
+
connectTimeout;
|
|
48
|
+
max;
|
|
49
|
+
min;
|
|
50
|
+
testOnBorrow;
|
|
51
|
+
enableTls;
|
|
52
|
+
poolMap = new Map();
|
|
53
|
+
constructor(config) {
|
|
54
|
+
if (!config.url) {
|
|
55
|
+
throw new Error(`${logHeader} Redis connection url is required.`);
|
|
56
|
+
}
|
|
57
|
+
this.url = config.url;
|
|
58
|
+
this.db = config.dbIndex ?? 0;
|
|
59
|
+
this.connectTimeout = config.connectTimeout ?? DEFAULT_CONNECT_TIMEOUT;
|
|
60
|
+
this.max = config.max ?? DEFAULT_MAX_POOLING_SIZE;
|
|
61
|
+
this.min = config.min ?? DEFAULT_MIN_POOLING_SIZE;
|
|
62
|
+
this.testOnBorrow = config.testOnBorrow ?? true;
|
|
63
|
+
this.enableTls = config.enableTls;
|
|
64
|
+
}
|
|
65
|
+
async acquire(dbIndex) {
|
|
66
|
+
const index = dbIndex ?? this.db;
|
|
67
|
+
const pool = this.getPool(index);
|
|
68
|
+
const client = await pool.acquire();
|
|
69
|
+
console.debug(logHeader, `Redis client ${index} has been acquired.`);
|
|
70
|
+
return client;
|
|
71
|
+
}
|
|
72
|
+
async release(client) {
|
|
73
|
+
if (client) {
|
|
74
|
+
const dbIndex = client._originalDbIndex ?? this.db;
|
|
75
|
+
const pool = this.poolMap.get(dbIndex);
|
|
76
|
+
if (!pool) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
let needDestroy = false;
|
|
80
|
+
switch (true) {
|
|
81
|
+
case client.status === 'end':
|
|
82
|
+
case client.status === 'close':
|
|
83
|
+
// Redisクライアントの状態が再利用できない場合はプールから破棄
|
|
84
|
+
needDestroy = true;
|
|
85
|
+
break;
|
|
86
|
+
case client.status === 'ready':
|
|
87
|
+
try {
|
|
88
|
+
// 元のDBインデックスに戻す
|
|
89
|
+
await client.select(dbIndex);
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
// selectに失敗する→Redisクライアントが不正な状態にあると判断できるのでプールから破棄
|
|
93
|
+
needDestroy = true;
|
|
94
|
+
}
|
|
95
|
+
break;
|
|
96
|
+
default:
|
|
97
|
+
}
|
|
98
|
+
if (needDestroy) {
|
|
99
|
+
// Redisクライアントの破棄
|
|
100
|
+
await pool.destroy(client);
|
|
101
|
+
console.debug(logHeader, `Redis client ${dbIndex} destroyed due to invalid status.`);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
// Redisクライアントの返却
|
|
105
|
+
await pool.release(client);
|
|
106
|
+
console.debug(logHeader, `Redis client ${dbIndex} released.`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
async destroy(timeoutMs = 5000) {
|
|
111
|
+
for (const [dbIndex, pool] of this.poolMap.entries()) {
|
|
112
|
+
console.debug(logHeader, `Destroying Redis pool for DB index ${dbIndex}...`);
|
|
113
|
+
await Promise.race([
|
|
114
|
+
(async () => {
|
|
115
|
+
await pool.drain();
|
|
116
|
+
await pool.clear();
|
|
117
|
+
})(),
|
|
118
|
+
new Promise((_, reject) => {
|
|
119
|
+
setTimeout(() => reject(new Error(`${logHeader} Timeout while draining Redis pool for DB index ${dbIndex}`)), timeoutMs);
|
|
120
|
+
})
|
|
121
|
+
]);
|
|
122
|
+
console.debug(logHeader, `Redis pool for DB index ${dbIndex} destroyed.`);
|
|
123
|
+
this.poolMap.delete(dbIndex);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
getPool(dbIndex) {
|
|
127
|
+
let pool = this.poolMap.get(dbIndex);
|
|
128
|
+
if (!pool) {
|
|
129
|
+
pool = this.createSingleDbPool(dbIndex);
|
|
130
|
+
this.poolMap.set(dbIndex, pool);
|
|
131
|
+
}
|
|
132
|
+
return pool;
|
|
59
133
|
}
|
|
60
|
-
|
|
61
|
-
const poolMap = new Map();
|
|
62
|
-
const createSingleDbPool = (dbIndex) => {
|
|
134
|
+
createSingleDbPool(dbIndex) {
|
|
63
135
|
const factory = {
|
|
64
136
|
create: async () => {
|
|
65
137
|
let client;
|
|
66
138
|
try {
|
|
67
|
-
client = new ioredis_1.Redis(url, {
|
|
139
|
+
client = new ioredis_1.Redis(this.url, {
|
|
68
140
|
db: dbIndex,
|
|
69
|
-
connectTimeout: connectTimeout,
|
|
141
|
+
connectTimeout: this.connectTimeout,
|
|
70
142
|
keepAlive: 1,
|
|
71
143
|
enableOfflineQueue: true,
|
|
72
|
-
tls:
|
|
144
|
+
tls: this.enableTls ? { rejectUnauthorized: false } : undefined,
|
|
73
145
|
retryStrategy: (times) => {
|
|
74
146
|
const delay = Math.min(times * 50, 1000);
|
|
75
147
|
if (process.env.NODE_ENV !== 'test') {
|
|
@@ -207,81 +279,11 @@ const createRedisPool = (config) => {
|
|
|
207
279
|
}
|
|
208
280
|
};
|
|
209
281
|
return genericPool.createPool(factory, {
|
|
210
|
-
max,
|
|
211
|
-
min,
|
|
212
|
-
testOnBorrow
|
|
282
|
+
max: this.max,
|
|
283
|
+
min: this.min,
|
|
284
|
+
testOnBorrow: this.testOnBorrow
|
|
213
285
|
});
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
if (!pool) {
|
|
218
|
-
pool = createSingleDbPool(dbIndex);
|
|
219
|
-
poolMap.set(dbIndex, pool);
|
|
220
|
-
}
|
|
221
|
-
return pool;
|
|
222
|
-
};
|
|
223
|
-
return {
|
|
224
|
-
acquire: async (dbIndex) => {
|
|
225
|
-
const index = dbIndex ?? 0;
|
|
226
|
-
const pool = getPool(index);
|
|
227
|
-
const client = await pool.acquire();
|
|
228
|
-
console.debug(logHeader, `Redis client ${index} has been acquired.`);
|
|
229
|
-
return client;
|
|
230
|
-
},
|
|
231
|
-
release: async (client) => {
|
|
232
|
-
if (client) {
|
|
233
|
-
const dbIndex = client._originalDbIndex ?? db;
|
|
234
|
-
let needDestroy = false;
|
|
235
|
-
switch (true) {
|
|
236
|
-
case client.status === 'end':
|
|
237
|
-
case client.status === 'close':
|
|
238
|
-
// Redisクライアントの状態が再利用できない場合はプールから破棄
|
|
239
|
-
needDestroy = true;
|
|
240
|
-
break;
|
|
241
|
-
case client.status === 'ready':
|
|
242
|
-
try {
|
|
243
|
-
// 元のDBインデックスに戻す
|
|
244
|
-
await client.select(dbIndex);
|
|
245
|
-
}
|
|
246
|
-
catch (error) {
|
|
247
|
-
// selectに失敗する→Redisクライアントが不正な状態にあると判断できるのでプールから破棄
|
|
248
|
-
needDestroy = true;
|
|
249
|
-
}
|
|
250
|
-
break;
|
|
251
|
-
default:
|
|
252
|
-
}
|
|
253
|
-
const pool = poolMap.get(dbIndex);
|
|
254
|
-
if (pool) {
|
|
255
|
-
if (needDestroy) {
|
|
256
|
-
// Redisクライアントの破棄
|
|
257
|
-
await pool.destroy(client);
|
|
258
|
-
console.debug(logHeader, `Redis client ${dbIndex} destroyed due to invalid status.`);
|
|
259
|
-
}
|
|
260
|
-
else {
|
|
261
|
-
// Redisクライアントの返却
|
|
262
|
-
await pool.release(client);
|
|
263
|
-
console.debug(logHeader, `Redis client ${dbIndex} released.`);
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
},
|
|
268
|
-
destroy: async (timeoutMs = 5000) => {
|
|
269
|
-
for (const [dbIndex, pool] of poolMap.entries()) {
|
|
270
|
-
console.debug(logHeader, `Destroying Redis pool for DB index ${dbIndex}...`);
|
|
271
|
-
await Promise.race([
|
|
272
|
-
(async () => {
|
|
273
|
-
await pool.drain();
|
|
274
|
-
await pool.clear();
|
|
275
|
-
})(),
|
|
276
|
-
new Promise((_, reject) => {
|
|
277
|
-
setTimeout(() => reject(new Error(`${logHeader} Timeout while draining Redis pool for DB index ${dbIndex}`)), timeoutMs);
|
|
278
|
-
})
|
|
279
|
-
]);
|
|
280
|
-
console.debug(logHeader, `Redis pool for DB index ${dbIndex} destroyed.`);
|
|
281
|
-
poolMap.delete(dbIndex);
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
};
|
|
285
|
-
};
|
|
286
|
-
exports.createRedisPool = createRedisPool;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
exports.RedisPool = RedisPool;
|
|
287
289
|
//# sourceMappingURL=redis-pooling.js.map
|
package/lib/redis-pooling.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"redis-pooling.js","sourceRoot":"","sources":["../src/redis-pooling.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,qCAAgC;AAChC,0DAA4C;
|
|
1
|
+
{"version":3,"file":"redis-pooling.js","sourceRoot":"","sources":["../src/redis-pooling.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/redis-pooling.ts
CHANGED
|
@@ -3,7 +3,7 @@ import * as genericPool from 'generic-pool';
|
|
|
3
3
|
|
|
4
4
|
export interface RedisConfig {
|
|
5
5
|
url: string;
|
|
6
|
-
|
|
6
|
+
dbIndex?: number;
|
|
7
7
|
connectTimeout?: number;
|
|
8
8
|
max?: number;
|
|
9
9
|
min?: number;
|
|
@@ -11,13 +11,7 @@ export interface RedisConfig {
|
|
|
11
11
|
enableTls?: boolean;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
export interface
|
|
15
|
-
acquire(dbIndex?: number): Promise<Redis>;
|
|
16
|
-
release(client?: Redis): Promise<void>;
|
|
17
|
-
destroy(timeoutMs?: number): Promise<void>;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export interface ManagedRedisClient extends Redis {
|
|
14
|
+
export interface RedisClient extends Redis {
|
|
21
15
|
getKeys(pattern: string): Promise<string[]>;
|
|
22
16
|
deleteKeys(pattern: string): Promise<number>;
|
|
23
17
|
_originalDbIndex?: number; // 内部状態管理用変数
|
|
@@ -29,39 +23,115 @@ const DEFAULT_MAX_POOLING_SIZE = 10;
|
|
|
29
23
|
const DEFAULT_MIN_POOLING_SIZE = 0;
|
|
30
24
|
const REDIS_PING_TIMEOUT_MS = 3000; // 3秒
|
|
31
25
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
26
|
+
export class RedisPool {
|
|
27
|
+
|
|
28
|
+
private readonly url: string;
|
|
29
|
+
private readonly db: number;
|
|
30
|
+
private readonly connectTimeout: number;
|
|
31
|
+
private readonly max: number;
|
|
32
|
+
private readonly min: number;
|
|
33
|
+
private readonly testOnBorrow: boolean;
|
|
34
|
+
private readonly enableTls?: boolean;
|
|
39
35
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
36
|
+
private readonly poolMap = new Map<number, genericPool.Pool<RedisClient>>();
|
|
37
|
+
|
|
38
|
+
constructor(config: RedisConfig) {
|
|
39
|
+
if (!config.url) {
|
|
40
|
+
throw new Error(`${logHeader} Redis connection url is required.`);
|
|
41
|
+
}
|
|
46
42
|
|
|
47
|
-
|
|
48
|
-
|
|
43
|
+
this.url = config.url;
|
|
44
|
+
this.db = config.dbIndex ?? 0;
|
|
45
|
+
this.connectTimeout = config.connectTimeout ?? DEFAULT_CONNECT_TIMEOUT;
|
|
46
|
+
this.max = config.max ?? DEFAULT_MAX_POOLING_SIZE;
|
|
47
|
+
this.min = config.min ?? DEFAULT_MIN_POOLING_SIZE;
|
|
48
|
+
this.testOnBorrow = config.testOnBorrow ?? true;
|
|
49
|
+
this.enableTls = config.enableTls;
|
|
49
50
|
}
|
|
50
51
|
|
|
51
|
-
|
|
52
|
-
|
|
52
|
+
public async acquire(dbIndex?: number): Promise<RedisClient> {
|
|
53
|
+
const index = dbIndex ?? this.db;
|
|
54
|
+
const pool = this.getPool(index);
|
|
55
|
+
const client = await pool.acquire();
|
|
53
56
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
+
console.debug(logHeader, `Redis client ${index} has been acquired.`);
|
|
58
|
+
return client;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
public async release(client?: RedisClient): Promise<void> {
|
|
62
|
+
if (client) {
|
|
63
|
+
const dbIndex = client._originalDbIndex ?? this.db;
|
|
64
|
+
const pool = this.poolMap.get(dbIndex);
|
|
65
|
+
if (!pool) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
let needDestroy = false;
|
|
69
|
+
switch (true) {
|
|
70
|
+
case client.status === 'end':
|
|
71
|
+
case client.status === 'close':
|
|
72
|
+
// Redisクライアントの状態が再利用できない場合はプールから破棄
|
|
73
|
+
needDestroy = true;
|
|
74
|
+
break;
|
|
75
|
+
case client.status === 'ready':
|
|
76
|
+
try {
|
|
77
|
+
// 元のDBインデックスに戻す
|
|
78
|
+
await client.select(dbIndex);
|
|
79
|
+
} catch (error) {
|
|
80
|
+
// selectに失敗する→Redisクライアントが不正な状態にあると判断できるのでプールから破棄
|
|
81
|
+
needDestroy = true;
|
|
82
|
+
}
|
|
83
|
+
break;
|
|
84
|
+
default:
|
|
85
|
+
}
|
|
86
|
+
if (needDestroy) {
|
|
87
|
+
// Redisクライアントの破棄
|
|
88
|
+
await pool.destroy(client);
|
|
89
|
+
console.debug(logHeader, `Redis client ${dbIndex} destroyed due to invalid status.`);
|
|
90
|
+
} else {
|
|
91
|
+
// Redisクライアントの返却
|
|
92
|
+
await pool.release(client);
|
|
93
|
+
console.debug(logHeader, `Redis client ${dbIndex} released.`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
public async destroy(timeoutMs = 5000): Promise<void> {
|
|
99
|
+
for (const [dbIndex, pool] of this.poolMap.entries()) {
|
|
100
|
+
console.debug(logHeader, `Destroying Redis pool for DB index ${dbIndex}...`);
|
|
101
|
+
await Promise.race([
|
|
102
|
+
(async () => {
|
|
103
|
+
await pool.drain();
|
|
104
|
+
await pool.clear();
|
|
105
|
+
})(),
|
|
106
|
+
new Promise<void>((_, reject) => {
|
|
107
|
+
setTimeout(() => reject(new Error(`${logHeader} Timeout while draining Redis pool for DB index ${dbIndex}`)), timeoutMs);
|
|
108
|
+
})
|
|
109
|
+
]);
|
|
110
|
+
console.debug(logHeader, `Redis pool for DB index ${dbIndex} destroyed.`);
|
|
111
|
+
this.poolMap.delete(dbIndex);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
private getPool(dbIndex: number): genericPool.Pool<RedisClient> {
|
|
116
|
+
let pool = this.poolMap.get(dbIndex);
|
|
117
|
+
if (!pool) {
|
|
118
|
+
pool = this.createSingleDbPool(dbIndex);
|
|
119
|
+
this.poolMap.set(dbIndex, pool);
|
|
120
|
+
}
|
|
121
|
+
return pool;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
private createSingleDbPool(dbIndex: number): genericPool.Pool<RedisClient> {
|
|
125
|
+
const factory: genericPool.Factory<RedisClient> = {
|
|
126
|
+
create: async (): Promise<RedisClient> => {
|
|
57
127
|
let client;
|
|
58
128
|
try {
|
|
59
|
-
client = new Redis(url, {
|
|
129
|
+
client = new Redis(this.url, {
|
|
60
130
|
db: dbIndex,
|
|
61
|
-
connectTimeout: connectTimeout,
|
|
131
|
+
connectTimeout: this.connectTimeout,
|
|
62
132
|
keepAlive: 1,
|
|
63
133
|
enableOfflineQueue: true,
|
|
64
|
-
tls:
|
|
134
|
+
tls: this.enableTls ? { rejectUnauthorized: false } : undefined,
|
|
65
135
|
retryStrategy: (times) => {
|
|
66
136
|
const delay = Math.min(times * 50, 1000);
|
|
67
137
|
if (process.env.NODE_ENV !== 'test') {
|
|
@@ -73,7 +143,7 @@ export const createRedisPool = (config: RedisConfig): ManagedRedisPool => {
|
|
|
73
143
|
process.emitWarning(`${logHeader} detected error (on reconnect). ${error.message}`);
|
|
74
144
|
return true;
|
|
75
145
|
}
|
|
76
|
-
}) as
|
|
146
|
+
}) as RedisClient;
|
|
77
147
|
} catch (error) {
|
|
78
148
|
console.error(error);
|
|
79
149
|
throw error;
|
|
@@ -203,79 +273,9 @@ export const createRedisPool = (config: RedisConfig): ManagedRedisPool => {
|
|
|
203
273
|
};
|
|
204
274
|
|
|
205
275
|
return genericPool.createPool(factory, {
|
|
206
|
-
max,
|
|
207
|
-
min,
|
|
208
|
-
testOnBorrow
|
|
276
|
+
max: this.max,
|
|
277
|
+
min: this.min,
|
|
278
|
+
testOnBorrow: this.testOnBorrow
|
|
209
279
|
});
|
|
210
|
-
};
|
|
211
|
-
|
|
212
|
-
const getPool = (dbIndex: number) => {
|
|
213
|
-
let pool = poolMap.get(dbIndex);
|
|
214
|
-
if (!pool) {
|
|
215
|
-
pool = createSingleDbPool(dbIndex);
|
|
216
|
-
poolMap.set(dbIndex, pool);
|
|
217
|
-
}
|
|
218
|
-
return pool;
|
|
219
|
-
};
|
|
220
|
-
|
|
221
|
-
return {
|
|
222
|
-
acquire: async (dbIndex?: number): Promise<ManagedRedisClient> => {
|
|
223
|
-
const index = dbIndex ?? 0;
|
|
224
|
-
const pool = getPool(index);
|
|
225
|
-
const client = await pool.acquire();
|
|
226
|
-
console.debug(logHeader, `Redis client ${index} has been acquired.`);
|
|
227
|
-
return client;
|
|
228
|
-
},
|
|
229
|
-
release: async (client?: ManagedRedisClient): Promise<void> => {
|
|
230
|
-
if (client) {
|
|
231
|
-
const dbIndex = client._originalDbIndex ?? db;
|
|
232
|
-
let needDestroy = false;
|
|
233
|
-
switch (true) {
|
|
234
|
-
case client.status === 'end':
|
|
235
|
-
case client.status === 'close':
|
|
236
|
-
// Redisクライアントの状態が再利用できない場合はプールから破棄
|
|
237
|
-
needDestroy = true;
|
|
238
|
-
break;
|
|
239
|
-
case client.status === 'ready':
|
|
240
|
-
try {
|
|
241
|
-
// 元のDBインデックスに戻す
|
|
242
|
-
await client.select(dbIndex);
|
|
243
|
-
} catch (error) {
|
|
244
|
-
// selectに失敗する→Redisクライアントが不正な状態にあると判断できるのでプールから破棄
|
|
245
|
-
needDestroy = true;
|
|
246
|
-
}
|
|
247
|
-
break;
|
|
248
|
-
default:
|
|
249
|
-
}
|
|
250
|
-
const pool = poolMap.get(dbIndex);
|
|
251
|
-
if (pool) {
|
|
252
|
-
if (needDestroy) {
|
|
253
|
-
// Redisクライアントの破棄
|
|
254
|
-
await pool.destroy(client);
|
|
255
|
-
console.debug(logHeader, `Redis client ${dbIndex} destroyed due to invalid status.`);
|
|
256
|
-
} else {
|
|
257
|
-
// Redisクライアントの返却
|
|
258
|
-
await pool.release(client);
|
|
259
|
-
console.debug(logHeader, `Redis client ${dbIndex} released.`);
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
},
|
|
264
|
-
destroy: async (timeoutMs = 5000): Promise<void> => {
|
|
265
|
-
for (const [dbIndex, pool] of poolMap.entries()) {
|
|
266
|
-
console.debug(logHeader, `Destroying Redis pool for DB index ${dbIndex}...`);
|
|
267
|
-
await Promise.race([
|
|
268
|
-
(async () => {
|
|
269
|
-
await pool.drain();
|
|
270
|
-
await pool.clear();
|
|
271
|
-
})(),
|
|
272
|
-
new Promise<void>((_, reject) => {
|
|
273
|
-
setTimeout(() => reject(new Error(`${logHeader} Timeout while draining Redis pool for DB index ${dbIndex}`)), timeoutMs);
|
|
274
|
-
})
|
|
275
|
-
]);
|
|
276
|
-
console.debug(logHeader, `Redis pool for DB index ${dbIndex} destroyed.`);
|
|
277
|
-
poolMap.delete(dbIndex);
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
280
|
}
|
|
281
|
-
}
|
|
281
|
+
}
|