extra-pool 0.1.4 → 0.1.6
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 +3 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -17
- package/lib/index.js.map +1 -1
- package/lib/instance.d.ts +13 -11
- package/lib/instance.js +153 -68
- package/lib/instance.js.map +1 -1
- package/lib/pool.d.ts +3 -3
- package/lib/pool.js +55 -40
- package/lib/pool.js.map +1 -1
- package/package.json +31 -31
- package/src/index.ts +1 -1
- package/src/instance.ts +171 -80
- package/src/pool.ts +75 -24
package/README.md
CHANGED
package/lib/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from './pool';
|
|
1
|
+
export * from './pool.js';
|
package/lib/index.js
CHANGED
|
@@ -1,18 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
-
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
-
};
|
|
16
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
__exportStar(require("./pool"), exports);
|
|
1
|
+
export * from './pool.js';
|
|
18
2
|
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAA"}
|
package/lib/instance.d.ts
CHANGED
|
@@ -1,21 +1,23 @@
|
|
|
1
|
-
import { Awaitable } from '@blackglory/prelude';
|
|
2
|
-
import { Deferred } from 'extra-promise';
|
|
1
|
+
import { Awaitable, NonEmptyArray } from '@blackglory/prelude';
|
|
3
2
|
export declare enum InstanceState {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
Created = 0,
|
|
4
|
+
Initializing = 1,
|
|
5
|
+
Idle = 2,
|
|
6
|
+
Busy = 3,
|
|
7
|
+
Destroying = 4,
|
|
8
|
+
Destroyed = 5
|
|
9
9
|
}
|
|
10
10
|
export declare class Instance<T> {
|
|
11
|
+
#private;
|
|
12
|
+
private createInstance;
|
|
11
13
|
private destroyInstance?;
|
|
12
14
|
private fsm;
|
|
13
|
-
private
|
|
14
|
-
|
|
15
|
+
private deferredCreateInstance;
|
|
16
|
+
private deferredDestroyInstance;
|
|
15
17
|
get users(): number;
|
|
18
|
+
get state(): InstanceState;
|
|
16
19
|
constructor(createInstance: () => Awaitable<T>, destroyInstance?: ((value: T) => Awaitable<void>) | undefined);
|
|
17
|
-
waitForCreated(): Promise<void>;
|
|
18
|
-
getState(): InstanceState;
|
|
19
20
|
use<U>(fn: (instance: T) => Awaitable<U>): Promise<U>;
|
|
20
21
|
destroy(): Promise<void>;
|
|
22
|
+
waitForState<States extends NonEmptyArray<InstanceState>>(...states: States): Promise<States[number]>;
|
|
21
23
|
}
|
package/lib/instance.js
CHANGED
|
@@ -1,28 +1,43 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
2
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
3
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
4
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
5
|
+
};
|
|
6
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
7
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
8
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
9
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
10
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
11
|
+
};
|
|
12
|
+
var _Instance_users;
|
|
13
|
+
import { pass } from '@blackglory/prelude';
|
|
14
|
+
import { ObservableFiniteStateMachine } from 'extra-fsm';
|
|
15
|
+
import { Deferred } from 'extra-promise';
|
|
16
|
+
import { firstValueFrom } from 'rxjs';
|
|
17
|
+
import { filter, map } from 'rxjs/operators';
|
|
18
|
+
export var InstanceState;
|
|
10
19
|
(function (InstanceState) {
|
|
11
|
-
InstanceState["
|
|
12
|
-
InstanceState["
|
|
13
|
-
InstanceState["
|
|
14
|
-
InstanceState["
|
|
15
|
-
InstanceState["
|
|
16
|
-
|
|
20
|
+
InstanceState[InstanceState["Created"] = 0] = "Created";
|
|
21
|
+
InstanceState[InstanceState["Initializing"] = 1] = "Initializing";
|
|
22
|
+
InstanceState[InstanceState["Idle"] = 2] = "Idle";
|
|
23
|
+
InstanceState[InstanceState["Busy"] = 3] = "Busy";
|
|
24
|
+
InstanceState[InstanceState["Destroying"] = 4] = "Destroying";
|
|
25
|
+
InstanceState[InstanceState["Destroyed"] = 5] = "Destroyed";
|
|
26
|
+
})(InstanceState || (InstanceState = {}));
|
|
17
27
|
const instanceSchema = {
|
|
18
|
-
[InstanceState.
|
|
19
|
-
|
|
28
|
+
[InstanceState.Created]: {
|
|
29
|
+
init: InstanceState.Initializing,
|
|
30
|
+
destroy: InstanceState.Destroyed
|
|
31
|
+
},
|
|
32
|
+
[InstanceState.Initializing]: {
|
|
33
|
+
inited: InstanceState.Idle,
|
|
34
|
+
fail: InstanceState.Created
|
|
20
35
|
},
|
|
21
36
|
[InstanceState.Idle]: {
|
|
22
|
-
use: InstanceState.
|
|
37
|
+
use: InstanceState.Busy,
|
|
23
38
|
destroy: InstanceState.Destroying
|
|
24
39
|
},
|
|
25
|
-
[InstanceState.
|
|
40
|
+
[InstanceState.Busy]: {
|
|
26
41
|
idle: InstanceState.Idle
|
|
27
42
|
},
|
|
28
43
|
[InstanceState.Destroying]: {
|
|
@@ -30,69 +45,139 @@ const instanceSchema = {
|
|
|
30
45
|
},
|
|
31
46
|
[InstanceState.Destroyed]: {}
|
|
32
47
|
};
|
|
33
|
-
class Instance {
|
|
48
|
+
export class Instance {
|
|
34
49
|
get users() {
|
|
35
|
-
return this
|
|
50
|
+
return __classPrivateFieldGet(this, _Instance_users, "f");
|
|
51
|
+
}
|
|
52
|
+
get state() {
|
|
53
|
+
return this.fsm.state;
|
|
36
54
|
}
|
|
37
55
|
constructor(createInstance, destroyInstance) {
|
|
56
|
+
this.createInstance = createInstance;
|
|
38
57
|
this.destroyInstance = destroyInstance;
|
|
39
|
-
this.
|
|
40
|
-
this.
|
|
41
|
-
this.
|
|
42
|
-
(
|
|
43
|
-
try {
|
|
44
|
-
const instance = await createInstance();
|
|
45
|
-
this.fsm.send('created');
|
|
46
|
-
this._instance.resolve(instance);
|
|
47
|
-
}
|
|
48
|
-
catch (e) {
|
|
49
|
-
this._instance.reject(e);
|
|
50
|
-
}
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
async waitForCreated() {
|
|
54
|
-
await this._instance;
|
|
55
|
-
}
|
|
56
|
-
getState() {
|
|
57
|
-
return this.fsm.state;
|
|
58
|
+
this.fsm = new ObservableFiniteStateMachine(instanceSchema, InstanceState.Created);
|
|
59
|
+
this.deferredCreateInstance = createDeferred();
|
|
60
|
+
this.deferredDestroyInstance = createDeferred();
|
|
61
|
+
_Instance_users.set(this, 0);
|
|
58
62
|
}
|
|
59
63
|
async use(fn) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
64
|
+
const self = this;
|
|
65
|
+
switch (this.fsm.state) {
|
|
66
|
+
case InstanceState.Created: {
|
|
67
|
+
addUser();
|
|
68
|
+
try {
|
|
69
|
+
this.fsm.send('init');
|
|
70
|
+
let instance;
|
|
71
|
+
try {
|
|
72
|
+
instance = await this.createInstance();
|
|
73
|
+
this.deferredCreateInstance.resolve(instance);
|
|
74
|
+
this.fsm.send('inited');
|
|
75
|
+
}
|
|
76
|
+
catch (e) {
|
|
77
|
+
this.deferredCreateInstance.reject(e);
|
|
78
|
+
this.deferredCreateInstance = createDeferred();
|
|
79
|
+
this.fsm.send('fail');
|
|
80
|
+
throw e;
|
|
81
|
+
}
|
|
82
|
+
this.fsm.send('use');
|
|
83
|
+
return await fn(instance);
|
|
84
|
+
}
|
|
85
|
+
finally {
|
|
86
|
+
removeUser();
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
case InstanceState.Initializing: {
|
|
90
|
+
addUser();
|
|
91
|
+
try {
|
|
92
|
+
const instance = await this.deferredCreateInstance;
|
|
93
|
+
return await fn(instance);
|
|
94
|
+
}
|
|
95
|
+
finally {
|
|
96
|
+
removeUser();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
case InstanceState.Idle: {
|
|
100
|
+
addUser();
|
|
101
|
+
try {
|
|
102
|
+
this.fsm.send('use');
|
|
103
|
+
const instance = await this.deferredCreateInstance;
|
|
104
|
+
return await fn(instance);
|
|
105
|
+
}
|
|
106
|
+
finally {
|
|
107
|
+
removeUser();
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
case InstanceState.Busy: {
|
|
111
|
+
addUser();
|
|
112
|
+
try {
|
|
113
|
+
const instance = await this.deferredCreateInstance;
|
|
114
|
+
return await fn(instance);
|
|
115
|
+
}
|
|
116
|
+
finally {
|
|
117
|
+
removeUser();
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
case InstanceState.Destroying:
|
|
121
|
+
case InstanceState.Destroyed: throw new Error('The instance is not available');
|
|
122
|
+
default: throw new Error(`Unhandled state`);
|
|
68
123
|
}
|
|
69
|
-
(
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const result = await fn(instance);
|
|
73
|
-
return result;
|
|
124
|
+
function addUser() {
|
|
125
|
+
var _a, _b;
|
|
126
|
+
__classPrivateFieldSet(_a = self, _Instance_users, (_b = __classPrivateFieldGet(_a, _Instance_users, "f"), _b++, _b), "f");
|
|
74
127
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
128
|
+
function removeUser() {
|
|
129
|
+
var _a, _b;
|
|
130
|
+
if ((__classPrivateFieldSet(_a = self, _Instance_users, (_b = __classPrivateFieldGet(_a, _Instance_users, "f"), --_b), "f")) === 0) {
|
|
131
|
+
if (self.fsm.can('idle'))
|
|
132
|
+
self.fsm.send('idle');
|
|
78
133
|
}
|
|
79
134
|
}
|
|
80
135
|
}
|
|
81
136
|
async destroy() {
|
|
82
137
|
var _a;
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
138
|
+
switch (this.fsm.state) {
|
|
139
|
+
case InstanceState.Created: {
|
|
140
|
+
this.fsm.send('destroy');
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
case InstanceState.Initializing: {
|
|
144
|
+
await this.waitForState(InstanceState.Idle, InstanceState.Created);
|
|
145
|
+
return await this.destroy();
|
|
146
|
+
}
|
|
147
|
+
case InstanceState.Idle: {
|
|
148
|
+
this.fsm.send('destroy');
|
|
149
|
+
const instance = await this.deferredCreateInstance;
|
|
150
|
+
try {
|
|
151
|
+
await ((_a = this.destroyInstance) === null || _a === void 0 ? void 0 : _a.call(this, instance));
|
|
152
|
+
}
|
|
153
|
+
catch (e) {
|
|
154
|
+
this.deferredDestroyInstance.reject(e);
|
|
155
|
+
throw e;
|
|
156
|
+
}
|
|
157
|
+
this.fsm.send('destroyed');
|
|
158
|
+
this.deferredDestroyInstance.resolve();
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
case InstanceState.Busy: {
|
|
162
|
+
await this.waitForState(InstanceState.Idle);
|
|
163
|
+
return await this.destroy();
|
|
164
|
+
}
|
|
165
|
+
case InstanceState.Destroying: {
|
|
166
|
+
await this.deferredDestroyInstance;
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
case InstanceState.Destroyed: return;
|
|
170
|
+
default: throw new Error(`Unhandled state`);
|
|
94
171
|
}
|
|
95
172
|
}
|
|
173
|
+
async waitForState(...states) {
|
|
174
|
+
return await firstValueFrom(this.fsm.observeStateChanges().pipe(map(change => change.newState), filter(newState => states.includes(newState))));
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
_Instance_users = new WeakMap();
|
|
178
|
+
function createDeferred() {
|
|
179
|
+
const deferred = new Deferred();
|
|
180
|
+
Promise.resolve(deferred).catch(pass);
|
|
181
|
+
return deferred;
|
|
96
182
|
}
|
|
97
|
-
exports.Instance = Instance;
|
|
98
183
|
//# sourceMappingURL=instance.js.map
|
package/lib/instance.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instance.js","sourceRoot":"","sources":["../src/instance.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"instance.js","sourceRoot":"","sources":["../src/instance.ts"],"names":[],"mappings":"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"}
|
package/lib/pool.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { CustomError } from '@blackglory/
|
|
2
|
-
import { Awaitable } from '@blackglory/prelude';
|
|
1
|
+
import { Awaitable, CustomError } from '@blackglory/prelude';
|
|
3
2
|
interface IPoolOptions<T> {
|
|
4
3
|
create: () => Awaitable<T>;
|
|
5
4
|
destroy?: (value: T) => Awaitable<void>;
|
|
@@ -14,13 +13,14 @@ export declare class Pool<T> {
|
|
|
14
13
|
private readonly fsm;
|
|
15
14
|
private readonly waitingUsers;
|
|
16
15
|
private readonly items;
|
|
17
|
-
private readonly maxInstances;
|
|
18
16
|
private readonly minInstances;
|
|
17
|
+
private readonly maxInstances;
|
|
19
18
|
private readonly idleTimeout;
|
|
20
19
|
private readonly concurrencyPerInstance;
|
|
21
20
|
get capacity(): number;
|
|
22
21
|
get size(): number;
|
|
23
22
|
constructor(options: IPoolOptions<T>);
|
|
23
|
+
prewarm(targetInstances: number): Promise<void>;
|
|
24
24
|
use<U>(fn: (instance: T) => Awaitable<U>): Promise<U>;
|
|
25
25
|
destroy(): Promise<void>;
|
|
26
26
|
}
|
package/lib/pool.js
CHANGED
|
@@ -1,19 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const extra_promise_1 = require("extra-promise");
|
|
9
|
-
const iterable_operator_1 = require("iterable-operator");
|
|
10
|
-
const extra_timers_1 = require("extra-timers");
|
|
11
|
-
const instance_1 = require("./instance");
|
|
1
|
+
import { go, CustomError, isntEmptyArray, assert, isPositiveInfinity } from '@blackglory/prelude';
|
|
2
|
+
import { Queue } from '@blackglory/structures';
|
|
3
|
+
import { FiniteStateMachine } from 'extra-fsm';
|
|
4
|
+
import { Deferred, DeferredGroup, each } from 'extra-promise';
|
|
5
|
+
import { toArray, filter } from 'iterable-operator';
|
|
6
|
+
import { setTimeout } from 'extra-timers';
|
|
7
|
+
import { Instance } from './instance.js';
|
|
12
8
|
var PoolState;
|
|
13
9
|
(function (PoolState) {
|
|
14
|
-
PoolState["Running"] = "
|
|
15
|
-
PoolState["Destroying"] = "
|
|
16
|
-
PoolState["Destroyed"] = "
|
|
10
|
+
PoolState[PoolState["Running"] = 0] = "Running";
|
|
11
|
+
PoolState[PoolState["Destroying"] = 1] = "Destroying";
|
|
12
|
+
PoolState[PoolState["Destroyed"] = 2] = "Destroyed";
|
|
17
13
|
})(PoolState || (PoolState = {}));
|
|
18
14
|
const poolSchema = {
|
|
19
15
|
[PoolState.Running]: {
|
|
@@ -24,7 +20,7 @@ const poolSchema = {
|
|
|
24
20
|
},
|
|
25
21
|
[PoolState.Destroyed]: {}
|
|
26
22
|
};
|
|
27
|
-
class Pool {
|
|
23
|
+
export class Pool {
|
|
28
24
|
get capacity() {
|
|
29
25
|
return this.maxInstances;
|
|
30
26
|
}
|
|
@@ -33,28 +29,51 @@ class Pool {
|
|
|
33
29
|
}
|
|
34
30
|
constructor(options) {
|
|
35
31
|
var _a, _b, _c, _d;
|
|
36
|
-
this.fsm = new
|
|
37
|
-
this.waitingUsers = new
|
|
32
|
+
this.fsm = new FiniteStateMachine(poolSchema, PoolState.Running);
|
|
33
|
+
this.waitingUsers = new Queue();
|
|
38
34
|
this.items = new Set();
|
|
39
35
|
this.createInstance = options.create;
|
|
40
36
|
this.destroyInstance = options.destroy;
|
|
41
|
-
this.
|
|
42
|
-
|
|
37
|
+
this.minInstances = (_a = options.minInstances) !== null && _a !== void 0 ? _a : 0;
|
|
38
|
+
assert(Number.isInteger(this.minInstances) &&
|
|
39
|
+
Number.isFinite(this.minInstances) &&
|
|
40
|
+
this.minInstances >= 0, 'The minInstances must be a non-negative finite integer');
|
|
41
|
+
this.maxInstances = (_b = options.maxInstances) !== null && _b !== void 0 ? _b : Infinity;
|
|
42
|
+
assert((Number.isInteger(this.maxInstances) ||
|
|
43
|
+
isPositiveInfinity(this.maxInstances)) &&
|
|
44
|
+
this.maxInstances >= this.minInstances, 'The maxInstances must be either an integer greater than or equal to minInstances, or Infinity');
|
|
43
45
|
this.idleTimeout = (_c = options.idleTimeout) !== null && _c !== void 0 ? _c : 0;
|
|
46
|
+
assert(Number.isInteger(this.idleTimeout) &&
|
|
47
|
+
Number.isFinite(this.idleTimeout) &&
|
|
48
|
+
this.idleTimeout >= 0, 'The idleTimeout must be a non-negative finite integer');
|
|
44
49
|
this.concurrencyPerInstance = (_d = options.concurrencyPerInstance) !== null && _d !== void 0 ? _d : 1;
|
|
50
|
+
assert(Number.isInteger(this.concurrencyPerInstance) &&
|
|
51
|
+
this.concurrencyPerInstance >= 1, 'The concurrencyPerInstance must an integer greater than or equal to 1');
|
|
52
|
+
}
|
|
53
|
+
async prewarm(targetInstances) {
|
|
54
|
+
assert(targetInstances >= this.minInstances &&
|
|
55
|
+
targetInstances <= this.maxInstances &&
|
|
56
|
+
Number.isFinite(targetInstances), 'The targetInstances must be an finite integer in [minInstances, maxInstances]');
|
|
57
|
+
const promises = [];
|
|
58
|
+
const deferredGroup = new DeferredGroup();
|
|
59
|
+
while (this.size < targetInstances) {
|
|
60
|
+
const deferred = new Deferred();
|
|
61
|
+
deferredGroup.add(deferred);
|
|
62
|
+
promises.push(this.use(() => deferred));
|
|
63
|
+
}
|
|
64
|
+
deferredGroup.resolve();
|
|
65
|
+
await Promise.all(promises);
|
|
45
66
|
}
|
|
46
67
|
async use(fn) {
|
|
68
|
+
assert(this.fsm.matches(PoolState.Running), 'The pool is not available');
|
|
47
69
|
const self = this;
|
|
48
|
-
const item =
|
|
49
|
-
const candidateItems =
|
|
50
|
-
if (candidateItems
|
|
70
|
+
const item = go(() => {
|
|
71
|
+
const candidateItems = toArray(filter(this.items, item => item.instance.users < this.concurrencyPerInstance));
|
|
72
|
+
if (isntEmptyArray(candidateItems)) {
|
|
51
73
|
return candidateItems.reduce((previous, current) => {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
else {
|
|
56
|
-
return previous;
|
|
57
|
-
}
|
|
74
|
+
return current.instance.users < previous.instance.users
|
|
75
|
+
? current
|
|
76
|
+
: previous;
|
|
58
77
|
});
|
|
59
78
|
}
|
|
60
79
|
});
|
|
@@ -63,13 +82,13 @@ class Pool {
|
|
|
63
82
|
}
|
|
64
83
|
else {
|
|
65
84
|
if (this.items.size < this.maxInstances) {
|
|
66
|
-
const instance = new
|
|
85
|
+
const instance = new Instance(this.createInstance, this.destroyInstance);
|
|
67
86
|
const item = { instance };
|
|
68
87
|
this.items.add(item);
|
|
69
88
|
return await useItem(item);
|
|
70
89
|
}
|
|
71
90
|
else {
|
|
72
|
-
const waitingUser = new
|
|
91
|
+
const waitingUser = new Deferred();
|
|
73
92
|
this.waitingUsers.enqueue(waitingUser);
|
|
74
93
|
const item = await waitingUser;
|
|
75
94
|
return await useItem(item);
|
|
@@ -92,7 +111,7 @@ class Pool {
|
|
|
92
111
|
if (item.instance.users === 0 &&
|
|
93
112
|
self.items.size > self.minInstances) {
|
|
94
113
|
if (self.idleTimeout > 0) {
|
|
95
|
-
item.cancelScheduledDeletion =
|
|
114
|
+
item.cancelScheduledDeletion = setTimeout(self.idleTimeout, deleteInstance);
|
|
96
115
|
}
|
|
97
116
|
else {
|
|
98
117
|
await deleteInstance();
|
|
@@ -108,19 +127,15 @@ class Pool {
|
|
|
108
127
|
}
|
|
109
128
|
async destroy() {
|
|
110
129
|
this.fsm.send('destroy');
|
|
111
|
-
|
|
112
|
-
await item.instance.destroy();
|
|
113
|
-
}
|
|
130
|
+
await each(this.items, item => item.instance.destroy());
|
|
114
131
|
this.items.clear();
|
|
115
|
-
let
|
|
116
|
-
while (
|
|
117
|
-
|
|
132
|
+
let waitingUser;
|
|
133
|
+
while (waitingUser = this.waitingUsers.dequeue()) {
|
|
134
|
+
waitingUser.reject(new UnavailablePool());
|
|
118
135
|
}
|
|
119
136
|
this.fsm.send('destroyed');
|
|
120
137
|
}
|
|
121
138
|
}
|
|
122
|
-
|
|
123
|
-
class UnavailablePool extends errors_1.CustomError {
|
|
139
|
+
export class UnavailablePool extends CustomError {
|
|
124
140
|
}
|
|
125
|
-
exports.UnavailablePool = UnavailablePool;
|
|
126
141
|
//# sourceMappingURL=pool.js.map
|
package/lib/pool.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pool.js","sourceRoot":"","sources":["../src/pool.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"pool.js","sourceRoot":"","sources":["../src/pool.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "extra-pool",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "A library that helps you create object/thread/connection pools",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pool",
|
|
@@ -12,57 +12,57 @@
|
|
|
12
12
|
"lib",
|
|
13
13
|
"src"
|
|
14
14
|
],
|
|
15
|
+
"type": "module",
|
|
15
16
|
"main": "lib/index.js",
|
|
16
17
|
"types": "lib/index.d.ts",
|
|
17
18
|
"sideEffects": false,
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=22"
|
|
21
|
+
},
|
|
18
22
|
"repository": "git@github.com:BlackGlory/extra-pool.git",
|
|
19
23
|
"author": "BlackGlory <woshenmedoubuzhidao@blackglory.me>",
|
|
20
24
|
"license": "MIT",
|
|
21
25
|
"scripts": {
|
|
22
|
-
"
|
|
26
|
+
"prepare": "ts-patch install -s",
|
|
23
27
|
"lint": "eslint --ext .js,.jsx,.ts,.tsx --quiet src __tests__",
|
|
24
|
-
"test": "
|
|
25
|
-
"
|
|
26
|
-
"test:coverage": "jest --coverage --config jest.config.js",
|
|
27
|
-
"prepublishOnly": "run-s clean build",
|
|
28
|
+
"test": "vitest run",
|
|
29
|
+
"prepublishOnly": "run-s prepare clean build",
|
|
28
30
|
"clean": "rimraf lib",
|
|
29
|
-
"build": "
|
|
30
|
-
"build:compile": "tsc --project tsconfig.build.json --module commonjs --target es2018 --outDir lib",
|
|
31
|
-
"build:patch": "tscpaths -p tsconfig.build.json -s ./src -o ./lib",
|
|
31
|
+
"build": "tsc --project tsconfig.build.json",
|
|
32
32
|
"release": "standard-version"
|
|
33
33
|
},
|
|
34
34
|
"husky": {
|
|
35
35
|
"hooks": {
|
|
36
|
-
"pre-commit": "run-s lint build test",
|
|
36
|
+
"pre-commit": "run-s prepare lint build test",
|
|
37
37
|
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
|
|
38
38
|
}
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
|
-
"@commitlint/cli": "^
|
|
42
|
-
"@commitlint/config-conventional": "^
|
|
43
|
-
"@
|
|
44
|
-
"
|
|
45
|
-
"@typescript-eslint/parser": "^5.44.0",
|
|
46
|
-
"eslint": "^8.28.0",
|
|
41
|
+
"@commitlint/cli": "^20.4.2",
|
|
42
|
+
"@commitlint/config-conventional": "^20.4.2",
|
|
43
|
+
"@eslint/js": "^10.0.1",
|
|
44
|
+
"eslint": "^10.0.2",
|
|
47
45
|
"husky": "^4.3.8",
|
|
48
|
-
"jest": "^29.3.1",
|
|
49
46
|
"npm-run-all": "^4.1.5",
|
|
50
|
-
"
|
|
47
|
+
"return-style": "^4.0.0",
|
|
48
|
+
"rimraf": "^6.1.3",
|
|
51
49
|
"standard-version": "^9.5.0",
|
|
52
|
-
"ts-
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"typescript": "^
|
|
56
|
-
"
|
|
50
|
+
"ts-patch": "^3.3.0",
|
|
51
|
+
"tslib": "^2.8.1",
|
|
52
|
+
"typescript": "^5.9.3",
|
|
53
|
+
"typescript-eslint": "^8.56.1",
|
|
54
|
+
"typescript-transform-paths": "^3.5.6",
|
|
55
|
+
"vite": "^7.3.1",
|
|
56
|
+
"vite-tsconfig-paths": "^6.1.1",
|
|
57
|
+
"vitest": "^4.0.18"
|
|
57
58
|
},
|
|
58
59
|
"dependencies": {
|
|
59
|
-
"@blackglory/
|
|
60
|
-
"@blackglory/
|
|
61
|
-
"
|
|
62
|
-
"extra-
|
|
63
|
-
"extra-
|
|
64
|
-
"
|
|
65
|
-
"
|
|
66
|
-
"rxjs": "^7.5.7"
|
|
60
|
+
"@blackglory/prelude": "^0.4.0",
|
|
61
|
+
"@blackglory/structures": "^0.14.12",
|
|
62
|
+
"extra-fsm": "^0.2.1",
|
|
63
|
+
"extra-promise": "^7.1.1",
|
|
64
|
+
"extra-timers": "^0.3.0",
|
|
65
|
+
"iterable-operator": "^6.0.0",
|
|
66
|
+
"rxjs": "^7.8.2"
|
|
67
67
|
}
|
|
68
68
|
}
|
package/src/index.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from './pool'
|
|
1
|
+
export * from './pool.js'
|
package/src/instance.ts
CHANGED
|
@@ -1,33 +1,41 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Awaitable, NonEmptyArray, pass } from '@blackglory/prelude'
|
|
2
|
+
import { ObservableFiniteStateMachine, IFiniteStateMachineSchema } from 'extra-fsm'
|
|
2
3
|
import { Deferred } from 'extra-promise'
|
|
3
|
-
import { ObservableFiniteStateMachine } from 'extra-fsm'
|
|
4
4
|
import { firstValueFrom } from 'rxjs'
|
|
5
|
-
import { filter } from 'rxjs/operators'
|
|
5
|
+
import { filter, map } from 'rxjs/operators'
|
|
6
6
|
|
|
7
7
|
export enum InstanceState {
|
|
8
|
-
|
|
9
|
-
,
|
|
10
|
-
,
|
|
11
|
-
,
|
|
12
|
-
,
|
|
8
|
+
Created
|
|
9
|
+
, Initializing
|
|
10
|
+
, Idle
|
|
11
|
+
, Busy
|
|
12
|
+
, Destroying
|
|
13
|
+
, Destroyed
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
type InstanceEvent =
|
|
16
|
-
| '
|
|
17
|
+
| 'init'
|
|
18
|
+
| 'inited'
|
|
19
|
+
| 'fail'
|
|
17
20
|
| 'use'
|
|
18
21
|
| 'idle'
|
|
19
22
|
| 'destroy'
|
|
20
23
|
| 'destroyed'
|
|
21
24
|
|
|
22
|
-
const instanceSchema = {
|
|
23
|
-
[InstanceState.
|
|
24
|
-
|
|
25
|
+
const instanceSchema: IFiniteStateMachineSchema<InstanceState, InstanceEvent> = {
|
|
26
|
+
[InstanceState.Created]: {
|
|
27
|
+
init: InstanceState.Initializing
|
|
28
|
+
, destroy: InstanceState.Destroyed
|
|
29
|
+
}
|
|
30
|
+
, [InstanceState.Initializing]: {
|
|
31
|
+
inited: InstanceState.Idle
|
|
32
|
+
, fail: InstanceState.Created
|
|
25
33
|
}
|
|
26
34
|
, [InstanceState.Idle]: {
|
|
27
|
-
use: InstanceState.
|
|
35
|
+
use: InstanceState.Busy
|
|
28
36
|
, destroy: InstanceState.Destroying
|
|
29
37
|
}
|
|
30
|
-
, [InstanceState.
|
|
38
|
+
, [InstanceState.Busy]: {
|
|
31
39
|
idle: InstanceState.Idle
|
|
32
40
|
}
|
|
33
41
|
, [InstanceState.Destroying]: {
|
|
@@ -37,97 +45,180 @@ const instanceSchema = {
|
|
|
37
45
|
}
|
|
38
46
|
|
|
39
47
|
export class Instance<T> {
|
|
40
|
-
private fsm: ObservableFiniteStateMachine<
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
48
|
+
private fsm: ObservableFiniteStateMachine<
|
|
49
|
+
InstanceState
|
|
50
|
+
, InstanceEvent
|
|
51
|
+
> = new ObservableFiniteStateMachine(
|
|
52
|
+
instanceSchema
|
|
53
|
+
, InstanceState.Created
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
private deferredCreateInstance: Deferred<T> = createDeferred()
|
|
57
|
+
private deferredDestroyInstance: Deferred<void> = createDeferred()
|
|
58
|
+
|
|
59
|
+
#users = 0
|
|
44
60
|
get users(): number {
|
|
45
|
-
return this
|
|
61
|
+
return this.#users
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
get state(): InstanceState {
|
|
65
|
+
return this.fsm.state
|
|
46
66
|
}
|
|
47
67
|
|
|
48
68
|
constructor(
|
|
49
|
-
createInstance: () => Awaitable<T>
|
|
69
|
+
private createInstance: () => Awaitable<T>
|
|
50
70
|
, private destroyInstance?: (value: T) => Awaitable<void>
|
|
51
|
-
) {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
)
|
|
71
|
+
) {}
|
|
72
|
+
|
|
73
|
+
async use<U>(fn: (instance: T) => Awaitable<U>): Promise<U> {
|
|
74
|
+
const self = this
|
|
75
|
+
|
|
76
|
+
switch (this.fsm.state) {
|
|
77
|
+
case InstanceState.Created: {
|
|
78
|
+
addUser()
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
this.fsm.send('init')
|
|
82
|
+
|
|
83
|
+
let instance: T
|
|
84
|
+
try {
|
|
85
|
+
instance = await this.createInstance()
|
|
86
|
+
this.deferredCreateInstance.resolve(instance)
|
|
87
|
+
|
|
88
|
+
this.fsm.send('inited')
|
|
89
|
+
} catch (e) {
|
|
90
|
+
this.deferredCreateInstance.reject(e)
|
|
91
|
+
this.deferredCreateInstance = createDeferred()
|
|
57
92
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
93
|
+
this.fsm.send('fail')
|
|
94
|
+
|
|
95
|
+
throw e
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
this.fsm.send('use')
|
|
99
|
+
return await fn(instance)
|
|
100
|
+
} finally {
|
|
101
|
+
removeUser()
|
|
102
|
+
}
|
|
65
103
|
}
|
|
66
|
-
|
|
67
|
-
|
|
104
|
+
case InstanceState.Initializing: {
|
|
105
|
+
addUser()
|
|
68
106
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
107
|
+
try {
|
|
108
|
+
const instance = await this.deferredCreateInstance
|
|
72
109
|
|
|
73
|
-
|
|
74
|
-
return this.fsm.state
|
|
75
|
-
}
|
|
110
|
+
// 由导致初始化的调用将负责状态转换.
|
|
76
111
|
|
|
77
|
-
|
|
78
|
-
|
|
112
|
+
return await fn(instance)
|
|
113
|
+
} finally {
|
|
114
|
+
removeUser()
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
case InstanceState.Idle: {
|
|
118
|
+
addUser()
|
|
79
119
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
120
|
+
try {
|
|
121
|
+
this.fsm.send('use')
|
|
122
|
+
|
|
123
|
+
const instance = await this.deferredCreateInstance
|
|
124
|
+
|
|
125
|
+
return await fn(instance)
|
|
126
|
+
} finally {
|
|
127
|
+
removeUser()
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
case InstanceState.Busy: {
|
|
131
|
+
addUser()
|
|
85
132
|
|
|
86
|
-
|
|
133
|
+
try {
|
|
134
|
+
const instance = await this.deferredCreateInstance
|
|
87
135
|
|
|
88
|
-
|
|
89
|
-
|
|
136
|
+
return await fn(instance)
|
|
137
|
+
} finally {
|
|
138
|
+
removeUser()
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
case InstanceState.Destroying:
|
|
142
|
+
case InstanceState.Destroyed: throw new Error('The instance is not available')
|
|
143
|
+
default: throw new Error(`Unhandled state`)
|
|
90
144
|
}
|
|
91
145
|
|
|
92
|
-
|
|
93
|
-
|
|
146
|
+
function addUser(): void {
|
|
147
|
+
self.#users++
|
|
94
148
|
}
|
|
95
149
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
const result = await fn(instance)
|
|
100
|
-
return result
|
|
101
|
-
} finally {
|
|
102
|
-
if ((--this._users) === 0) {
|
|
103
|
-
this.fsm.send('idle')
|
|
150
|
+
function removeUser(): void {
|
|
151
|
+
if ((--self.#users) === 0) {
|
|
152
|
+
if (self.fsm.can('idle')) self.fsm.send('idle')
|
|
104
153
|
}
|
|
105
154
|
}
|
|
106
155
|
}
|
|
107
156
|
|
|
108
157
|
async destroy(): Promise<void> {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
158
|
+
switch (this.fsm.state) {
|
|
159
|
+
case InstanceState.Created: {
|
|
160
|
+
this.fsm.send('destroy')
|
|
161
|
+
|
|
162
|
+
return
|
|
163
|
+
}
|
|
164
|
+
case InstanceState.Initializing: {
|
|
165
|
+
await this.waitForState(
|
|
166
|
+
InstanceState.Idle
|
|
167
|
+
, InstanceState.Created
|
|
116
168
|
)
|
|
117
|
-
|
|
169
|
+
|
|
170
|
+
return await this.destroy()
|
|
171
|
+
}
|
|
172
|
+
case InstanceState.Idle: {
|
|
173
|
+
this.fsm.send('destroy')
|
|
174
|
+
|
|
175
|
+
const instance = await this.deferredCreateInstance
|
|
176
|
+
|
|
177
|
+
try {
|
|
178
|
+
await this.destroyInstance?.(instance)
|
|
179
|
+
} catch (e) {
|
|
180
|
+
// 如果destroy过程中出错, 之后的所有destroy调用都会抛出相同错误.
|
|
181
|
+
// 此实例的状态将停留在Destroying, 这是预期行为.
|
|
182
|
+
this.deferredDestroyInstance.reject(e)
|
|
183
|
+
|
|
184
|
+
throw e
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
this.fsm.send('destroyed')
|
|
188
|
+
|
|
189
|
+
this.deferredDestroyInstance.resolve()
|
|
190
|
+
|
|
191
|
+
return
|
|
192
|
+
}
|
|
193
|
+
case InstanceState.Busy: {
|
|
194
|
+
await this.waitForState(InstanceState.Idle)
|
|
195
|
+
|
|
196
|
+
return await this.destroy()
|
|
197
|
+
}
|
|
198
|
+
case InstanceState.Destroying: {
|
|
199
|
+
await this.deferredDestroyInstance
|
|
200
|
+
|
|
201
|
+
return
|
|
202
|
+
}
|
|
203
|
+
case InstanceState.Destroyed: return
|
|
204
|
+
default: throw new Error(`Unhandled state`)
|
|
118
205
|
}
|
|
206
|
+
}
|
|
119
207
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
this.fsm.
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
this.fsm.observeStateChanges().pipe(
|
|
128
|
-
filter(state => state.newState === InstanceState.Destroyed)
|
|
129
|
-
)
|
|
208
|
+
async waitForState<States extends NonEmptyArray<InstanceState>>(
|
|
209
|
+
...states: States
|
|
210
|
+
): Promise<States[number]> {
|
|
211
|
+
return await firstValueFrom(
|
|
212
|
+
this.fsm.observeStateChanges().pipe(
|
|
213
|
+
map(change => change.newState)
|
|
214
|
+
, filter(newState => states.includes(newState))
|
|
130
215
|
)
|
|
131
|
-
|
|
216
|
+
)
|
|
132
217
|
}
|
|
133
218
|
}
|
|
219
|
+
|
|
220
|
+
function createDeferred<T>(): Deferred<T> {
|
|
221
|
+
const deferred = new Deferred<T>()
|
|
222
|
+
Promise.resolve(deferred).catch(pass)
|
|
223
|
+
return deferred
|
|
224
|
+
}
|
package/src/pool.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import { CustomError } from '@blackglory/
|
|
2
|
-
import { go, Awaitable } from '@blackglory/prelude'
|
|
1
|
+
import { go, Awaitable, CustomError, isntEmptyArray, assert, isPositiveInfinity } from '@blackglory/prelude'
|
|
3
2
|
import { Queue } from '@blackglory/structures'
|
|
4
|
-
import { FiniteStateMachine } from 'extra-fsm'
|
|
5
|
-
import { Deferred } from 'extra-promise'
|
|
3
|
+
import { FiniteStateMachine, IFiniteStateMachineSchema } from 'extra-fsm'
|
|
4
|
+
import { Deferred, DeferredGroup, each } from 'extra-promise'
|
|
6
5
|
import { toArray, filter } from 'iterable-operator'
|
|
7
6
|
import { setTimeout } from 'extra-timers'
|
|
8
|
-
import { Instance } from './instance'
|
|
7
|
+
import { Instance } from './instance.js'
|
|
9
8
|
|
|
10
9
|
interface IPoolOptions<T> {
|
|
11
10
|
create: () => Awaitable<T>
|
|
@@ -51,16 +50,16 @@ interface IPoolItem<T> {
|
|
|
51
50
|
}
|
|
52
51
|
|
|
53
52
|
enum PoolState {
|
|
54
|
-
Running
|
|
55
|
-
, Destroying
|
|
56
|
-
, Destroyed
|
|
53
|
+
Running
|
|
54
|
+
, Destroying
|
|
55
|
+
, Destroyed
|
|
57
56
|
}
|
|
58
57
|
|
|
59
58
|
type PoolEvent =
|
|
60
59
|
| 'destroy'
|
|
61
60
|
| 'destroyed'
|
|
62
61
|
|
|
63
|
-
const poolSchema = {
|
|
62
|
+
const poolSchema: IFiniteStateMachineSchema<PoolState, PoolEvent> = {
|
|
64
63
|
[PoolState.Running]: {
|
|
65
64
|
destroy: PoolState.Destroying
|
|
66
65
|
}
|
|
@@ -73,14 +72,17 @@ const poolSchema = {
|
|
|
73
72
|
export class Pool<T> {
|
|
74
73
|
private readonly createInstance: () => Awaitable<T>
|
|
75
74
|
private readonly destroyInstance?: (value: T) => Awaitable<void>
|
|
76
|
-
private readonly fsm
|
|
75
|
+
private readonly fsm: FiniteStateMachine<
|
|
76
|
+
PoolState
|
|
77
|
+
, PoolEvent
|
|
78
|
+
> = new FiniteStateMachine(
|
|
77
79
|
poolSchema
|
|
78
80
|
, PoolState.Running
|
|
79
81
|
)
|
|
80
82
|
private readonly waitingUsers: Queue<Deferred<IPoolItem<T>>> = new Queue()
|
|
81
83
|
private readonly items: Set<IPoolItem<T>> = new Set()
|
|
82
|
-
private readonly maxInstances: number
|
|
83
84
|
private readonly minInstances: number
|
|
85
|
+
private readonly maxInstances: number
|
|
84
86
|
private readonly idleTimeout: number
|
|
85
87
|
private readonly concurrencyPerInstance: number
|
|
86
88
|
|
|
@@ -95,10 +97,60 @@ export class Pool<T> {
|
|
|
95
97
|
constructor(options: IPoolOptions<T>) {
|
|
96
98
|
this.createInstance = options.create
|
|
97
99
|
this.destroyInstance = options.destroy
|
|
98
|
-
|
|
100
|
+
|
|
99
101
|
this.minInstances = options.minInstances ?? 0
|
|
102
|
+
assert(
|
|
103
|
+
Number.isInteger(this.minInstances) &&
|
|
104
|
+
Number.isFinite(this.minInstances) &&
|
|
105
|
+
this.minInstances >= 0
|
|
106
|
+
, 'The minInstances must be a non-negative finite integer'
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
this.maxInstances = options.maxInstances ?? Infinity
|
|
110
|
+
assert(
|
|
111
|
+
(
|
|
112
|
+
Number.isInteger(this.maxInstances) ||
|
|
113
|
+
isPositiveInfinity(this.maxInstances)
|
|
114
|
+
) &&
|
|
115
|
+
this.maxInstances >= this.minInstances
|
|
116
|
+
, 'The maxInstances must be either an integer greater than or equal to minInstances, or Infinity'
|
|
117
|
+
)
|
|
118
|
+
|
|
100
119
|
this.idleTimeout = options.idleTimeout ?? 0
|
|
120
|
+
assert(
|
|
121
|
+
Number.isInteger(this.idleTimeout) &&
|
|
122
|
+
Number.isFinite(this.idleTimeout) &&
|
|
123
|
+
this.idleTimeout >= 0
|
|
124
|
+
, 'The idleTimeout must be a non-negative finite integer'
|
|
125
|
+
)
|
|
126
|
+
|
|
101
127
|
this.concurrencyPerInstance = options.concurrencyPerInstance ?? 1
|
|
128
|
+
assert(
|
|
129
|
+
Number.isInteger(this.concurrencyPerInstance) &&
|
|
130
|
+
this.concurrencyPerInstance >= 1
|
|
131
|
+
, 'The concurrencyPerInstance must an integer greater than or equal to 1'
|
|
132
|
+
)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async prewarm(targetInstances: number): Promise<void> {
|
|
136
|
+
assert(
|
|
137
|
+
targetInstances >= this.minInstances &&
|
|
138
|
+
targetInstances <= this.maxInstances &&
|
|
139
|
+
Number.isFinite(targetInstances)
|
|
140
|
+
, 'The targetInstances must be an finite integer in [minInstances, maxInstances]'
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
const promises: Array<Promise<void>> = []
|
|
144
|
+
|
|
145
|
+
const deferredGroup = new DeferredGroup<void>()
|
|
146
|
+
while (this.size < targetInstances) {
|
|
147
|
+
const deferred = new Deferred<void>()
|
|
148
|
+
deferredGroup.add(deferred)
|
|
149
|
+
promises.push(this.use(() => deferred))
|
|
150
|
+
}
|
|
151
|
+
deferredGroup.resolve()
|
|
152
|
+
|
|
153
|
+
await Promise.all(promises)
|
|
102
154
|
}
|
|
103
155
|
|
|
104
156
|
/**
|
|
@@ -106,6 +158,8 @@ export class Pool<T> {
|
|
|
106
158
|
* 函数的使用者应该尊重阻塞, 否则会意外制造大量非必要的Promise.
|
|
107
159
|
*/
|
|
108
160
|
async use<U>(fn: (instance: T) => Awaitable<U>): Promise<U> {
|
|
161
|
+
assert(this.fsm.matches(PoolState.Running), 'The pool is not available')
|
|
162
|
+
|
|
109
163
|
const self = this
|
|
110
164
|
|
|
111
165
|
const item = go(() => {
|
|
@@ -114,13 +168,12 @@ export class Pool<T> {
|
|
|
114
168
|
, item => item.instance.users < this.concurrencyPerInstance
|
|
115
169
|
))
|
|
116
170
|
|
|
117
|
-
if (candidateItems
|
|
171
|
+
if (isntEmptyArray(candidateItems)) {
|
|
172
|
+
// 找到负载最低(用户量最少)的项目.
|
|
118
173
|
return candidateItems.reduce((previous, current) => {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
return previous
|
|
123
|
-
}
|
|
174
|
+
return current.instance.users < previous.instance.users
|
|
175
|
+
? current
|
|
176
|
+
: previous
|
|
124
177
|
})
|
|
125
178
|
}
|
|
126
179
|
})
|
|
@@ -181,14 +234,12 @@ export class Pool<T> {
|
|
|
181
234
|
async destroy(): Promise<void> {
|
|
182
235
|
this.fsm.send('destroy')
|
|
183
236
|
|
|
184
|
-
|
|
185
|
-
await item.instance.destroy()
|
|
186
|
-
}
|
|
237
|
+
await each(this.items, item => item.instance.destroy())
|
|
187
238
|
this.items.clear()
|
|
188
239
|
|
|
189
|
-
let
|
|
190
|
-
while (
|
|
191
|
-
|
|
240
|
+
let waitingUser: Deferred<IPoolItem<T>> | undefined
|
|
241
|
+
while (waitingUser = this.waitingUsers.dequeue()) {
|
|
242
|
+
waitingUser.reject(new UnavailablePool())
|
|
192
243
|
}
|
|
193
244
|
|
|
194
245
|
this.fsm.send('destroyed')
|