extra-pool 0.1.1 → 0.1.3

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/lib/pool.js CHANGED
@@ -75,7 +75,7 @@ class Pool {
75
75
  async function useItem(item) {
76
76
  if (item.cancelScheduledDeletion) {
77
77
  item.cancelScheduledDeletion();
78
- delete item.cancelScheduledDeletion;
78
+ item.cancelScheduledDeletion = undefined;
79
79
  }
80
80
  try {
81
81
  return await item.instance.use(fn);
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.1",
3
+ "version": "0.1.3",
4
4
  "description": "A library that helps you create object/thread/connection pools",
5
5
  "keywords": [
6
6
  "pool",
@@ -9,7 +9,8 @@
9
9
  "connection"
10
10
  ],
11
11
  "files": [
12
- "lib"
12
+ "lib",
13
+ "src"
13
14
  ],
14
15
  "main": "lib/index.js",
15
16
  "types": "lib/index.d.ts",
@@ -37,7 +38,6 @@
37
38
  }
38
39
  },
39
40
  "devDependencies": {
40
- "@blackglory/jest-matchers": "^0.5.0",
41
41
  "@commitlint/cli": "^17.3.0",
42
42
  "@commitlint/config-conventional": "^17.3.0",
43
43
  "@types/jest": "^29.2.3",
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './pool'
@@ -0,0 +1,133 @@
1
+ import { go, assert, Awaitable } from '@blackglory/prelude'
2
+ import { Deferred } from 'extra-promise'
3
+ import { ObservableFiniteStateMachine } from 'extra-fsm'
4
+ import { firstValueFrom } from 'rxjs'
5
+ import { filter } from 'rxjs/operators'
6
+
7
+ export enum InstanceState {
8
+ Creating = 'creating'
9
+ , Idle = 'idle'
10
+ , Using = 'using'
11
+ , Destroying = 'destroying'
12
+ , Destroyed = 'destroyed'
13
+ }
14
+
15
+ type InstanceEvent =
16
+ | 'created'
17
+ | 'use'
18
+ | 'idle'
19
+ | 'destroy'
20
+ | 'destroyed'
21
+
22
+ const instanceSchema = {
23
+ [InstanceState.Creating]: {
24
+ created: InstanceState.Idle
25
+ }
26
+ , [InstanceState.Idle]: {
27
+ use: InstanceState.Using
28
+ , destroy: InstanceState.Destroying
29
+ }
30
+ , [InstanceState.Using]: {
31
+ idle: InstanceState.Idle
32
+ }
33
+ , [InstanceState.Destroying]: {
34
+ destroyed: InstanceState.Destroyed
35
+ }
36
+ , [InstanceState.Destroyed]: {}
37
+ }
38
+
39
+ export class Instance<T> {
40
+ private fsm: ObservableFiniteStateMachine<InstanceState, InstanceEvent>
41
+ private _users = 0
42
+ readonly _instance: Deferred<T>
43
+
44
+ get users(): number {
45
+ return this._users
46
+ }
47
+
48
+ constructor(
49
+ createInstance: () => Awaitable<T>
50
+ , 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
+ )
57
+
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)
65
+ }
66
+ })
67
+ }
68
+
69
+ async waitForCreated(): Promise<void> {
70
+ await this._instance
71
+ }
72
+
73
+ getState(): InstanceState {
74
+ return this.fsm.state
75
+ }
76
+
77
+ async use<U>(fn: (instance: T) => Awaitable<U>): Promise<U> {
78
+ // 不要尝试将此处的代码改编成switch管道, 很难正确编写.
79
+
80
+ assert(
81
+ this.fsm.state !== InstanceState.Destroying &&
82
+ this.fsm.state !== InstanceState.Destroyed
83
+ , 'The instance is not available'
84
+ )
85
+
86
+ this._users++
87
+
88
+ if (this.fsm.state === InstanceState.Creating) {
89
+ await this._instance
90
+ }
91
+
92
+ if (this.fsm.state === InstanceState.Idle) {
93
+ this.fsm.send('use')
94
+ }
95
+
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')
104
+ }
105
+ }
106
+ }
107
+
108
+ 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)
116
+ )
117
+ )
118
+ }
119
+
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
+ )
130
+ )
131
+ }
132
+ }
133
+ }
package/src/pool.ts ADDED
@@ -0,0 +1,194 @@
1
+ import { CustomError } from '@blackglory/errors'
2
+ import { go, Awaitable } from '@blackglory/prelude'
3
+ import { Queue } from '@blackglory/structures'
4
+ import { FiniteStateMachine } from 'extra-fsm'
5
+ import { Deferred } from 'extra-promise'
6
+ import { toArray, filter } from 'iterable-operator'
7
+ import { setTimeout } from 'extra-timers'
8
+ import { Instance, InstanceState } from './instance'
9
+
10
+ interface IPoolOptions<T> {
11
+ create: () => Awaitable<T>
12
+
13
+ /**
14
+ * 该函数用于销毁实例.
15
+ * 它是可选的, 有一些实例可能不需要销毁或不需要手动销毁.
16
+ */
17
+ destroy?: (value: T) => Awaitable<void>
18
+
19
+ /**
20
+ * 池中实例的最高数量, 默认为Infinity.
21
+ */
22
+ maxInstances?: number
23
+
24
+ /**
25
+ * 池中实例的最低数量, 默认为0.
26
+ * 注意, 非零值不意味着最低数量的实例会与池一同被创建,
27
+ * 该值的主要作用是防止空闲实例被全部销毁(导致下次使用时需要重新创建实例).
28
+ */
29
+ minInstances?: number
30
+
31
+ /**
32
+ * 空闲实例的存活时间(毫秒).
33
+ * 默认为0, 空闲实例会被立即销毁.
34
+ */
35
+ idleTimeout?: number
36
+
37
+ /**
38
+ * 每个实例的并发数.
39
+ * 默认为1, 每个实例只能同时有一个用户.
40
+ */
41
+ concurrencyPerInstance?: number
42
+ }
43
+
44
+ interface IPoolItem<T> {
45
+ instance: Instance<T>
46
+
47
+ /**
48
+ * 空闲实例会在idleTimeout之后被删除, 该函数用于取消预定的删除操作.
49
+ */
50
+ cancelScheduledDeletion?: () => void
51
+ }
52
+
53
+ enum PoolState {
54
+ Running = 'running'
55
+ , Destroying = 'destroying'
56
+ , Destroyed = 'destroyed'
57
+ }
58
+
59
+ type PoolEvent =
60
+ | 'destroy'
61
+ | 'destroyed'
62
+
63
+ const poolSchema = {
64
+ [PoolState.Running]: {
65
+ destroy: PoolState.Destroying
66
+ }
67
+ , [PoolState.Destroying]: {
68
+ destroyed: PoolState.Destroyed
69
+ }
70
+ , [PoolState.Destroyed]: {}
71
+ }
72
+
73
+ export class Pool<T> {
74
+ private createInstance: () => Awaitable<T>
75
+ private destroyInstance?: (value: T) => Awaitable<void>
76
+ private fsm = new FiniteStateMachine<PoolState, PoolEvent>(
77
+ poolSchema
78
+ , PoolState.Running
79
+ )
80
+ private waitingUsers: Queue<Deferred<IPoolItem<T>>> = new Queue()
81
+ private items: Set<IPoolItem<T>> = new Set()
82
+ private maxInstances: number
83
+ private minInstances: number
84
+ private idleTimeout: number
85
+ private concurrencyPerInstance: number
86
+
87
+ get size(): number {
88
+ return this.items.size
89
+ }
90
+
91
+ constructor(options: IPoolOptions<T>) {
92
+ this.createInstance = options.create
93
+ this.destroyInstance = options.destroy
94
+ this.maxInstances = options.maxInstances ?? Infinity
95
+ this.minInstances = options.minInstances ?? 0
96
+ this.idleTimeout = options.idleTimeout ?? 0
97
+ this.concurrencyPerInstance = options.concurrencyPerInstance ?? 1
98
+ }
99
+
100
+ /**
101
+ * 如果所有实例都不空闲, 该函数会通过返回Promise来阻塞使用者, 直到有空闲的实例.
102
+ * 函数的使用者应该尊重阻塞, 否则会意外制造大量非必要的Promise.
103
+ */
104
+ async use<U>(fn: (instance: T) => Awaitable<U>): Promise<U> {
105
+ const self = this
106
+
107
+ const item = go(() => {
108
+ const candidateItems = toArray(filter(
109
+ this.items
110
+ , item => item.instance.users < this.concurrencyPerInstance
111
+ ))
112
+
113
+ if (candidateItems.length) {
114
+ return candidateItems.reduce((previous, current) => {
115
+ if (current.instance.users < previous.instance.users) {
116
+ return current
117
+ } else {
118
+ return previous
119
+ }
120
+ })
121
+ }
122
+ })
123
+
124
+ if (item) {
125
+ return await useItem(item)
126
+ } else {
127
+ if (this.items.size < this.maxInstances) {
128
+ const instance = new Instance(this.createInstance, this.destroyInstance)
129
+ const item: IPoolItem<T> = { instance }
130
+ this.items.add(item)
131
+ return await useItem(item)
132
+ } else {
133
+ const waitingUser = new Deferred<IPoolItem<T>>()
134
+ this.waitingUsers.enqueue(waitingUser)
135
+ const item = await waitingUser
136
+ return await useItem(item)
137
+ }
138
+ }
139
+
140
+ async function useItem(item: IPoolItem<T>): Promise<U> {
141
+ // 由于使用该实例, 取消其预定的删除动作.
142
+ if (item.cancelScheduledDeletion) {
143
+ item.cancelScheduledDeletion()
144
+ item.cancelScheduledDeletion = undefined
145
+ }
146
+
147
+ try {
148
+ return await item.instance.use(fn)
149
+ } finally {
150
+ const waitingUser = self.waitingUsers.dequeue()
151
+ if (waitingUser) {
152
+ waitingUser.resolve(item)
153
+ } else {
154
+ if (
155
+ item.instance.users === 0 &&
156
+ self.items.size > self.minInstances
157
+ ) {
158
+ if (self.idleTimeout > 0) {
159
+ item.cancelScheduledDeletion = setTimeout(
160
+ self.idleTimeout
161
+ , deleteInstance
162
+ )
163
+ } else {
164
+ await deleteInstance()
165
+ }
166
+ }
167
+ }
168
+ }
169
+
170
+ async function deleteInstance(): Promise<void> {
171
+ self.items.delete(item)
172
+ await item.instance.destroy()
173
+ }
174
+ }
175
+ }
176
+
177
+ async destroy(): Promise<void> {
178
+ this.fsm.send('destroy')
179
+
180
+ for (const item of this.items) {
181
+ await item.instance.destroy()
182
+ }
183
+ this.items.clear()
184
+
185
+ let deferred: Deferred<IPoolItem<T>> | undefined
186
+ while (deferred = this.waitingUsers.dequeue()) {
187
+ deferred.reject(new UnavailablePool())
188
+ }
189
+
190
+ this.fsm.send('destroyed')
191
+ }
192
+ }
193
+
194
+ export class UnavailablePool extends CustomError {}