fib-pool 1.6.1 → 1.6.2
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 +39 -0
- package/lib/index.js +0 -1
- package/package.json +7 -6
- package/src/index.ts +287 -0
- package/tsconfig.json +7 -3
- package/typings/index.d.ts +2 -0
package/Readme.md
CHANGED
|
@@ -103,6 +103,45 @@ var res = p("test", (conn) => {
|
|
|
103
103
|
|
|
104
104
|
```
|
|
105
105
|
|
|
106
|
+
## Strict mode (default: enabled)
|
|
107
|
+
|
|
108
|
+
By default, the pool uses strict mode to prevent accessing pooled objects outside of the callback function. This helps prevent resource leaks and ensures proper resource management.
|
|
109
|
+
|
|
110
|
+
```js
|
|
111
|
+
var Pool = require("fib-pool");
|
|
112
|
+
|
|
113
|
+
var p = Pool({
|
|
114
|
+
create: () => {
|
|
115
|
+
return { value: 1, getValue: function() { return this.value; } };
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
var leaked;
|
|
120
|
+
p((obj) => {
|
|
121
|
+
leaked = obj;
|
|
122
|
+
// This works inside the pool callback
|
|
123
|
+
console.log(obj.getValue()); // OK
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// This will throw an error: "access object outside of pool scope"
|
|
127
|
+
leaked.getValue(); // Error!
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
When strict mode is enabled, it also prevents:
|
|
131
|
+
- Setting properties on pooled objects
|
|
132
|
+
- Calling `close()`, `destroy()`, or `dispose()` methods directly
|
|
133
|
+
|
|
134
|
+
To disable strict mode:
|
|
135
|
+
|
|
136
|
+
```js
|
|
137
|
+
var p = Pool({
|
|
138
|
+
create: () => {
|
|
139
|
+
return { value: 1 };
|
|
140
|
+
},
|
|
141
|
+
strict: false
|
|
142
|
+
});
|
|
143
|
+
```
|
|
144
|
+
|
|
106
145
|
## Clear a pool
|
|
107
146
|
|
|
108
147
|
Simple example.
|
package/lib/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fib-pool",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.2",
|
|
4
4
|
"description": "Generic resource pooling for fibjs",
|
|
5
|
-
"main": "./
|
|
5
|
+
"main": "./src",
|
|
6
6
|
"types": "typings/index.d.ts",
|
|
7
|
+
"type": "module",
|
|
7
8
|
"repository": "git://github.com/fibjs/fib-pool.git",
|
|
8
9
|
"author": "Liu Hu <lion@9465.net>",
|
|
9
10
|
"contributors": [
|
|
@@ -20,15 +21,15 @@
|
|
|
20
21
|
"fibjs"
|
|
21
22
|
],
|
|
22
23
|
"scripts": {
|
|
23
|
-
"build": "ftsc ./src/* --outDir lib",
|
|
24
24
|
"test": "fibjs test",
|
|
25
|
-
"
|
|
26
|
-
"
|
|
25
|
+
"typecheck": "tsc --noEmit",
|
|
26
|
+
"ci": "npm run typecheck && npm run test"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@fibjs/ci": "^2.3.0",
|
|
30
30
|
"@fibjs/types": "^0.27.1",
|
|
31
|
-
"fib-typify": "^0.8.4"
|
|
31
|
+
"fib-typify": "^0.8.4",
|
|
32
|
+
"typescript": "^5.9.3"
|
|
32
33
|
},
|
|
33
34
|
"ci": {
|
|
34
35
|
"type": "travis, appveyor",
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
import * as coroutine from 'coroutine';
|
|
2
|
+
import * as util from 'util';
|
|
3
|
+
|
|
4
|
+
declare namespace FibPoolNS {
|
|
5
|
+
type FirstParameter<T> = T extends (arg: infer T, ...args: any[]) => any ? T : never;
|
|
6
|
+
|
|
7
|
+
interface DippedItemStruct {
|
|
8
|
+
close?: FibPoolOptionResult['destroy'];
|
|
9
|
+
destroy?: FibPoolOptionResult['destroy'];
|
|
10
|
+
dispose?: FibPoolOptionResult['destroy'];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface FibPoolOptionArgs<T extends DippedItemStruct = {}> {
|
|
14
|
+
create: (name: string) => T
|
|
15
|
+
destroy?: (item?: T) => any;
|
|
16
|
+
maxsize?: number
|
|
17
|
+
timeout?: number
|
|
18
|
+
retry?: number | boolean
|
|
19
|
+
strict?: boolean
|
|
20
|
+
}
|
|
21
|
+
interface FibPoolOptionResult<T extends DippedItemStruct = {}> {
|
|
22
|
+
create: FibPoolOptionArgs<T>['create']
|
|
23
|
+
destroy: FibPoolOptionArgs<T>['destroy']
|
|
24
|
+
maxsize: FibPoolOptionArgs<T>['maxsize']
|
|
25
|
+
timeout: FibPoolOptionArgs<T>['timeout']
|
|
26
|
+
retry: FibPoolOptionArgs<T>['retry']
|
|
27
|
+
strict: FibPoolOptionArgs<T>['strict']
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
interface FibPoolDipperFn<T_CREATED_ITEM, T_POOL_FUNC_RETURN = any> {
|
|
31
|
+
(o: T_CREATED_ITEM): T_POOL_FUNC_RETURN
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
type FibPool<T_CREATED_ITEM, T_POOL_FUNC_RETURN = any> = {
|
|
35
|
+
<T_RETURN = T_POOL_FUNC_RETURN>(name: string, func: FibPoolDipperFn<T_CREATED_ITEM, T_RETURN>): T_RETURN
|
|
36
|
+
<T_RETURN = T_POOL_FUNC_RETURN>(func: FibPoolDipperFn<T_CREATED_ITEM, T_RETURN>): T_RETURN
|
|
37
|
+
} & {
|
|
38
|
+
connections?(): number;
|
|
39
|
+
info?(): {
|
|
40
|
+
maxsize: number;
|
|
41
|
+
count: number;
|
|
42
|
+
running: number;
|
|
43
|
+
wait: number;
|
|
44
|
+
timeout: number;
|
|
45
|
+
};
|
|
46
|
+
clear?(): void;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function ensureError (e: string | Error) {
|
|
51
|
+
if (typeof e === 'string')
|
|
52
|
+
e = new Error(e)
|
|
53
|
+
|
|
54
|
+
return e
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export default function GetPool<
|
|
58
|
+
T_CREATED_ITEM extends FibPoolNS.DippedItemStruct,
|
|
59
|
+
T_POOL_FUNC_RETURN
|
|
60
|
+
> (
|
|
61
|
+
_opt: FibPoolNS.FibPoolOptionArgs<T_CREATED_ITEM> | FibPoolNS.FibPoolOptionArgs<T_CREATED_ITEM>['create'],
|
|
62
|
+
maxsize?: number,
|
|
63
|
+
timeout?: number
|
|
64
|
+
): FibPoolNS.FibPool<T_CREATED_ITEM, T_POOL_FUNC_RETURN> {
|
|
65
|
+
/* clean input arguments :start */
|
|
66
|
+
let opt = <FibPoolNS.FibPoolOptionResult<T_CREATED_ITEM>>_opt;
|
|
67
|
+
if (util.isFunction(_opt)) {
|
|
68
|
+
opt = <FibPoolNS.FibPoolOptionResult<T_CREATED_ITEM>>{
|
|
69
|
+
create: _opt,
|
|
70
|
+
maxsize: maxsize,
|
|
71
|
+
timeout: timeout
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const create = opt.create;
|
|
76
|
+
const destroy = opt.destroy || ((o: {
|
|
77
|
+
close?: FibPoolNS.FibPoolOptionResult<T_CREATED_ITEM>['destroy'];
|
|
78
|
+
destroy?: FibPoolNS.FibPoolOptionResult<T_CREATED_ITEM>['destroy'];
|
|
79
|
+
dispose?: FibPoolNS.FibPoolOptionResult<T_CREATED_ITEM>['destroy'];
|
|
80
|
+
}) => {
|
|
81
|
+
if (util.isFunction(o.close)) o.close();
|
|
82
|
+
if (util.isFunction(o.destroy)) o.destroy();
|
|
83
|
+
if (util.isFunction(o.dispose)) o.dispose();
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
maxsize = opt.maxsize || 10;
|
|
87
|
+
timeout = opt.timeout || 60000;
|
|
88
|
+
let tm = timeout / 10;
|
|
89
|
+
if (tm < 10)
|
|
90
|
+
tm = 10;
|
|
91
|
+
|
|
92
|
+
const retry = opt.retry || 1;
|
|
93
|
+
const _strict = opt.strict === undefined ? true : opt.strict;
|
|
94
|
+
/* clean input arguments :end */
|
|
95
|
+
|
|
96
|
+
let pools = <{
|
|
97
|
+
o: T_CREATED_ITEM;
|
|
98
|
+
name: string;
|
|
99
|
+
time: Date;
|
|
100
|
+
}[]>[];
|
|
101
|
+
|
|
102
|
+
const jobs = <{
|
|
103
|
+
name: string;
|
|
104
|
+
ev: Class_Event;
|
|
105
|
+
o?: T_CREATED_ITEM;
|
|
106
|
+
e?: Error;
|
|
107
|
+
}[]>[];
|
|
108
|
+
|
|
109
|
+
let count = 0;
|
|
110
|
+
let running = 0;
|
|
111
|
+
|
|
112
|
+
const sem = new coroutine.Semaphore(maxsize);
|
|
113
|
+
let clearTimer: Class_Timer;
|
|
114
|
+
|
|
115
|
+
function clearPool() {
|
|
116
|
+
let c: typeof pools[any];
|
|
117
|
+
const d = new Date().getTime();
|
|
118
|
+
|
|
119
|
+
while (count) {
|
|
120
|
+
c = pools[0];
|
|
121
|
+
|
|
122
|
+
if (d - c.time.getTime() > timeout) {
|
|
123
|
+
pools = pools.slice(1);
|
|
124
|
+
count--;
|
|
125
|
+
|
|
126
|
+
if (c.o !== undefined)
|
|
127
|
+
coroutine.start(destroy, c.o);
|
|
128
|
+
} else
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (!count) {
|
|
133
|
+
if (clearTimer) {
|
|
134
|
+
clearTimer.clear();
|
|
135
|
+
clearTimer = null;
|
|
136
|
+
}
|
|
137
|
+
} else if (!clearTimer) {
|
|
138
|
+
clearTimer = setInterval(clearPool, tm);
|
|
139
|
+
if (clearTimer && typeof clearTimer.unref === 'function')
|
|
140
|
+
clearTimer.unref();
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function putback(
|
|
145
|
+
name: typeof jobs[any]['name'],
|
|
146
|
+
o: T_CREATED_ITEM,
|
|
147
|
+
e?: Error
|
|
148
|
+
) {
|
|
149
|
+
e = ensureError(e);
|
|
150
|
+
for (let i = 0; i < jobs.length; i++) {
|
|
151
|
+
const job = jobs[i];
|
|
152
|
+
if (job.name === name) {
|
|
153
|
+
jobs.splice(i, 1);
|
|
154
|
+
job.o = o;
|
|
155
|
+
job.e = e;
|
|
156
|
+
job.ev.set();
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (e === undefined)
|
|
162
|
+
pools[count++] = {
|
|
163
|
+
o: o,
|
|
164
|
+
name: name,
|
|
165
|
+
time: new Date()
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function connect(name: string) {
|
|
170
|
+
let o: T_CREATED_ITEM;
|
|
171
|
+
let cn = 0;
|
|
172
|
+
let err: Error;
|
|
173
|
+
|
|
174
|
+
while (true) {
|
|
175
|
+
try {
|
|
176
|
+
o = create(name);
|
|
177
|
+
break;
|
|
178
|
+
} catch (e) {
|
|
179
|
+
if (++cn >= (retry as number)) {
|
|
180
|
+
err = e;
|
|
181
|
+
break;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
putback(name, o, err);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const pool: FibPoolNS.FibPool<T_CREATED_ITEM, T_POOL_FUNC_RETURN> = (
|
|
190
|
+
name: string | FibPoolNS.FibPoolDipperFn<T_CREATED_ITEM, T_POOL_FUNC_RETURN>,
|
|
191
|
+
func?: FibPoolNS.FibPoolDipperFn<T_CREATED_ITEM, T_POOL_FUNC_RETURN>
|
|
192
|
+
) => {
|
|
193
|
+
if (typeof name === 'function') {
|
|
194
|
+
func = name as any;
|
|
195
|
+
name = "";
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
let r: T_POOL_FUNC_RETURN;
|
|
199
|
+
let o: typeof jobs[any]['o'];
|
|
200
|
+
let p = false;
|
|
201
|
+
|
|
202
|
+
clearPool();
|
|
203
|
+
sem.acquire();
|
|
204
|
+
|
|
205
|
+
if (count) {
|
|
206
|
+
for (let i = count - 1; i >= 0; i--)
|
|
207
|
+
if (pools[i].name === name) {
|
|
208
|
+
p = true
|
|
209
|
+
o = pools[i].o;
|
|
210
|
+
pools.splice(i, 1);
|
|
211
|
+
count--;
|
|
212
|
+
break;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (!p) {
|
|
217
|
+
coroutine.start(connect, name);
|
|
218
|
+
|
|
219
|
+
const job = <typeof jobs[any]>{
|
|
220
|
+
name: name,
|
|
221
|
+
ev: new coroutine.Event()
|
|
222
|
+
};
|
|
223
|
+
jobs.push(job);
|
|
224
|
+
|
|
225
|
+
job.ev.wait();
|
|
226
|
+
if (job.e) {
|
|
227
|
+
sem.post();
|
|
228
|
+
throw ensureError(job.e);
|
|
229
|
+
}
|
|
230
|
+
o = job.o;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
running++;
|
|
234
|
+
try {
|
|
235
|
+
const po = (_strict && typeof o === 'object') ? new Proxy({}, {
|
|
236
|
+
get: function (target, prop) {
|
|
237
|
+
if (prop === 'close' || prop === 'destroy' || prop === 'dispose')
|
|
238
|
+
throw new Error("Cannot close/destroy/dispose");
|
|
239
|
+
if (o === undefined)
|
|
240
|
+
throw new Error("access object outside of pool scope");
|
|
241
|
+
const value = o[prop];
|
|
242
|
+
if (typeof value === 'function') {
|
|
243
|
+
return function () {
|
|
244
|
+
return value.apply(o, arguments);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return value;
|
|
248
|
+
},
|
|
249
|
+
set: function (target, prop, value) {
|
|
250
|
+
throw new Error("Cannot set property");
|
|
251
|
+
}
|
|
252
|
+
}) as any : o;
|
|
253
|
+
|
|
254
|
+
r = func(po as T_CREATED_ITEM);
|
|
255
|
+
putback(name, o);
|
|
256
|
+
o = undefined;
|
|
257
|
+
} catch (e) {
|
|
258
|
+
if (o !== undefined)
|
|
259
|
+
coroutine.start(destroy, o);
|
|
260
|
+
o = undefined;
|
|
261
|
+
throw ensureError(e);
|
|
262
|
+
} finally {
|
|
263
|
+
running--;
|
|
264
|
+
sem.post();
|
|
265
|
+
clearPool();
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return r;
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
pool.connections = () => count
|
|
272
|
+
|
|
273
|
+
pool.info = () => ({
|
|
274
|
+
maxsize: maxsize,
|
|
275
|
+
count: count,
|
|
276
|
+
running: running,
|
|
277
|
+
wait: sem.count(),
|
|
278
|
+
timeout: timeout
|
|
279
|
+
})
|
|
280
|
+
|
|
281
|
+
pool.clear = () => {
|
|
282
|
+
timeout = -1;
|
|
283
|
+
clearPool();
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
return pool;
|
|
287
|
+
}
|
package/tsconfig.json
CHANGED
|
@@ -4,10 +4,14 @@
|
|
|
4
4
|
"module": "commonjs",
|
|
5
5
|
"alwaysStrict": false,
|
|
6
6
|
"removeComments": true,
|
|
7
|
-
"
|
|
8
|
-
"declarationDir": "typings",
|
|
7
|
+
"noEmit": true,
|
|
9
8
|
"types": [
|
|
10
9
|
"@fibjs/types"
|
|
11
10
|
]
|
|
12
|
-
}
|
|
11
|
+
},
|
|
12
|
+
"exclude": [
|
|
13
|
+
"test",
|
|
14
|
+
"typings",
|
|
15
|
+
"node_modules"
|
|
16
|
+
]
|
|
13
17
|
}
|
package/typings/index.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ declare namespace FibPoolNS {
|
|
|
11
11
|
maxsize?: number;
|
|
12
12
|
timeout?: number;
|
|
13
13
|
retry?: number | boolean;
|
|
14
|
+
strict?: boolean;
|
|
14
15
|
}
|
|
15
16
|
interface FibPoolOptionResult<T extends DippedItemStruct = {}> {
|
|
16
17
|
create: FibPoolOptionArgs<T>['create'];
|
|
@@ -18,6 +19,7 @@ declare namespace FibPoolNS {
|
|
|
18
19
|
maxsize: FibPoolOptionArgs<T>['maxsize'];
|
|
19
20
|
timeout: FibPoolOptionArgs<T>['timeout'];
|
|
20
21
|
retry: FibPoolOptionArgs<T>['retry'];
|
|
22
|
+
strict: FibPoolOptionArgs<T>['strict'];
|
|
21
23
|
}
|
|
22
24
|
interface FibPoolDipperFn<T_CREATED_ITEM, T_POOL_FUNC_RETURN = any> {
|
|
23
25
|
(o: T_CREATED_ITEM): T_POOL_FUNC_RETURN;
|