effect 3.2.9 → 3.3.1
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/Redacted/package.json +6 -0
- package/dist/cjs/Chunk.js +16 -6
- package/dist/cjs/Chunk.js.map +1 -1
- package/dist/cjs/Config.js +9 -1
- package/dist/cjs/Config.js.map +1 -1
- package/dist/cjs/Either.js +3 -2
- package/dist/cjs/Either.js.map +1 -1
- package/dist/cjs/Iterable.js +15 -2
- package/dist/cjs/Iterable.js.map +1 -1
- package/dist/cjs/Layer.js +11 -1
- package/dist/cjs/Layer.js.map +1 -1
- package/dist/cjs/Option.js +8 -2
- package/dist/cjs/Option.js.map +1 -1
- package/dist/cjs/Pool.js +26 -0
- package/dist/cjs/Pool.js.map +1 -1
- package/dist/cjs/Predicate.js +92 -2
- package/dist/cjs/Predicate.js.map +1 -1
- package/dist/cjs/Redacted.js +114 -0
- package/dist/cjs/Redacted.js.map +1 -0
- package/dist/cjs/STM.js.map +1 -1
- package/dist/cjs/Secret.js +7 -0
- package/dist/cjs/Secret.js.map +1 -1
- package/dist/cjs/Stream.js +32 -1
- package/dist/cjs/Stream.js.map +1 -1
- package/dist/cjs/Struct.js.map +1 -1
- package/dist/cjs/Tuple.js +15 -1
- package/dist/cjs/Tuple.js.map +1 -1
- package/dist/cjs/Utils.js.map +1 -1
- package/dist/cjs/index.js +4 -2
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/internal/config.js +9 -2
- package/dist/cjs/internal/config.js.map +1 -1
- package/dist/cjs/internal/layer.js +14 -2
- package/dist/cjs/internal/layer.js.map +1 -1
- package/dist/cjs/internal/pool.js +206 -235
- package/dist/cjs/internal/pool.js.map +1 -1
- package/dist/cjs/internal/redacted.js +87 -0
- package/dist/cjs/internal/redacted.js.map +1 -0
- package/dist/cjs/internal/secret.js +40 -23
- package/dist/cjs/internal/secret.js.map +1 -1
- package/dist/cjs/internal/stm/stm.js +2 -1
- package/dist/cjs/internal/stm/stm.js.map +1 -1
- package/dist/cjs/internal/stream.js +15 -7
- package/dist/cjs/internal/stream.js.map +1 -1
- package/dist/cjs/internal/version.js +1 -1
- package/dist/dts/Chunk.d.ts +11 -1
- package/dist/dts/Chunk.d.ts.map +1 -1
- package/dist/dts/Config.d.ts +10 -1
- package/dist/dts/Config.d.ts.map +1 -1
- package/dist/dts/Either.d.ts.map +1 -1
- package/dist/dts/Iterable.d.ts +7 -0
- package/dist/dts/Iterable.d.ts.map +1 -1
- package/dist/dts/Layer.d.ts +20 -0
- package/dist/dts/Layer.d.ts.map +1 -1
- package/dist/dts/Option.d.ts.map +1 -1
- package/dist/dts/Pool.d.ts +31 -0
- package/dist/dts/Pool.d.ts.map +1 -1
- package/dist/dts/Predicate.d.ts +99 -2
- package/dist/dts/Predicate.d.ts.map +1 -1
- package/dist/dts/Redacted.d.ts +122 -0
- package/dist/dts/Redacted.d.ts.map +1 -0
- package/dist/dts/STM.d.ts +4 -1
- package/dist/dts/STM.d.ts.map +1 -1
- package/dist/dts/Secret.d.ts +14 -1
- package/dist/dts/Secret.d.ts.map +1 -1
- package/dist/dts/Stream.d.ts +61 -4
- package/dist/dts/Stream.d.ts.map +1 -1
- package/dist/dts/Struct.d.ts +7 -7
- package/dist/dts/Struct.d.ts.map +1 -1
- package/dist/dts/Tuple.d.ts +51 -0
- package/dist/dts/Tuple.d.ts.map +1 -1
- package/dist/dts/Types.d.ts +46 -0
- package/dist/dts/Types.d.ts.map +1 -1
- package/dist/dts/Utils.d.ts +6 -5
- package/dist/dts/Utils.d.ts.map +1 -1
- package/dist/dts/index.d.ts +10 -0
- package/dist/dts/index.d.ts.map +1 -1
- package/dist/dts/internal/layer.d.ts +15 -1
- package/dist/dts/internal/layer.d.ts.map +1 -1
- package/dist/dts/internal/redacted.d.ts +2 -0
- package/dist/dts/internal/redacted.d.ts.map +1 -0
- package/dist/dts/internal/stm/stm.d.ts.map +1 -1
- package/dist/dts/internal/stream.d.ts +1 -0
- package/dist/dts/internal/stream.d.ts.map +1 -1
- package/dist/esm/Chunk.js +16 -5
- package/dist/esm/Chunk.js.map +1 -1
- package/dist/esm/Config.js +8 -0
- package/dist/esm/Config.js.map +1 -1
- package/dist/esm/Either.js +3 -2
- package/dist/esm/Either.js.map +1 -1
- package/dist/esm/Iterable.js +12 -0
- package/dist/esm/Iterable.js.map +1 -1
- package/dist/esm/Layer.js +10 -0
- package/dist/esm/Layer.js.map +1 -1
- package/dist/esm/Option.js +8 -2
- package/dist/esm/Option.js.map +1 -1
- package/dist/esm/Pool.js +26 -0
- package/dist/esm/Pool.js.map +1 -1
- package/dist/esm/Predicate.js +91 -1
- package/dist/esm/Predicate.js.map +1 -1
- package/dist/esm/Redacted.js +82 -0
- package/dist/esm/Redacted.js.map +1 -0
- package/dist/esm/STM.js.map +1 -1
- package/dist/esm/Secret.js +7 -0
- package/dist/esm/Secret.js.map +1 -1
- package/dist/esm/Stream.js +31 -0
- package/dist/esm/Stream.js.map +1 -1
- package/dist/esm/Struct.js.map +1 -1
- package/dist/esm/Tuple.js +51 -0
- package/dist/esm/Tuple.js.map +1 -1
- package/dist/esm/Utils.js.map +1 -1
- package/dist/esm/index.js +10 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/internal/config.js +7 -1
- package/dist/esm/internal/config.js.map +1 -1
- package/dist/esm/internal/layer.js +12 -0
- package/dist/esm/internal/layer.js.map +1 -1
- package/dist/esm/internal/pool.js +205 -235
- package/dist/esm/internal/pool.js.map +1 -1
- package/dist/esm/internal/redacted.js +52 -0
- package/dist/esm/internal/redacted.js.map +1 -0
- package/dist/esm/internal/secret.js +39 -22
- package/dist/esm/internal/secret.js.map +1 -1
- package/dist/esm/internal/stm/stm.js +2 -1
- package/dist/esm/internal/stm/stm.js.map +1 -1
- package/dist/esm/internal/stream.js +13 -4
- package/dist/esm/internal/stream.js.map +1 -1
- package/dist/esm/internal/version.js +1 -1
- package/package.json +9 -1
- package/src/Chunk.ts +17 -5
- package/src/Config.ts +11 -1
- package/src/Either.ts +6 -4
- package/src/Iterable.ts +13 -0
- package/src/Layer.ts +22 -0
- package/src/Option.ts +8 -2
- package/src/Pool.ts +39 -6
- package/src/Predicate.ts +102 -3
- package/src/Redacted.ts +133 -0
- package/src/STM.ts +7 -2
- package/src/Secret.ts +14 -1
- package/src/Stream.ts +67 -7
- package/src/Struct.ts +8 -13
- package/src/Tuple.ts +53 -0
- package/src/Types.ts +48 -0
- package/src/Utils.ts +9 -6
- package/src/index.ts +11 -0
- package/src/internal/config.ts +13 -2
- package/src/internal/layer.ts +63 -0
- package/src/internal/pool.ts +320 -447
- package/src/internal/redacted.ts +69 -0
- package/src/internal/secret.ts +39 -28
- package/src/internal/stm/stm.ts +4 -1
- package/src/internal/stream.ts +111 -50
- package/src/internal/version.ts +1 -1
package/src/internal/pool.ts
CHANGED
|
@@ -1,31 +1,23 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import type { Cause } from "effect/Cause"
|
|
2
2
|
import * as Context from "../Context.js"
|
|
3
3
|
import * as Duration from "../Duration.js"
|
|
4
|
-
import type
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import * as Hash from "../Hash.js"
|
|
9
|
-
import * as HashSet from "../HashSet.js"
|
|
4
|
+
import type { Effect, Semaphore } from "../Effect.js"
|
|
5
|
+
import type { Exit } from "../Exit.js"
|
|
6
|
+
import { dual, identity } from "../Function.js"
|
|
7
|
+
import * as Iterable from "../Iterable.js"
|
|
10
8
|
import { pipeArguments } from "../Pipeable.js"
|
|
11
|
-
import type
|
|
9
|
+
import type { Pool, PoolTypeId as PoolTypeId_ } from "../Pool.js"
|
|
12
10
|
import { hasProperty } from "../Predicate.js"
|
|
13
|
-
import type
|
|
14
|
-
import
|
|
15
|
-
import type * as Scope from "../Scope.js"
|
|
16
|
-
import * as effect from "./core-effect.js"
|
|
11
|
+
import type { Scope } from "../Scope.js"
|
|
12
|
+
import * as coreEffect from "./core-effect.js"
|
|
17
13
|
import * as core from "./core.js"
|
|
14
|
+
import * as defaultServices from "./defaultServices.js"
|
|
15
|
+
import * as circular from "./effect/circular.js"
|
|
18
16
|
import * as fiberRuntime from "./fiberRuntime.js"
|
|
19
|
-
import * as
|
|
20
|
-
import * as ref from "./ref.js"
|
|
17
|
+
import * as internalQueue from "./queue.js"
|
|
21
18
|
|
|
22
19
|
/** @internal */
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
/** @internal */
|
|
26
|
-
export const PoolTypeId: Pool.PoolTypeId = Symbol.for(
|
|
27
|
-
PoolSymbolKey
|
|
28
|
-
) as Pool.PoolTypeId
|
|
20
|
+
export const PoolTypeId: PoolTypeId_ = Symbol.for("effect/Pool") as PoolTypeId_
|
|
29
21
|
|
|
30
22
|
const poolVariance = {
|
|
31
23
|
/* c8 ignore next */
|
|
@@ -34,467 +26,348 @@ const poolVariance = {
|
|
|
34
26
|
_A: (_: any) => _
|
|
35
27
|
}
|
|
36
28
|
|
|
37
|
-
|
|
29
|
+
/** @internal */
|
|
30
|
+
export const isPool = (u: unknown): u is Pool<unknown, unknown> => hasProperty(u, PoolTypeId)
|
|
31
|
+
|
|
32
|
+
/** @internal */
|
|
33
|
+
export const makeWith = <A, E, R>(options: {
|
|
34
|
+
readonly acquire: Effect<A, E, R>
|
|
35
|
+
readonly min: number
|
|
36
|
+
readonly max: number
|
|
37
|
+
readonly concurrency?: number | undefined
|
|
38
|
+
readonly targetUtilization?: number | undefined
|
|
39
|
+
readonly strategy: Strategy<A, E>
|
|
40
|
+
}): Effect<Pool<A, E>, never, Scope | R> =>
|
|
41
|
+
core.uninterruptibleMask((restore) =>
|
|
42
|
+
core.flatMap(core.context<R | Scope>(), (context) => {
|
|
43
|
+
const scope = Context.get(context, fiberRuntime.scopeTag)
|
|
44
|
+
const acquire = core.mapInputContext(
|
|
45
|
+
options.acquire,
|
|
46
|
+
(input) => Context.merge(context, input)
|
|
47
|
+
) as Effect<
|
|
48
|
+
A,
|
|
49
|
+
E,
|
|
50
|
+
Scope
|
|
51
|
+
>
|
|
52
|
+
const pool = new PoolImpl<A, E>(
|
|
53
|
+
acquire,
|
|
54
|
+
options.concurrency ?? 1,
|
|
55
|
+
options.min,
|
|
56
|
+
options.max,
|
|
57
|
+
options.strategy,
|
|
58
|
+
Math.min(Math.max(options.targetUtilization ?? 1, 0.1), 1)
|
|
59
|
+
)
|
|
60
|
+
const initialize = core.tap(fiberRuntime.forkDaemon(restore(pool.resize)), (fiber) =>
|
|
61
|
+
scope.addFinalizer(() => core.interruptFiber(fiber)))
|
|
62
|
+
const runStrategy = core.tap(fiberRuntime.forkDaemon(restore(options.strategy.run(pool))), (fiber) =>
|
|
63
|
+
scope.addFinalizer(() =>
|
|
64
|
+
core.interruptFiber(fiber)
|
|
65
|
+
))
|
|
66
|
+
return core.succeed(pool).pipe(
|
|
67
|
+
core.zipLeft(scope.addFinalizer(() =>
|
|
68
|
+
pool.shutdown
|
|
69
|
+
)),
|
|
70
|
+
core.zipLeft(initialize),
|
|
71
|
+
core.zipLeft(runStrategy)
|
|
72
|
+
)
|
|
73
|
+
})
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
/** @internal */
|
|
77
|
+
export const make = <A, E, R>(options: {
|
|
78
|
+
readonly acquire: Effect<A, E, R>
|
|
38
79
|
readonly size: number
|
|
39
|
-
readonly
|
|
40
|
-
|
|
80
|
+
readonly concurrency?: number | undefined
|
|
81
|
+
readonly targetUtilization?: number | undefined
|
|
82
|
+
}): Effect<Pool<A, E>, never, R | Scope> =>
|
|
83
|
+
makeWith({ ...options, min: options.size, max: options.size, strategy: strategyNoop() })
|
|
41
84
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
readonly
|
|
45
|
-
|
|
85
|
+
/** @internal */
|
|
86
|
+
export const makeWithTTL = <A, E, R>(options: {
|
|
87
|
+
readonly acquire: Effect<A, E, R>
|
|
88
|
+
readonly min: number
|
|
89
|
+
readonly max: number
|
|
90
|
+
readonly concurrency?: number | undefined
|
|
91
|
+
readonly targetUtilization?: number | undefined
|
|
92
|
+
readonly timeToLive: Duration.DurationInput
|
|
93
|
+
readonly timeToLiveStrategy?: "creation" | "usage" | undefined
|
|
94
|
+
}): Effect<Pool<A, E>, never, R | Scope> =>
|
|
95
|
+
core.flatMap(
|
|
96
|
+
options.timeToLiveStrategy === "creation" ?
|
|
97
|
+
strategyCreationTTL<A, E>(options.timeToLive) :
|
|
98
|
+
strategyUsageTTL<A, E>(options.timeToLive),
|
|
99
|
+
(strategy) => makeWith({ ...options, strategy })
|
|
100
|
+
)
|
|
46
101
|
|
|
47
|
-
/**
|
|
48
|
-
|
|
49
|
-
* not being used should shrink down to the minimum pool size.
|
|
50
|
-
*/
|
|
51
|
-
interface Strategy<S, R, E, A> {
|
|
52
|
-
/**
|
|
53
|
-
* Describes how the initial state of the strategy should be allocated.
|
|
54
|
-
*/
|
|
55
|
-
initial(): Effect.Effect<S, never, R>
|
|
56
|
-
/**
|
|
57
|
-
* Describes how the state of the strategy should be updated when an item is
|
|
58
|
-
* added to the pool or returned to the pool.
|
|
59
|
-
*/
|
|
60
|
-
track(state: S, attempted: Exit.Exit<A, E>): Effect.Effect<void>
|
|
61
|
-
/**
|
|
62
|
-
* Describes how excess items that are not being used should shrink down.
|
|
63
|
-
*/
|
|
64
|
-
run(
|
|
65
|
-
state: S,
|
|
66
|
-
getExcess: Effect.Effect<number>,
|
|
67
|
-
shrink: Effect.Effect<void>
|
|
68
|
-
): Effect.Effect<void>
|
|
69
|
-
}
|
|
102
|
+
/** @internal */
|
|
103
|
+
export const get = <A, E>(self: Pool<A, E>): Effect<A, E, Scope> => self.get
|
|
70
104
|
|
|
71
|
-
/**
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
return core.void
|
|
82
|
-
}
|
|
83
|
-
run(): Effect.Effect<void> {
|
|
84
|
-
return core.void
|
|
85
|
-
}
|
|
105
|
+
/** @internal */
|
|
106
|
+
export const invalidate: {
|
|
107
|
+
<A>(item: A): <E>(self: Pool<A, E>) => Effect<void>
|
|
108
|
+
<A, E>(self: Pool<A, E>, item: A): Effect<void>
|
|
109
|
+
} = dual(2, <A, E>(self: Pool<A, E>, item: A): Effect<void> => self.invalidate(item))
|
|
110
|
+
|
|
111
|
+
interface PoolItem<A, E> {
|
|
112
|
+
readonly exit: Exit<A, E>
|
|
113
|
+
finalizer: Effect<void>
|
|
114
|
+
refCount: number
|
|
86
115
|
}
|
|
87
116
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
*/
|
|
92
|
-
class TimeToLiveStrategy implements Strategy<readonly [Clock.Clock, Ref.Ref<number>], never, never, never> {
|
|
93
|
-
constructor(readonly timeToLive: Duration.Duration) {}
|
|
94
|
-
initial(): Effect.Effect<readonly [Clock.Clock, Ref.Ref<number>]> {
|
|
95
|
-
return core.flatMap(effect.clock, (clock) =>
|
|
96
|
-
core.flatMap(clock.currentTimeMillis, (now) =>
|
|
97
|
-
core.map(
|
|
98
|
-
ref.make(now),
|
|
99
|
-
(ref) => [clock, ref] as const
|
|
100
|
-
)))
|
|
101
|
-
}
|
|
102
|
-
track(state: readonly [Clock.Clock, Ref.Ref<number>]): Effect.Effect<void> {
|
|
103
|
-
return core.asVoid(core.flatMap(
|
|
104
|
-
state[0].currentTimeMillis,
|
|
105
|
-
(now) => ref.set(state[1], now)
|
|
106
|
-
))
|
|
107
|
-
}
|
|
108
|
-
run(
|
|
109
|
-
state: readonly [Clock.Clock, Ref.Ref<number>],
|
|
110
|
-
getExcess: Effect.Effect<number>,
|
|
111
|
-
shrink: Effect.Effect<void>
|
|
112
|
-
): Effect.Effect<void> {
|
|
113
|
-
return core.flatMap(getExcess, (excess) =>
|
|
114
|
-
excess <= 0
|
|
115
|
-
? core.zipRight(
|
|
116
|
-
state[0].sleep(this.timeToLive),
|
|
117
|
-
this.run(state, getExcess, shrink)
|
|
118
|
-
)
|
|
119
|
-
: pipe(
|
|
120
|
-
core.zipWith(
|
|
121
|
-
ref.get(state[1]),
|
|
122
|
-
state[0].currentTimeMillis,
|
|
123
|
-
(start, end) => end - start
|
|
124
|
-
),
|
|
125
|
-
core.flatMap((duration) => {
|
|
126
|
-
if (duration >= Duration.toMillis(this.timeToLive)) {
|
|
127
|
-
return core.zipRight(shrink, this.run(state, getExcess, shrink))
|
|
128
|
-
} else {
|
|
129
|
-
return core.zipRight(state[0].sleep(this.timeToLive), this.run(state, getExcess, shrink))
|
|
130
|
-
}
|
|
131
|
-
})
|
|
132
|
-
))
|
|
133
|
-
}
|
|
117
|
+
interface Strategy<A, E> {
|
|
118
|
+
readonly run: (pool: PoolImpl<A, E>) => Effect<void>
|
|
119
|
+
readonly onAcquire: (item: PoolItem<A, E>) => Effect<void>
|
|
134
120
|
}
|
|
135
121
|
|
|
136
|
-
class PoolImpl<
|
|
137
|
-
readonly [PoolTypeId]
|
|
122
|
+
class PoolImpl<A, E> implements Pool<A, E> {
|
|
123
|
+
readonly [PoolTypeId]: Pool.Variance<A, E>[PoolTypeId_]
|
|
124
|
+
|
|
125
|
+
isShuttingDown = false
|
|
126
|
+
readonly semaphore: Semaphore
|
|
127
|
+
readonly items = new Set<PoolItem<A, E>>()
|
|
128
|
+
readonly available = new Set<PoolItem<A, E>>()
|
|
129
|
+
readonly invalidated = new Set<PoolItem<A, E>>()
|
|
130
|
+
waiters = 0
|
|
131
|
+
|
|
138
132
|
constructor(
|
|
139
|
-
readonly
|
|
140
|
-
readonly
|
|
141
|
-
readonly
|
|
142
|
-
readonly
|
|
143
|
-
readonly
|
|
144
|
-
readonly
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
133
|
+
readonly acquire: Effect<A, E, Scope>,
|
|
134
|
+
readonly concurrency: number,
|
|
135
|
+
readonly minSize: number,
|
|
136
|
+
readonly maxSize: number,
|
|
137
|
+
readonly strategy: Strategy<A, E>,
|
|
138
|
+
readonly targetUtilization: number
|
|
139
|
+
) {
|
|
140
|
+
this[PoolTypeId] = poolVariance
|
|
141
|
+
this.semaphore = circular.unsafeMakeSemaphore(concurrency * maxSize)
|
|
142
|
+
}
|
|
148
143
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
144
|
+
readonly allocate: Effect<PoolItem<A, E>> = core.acquireUseRelease(
|
|
145
|
+
fiberRuntime.scopeMake(),
|
|
146
|
+
(scope) =>
|
|
147
|
+
this.acquire.pipe(
|
|
148
|
+
fiberRuntime.scopeExtend(scope),
|
|
149
|
+
core.exit,
|
|
150
|
+
core.flatMap((exit) => {
|
|
151
|
+
const item: PoolItem<A, E> = {
|
|
152
|
+
exit,
|
|
153
|
+
finalizer: core.catchAllCause(scope.close(exit), reportUnhandledError),
|
|
154
|
+
refCount: 0
|
|
155
|
+
}
|
|
156
|
+
this.items.add(item)
|
|
157
|
+
this.available.add(item)
|
|
158
|
+
return core.as(
|
|
159
|
+
exit._tag === "Success"
|
|
160
|
+
? this.strategy.onAcquire(item)
|
|
161
|
+
: core.zipRight(item.finalizer, this.strategy.onAcquire(item)),
|
|
162
|
+
item
|
|
163
|
+
)
|
|
164
|
+
})
|
|
165
|
+
),
|
|
166
|
+
(scope, exit) => exit._tag === "Failure" ? scope.close(exit) : core.void
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
get currentUsage() {
|
|
170
|
+
let count = this.waiters
|
|
171
|
+
for (const item of this.items) {
|
|
172
|
+
count += item.refCount
|
|
173
|
+
}
|
|
174
|
+
return count
|
|
161
175
|
}
|
|
162
176
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
Equal.equals(this.isShuttingDown, (that as PoolImpl<A, E>).isShuttingDown) &&
|
|
169
|
-
Equal.equals(this.state, (that as PoolImpl<A, E>).state) &&
|
|
170
|
-
Equal.equals(this.items, (that as PoolImpl<A, E>).items) &&
|
|
171
|
-
Equal.equals(this.invalidated, (that as PoolImpl<A, E>).invalidated) &&
|
|
172
|
-
Equal.equals(this.track, (that as PoolImpl<A, E>).track)
|
|
177
|
+
get targetSize() {
|
|
178
|
+
if (this.isShuttingDown) return 0
|
|
179
|
+
const utilization = this.currentUsage / this.targetUtilization
|
|
180
|
+
const target = Math.ceil(utilization / this.concurrency)
|
|
181
|
+
return Math.min(Math.max(this.minSize, target), this.maxSize)
|
|
173
182
|
}
|
|
174
183
|
|
|
175
|
-
|
|
176
|
-
return
|
|
184
|
+
get activeSize() {
|
|
185
|
+
return this.items.size - this.invalidated.size
|
|
177
186
|
}
|
|
178
187
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
188
|
+
readonly resizeLoop: Effect<void> = core.suspend(() => {
|
|
189
|
+
if (this.activeSize >= this.targetSize) {
|
|
190
|
+
return core.void
|
|
191
|
+
}
|
|
192
|
+
return core.zipRight(this.allocate, this.resizeLoop)
|
|
193
|
+
})
|
|
194
|
+
readonly resizeSemaphore = circular.unsafeMakeSemaphore(1)
|
|
195
|
+
readonly resize = this.resizeSemaphore.withPermits(1)(this.resizeLoop)
|
|
196
|
+
|
|
197
|
+
readonly getPoolItem: Effect<PoolItem<A, E>, never, Scope> = core.uninterruptibleMask((restore) =>
|
|
198
|
+
restore(this.semaphore.take(1)).pipe(
|
|
199
|
+
core.zipRight(fiberRuntime.scopeTag),
|
|
200
|
+
core.flatMap((scope) =>
|
|
201
|
+
core.suspend(() => {
|
|
202
|
+
this.waiters++
|
|
203
|
+
if (this.isShuttingDown) {
|
|
204
|
+
return core.interrupt
|
|
205
|
+
} else if (this.targetSize > this.activeSize) {
|
|
206
|
+
return core.zipRight(
|
|
207
|
+
restore(this.resize),
|
|
208
|
+
core.sync(() => Iterable.unsafeHead(this.available))
|
|
209
|
+
)
|
|
210
|
+
}
|
|
211
|
+
return core.succeed(Iterable.unsafeHead(this.available))
|
|
212
|
+
}).pipe(
|
|
213
|
+
fiberRuntime.ensuring(core.sync(() => this.waiters--)),
|
|
214
|
+
core.tap((item) => {
|
|
215
|
+
if (item.exit._tag === "Failure") {
|
|
216
|
+
this.items.delete(item)
|
|
217
|
+
this.invalidated.delete(item)
|
|
218
|
+
this.available.delete(item)
|
|
219
|
+
return this.semaphore.release(1)
|
|
208
220
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
] as const
|
|
221
|
+
item.refCount++
|
|
222
|
+
this.available.delete(item)
|
|
223
|
+
if (item.refCount < this.concurrency) {
|
|
224
|
+
this.available.add(item)
|
|
214
225
|
}
|
|
215
|
-
return
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
return [core.void, { ...state, size: state.size - 1 }] as const
|
|
228
|
-
}))
|
|
229
|
-
),
|
|
230
|
-
onSuccess: (item) =>
|
|
231
|
-
core.flatMap(ref.get(this.invalidated), (set) => {
|
|
232
|
-
if (pipe(set, HashSet.has(item))) {
|
|
233
|
-
return finalizeInvalid(this, attempted)
|
|
234
|
-
}
|
|
235
|
-
return pipe(
|
|
236
|
-
ref.update(this.state, (state) => ({ ...state, free: state.free + 1 })),
|
|
237
|
-
core.zipRight(queue.offer(this.items, attempted)),
|
|
238
|
-
core.zipRight(this.track(attempted.result)),
|
|
239
|
-
core.zipRight(core.whenEffect(getAndShutdown(this), ref.get(this.isShuttingDown)))
|
|
226
|
+
return scope.addFinalizer(() =>
|
|
227
|
+
core.zipRight(
|
|
228
|
+
core.suspend(() => {
|
|
229
|
+
item.refCount--
|
|
230
|
+
if (this.invalidated.has(item)) {
|
|
231
|
+
return this.invalidatePoolItem(item)
|
|
232
|
+
}
|
|
233
|
+
this.available.add(item)
|
|
234
|
+
return core.void
|
|
235
|
+
}),
|
|
236
|
+
this.semaphore.release(1)
|
|
237
|
+
)
|
|
240
238
|
)
|
|
241
|
-
})
|
|
242
|
-
})
|
|
243
|
-
|
|
244
|
-
return pipe(
|
|
245
|
-
core.uninterruptibleMask((restore) =>
|
|
246
|
-
core.tap(acquire(restore), (a) => fiberRuntime.addFinalizer((_exit) => release(a)))
|
|
247
|
-
),
|
|
248
|
-
fiberRuntime.withEarlyRelease,
|
|
249
|
-
fiberRuntime.disconnect,
|
|
250
|
-
core.flatMap(([release, attempted]) =>
|
|
251
|
-
pipe(
|
|
252
|
-
effect.when(release, () => isFailure(attempted)),
|
|
253
|
-
core.zipRight(toEffect(attempted))
|
|
254
|
-
)
|
|
255
|
-
)
|
|
256
|
-
)
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
invalidate(item: A): Effect.Effect<void> {
|
|
260
|
-
return ref.update(this.invalidated, HashSet.add(item))
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
const allocate = <A, E>(
|
|
265
|
-
self: PoolImpl<A, E>,
|
|
266
|
-
restore: <AX, EX, RX>(effect: Effect.Effect<AX, EX, RX>) => Effect.Effect<AX, EX, RX>
|
|
267
|
-
): Effect.Effect<unknown> =>
|
|
268
|
-
core.flatMap(fiberRuntime.scopeMake(), (scope) =>
|
|
269
|
-
core.flatMap(
|
|
270
|
-
core.exit(restore(fiberRuntime.scopeExtend(self.creator, scope))),
|
|
271
|
-
(exit) =>
|
|
272
|
-
core.flatMap(
|
|
273
|
-
core.succeed<Attempted<A, E>>({
|
|
274
|
-
result: exit as Exit.Exit<A, E>,
|
|
275
|
-
finalizer: core.scopeClose(scope, core.exitSucceed(void 0))
|
|
276
239
|
}),
|
|
277
|
-
(
|
|
278
|
-
pipe(
|
|
279
|
-
queue.offer(self.items, attempted),
|
|
280
|
-
core.zipRight(self.track(attempted.result)),
|
|
281
|
-
core.zipRight(core.whenEffect(getAndShutdown(self), ref.get(self.isShuttingDown))),
|
|
282
|
-
core.as(attempted)
|
|
283
|
-
)
|
|
240
|
+
core.onInterrupt(() => this.semaphore.release(1))
|
|
284
241
|
)
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
const allocateUinterruptible = <A, E>(
|
|
288
|
-
self: PoolImpl<A, E>
|
|
289
|
-
): Effect.Effect<unknown> => core.uninterruptibleMask((restore) => allocate(self, restore))
|
|
290
|
-
|
|
291
|
-
/**
|
|
292
|
-
* Returns the number of items in the pool in excess of the minimum size.
|
|
293
|
-
*/
|
|
294
|
-
const excess = <A, E>(self: PoolImpl<A, E>): Effect.Effect<number> =>
|
|
295
|
-
core.map(ref.get(self.state), (state) => state.size - Math.min(self.min, state.free))
|
|
296
|
-
|
|
297
|
-
const finalizeInvalid = <A, E>(
|
|
298
|
-
self: PoolImpl<A, E>,
|
|
299
|
-
attempted: Attempted<A, E>
|
|
300
|
-
): Effect.Effect<unknown> =>
|
|
301
|
-
pipe(
|
|
302
|
-
forEach(attempted, (a) => ref.update(self.invalidated, HashSet.remove(a))),
|
|
303
|
-
core.zipRight(attempted.finalizer),
|
|
304
|
-
core.zipRight(
|
|
305
|
-
core.flatten(ref.modify(self.state, (state) => {
|
|
306
|
-
if (state.size <= self.min) {
|
|
307
|
-
return [allocateUinterruptible(self), { ...state, free: state.free + 1 }] as const
|
|
308
|
-
}
|
|
309
|
-
return [core.void, { ...state, size: state.size - 1 }] as const
|
|
310
|
-
}))
|
|
242
|
+
)
|
|
311
243
|
)
|
|
312
244
|
)
|
|
313
245
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
*/
|
|
318
|
-
const getAndShutdown = <A, E>(self: PoolImpl<A, E>): Effect.Effect<void> =>
|
|
319
|
-
core.flatten(ref.modify(self.state, (state) => {
|
|
320
|
-
if (state.free > 0) {
|
|
321
|
-
return [
|
|
322
|
-
core.matchCauseEffect(queue.take(self.items), {
|
|
323
|
-
onFailure: () => core.void,
|
|
324
|
-
onSuccess: (attempted) =>
|
|
325
|
-
pipe(
|
|
326
|
-
forEach(attempted, (a) => ref.update(self.invalidated, HashSet.remove(a))),
|
|
327
|
-
core.zipRight(attempted.finalizer),
|
|
328
|
-
core.zipRight(ref.update(self.state, (state) => ({ ...state, size: state.size - 1 }))),
|
|
329
|
-
core.flatMap(() => getAndShutdown(self))
|
|
330
|
-
)
|
|
331
|
-
}),
|
|
332
|
-
{ ...state, free: state.free - 1 }
|
|
333
|
-
] as const
|
|
334
|
-
}
|
|
335
|
-
if (state.size > 0) {
|
|
336
|
-
return [core.void, state] as const
|
|
337
|
-
}
|
|
338
|
-
return [queue.shutdown(self.items), { ...state, size: state.size - 1 }] as const
|
|
339
|
-
}))
|
|
340
|
-
|
|
341
|
-
/**
|
|
342
|
-
* Begins pre-allocating pool entries based on minimum pool size.
|
|
343
|
-
*/
|
|
344
|
-
const initialize = <A, E>(self: PoolImpl<A, E>): Effect.Effect<void> =>
|
|
345
|
-
fiberRuntime.replicateEffect(
|
|
346
|
-
core.uninterruptibleMask((restore) =>
|
|
347
|
-
core.flatten(ref.modify(self.state, (state) => {
|
|
348
|
-
if (state.size < self.min && state.size >= 0) {
|
|
349
|
-
return [
|
|
350
|
-
allocate(self, restore),
|
|
351
|
-
{ size: state.size + 1, free: state.free + 1 }
|
|
352
|
-
] as const
|
|
353
|
-
}
|
|
354
|
-
return [core.void, state] as const
|
|
355
|
-
}))
|
|
356
|
-
),
|
|
357
|
-
self.min,
|
|
358
|
-
{ discard: true }
|
|
246
|
+
readonly get: Effect<A, E, Scope> = core.flatMap(
|
|
247
|
+
core.suspend(() => this.isShuttingDown ? core.interrupt : this.getPoolItem),
|
|
248
|
+
(_) => _.exit
|
|
359
249
|
)
|
|
360
250
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
return [
|
|
369
|
-
pipe(
|
|
370
|
-
queue.take(self.items),
|
|
371
|
-
core.flatMap((attempted) =>
|
|
372
|
-
pipe(
|
|
373
|
-
forEach(attempted, (a) => ref.update(self.invalidated, HashSet.remove(a))),
|
|
374
|
-
core.zipRight(attempted.finalizer),
|
|
375
|
-
core.zipRight(ref.update(self.state, (state) => ({ ...state, size: state.size - 1 })))
|
|
376
|
-
)
|
|
377
|
-
)
|
|
378
|
-
),
|
|
379
|
-
{ ...state, free: state.free - 1 }
|
|
380
|
-
] as const
|
|
251
|
+
invalidate(item: A): Effect<void> {
|
|
252
|
+
return core.suspend(() => {
|
|
253
|
+
if (this.isShuttingDown) return core.void
|
|
254
|
+
for (const poolItem of this.items) {
|
|
255
|
+
if (poolItem.exit._tag === "Success" && poolItem.exit.value === item) {
|
|
256
|
+
return core.uninterruptible(this.invalidatePoolItem(poolItem))
|
|
257
|
+
}
|
|
381
258
|
}
|
|
382
|
-
return
|
|
383
|
-
})
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
const shutdown = <A, E>(self: PoolImpl<A, E>): Effect.Effect<void> =>
|
|
387
|
-
core.flatten(ref.modify(self.isShuttingDown, (down) =>
|
|
388
|
-
down
|
|
389
|
-
? [queue.awaitShutdown(self.items), true] as const
|
|
390
|
-
: [core.zipRight(getAndShutdown(self), queue.awaitShutdown(self.items)), true]))
|
|
391
|
-
|
|
392
|
-
const isFailure = <A, E>(self: Attempted<A, E>): boolean => core.exitIsFailure(self.result)
|
|
259
|
+
return core.void
|
|
260
|
+
})
|
|
261
|
+
}
|
|
393
262
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
263
|
+
invalidatePoolItem(poolItem: PoolItem<A, E>): Effect<void> {
|
|
264
|
+
return core.suspend(() => {
|
|
265
|
+
if (!this.items.has(poolItem)) {
|
|
266
|
+
return core.void
|
|
267
|
+
} else if (poolItem.refCount === 0) {
|
|
268
|
+
this.items.delete(poolItem)
|
|
269
|
+
this.available.delete(poolItem)
|
|
270
|
+
this.invalidated.delete(poolItem)
|
|
271
|
+
return core.zipRight(poolItem.finalizer, this.resize)
|
|
272
|
+
}
|
|
273
|
+
this.invalidated.add(poolItem)
|
|
274
|
+
this.available.delete(poolItem)
|
|
275
|
+
return core.void
|
|
276
|
+
})
|
|
277
|
+
}
|
|
402
278
|
|
|
403
|
-
|
|
279
|
+
get shutdown(): Effect<void> {
|
|
280
|
+
return core.suspend(() => {
|
|
281
|
+
if (this.isShuttingDown) return core.void
|
|
282
|
+
this.isShuttingDown = true
|
|
283
|
+
const size = this.items.size
|
|
284
|
+
const semaphore = circular.unsafeMakeSemaphore(size)
|
|
285
|
+
return core.forEachSequentialDiscard(this.items, (item) => {
|
|
286
|
+
if (item.refCount > 0) {
|
|
287
|
+
item.finalizer = core.zipLeft(item.finalizer, semaphore.release(1))
|
|
288
|
+
this.invalidated.add(item)
|
|
289
|
+
return semaphore.take(1)
|
|
290
|
+
}
|
|
291
|
+
this.items.delete(item)
|
|
292
|
+
this.available.delete(item)
|
|
293
|
+
this.invalidated.delete(item)
|
|
294
|
+
return item.finalizer
|
|
295
|
+
}).pipe(
|
|
296
|
+
core.zipRight(this.semaphore.releaseAll),
|
|
297
|
+
core.zipRight(semaphore.take(size))
|
|
298
|
+
)
|
|
299
|
+
})
|
|
300
|
+
}
|
|
404
301
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
* describes how a pool whose excess items are not being used will be shrunk
|
|
408
|
-
* down to the minimum size.
|
|
409
|
-
*/
|
|
410
|
-
const makeWith = <A, E, R, S, R2>(
|
|
411
|
-
options: {
|
|
412
|
-
readonly acquire: Effect.Effect<A, E, R>
|
|
413
|
-
readonly min: number
|
|
414
|
-
readonly max: number
|
|
415
|
-
readonly strategy: Strategy<S, R2, E, A>
|
|
302
|
+
pipe() {
|
|
303
|
+
return pipeArguments(this, arguments)
|
|
416
304
|
}
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
),
|
|
454
|
-
core.as<Pool.Pool<A, E>>(pool)
|
|
455
|
-
)
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const strategyNoop = <A, E>(): Strategy<A, E> => ({
|
|
308
|
+
run: (_) => core.void,
|
|
309
|
+
onAcquire: (_) => core.void
|
|
310
|
+
})
|
|
311
|
+
|
|
312
|
+
const strategyCreationTTL = <A, E>(ttl: Duration.DurationInput) =>
|
|
313
|
+
defaultServices.clockWith((clock) =>
|
|
314
|
+
core.map(internalQueue.unbounded<PoolItem<A, E>>(), (queue) => {
|
|
315
|
+
const ttlMillis = Duration.toMillis(ttl)
|
|
316
|
+
const creationTimes = new WeakMap<PoolItem<A, E>, number>()
|
|
317
|
+
return identity<Strategy<A, E>>({
|
|
318
|
+
run: (pool) => {
|
|
319
|
+
const process = (item: PoolItem<A, E>): Effect<void> =>
|
|
320
|
+
core.suspend(() => {
|
|
321
|
+
if (!pool.items.has(item) || pool.invalidated.has(item)) {
|
|
322
|
+
return core.void
|
|
323
|
+
}
|
|
324
|
+
const now = clock.unsafeCurrentTimeMillis()
|
|
325
|
+
const created = creationTimes.get(item)!
|
|
326
|
+
const remaining = ttlMillis - (now - created)
|
|
327
|
+
return remaining > 0
|
|
328
|
+
? coreEffect.delay(process(item), remaining)
|
|
329
|
+
: pool.invalidatePoolItem(item)
|
|
330
|
+
})
|
|
331
|
+
return queue.take.pipe(
|
|
332
|
+
core.tap(process),
|
|
333
|
+
coreEffect.forever
|
|
334
|
+
)
|
|
335
|
+
},
|
|
336
|
+
onAcquire: (item) =>
|
|
337
|
+
core.suspend(() => {
|
|
338
|
+
creationTimes.set(item, clock.unsafeCurrentTimeMillis())
|
|
339
|
+
return queue.offer(item)
|
|
340
|
+
})
|
|
456
341
|
})
|
|
457
|
-
)
|
|
342
|
+
})
|
|
458
343
|
)
|
|
459
344
|
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
)
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
345
|
+
const strategyUsageTTL = <A, E>(ttl: Duration.DurationInput) =>
|
|
346
|
+
core.map(internalQueue.unbounded<PoolItem<A, E>>(), (queue) => {
|
|
347
|
+
return identity<Strategy<A, E>>({
|
|
348
|
+
run: (pool) => {
|
|
349
|
+
const process: Effect<void> = core.suspend(() => {
|
|
350
|
+
const excess = pool.activeSize - pool.targetSize
|
|
351
|
+
if (excess <= 0) return core.void
|
|
352
|
+
return queue.take.pipe(
|
|
353
|
+
core.tap((item) => pool.invalidatePoolItem(item)),
|
|
354
|
+
core.zipRight(process)
|
|
355
|
+
)
|
|
356
|
+
})
|
|
357
|
+
return process.pipe(
|
|
358
|
+
coreEffect.delay(ttl),
|
|
359
|
+
coreEffect.forever
|
|
360
|
+
)
|
|
361
|
+
},
|
|
362
|
+
onAcquire: (item) => queue.offer(item)
|
|
363
|
+
})
|
|
475
364
|
})
|
|
476
365
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
}
|
|
485
|
-
): Effect.Effect<Pool.Pool<A, E>, never, R | Scope.Scope> =>
|
|
486
|
-
makeWith({
|
|
487
|
-
acquire: options.acquire,
|
|
488
|
-
min: options.min,
|
|
489
|
-
max: options.max,
|
|
490
|
-
strategy: new TimeToLiveStrategy(Duration.decode(options.timeToLive))
|
|
366
|
+
const reportUnhandledError = <E>(cause: Cause<E>) =>
|
|
367
|
+
core.withFiberRuntime<void>((fiber) => {
|
|
368
|
+
const unhandledLogLevel = fiber.getFiberRef(core.currentUnhandledErrorLogLevel)
|
|
369
|
+
if (unhandledLogLevel._tag === "Some") {
|
|
370
|
+
fiber.log("Unhandled error in pool finalizer", cause, unhandledLogLevel)
|
|
371
|
+
}
|
|
372
|
+
return core.void
|
|
491
373
|
})
|
|
492
|
-
|
|
493
|
-
/** @internal */
|
|
494
|
-
export const get = <A, E>(self: Pool.Pool<A, E>): Effect.Effect<A, E, Scope.Scope> => self.get
|
|
495
|
-
|
|
496
|
-
/** @internal */
|
|
497
|
-
export const invalidate = dual<
|
|
498
|
-
<A>(value: A) => <E>(self: Pool.Pool<A, E>) => Effect.Effect<void, never, Scope.Scope>,
|
|
499
|
-
<A, E>(self: Pool.Pool<A, E>, value: A) => Effect.Effect<void, never, Scope.Scope>
|
|
500
|
-
>(2, (self, value) => self.invalidate(value))
|