@effect/cluster 0.50.6 → 0.52.0
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/RunnerStorage/package.json +6 -0
- package/SqlRunnerStorage/package.json +6 -0
- package/dist/cjs/ClusterError.js +2 -24
- package/dist/cjs/ClusterError.js.map +1 -1
- package/dist/cjs/ClusterMetrics.js +13 -15
- package/dist/cjs/ClusterMetrics.js.map +1 -1
- package/dist/cjs/ClusterSchema.js +17 -2
- package/dist/cjs/ClusterSchema.js.map +1 -1
- package/dist/cjs/ClusterWorkflowEngine.js +50 -83
- package/dist/cjs/ClusterWorkflowEngine.js.map +1 -1
- package/dist/cjs/Entity.js +1 -13
- package/dist/cjs/Entity.js.map +1 -1
- package/dist/cjs/EntityAddress.js +9 -1
- package/dist/cjs/EntityAddress.js.map +1 -1
- package/dist/cjs/EntityId.js +7 -1
- package/dist/cjs/EntityId.js.map +1 -1
- package/dist/cjs/EntityProxy.js +1 -1
- package/dist/cjs/EntityProxy.js.map +1 -1
- package/dist/cjs/HttpRunner.js +69 -43
- package/dist/cjs/HttpRunner.js.map +1 -1
- package/dist/cjs/MessageStorage.js +64 -16
- package/dist/cjs/MessageStorage.js.map +1 -1
- package/dist/cjs/Runner.js +3 -3
- package/dist/cjs/Runner.js.map +1 -1
- package/dist/cjs/RunnerAddress.js +7 -0
- package/dist/cjs/RunnerAddress.js.map +1 -1
- package/dist/cjs/RunnerHealth.js +91 -32
- package/dist/cjs/RunnerHealth.js.map +1 -1
- package/dist/cjs/RunnerServer.js +38 -24
- package/dist/cjs/RunnerServer.js.map +1 -1
- package/dist/cjs/RunnerStorage.js +100 -0
- package/dist/cjs/RunnerStorage.js.map +1 -0
- package/dist/cjs/Runners.js +18 -22
- package/dist/cjs/Runners.js.map +1 -1
- package/dist/cjs/ShardId.js +17 -7
- package/dist/cjs/ShardId.js.map +1 -1
- package/dist/cjs/Sharding.js +444 -320
- package/dist/cjs/Sharding.js.map +1 -1
- package/dist/cjs/ShardingConfig.js +10 -14
- package/dist/cjs/ShardingConfig.js.map +1 -1
- package/dist/cjs/Snowflake.js +1 -1
- package/dist/cjs/SocketRunner.js +1 -1
- package/dist/cjs/SocketRunner.js.map +1 -1
- package/dist/cjs/SqlMessageStorage.js +22 -28
- package/dist/cjs/SqlMessageStorage.js.map +1 -1
- package/dist/cjs/SqlRunnerStorage.js +375 -0
- package/dist/cjs/SqlRunnerStorage.js.map +1 -0
- package/dist/cjs/index.js +5 -15
- package/dist/cjs/internal/entityManager.js +42 -23
- package/dist/cjs/internal/entityManager.js.map +1 -1
- package/dist/dts/ClusterError.d.ts +0 -22
- package/dist/dts/ClusterError.d.ts.map +1 -1
- package/dist/dts/ClusterMetrics.d.ts +4 -14
- package/dist/dts/ClusterMetrics.d.ts.map +1 -1
- package/dist/dts/ClusterSchema.d.ts +9 -1
- package/dist/dts/ClusterSchema.d.ts.map +1 -1
- package/dist/dts/ClusterWorkflowEngine.d.ts.map +1 -1
- package/dist/dts/Entity.d.ts +3 -14
- package/dist/dts/Entity.d.ts.map +1 -1
- package/dist/dts/EntityAddress.d.ts +11 -0
- package/dist/dts/EntityAddress.d.ts.map +1 -1
- package/dist/dts/EntityId.d.ts +5 -0
- package/dist/dts/EntityId.d.ts.map +1 -1
- package/dist/dts/EntityProxy.d.ts +5 -6
- package/dist/dts/EntityProxy.d.ts.map +1 -1
- package/dist/dts/HttpRunner.d.ts +48 -25
- package/dist/dts/HttpRunner.d.ts.map +1 -1
- package/dist/dts/MessageStorage.d.ts +13 -5
- package/dist/dts/MessageStorage.d.ts.map +1 -1
- package/dist/dts/Runner.d.ts +4 -4
- package/dist/dts/Runner.d.ts.map +1 -1
- package/dist/dts/RunnerAddress.d.ts +5 -0
- package/dist/dts/RunnerAddress.d.ts.map +1 -1
- package/dist/dts/RunnerHealth.d.ts +24 -16
- package/dist/dts/RunnerHealth.d.ts.map +1 -1
- package/dist/dts/RunnerServer.d.ts +5 -4
- package/dist/dts/RunnerServer.d.ts.map +1 -1
- package/dist/dts/{ShardStorage.d.ts → RunnerStorage.d.ts} +41 -54
- package/dist/dts/RunnerStorage.d.ts.map +1 -0
- package/dist/dts/Runners.d.ts +15 -11
- package/dist/dts/Runners.d.ts.map +1 -1
- package/dist/dts/ShardId.d.ts +1 -1
- package/dist/dts/ShardId.d.ts.map +1 -1
- package/dist/dts/Sharding.d.ts +20 -10
- package/dist/dts/Sharding.d.ts.map +1 -1
- package/dist/dts/ShardingConfig.d.ts +40 -14
- package/dist/dts/ShardingConfig.d.ts.map +1 -1
- package/dist/dts/SocketRunner.d.ts +4 -3
- package/dist/dts/SocketRunner.d.ts.map +1 -1
- package/dist/dts/SqlMessageStorage.d.ts +2 -3
- package/dist/dts/SqlMessageStorage.d.ts.map +1 -1
- package/dist/dts/SqlRunnerStorage.d.ts +40 -0
- package/dist/dts/SqlRunnerStorage.d.ts.map +1 -0
- package/dist/dts/index.d.ts +4 -24
- package/dist/dts/index.d.ts.map +1 -1
- package/dist/esm/ClusterError.js +0 -21
- package/dist/esm/ClusterError.js.map +1 -1
- package/dist/esm/ClusterMetrics.js +12 -14
- package/dist/esm/ClusterMetrics.js.map +1 -1
- package/dist/esm/ClusterSchema.js +17 -2
- package/dist/esm/ClusterSchema.js.map +1 -1
- package/dist/esm/ClusterWorkflowEngine.js +50 -83
- package/dist/esm/ClusterWorkflowEngine.js.map +1 -1
- package/dist/esm/Entity.js +0 -12
- package/dist/esm/Entity.js.map +1 -1
- package/dist/esm/EntityAddress.js +7 -0
- package/dist/esm/EntityAddress.js.map +1 -1
- package/dist/esm/EntityId.js +5 -0
- package/dist/esm/EntityId.js.map +1 -1
- package/dist/esm/EntityProxy.js +2 -2
- package/dist/esm/EntityProxy.js.map +1 -1
- package/dist/esm/HttpRunner.js +62 -39
- package/dist/esm/HttpRunner.js.map +1 -1
- package/dist/esm/MessageStorage.js +65 -17
- package/dist/esm/MessageStorage.js.map +1 -1
- package/dist/esm/Runner.js +3 -3
- package/dist/esm/Runner.js.map +1 -1
- package/dist/esm/RunnerAddress.js +7 -0
- package/dist/esm/RunnerAddress.js.map +1 -1
- package/dist/esm/RunnerHealth.js +88 -30
- package/dist/esm/RunnerHealth.js.map +1 -1
- package/dist/esm/RunnerServer.js +38 -24
- package/dist/esm/RunnerServer.js.map +1 -1
- package/dist/esm/RunnerStorage.js +90 -0
- package/dist/esm/RunnerStorage.js.map +1 -0
- package/dist/esm/Runners.js +19 -23
- package/dist/esm/Runners.js.map +1 -1
- package/dist/esm/ShardId.js +16 -6
- package/dist/esm/ShardId.js.map +1 -1
- package/dist/esm/Sharding.js +447 -323
- package/dist/esm/Sharding.js.map +1 -1
- package/dist/esm/ShardingConfig.js +10 -14
- package/dist/esm/ShardingConfig.js.map +1 -1
- package/dist/esm/Snowflake.js +1 -1
- package/dist/esm/SocketRunner.js +1 -1
- package/dist/esm/SocketRunner.js.map +1 -1
- package/dist/esm/SqlMessageStorage.js +22 -28
- package/dist/esm/SqlMessageStorage.js.map +1 -1
- package/dist/esm/SqlRunnerStorage.js +366 -0
- package/dist/esm/SqlRunnerStorage.js.map +1 -0
- package/dist/esm/index.js +4 -24
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/internal/entityManager.js +41 -22
- package/dist/esm/internal/entityManager.js.map +1 -1
- package/package.json +20 -60
- package/src/ClusterError.ts +0 -24
- package/src/ClusterMetrics.ts +12 -16
- package/src/ClusterSchema.ts +17 -2
- package/src/ClusterWorkflowEngine.ts +48 -80
- package/src/Entity.ts +3 -21
- package/src/EntityAddress.ts +10 -0
- package/src/EntityId.ts +6 -0
- package/src/EntityProxy.ts +10 -10
- package/src/HttpRunner.ts +132 -67
- package/src/MessageStorage.ts +89 -24
- package/src/Runner.ts +4 -4
- package/src/RunnerAddress.ts +8 -0
- package/src/RunnerHealth.ts +119 -56
- package/src/RunnerServer.ts +64 -47
- package/src/RunnerStorage.ts +218 -0
- package/src/Runners.ts +32 -45
- package/src/ShardId.ts +14 -3
- package/src/Sharding.ts +561 -417
- package/src/ShardingConfig.ts +39 -31
- package/src/Snowflake.ts +1 -1
- package/src/SocketRunner.ts +6 -4
- package/src/SqlMessageStorage.ts +28 -30
- package/src/SqlRunnerStorage.ts +537 -0
- package/src/index.ts +4 -29
- package/src/internal/entityManager.ts +45 -29
- package/HttpCommon/package.json +0 -6
- package/HttpShardManager/package.json +0 -6
- package/ShardManager/package.json +0 -6
- package/ShardStorage/package.json +0 -6
- package/SocketShardManager/package.json +0 -6
- package/SqlShardStorage/package.json +0 -6
- package/SynchronizedClock/package.json +0 -6
- package/dist/cjs/HttpCommon.js +0 -48
- package/dist/cjs/HttpCommon.js.map +0 -1
- package/dist/cjs/HttpShardManager.js +0 -139
- package/dist/cjs/HttpShardManager.js.map +0 -1
- package/dist/cjs/ShardManager.js +0 -549
- package/dist/cjs/ShardManager.js.map +0 -1
- package/dist/cjs/ShardStorage.js +0 -151
- package/dist/cjs/ShardStorage.js.map +0 -1
- package/dist/cjs/SocketShardManager.js +0 -32
- package/dist/cjs/SocketShardManager.js.map +0 -1
- package/dist/cjs/SqlShardStorage.js +0 -253
- package/dist/cjs/SqlShardStorage.js.map +0 -1
- package/dist/cjs/SynchronizedClock.js +0 -65
- package/dist/cjs/SynchronizedClock.js.map +0 -1
- package/dist/cjs/internal/shardManager.js +0 -353
- package/dist/cjs/internal/shardManager.js.map +0 -1
- package/dist/dts/HttpCommon.d.ts +0 -25
- package/dist/dts/HttpCommon.d.ts.map +0 -1
- package/dist/dts/HttpShardManager.d.ts +0 -119
- package/dist/dts/HttpShardManager.d.ts.map +0 -1
- package/dist/dts/ShardManager.d.ts +0 -459
- package/dist/dts/ShardManager.d.ts.map +0 -1
- package/dist/dts/ShardStorage.d.ts.map +0 -1
- package/dist/dts/SocketShardManager.d.ts +0 -17
- package/dist/dts/SocketShardManager.d.ts.map +0 -1
- package/dist/dts/SqlShardStorage.d.ts +0 -38
- package/dist/dts/SqlShardStorage.d.ts.map +0 -1
- package/dist/dts/SynchronizedClock.d.ts +0 -19
- package/dist/dts/SynchronizedClock.d.ts.map +0 -1
- package/dist/dts/internal/shardManager.d.ts +0 -2
- package/dist/dts/internal/shardManager.d.ts.map +0 -1
- package/dist/esm/HttpCommon.js +0 -38
- package/dist/esm/HttpCommon.js.map +0 -1
- package/dist/esm/HttpShardManager.js +0 -128
- package/dist/esm/HttpShardManager.js.map +0 -1
- package/dist/esm/ShardManager.js +0 -535
- package/dist/esm/ShardManager.js.map +0 -1
- package/dist/esm/ShardStorage.js +0 -141
- package/dist/esm/ShardStorage.js.map +0 -1
- package/dist/esm/SocketShardManager.js +0 -24
- package/dist/esm/SocketShardManager.js.map +0 -1
- package/dist/esm/SqlShardStorage.js +0 -244
- package/dist/esm/SqlShardStorage.js.map +0 -1
- package/dist/esm/SynchronizedClock.js +0 -57
- package/dist/esm/SynchronizedClock.js.map +0 -1
- package/dist/esm/internal/shardManager.js +0 -342
- package/dist/esm/internal/shardManager.js.map +0 -1
- package/src/HttpCommon.ts +0 -73
- package/src/HttpShardManager.ts +0 -273
- package/src/ShardManager.ts +0 -823
- package/src/ShardStorage.ts +0 -297
- package/src/SocketShardManager.ts +0 -48
- package/src/SqlShardStorage.ts +0 -329
- package/src/SynchronizedClock.ts +0 -82
- package/src/internal/shardManager.ts +0 -412
package/src/SynchronizedClock.ts
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @since 1.0.0
|
|
3
|
-
*/
|
|
4
|
-
import * as Clock from "effect/Clock"
|
|
5
|
-
import * as Duration from "effect/Duration"
|
|
6
|
-
import * as Effect from "effect/Effect"
|
|
7
|
-
import * as Layer from "effect/Layer"
|
|
8
|
-
import * as Schedule from "effect/Schedule"
|
|
9
|
-
import type { Scope } from "effect/Scope"
|
|
10
|
-
import { ShardManagerClient } from "./ShardManager.js"
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* @since 1.0.0
|
|
14
|
-
* @category Constructors
|
|
15
|
-
*/
|
|
16
|
-
export const make: (getRemoteTime: Effect.Effect<number, never, never>) => Effect.Effect<
|
|
17
|
-
Clock.Clock,
|
|
18
|
-
never,
|
|
19
|
-
Scope
|
|
20
|
-
> = Effect.fnUntraced(function*(getRemoteTime) {
|
|
21
|
-
const clock = yield* Effect.clock
|
|
22
|
-
|
|
23
|
-
let driftMillis = 0
|
|
24
|
-
let driftNanos = BigInt(0)
|
|
25
|
-
|
|
26
|
-
yield* getRemoteTime.pipe(
|
|
27
|
-
Effect.timed,
|
|
28
|
-
Effect.map(([duration, shardManagerTime]) => {
|
|
29
|
-
const halfTrip = Duration.unsafeDivide(duration, 2)
|
|
30
|
-
shardManagerTime = shardManagerTime + Duration.toMillis(halfTrip) + 1
|
|
31
|
-
const selfTime = clock.unsafeCurrentTimeMillis()
|
|
32
|
-
return shardManagerTime - selfTime
|
|
33
|
-
}),
|
|
34
|
-
Effect.replicateEffect(5),
|
|
35
|
-
Effect.flatMap((drifts) => {
|
|
36
|
-
drifts.sort()
|
|
37
|
-
const drift = (driftMillis + drifts[2]) / 2
|
|
38
|
-
driftMillis = Math.round(drift)
|
|
39
|
-
driftNanos = BigInt(Math.round(drift * 1_000_000))
|
|
40
|
-
return Effect.logDebug("Current drift", driftMillis)
|
|
41
|
-
}),
|
|
42
|
-
Effect.andThen(Effect.sleep(Duration.minutes(5))),
|
|
43
|
-
Effect.forever,
|
|
44
|
-
Effect.sandbox,
|
|
45
|
-
Effect.retry(Schedule.spaced(Duration.minutes(1))),
|
|
46
|
-
Effect.annotateLogs({
|
|
47
|
-
package: "@effect/cluster",
|
|
48
|
-
service: "SynchronizedClock"
|
|
49
|
-
}),
|
|
50
|
-
Effect.forkScoped
|
|
51
|
-
)
|
|
52
|
-
|
|
53
|
-
function unsafeCurrentTimeMillis() {
|
|
54
|
-
return clock.unsafeCurrentTimeMillis() + driftMillis
|
|
55
|
-
}
|
|
56
|
-
function unsafeCurrentTimeNanos() {
|
|
57
|
-
return clock.unsafeCurrentTimeNanos() + driftNanos
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return Clock.Clock.of({
|
|
61
|
-
[Clock.ClockTypeId]: Clock.ClockTypeId,
|
|
62
|
-
sleep: clock.sleep,
|
|
63
|
-
unsafeCurrentTimeMillis,
|
|
64
|
-
unsafeCurrentTimeNanos,
|
|
65
|
-
currentTimeMillis: Effect.sync(unsafeCurrentTimeMillis),
|
|
66
|
-
currentTimeNanos: Effect.sync(unsafeCurrentTimeNanos)
|
|
67
|
-
})
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* @since 1.0.0
|
|
72
|
-
* @category Layers
|
|
73
|
-
*/
|
|
74
|
-
export const layer: Layer.Layer<
|
|
75
|
-
never,
|
|
76
|
-
never,
|
|
77
|
-
ShardManagerClient
|
|
78
|
-
> = Layer.unwrapScoped(Effect.gen(function*() {
|
|
79
|
-
const shardManager = yield* ShardManagerClient
|
|
80
|
-
const clock = yield* make(shardManager.getTime)
|
|
81
|
-
return Layer.setClock(clock)
|
|
82
|
-
}))
|
|
@@ -1,412 +0,0 @@
|
|
|
1
|
-
import * as Arr from "effect/Array"
|
|
2
|
-
import * as Clock from "effect/Clock"
|
|
3
|
-
import * as Effect from "effect/Effect"
|
|
4
|
-
import { constFalse } from "effect/Function"
|
|
5
|
-
import * as MutableHashMap from "effect/MutableHashMap"
|
|
6
|
-
import * as MutableHashSet from "effect/MutableHashSet"
|
|
7
|
-
import * as Option from "effect/Option"
|
|
8
|
-
import type { Runner } from "../Runner.js"
|
|
9
|
-
import type { RunnerAddress } from "../RunnerAddress.js"
|
|
10
|
-
import { RunnerHealth } from "../RunnerHealth.js"
|
|
11
|
-
import { ShardId } from "../ShardId.js"
|
|
12
|
-
import { ShardStorage } from "../ShardStorage.js"
|
|
13
|
-
|
|
14
|
-
/** @internal */
|
|
15
|
-
export class State {
|
|
16
|
-
static fromStorage = Effect.fnUntraced(function*(
|
|
17
|
-
shardsPerGroup: number
|
|
18
|
-
) {
|
|
19
|
-
const storage = yield* ShardStorage
|
|
20
|
-
const runnerHealth = yield* RunnerHealth
|
|
21
|
-
|
|
22
|
-
// Fetch registered runners and shard assignments from cluster storage
|
|
23
|
-
const storedRunners = yield* storage.getRunners
|
|
24
|
-
const storedAssignments = yield* storage.getAssignments
|
|
25
|
-
|
|
26
|
-
// Determine which runners are still alive
|
|
27
|
-
const deadRunners = Arr.empty<Runner>()
|
|
28
|
-
const aliveRunners = MutableHashMap.empty<RunnerAddress, Runner>()
|
|
29
|
-
yield* Effect.forEach(storedRunners, ([address, runner]) =>
|
|
30
|
-
Effect.map(runnerHealth.isAlive(address), (isAlive) => {
|
|
31
|
-
if (isAlive) {
|
|
32
|
-
MutableHashMap.set(aliveRunners, address, runner)
|
|
33
|
-
} else {
|
|
34
|
-
deadRunners.push(runner)
|
|
35
|
-
}
|
|
36
|
-
}), { concurrency: "unbounded", discard: true })
|
|
37
|
-
if (deadRunners.length > 0) {
|
|
38
|
-
yield* Effect.logWarning("Ignoring runners that are no longer considered alive:", deadRunners)
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Determine which shards remain unassigned to a runner
|
|
42
|
-
const assignedShards = MutableHashMap.empty<ShardId, RunnerAddress>()
|
|
43
|
-
const invalidAssignments = Arr.empty<[ShardId, RunnerAddress]>()
|
|
44
|
-
for (const [shard, address] of storedAssignments) {
|
|
45
|
-
if (Option.isSome(address) && MutableHashMap.has(aliveRunners, address.value)) {
|
|
46
|
-
MutableHashMap.set(assignedShards, shard, address.value)
|
|
47
|
-
} else if (Option.isSome(address)) {
|
|
48
|
-
invalidAssignments.push([shard, address.value])
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
if (invalidAssignments.length > 0) {
|
|
52
|
-
yield* Effect.logWarning(
|
|
53
|
-
"Ignoring shard assignments for runners that are no longer considered alive: ",
|
|
54
|
-
invalidAssignments
|
|
55
|
-
)
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Construct the initial state
|
|
59
|
-
const now = yield* Clock.currentTimeMillis
|
|
60
|
-
const allRunners = MutableHashMap.empty<RunnerAddress, RunnerWithMetadata>()
|
|
61
|
-
const runnerState = new Map<string, MutableHashMap.MutableHashMap<RunnerAddress, RunnerWithMetadata>>()
|
|
62
|
-
for (const [address, runner] of aliveRunners) {
|
|
63
|
-
const withMetadata = RunnerWithMetadata({ runner, registeredAt: now })
|
|
64
|
-
MutableHashMap.set(allRunners, address, withMetadata)
|
|
65
|
-
for (const group of runner.groups) {
|
|
66
|
-
let groupMap = runnerState.get(group)
|
|
67
|
-
if (!groupMap) {
|
|
68
|
-
groupMap = MutableHashMap.empty<RunnerAddress, RunnerWithMetadata>()
|
|
69
|
-
runnerState.set(group, groupMap)
|
|
70
|
-
}
|
|
71
|
-
MutableHashMap.set(groupMap, address, withMetadata)
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const shardState = new Map<string, Map<number, Option.Option<RunnerAddress>>>()
|
|
76
|
-
for (const group of runnerState.keys()) {
|
|
77
|
-
const groupMap = new Map<number, Option.Option<RunnerAddress>>()
|
|
78
|
-
shardState.set(group, groupMap)
|
|
79
|
-
for (let n = 1; n <= shardsPerGroup; n++) {
|
|
80
|
-
const shardId = new ShardId({ group, id: n })
|
|
81
|
-
groupMap.set(n, MutableHashMap.get(assignedShards, shardId))
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return new State(allRunners, runnerState, shardState, shardsPerGroup)
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
constructor(
|
|
89
|
-
readonly allRunners: MutableHashMap.MutableHashMap<RunnerAddress, RunnerWithMetadata>,
|
|
90
|
-
readonly runners: Map<string, MutableHashMap.MutableHashMap<RunnerAddress, RunnerWithMetadata>>,
|
|
91
|
-
readonly shards: Map<string, Map<number, Option.Option<RunnerAddress>>>,
|
|
92
|
-
readonly shardsPerGroup: number
|
|
93
|
-
) {
|
|
94
|
-
this.assignments = MutableHashMap.empty<ShardId, Option.Option<RunnerAddress>>()
|
|
95
|
-
this.perRunner = new Map<string, MutableHashMap.MutableHashMap<RunnerAddress, Set<number>>>()
|
|
96
|
-
|
|
97
|
-
for (const [address, meta] of this.allRunners) {
|
|
98
|
-
for (const group of meta.runner.groups) {
|
|
99
|
-
let runnerMap = this.perRunner.get(group)
|
|
100
|
-
if (!runnerMap) {
|
|
101
|
-
runnerMap = MutableHashMap.empty<RunnerAddress, Set<number>>()
|
|
102
|
-
this.perRunner.set(group, runnerMap)
|
|
103
|
-
}
|
|
104
|
-
MutableHashMap.set(runnerMap, address, new Set())
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
for (const [group, groupMap] of this.shards) {
|
|
109
|
-
const perRunnerMap = this.perRunner.get(group)!
|
|
110
|
-
for (const [id, address_] of groupMap) {
|
|
111
|
-
const address = Option.filter(address_, (addr) => MutableHashMap.has(this.allRunners, addr))
|
|
112
|
-
MutableHashMap.set(this.assignments, new ShardId({ group, id }), address)
|
|
113
|
-
if (Option.isSome(address)) {
|
|
114
|
-
Option.getOrUndefined(MutableHashMap.get(perRunnerMap, address.value))?.add(id)
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
readonly assignments: MutableHashMap.MutableHashMap<ShardId, Option.Option<RunnerAddress>>
|
|
121
|
-
readonly perRunner: Map<string, MutableHashMap.MutableHashMap<RunnerAddress, Set<number>>>
|
|
122
|
-
|
|
123
|
-
addGroup(group: string): void {
|
|
124
|
-
this.runners.set(group, MutableHashMap.empty<RunnerAddress, RunnerWithMetadata>())
|
|
125
|
-
const shardMap = new Map<number, Option.Option<RunnerAddress>>()
|
|
126
|
-
this.shards.set(group, shardMap)
|
|
127
|
-
for (let n = 1; n <= this.shardsPerGroup; n++) {
|
|
128
|
-
shardMap.set(n, Option.none())
|
|
129
|
-
MutableHashMap.set(this.assignments, new ShardId({ group, id: n }), Option.none())
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
const perRunnerMap = MutableHashMap.empty<RunnerAddress, Set<number>>()
|
|
133
|
-
this.perRunner.set(group, perRunnerMap)
|
|
134
|
-
for (const [address] of this.allRunners) {
|
|
135
|
-
MutableHashMap.set(perRunnerMap, address, new Set())
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
addAssignments(
|
|
140
|
-
shards: Iterable<ShardId>,
|
|
141
|
-
address: Option.Option<RunnerAddress>
|
|
142
|
-
) {
|
|
143
|
-
for (const shardId of shards) {
|
|
144
|
-
const currentAddress = Option.flatten(MutableHashMap.get(this.assignments, shardId))
|
|
145
|
-
MutableHashMap.set(this.assignments, shardId, address)
|
|
146
|
-
this.shards.get(shardId.group)?.set(shardId.id, address)
|
|
147
|
-
|
|
148
|
-
const perRunner = this.perRunner.get(shardId.group)!
|
|
149
|
-
if (Option.isSome(currentAddress)) {
|
|
150
|
-
Option.getOrUndefined(MutableHashMap.get(perRunner, currentAddress.value))?.delete(shardId.id)
|
|
151
|
-
}
|
|
152
|
-
if (Option.isSome(address)) {
|
|
153
|
-
Option.getOrUndefined(MutableHashMap.get(perRunner, address.value))?.add(shardId.id)
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
addRunner(runner: Runner, registeredAt: number): void {
|
|
159
|
-
const withMetadata = RunnerWithMetadata({ runner, registeredAt })
|
|
160
|
-
MutableHashMap.set(this.allRunners, runner.address, withMetadata)
|
|
161
|
-
for (const group of runner.groups) {
|
|
162
|
-
if (!this.runners.has(group)) {
|
|
163
|
-
this.addGroup(group)
|
|
164
|
-
}
|
|
165
|
-
const groupMap = this.runners.get(group)!
|
|
166
|
-
MutableHashMap.set(groupMap, runner.address, withMetadata)
|
|
167
|
-
const perRunner = this.perRunner.get(group)!
|
|
168
|
-
MutableHashMap.set(perRunner, runner.address, new Set())
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
removeRunner(address: RunnerAddress): void {
|
|
173
|
-
MutableHashMap.remove(this.allRunners, address)
|
|
174
|
-
for (const group of this.runners.keys()) {
|
|
175
|
-
const groupMap = this.runners.get(group)!
|
|
176
|
-
MutableHashMap.remove(groupMap, address)
|
|
177
|
-
|
|
178
|
-
const perRunner = this.perRunner.get(group)!
|
|
179
|
-
MutableHashMap.remove(perRunner, address)
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
get maxVersion(): Option.Option<number> {
|
|
184
|
-
if (MutableHashMap.size(this.allRunners) === 0) return Option.none()
|
|
185
|
-
let version: number | undefined = undefined
|
|
186
|
-
for (const [, meta] of this.allRunners) {
|
|
187
|
-
if (version === undefined || meta.runner.version > version) {
|
|
188
|
-
version = meta.runner.version
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
return Option.some(version!)
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
allRunnersHaveVersion(version: Option.Option<number>): boolean {
|
|
195
|
-
return version.pipe(
|
|
196
|
-
Option.map((max) => Arr.every(this.runnerVersions, (version) => version === max)),
|
|
197
|
-
Option.getOrElse(constFalse)
|
|
198
|
-
)
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
get shardStats(): {
|
|
202
|
-
readonly perRunner: Map<string, number>
|
|
203
|
-
readonly unassigned: number
|
|
204
|
-
} {
|
|
205
|
-
const perRunner = new Map<string, number>()
|
|
206
|
-
let unassigned = 0
|
|
207
|
-
for (const [, address] of this.assignments) {
|
|
208
|
-
if (Option.isNone(address)) {
|
|
209
|
-
unassigned++
|
|
210
|
-
continue
|
|
211
|
-
}
|
|
212
|
-
const runner = address.value.toString()
|
|
213
|
-
const count = perRunner.get(runner) ?? 0
|
|
214
|
-
perRunner.set(runner, count + 1)
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
return { perRunner, unassigned }
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
shardsPerRunner(group: string): MutableHashMap.MutableHashMap<RunnerAddress, Set<number>> {
|
|
221
|
-
const shards = MutableHashMap.empty<RunnerAddress, Set<number>>()
|
|
222
|
-
const perRunner = this.perRunner.get(group)
|
|
223
|
-
if (!perRunner || MutableHashMap.isEmpty(perRunner)) return shards
|
|
224
|
-
|
|
225
|
-
for (const [address, shardSet] of perRunner) {
|
|
226
|
-
MutableHashMap.set(shards, address, new Set(shardSet))
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
return shards
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
averageShardsPerRunner(group: string): number {
|
|
233
|
-
const runnerCount = MutableHashMap.size(this.runners.get(group) ?? MutableHashMap.empty())
|
|
234
|
-
const shardGroup = this.shards.get(group) ?? new Map()
|
|
235
|
-
return runnerCount > 0 ? shardGroup.size / runnerCount : 0
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
get allUnassignedShards(): Array<ShardId> {
|
|
239
|
-
const unassigned: Array<ShardId> = []
|
|
240
|
-
for (const [shardId, address] of this.assignments) {
|
|
241
|
-
if (Option.isNone(address)) {
|
|
242
|
-
unassigned.push(shardId)
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
return unassigned
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
unassignedShards(group: string): Array<number> {
|
|
249
|
-
const shardIds: Array<number> = []
|
|
250
|
-
const assignments = this.shards.get(group)!
|
|
251
|
-
for (const [shard, address] of assignments) {
|
|
252
|
-
if (Option.isNone(address)) {
|
|
253
|
-
shardIds.push(shard)
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
return shardIds
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
private get runnerVersions(): Array<number> {
|
|
260
|
-
const runnerVersions: Array<number> = []
|
|
261
|
-
for (const [, meta] of this.allRunners) {
|
|
262
|
-
runnerVersions.push(meta.runner.version)
|
|
263
|
-
}
|
|
264
|
-
return runnerVersions
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
/** @internal */
|
|
269
|
-
export interface RunnerWithMetadata {
|
|
270
|
-
readonly runner: Runner
|
|
271
|
-
readonly registeredAt: number
|
|
272
|
-
}
|
|
273
|
-
/** @internal */
|
|
274
|
-
export const RunnerWithMetadata = (runner: RunnerWithMetadata): RunnerWithMetadata => runner
|
|
275
|
-
|
|
276
|
-
/** @internal */
|
|
277
|
-
export function decideAssignmentsForShards(state: State, group: string): readonly [
|
|
278
|
-
assignments: MutableHashMap.MutableHashMap<RunnerAddress, Set<number>>,
|
|
279
|
-
unassignments: MutableHashMap.MutableHashMap<RunnerAddress, Set<number>>,
|
|
280
|
-
changes: MutableHashSet.MutableHashSet<RunnerAddress>
|
|
281
|
-
] {
|
|
282
|
-
const shardsPerRunner = state.shardsPerRunner(group)
|
|
283
|
-
const maxVersion = state.maxVersion
|
|
284
|
-
const shardsToRebalance = state.unassignedShards(group)
|
|
285
|
-
|
|
286
|
-
if (state.allRunnersHaveVersion(maxVersion)) {
|
|
287
|
-
const averageShardsPerRunner = state.averageShardsPerRunner(group)
|
|
288
|
-
MutableHashMap.forEach(shardsPerRunner, (shards) => {
|
|
289
|
-
const extraShards = Math.max(0, shards.size - averageShardsPerRunner)
|
|
290
|
-
const iter = shards.values()
|
|
291
|
-
for (let i = 0; i < extraShards; i++) {
|
|
292
|
-
const shard = iter.next()
|
|
293
|
-
if (shard.done) break
|
|
294
|
-
shardsToRebalance.push(shard.value)
|
|
295
|
-
}
|
|
296
|
-
})
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
return pickNewRunners(shardsToRebalance, state, group, shardsPerRunner, maxVersion)
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
function pickNewRunners(
|
|
303
|
-
shardsToRebalance: ReadonlyArray<number>,
|
|
304
|
-
state: State,
|
|
305
|
-
group: string,
|
|
306
|
-
shardsPerRunner: MutableHashMap.MutableHashMap<RunnerAddress, Set<number>>,
|
|
307
|
-
maybeMaxVersion = state.maxVersion
|
|
308
|
-
): readonly [
|
|
309
|
-
assignments: MutableHashMap.MutableHashMap<RunnerAddress, Set<number>>,
|
|
310
|
-
unassignments: MutableHashMap.MutableHashMap<RunnerAddress, Set<number>>,
|
|
311
|
-
changes: MutableHashSet.MutableHashSet<RunnerAddress>
|
|
312
|
-
] {
|
|
313
|
-
const addressAssignments = MutableHashMap.empty<RunnerAddress, Set<number>>()
|
|
314
|
-
const unassignments = MutableHashMap.empty<RunnerAddress, Set<number>>()
|
|
315
|
-
const changes = MutableHashSet.empty<RunnerAddress>()
|
|
316
|
-
|
|
317
|
-
if (Option.isNone(maybeMaxVersion)) {
|
|
318
|
-
return [addressAssignments, unassignments, changes]
|
|
319
|
-
}
|
|
320
|
-
const maxVersion = maybeMaxVersion.value
|
|
321
|
-
|
|
322
|
-
const runnerGroup = state.runners.get(group)!
|
|
323
|
-
const shardsGroup = state.shards.get(group)!
|
|
324
|
-
|
|
325
|
-
for (const shardId of shardsToRebalance) {
|
|
326
|
-
// Find the runner with the fewest assigned shards
|
|
327
|
-
let candidate: RunnerAddress | undefined
|
|
328
|
-
let candidateShards: Set<number> | undefined
|
|
329
|
-
|
|
330
|
-
for (const [address, shards] of shardsPerRunner) {
|
|
331
|
-
// Keep only runners with the maximum version
|
|
332
|
-
const maybeRunnerMeta = MutableHashMap.get(runnerGroup, address)
|
|
333
|
-
if (Option.isNone(maybeRunnerMeta)) continue
|
|
334
|
-
const runnerMeta = maybeRunnerMeta.value
|
|
335
|
-
if (runnerMeta.runner.version !== maxVersion) continue
|
|
336
|
-
|
|
337
|
-
// Do not assign to a runner that has unassignments in the same rebalance
|
|
338
|
-
if (MutableHashMap.has(unassignments, address)) continue
|
|
339
|
-
|
|
340
|
-
if (candidate === undefined || shards.size < candidateShards!.size) {
|
|
341
|
-
candidate = address
|
|
342
|
-
candidateShards = shards
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
if (!candidate || !candidateShards) break
|
|
346
|
-
|
|
347
|
-
// If the old runner is the same as the new runner, do nothing
|
|
348
|
-
const oldRunner = Option.getOrUndefined(shardsGroup.get(shardId) ?? Option.none())
|
|
349
|
-
if (oldRunner && oldRunner.toString() === candidate.toString()) {
|
|
350
|
-
continue
|
|
351
|
-
}
|
|
352
|
-
const oldShards = oldRunner && Option.getOrUndefined(MutableHashMap.get(shardsPerRunner, oldRunner))
|
|
353
|
-
|
|
354
|
-
// If the new runner has one less, as many, or more shards than the
|
|
355
|
-
// old runner, do not change anything
|
|
356
|
-
if (oldShards && candidateShards.size + 1 >= oldShards.size) continue
|
|
357
|
-
|
|
358
|
-
// Otherwise create a new assignment
|
|
359
|
-
MutableHashMap.modifyAt(
|
|
360
|
-
addressAssignments,
|
|
361
|
-
candidate,
|
|
362
|
-
Option.match({
|
|
363
|
-
onNone: () => Option.some(new Set([shardId])),
|
|
364
|
-
onSome: (shards) => {
|
|
365
|
-
shards.add(shardId)
|
|
366
|
-
return Option.some(shards)
|
|
367
|
-
}
|
|
368
|
-
})
|
|
369
|
-
)
|
|
370
|
-
if (oldRunner) {
|
|
371
|
-
MutableHashMap.modifyAt(
|
|
372
|
-
unassignments,
|
|
373
|
-
oldRunner,
|
|
374
|
-
Option.match({
|
|
375
|
-
onNone: () => Option.some(new Set([shardId])),
|
|
376
|
-
onSome: (shards) => {
|
|
377
|
-
shards.add(shardId)
|
|
378
|
-
return Option.some(shards)
|
|
379
|
-
}
|
|
380
|
-
})
|
|
381
|
-
)
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
// Move the shard to the new runner
|
|
385
|
-
candidateShards.add(shardId)
|
|
386
|
-
if (oldShards) {
|
|
387
|
-
oldShards.delete(shardId)
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
// Track changes
|
|
391
|
-
MutableHashSet.add(changes, candidate)
|
|
392
|
-
if (oldRunner) MutableHashSet.add(changes, oldRunner)
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
return [addressAssignments, unassignments, changes]
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
/** @internal */
|
|
399
|
-
export const addAllNested = <K, V>(
|
|
400
|
-
self: MutableHashMap.MutableHashMap<K, MutableHashSet.MutableHashSet<V>>,
|
|
401
|
-
key: K,
|
|
402
|
-
values: Iterable<V>
|
|
403
|
-
) => {
|
|
404
|
-
const oset = MutableHashMap.get(self, key)
|
|
405
|
-
if (Option.isSome(oset)) {
|
|
406
|
-
for (const value of values) {
|
|
407
|
-
MutableHashSet.add(oset.value, value)
|
|
408
|
-
}
|
|
409
|
-
} else {
|
|
410
|
-
MutableHashMap.set(self, key, MutableHashSet.fromIterable(values))
|
|
411
|
-
}
|
|
412
|
-
}
|