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 CHANGED
@@ -43,8 +43,10 @@ class Pool<T> {
43
43
 
44
44
  constructor(options: IPoolOptions<T>)
45
45
 
46
- destroy(): Promise<void>
46
+ prewarm(targetInstances: number): Promise<void>
47
47
 
48
48
  use<U>(fn: (instance: T) => Awaitable<U>): Promise<U>
49
+
50
+ destroy(): Promise<void>
49
51
  }
50
52
  ```
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
- "use strict";
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":";;;;;;;;;;;;;;;;AAAA,yCAAsB"}
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
- Creating = "creating",
5
- Idle = "idle",
6
- Using = "using",
7
- Destroying = "destroying",
8
- Destroyed = "destroyed"
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 _users;
14
- readonly _instance: Deferred<T>;
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
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Instance = exports.InstanceState = void 0;
4
- const prelude_1 = require("@blackglory/prelude");
5
- const extra_promise_1 = require("extra-promise");
6
- const extra_fsm_1 = require("extra-fsm");
7
- const rxjs_1 = require("rxjs");
8
- const operators_1 = require("rxjs/operators");
9
- var InstanceState;
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["Creating"] = "creating";
12
- InstanceState["Idle"] = "idle";
13
- InstanceState["Using"] = "using";
14
- InstanceState["Destroying"] = "destroying";
15
- InstanceState["Destroyed"] = "destroyed";
16
- })(InstanceState = exports.InstanceState || (exports.InstanceState = {}));
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.Creating]: {
19
- created: InstanceState.Idle
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.Using,
37
+ use: InstanceState.Busy,
23
38
  destroy: InstanceState.Destroying
24
39
  },
25
- [InstanceState.Using]: {
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._users;
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._users = 0;
40
- this._instance = new extra_promise_1.Deferred();
41
- this.fsm = new extra_fsm_1.ObservableFiniteStateMachine(instanceSchema, InstanceState.Creating);
42
- (0, prelude_1.go)(async () => {
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
- (0, prelude_1.assert)(this.fsm.state !== InstanceState.Destroying &&
61
- this.fsm.state !== InstanceState.Destroyed, 'The instance is not available');
62
- this._users++;
63
- if (this.fsm.state === InstanceState.Creating) {
64
- await this._instance;
65
- }
66
- if (this.fsm.state === InstanceState.Idle) {
67
- this.fsm.send('use');
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
- (0, prelude_1.assert)(this.fsm.state === InstanceState.Using, 'The instance state should be using');
70
- const instance = await this._instance;
71
- try {
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
- finally {
76
- if ((--this._users) === 0) {
77
- this.fsm.send('idle');
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
- if (this.fsm.state === InstanceState.Creating ||
84
- this.fsm.state === InstanceState.Using) {
85
- await (0, rxjs_1.firstValueFrom)(this.fsm.observeStateChanges().pipe((0, operators_1.filter)(state => state.newState === InstanceState.Idle)));
86
- }
87
- if (this.fsm.state === InstanceState.Idle) {
88
- this.fsm.send('destroy');
89
- await ((_a = this.destroyInstance) === null || _a === void 0 ? void 0 : _a.call(this, await this._instance));
90
- this.fsm.send('destroyed');
91
- }
92
- else if (this.fsm.state === InstanceState.Destroying) {
93
- await (0, rxjs_1.firstValueFrom)(this.fsm.observeStateChanges().pipe((0, operators_1.filter)(state => state.newState === InstanceState.Destroyed)));
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
@@ -1 +1 @@
1
- {"version":3,"file":"instance.js","sourceRoot":"","sources":["../src/instance.ts"],"names":[],"mappings":"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"}
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/errors';
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
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.UnavailablePool = exports.Pool = void 0;
4
- const errors_1 = require("@blackglory/errors");
5
- const prelude_1 = require("@blackglory/prelude");
6
- const structures_1 = require("@blackglory/structures");
7
- const extra_fsm_1 = require("extra-fsm");
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"] = "running";
15
- PoolState["Destroying"] = "destroying";
16
- PoolState["Destroyed"] = "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 extra_fsm_1.FiniteStateMachine(poolSchema, PoolState.Running);
37
- this.waitingUsers = new structures_1.Queue();
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.maxInstances = (_a = options.maxInstances) !== null && _a !== void 0 ? _a : Infinity;
42
- this.minInstances = (_b = options.minInstances) !== null && _b !== void 0 ? _b : 0;
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 = (0, prelude_1.go)(() => {
49
- const candidateItems = (0, iterable_operator_1.toArray)((0, iterable_operator_1.filter)(this.items, item => item.instance.users < this.concurrencyPerInstance));
50
- if (candidateItems.length) {
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
- if (current.instance.users < previous.instance.users) {
53
- return current;
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 instance_1.Instance(this.createInstance, this.destroyInstance);
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 extra_promise_1.Deferred();
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 = (0, extra_timers_1.setTimeout)(self.idleTimeout, deleteInstance);
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
- for (const item of this.items) {
112
- await item.instance.destroy();
113
- }
130
+ await each(this.items, item => item.instance.destroy());
114
131
  this.items.clear();
115
- let deferred;
116
- while (deferred = this.waitingUsers.dequeue()) {
117
- deferred.reject(new UnavailablePool());
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
- exports.Pool = Pool;
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":"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"}
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.4",
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
- "deduplicate": "yarn-deduplicate",
26
+ "prepare": "ts-patch install -s",
23
27
  "lint": "eslint --ext .js,.jsx,.ts,.tsx --quiet src __tests__",
24
- "test": "jest --runInBand --config jest.config.js",
25
- "test:debug": "node --inspect-brk node_modules/.bin/jest --runInBand",
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": "run-s 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": "^17.3.0",
42
- "@commitlint/config-conventional": "^17.3.0",
43
- "@types/jest": "^29.2.3",
44
- "@typescript-eslint/eslint-plugin": "^5.44.0",
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
- "rimraf": "^3.0.2",
47
+ "return-style": "^4.0.0",
48
+ "rimraf": "^6.1.3",
51
49
  "standard-version": "^9.5.0",
52
- "ts-jest": "^29.0.3",
53
- "tscpaths": "^0.0.9",
54
- "tslib": "^2.4.1",
55
- "typescript": "^4.9.3",
56
- "yarn-deduplicate": "^6.0.0"
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/errors": "^2.3.0",
60
- "@blackglory/prelude": "^0.1.8",
61
- "@blackglory/structures": "^0.11.4",
62
- "extra-fsm": "^0.1.1",
63
- "extra-promise": "^4.4.0",
64
- "extra-timers": "^0.2.5",
65
- "iterable-operator": "^2.5.0",
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 { go, assert, Awaitable } from '@blackglory/prelude'
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
- Creating = 'creating'
9
- , Idle = 'idle'
10
- , Using = 'using'
11
- , Destroying = 'destroying'
12
- , Destroyed = 'destroyed'
8
+ Created
9
+ , Initializing
10
+ , Idle
11
+ , Busy
12
+ , Destroying
13
+ , Destroyed
13
14
  }
14
15
 
15
16
  type InstanceEvent =
16
- | 'created'
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.Creating]: {
24
- created: InstanceState.Idle
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.Using
35
+ use: InstanceState.Busy
28
36
  , destroy: InstanceState.Destroying
29
37
  }
30
- , [InstanceState.Using]: {
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<InstanceState, InstanceEvent>
41
- private _users = 0
42
- readonly _instance: Deferred<T>
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._users
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
- this._instance = new Deferred<T>()
53
- this.fsm = new ObservableFiniteStateMachine<InstanceState, InstanceEvent>(
54
- instanceSchema
55
- , InstanceState.Creating
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
- go(async () => {
59
- try {
60
- const instance = await createInstance()
61
- this.fsm.send('created')
62
- this._instance.resolve(instance)
63
- } catch (e) {
64
- this._instance.reject(e)
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
- async waitForCreated(): Promise<void> {
70
- await this._instance
71
- }
107
+ try {
108
+ const instance = await this.deferredCreateInstance
72
109
 
73
- getState(): InstanceState {
74
- return this.fsm.state
75
- }
110
+ // 由导致初始化的调用将负责状态转换.
76
111
 
77
- async use<U>(fn: (instance: T) => Awaitable<U>): Promise<U> {
78
- // 不要尝试将此处的代码改编成switch管道, 很难正确编写.
112
+ return await fn(instance)
113
+ } finally {
114
+ removeUser()
115
+ }
116
+ }
117
+ case InstanceState.Idle: {
118
+ addUser()
79
119
 
80
- assert(
81
- this.fsm.state !== InstanceState.Destroying &&
82
- this.fsm.state !== InstanceState.Destroyed
83
- , 'The instance is not available'
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
- this._users++
133
+ try {
134
+ const instance = await this.deferredCreateInstance
87
135
 
88
- if (this.fsm.state === InstanceState.Creating) {
89
- await this._instance
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
- if (this.fsm.state === InstanceState.Idle) {
93
- this.fsm.send('use')
146
+ function addUser(): void {
147
+ self.#users++
94
148
  }
95
149
 
96
- assert(this.fsm.state === InstanceState.Using, 'The instance state should be using')
97
- const instance = await this._instance
98
- try {
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
- if (
110
- this.fsm.state === InstanceState.Creating ||
111
- this.fsm.state === InstanceState.Using
112
- ) {
113
- await firstValueFrom(
114
- this.fsm.observeStateChanges().pipe(
115
- filter(state => state.newState === InstanceState.Idle)
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
- if (this.fsm.state === InstanceState.Idle) {
121
- this.fsm.send('destroy')
122
- // 如果destroyed过程报错, 则程序崩溃, 这是预期行为.
123
- await this.destroyInstance?.(await this._instance)
124
- this.fsm.send('destroyed')
125
- } else if (this.fsm.state === InstanceState.Destroying) {
126
- await firstValueFrom(
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/errors'
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 = 'running'
55
- , Destroying = 'destroying'
56
- , Destroyed = '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 = new FiniteStateMachine<PoolState, PoolEvent>(
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
- this.maxInstances = options.maxInstances ?? Infinity
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.length) {
171
+ if (isntEmptyArray(candidateItems)) {
172
+ // 找到负载最低(用户量最少)的项目.
118
173
  return candidateItems.reduce((previous, current) => {
119
- if (current.instance.users < previous.instance.users) {
120
- return current
121
- } else {
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
- for (const item of this.items) {
185
- await item.instance.destroy()
186
- }
237
+ await each(this.items, item => item.instance.destroy())
187
238
  this.items.clear()
188
239
 
189
- let deferred: Deferred<IPoolItem<T>> | undefined
190
- while (deferred = this.waitingUsers.dequeue()) {
191
- deferred.reject(new UnavailablePool())
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')