@effect/cluster 0.38.9 → 0.38.11
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/dist/cjs/ClusterWorkflowEngine.js +42 -6
- package/dist/cjs/ClusterWorkflowEngine.js.map +1 -1
- package/dist/cjs/ShardManager.js +22 -21
- package/dist/cjs/ShardManager.js.map +1 -1
- package/dist/cjs/internal/shardManager.js +17 -0
- package/dist/cjs/internal/shardManager.js.map +1 -1
- package/dist/dts/ClusterWorkflowEngine.d.ts.map +1 -1
- package/dist/dts/ShardManager.d.ts.map +1 -1
- package/dist/esm/ClusterWorkflowEngine.js +42 -6
- package/dist/esm/ClusterWorkflowEngine.js.map +1 -1
- package/dist/esm/ShardManager.js +22 -21
- package/dist/esm/ShardManager.js.map +1 -1
- package/dist/esm/internal/shardManager.js +17 -0
- package/dist/esm/internal/shardManager.js.map +1 -1
- package/package.json +6 -6
- package/src/ClusterWorkflowEngine.ts +81 -39
- package/src/ShardManager.ts +24 -37
- package/src/internal/shardManager.ts +19 -0
@@ -141,7 +141,15 @@ export const make = Effect.gen(function*() {
|
|
141
141
|
times: 3,
|
142
142
|
schedule: Schedule.exponential(250)
|
143
143
|
}),
|
144
|
-
Effect.orDie
|
144
|
+
Effect.orDie,
|
145
|
+
(effect, { activity, attempt, executionId }) =>
|
146
|
+
Effect.withSpan(effect, "WorkflowEngine.resetActivityAttempt", {
|
147
|
+
attributes: {
|
148
|
+
name: activity.name,
|
149
|
+
executionId,
|
150
|
+
attempt
|
151
|
+
}
|
152
|
+
})
|
145
153
|
)
|
146
154
|
|
147
155
|
const clearClock = Effect.fnUntraced(function*(options: {
|
@@ -289,40 +297,59 @@ export const make = Effect.gen(function*() {
|
|
289
297
|
times: 3,
|
290
298
|
schedule: Schedule.exponential(250)
|
291
299
|
}),
|
292
|
-
Effect.orDie
|
300
|
+
Effect.orDie,
|
301
|
+
(effect, workflow, executionId) =>
|
302
|
+
Effect.withSpan(effect, "WorkflowEngine.interrupt", {
|
303
|
+
attributes: {
|
304
|
+
name: workflow.name,
|
305
|
+
executionId
|
306
|
+
}
|
307
|
+
})
|
293
308
|
),
|
294
309
|
|
295
|
-
activityExecute: Effect.fnUntraced(
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
while (true) {
|
307
|
-
const result = yield* Effect.orDie(client.activity({ name: activity.name, attempt }))
|
308
|
-
// If the activity has suspended and did not execute, we need to resume
|
309
|
-
// it by resetting the attempt and re-executing.
|
310
|
-
if (result._tag === "Suspended" && activities.has(activityId)) {
|
311
|
-
yield* resetActivityAttempt({
|
312
|
-
workflow: instance.workflow,
|
313
|
-
executionId: instance.executionId,
|
314
|
-
activity,
|
315
|
-
attempt
|
316
|
-
})
|
317
|
-
continue
|
310
|
+
activityExecute: Effect.fnUntraced(
|
311
|
+
function*({ activity, attempt }) {
|
312
|
+
const context = yield* Effect.context<WorkflowInstance>()
|
313
|
+
const instance = Context.get(context, WorkflowInstance)
|
314
|
+
yield* Effect.annotateCurrentSpan("executionId", instance.executionId)
|
315
|
+
const activityId = `${instance.executionId}/${activity.name}`
|
316
|
+
activities.set(activityId, { activity, context })
|
317
|
+
const latch = activityLatches.get(activityId)
|
318
|
+
if (latch) {
|
319
|
+
yield* latch.release
|
320
|
+
activityLatches.delete(activityId)
|
318
321
|
}
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
322
|
+
const client = (yield* RcMap.get(clients, instance.workflow.name))(instance.executionId)
|
323
|
+
while (true) {
|
324
|
+
const result = yield* Effect.orDie(client.activity({ name: activity.name, attempt }))
|
325
|
+
// If the activity has suspended and did not execute, we need to resume
|
326
|
+
// it by resetting the attempt and re-executing.
|
327
|
+
if (result._tag === "Suspended" && activities.has(activityId)) {
|
328
|
+
yield* resetActivityAttempt({
|
329
|
+
workflow: instance.workflow,
|
330
|
+
executionId: instance.executionId,
|
331
|
+
activity,
|
332
|
+
attempt
|
333
|
+
})
|
334
|
+
continue
|
335
|
+
}
|
336
|
+
activities.delete(activityId)
|
337
|
+
return result
|
338
|
+
}
|
339
|
+
},
|
340
|
+
Effect.scoped,
|
341
|
+
(effect, { activity, attempt }) =>
|
342
|
+
Effect.withSpan(effect, "WorkflowEngine.activityExecute", {
|
343
|
+
attributes: {
|
344
|
+
name: activity.name,
|
345
|
+
attempt
|
346
|
+
}
|
347
|
+
})
|
348
|
+
),
|
323
349
|
|
324
350
|
deferredResult: (deferred) =>
|
325
351
|
WorkflowInstance.pipe(
|
352
|
+
Effect.tap((instance) => Effect.annotateCurrentSpan("executionId", instance.executionId)),
|
326
353
|
Effect.flatMap((instance) =>
|
327
354
|
requestReply({
|
328
355
|
workflow: instance.workflow,
|
@@ -338,18 +365,33 @@ export const make = Effect.gen(function*() {
|
|
338
365
|
times: 3,
|
339
366
|
schedule: Schedule.exponential(250)
|
340
367
|
}),
|
341
|
-
Effect.orDie
|
368
|
+
Effect.orDie,
|
369
|
+
Effect.withSpan("WorkflowEngine.deferredResult", {
|
370
|
+
attributes: {
|
371
|
+
name: deferred.name
|
372
|
+
}
|
373
|
+
})
|
342
374
|
),
|
343
375
|
|
344
|
-
deferredDone: Effect.fnUntraced(
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
376
|
+
deferredDone: Effect.fnUntraced(
|
377
|
+
function*({ deferred, executionId, exit, workflowName }) {
|
378
|
+
const client = yield* RcMap.get(clients, workflowName)
|
379
|
+
return yield* Effect.orDie(
|
380
|
+
client(executionId).deferred({
|
381
|
+
name: deferred.name,
|
382
|
+
exit
|
383
|
+
}, { discard: true })
|
384
|
+
)
|
385
|
+
},
|
386
|
+
Effect.scoped,
|
387
|
+
(effect, { deferred, executionId }) =>
|
388
|
+
Effect.withSpan(effect, "WorkflowEngine.deferredDone", {
|
389
|
+
attributes: {
|
390
|
+
name: deferred.name,
|
391
|
+
executionId
|
392
|
+
}
|
393
|
+
})
|
394
|
+
),
|
353
395
|
|
354
396
|
scheduleClock(options) {
|
355
397
|
const client = clockClient(options.executionId)
|
package/src/ShardManager.ts
CHANGED
@@ -22,6 +22,7 @@ import * as Iterable from "effect/Iterable"
|
|
22
22
|
import * as Layer from "effect/Layer"
|
23
23
|
import * as Mailbox from "effect/Mailbox"
|
24
24
|
import * as Metric from "effect/Metric"
|
25
|
+
import * as MetricLabel from "effect/MetricLabel"
|
25
26
|
import * as MutableHashMap from "effect/MutableHashMap"
|
26
27
|
import * as MutableHashSet from "effect/MutableHashSet"
|
27
28
|
import * as Option from "effect/Option"
|
@@ -431,14 +432,21 @@ export const make = Effect.gen(function*() {
|
|
431
432
|
const scope = yield* Effect.scope
|
432
433
|
const events = yield* PubSub.unbounded<ShardingEvent>()
|
433
434
|
|
434
|
-
|
435
|
+
function updateRunnerMetrics() {
|
436
|
+
ClusterMetrics.runners.unsafeUpdate(MutableHashMap.size(state.allRunners), [])
|
437
|
+
}
|
435
438
|
|
436
|
-
|
437
|
-
const
|
438
|
-
|
439
|
-
ClusterMetrics.
|
440
|
-
|
439
|
+
function updateShardMetrics() {
|
440
|
+
const stats = state.shardStats
|
441
|
+
for (const [address, shardCount] of stats.perRunner) {
|
442
|
+
ClusterMetrics.assignedShards.unsafeUpdate(
|
443
|
+
shardCount,
|
444
|
+
[MetricLabel.make("address", address)]
|
445
|
+
)
|
446
|
+
}
|
447
|
+
ClusterMetrics.unassignedShards.unsafeUpdate(stats.unassigned, [])
|
441
448
|
}
|
449
|
+
updateShardMetrics()
|
442
450
|
|
443
451
|
function withRetry<A, E, R>(effect: Effect.Effect<A, E, R>): Effect.Effect<void, never, R> {
|
444
452
|
return effect.pipe(
|
@@ -493,10 +501,11 @@ export const make = Effect.gen(function*() {
|
|
493
501
|
let machineId = 0
|
494
502
|
const register = Effect.fnUntraced(function*(runner: Runner) {
|
495
503
|
yield* Effect.logInfo(`Registering runner ${Runner.pretty(runner)}`)
|
496
|
-
state.addRunner(runner, clock.unsafeCurrentTimeMillis())
|
497
504
|
|
498
|
-
|
505
|
+
state.addRunner(runner, clock.unsafeCurrentTimeMillis())
|
506
|
+
updateRunnerMetrics()
|
499
507
|
yield* PubSub.publish(events, ShardingEvent.RunnerRegistered({ address: runner.address }))
|
508
|
+
|
500
509
|
if (state.allUnassignedShards.length > 0) {
|
501
510
|
yield* rebalance(false)
|
502
511
|
}
|
@@ -516,13 +525,9 @@ export const make = Effect.gen(function*() {
|
|
516
525
|
}
|
517
526
|
state.addAssignments(unassignments, Option.none())
|
518
527
|
state.removeRunner(address)
|
519
|
-
|
528
|
+
updateRunnerMetrics()
|
520
529
|
|
521
530
|
if (unassignments.length > 0) {
|
522
|
-
yield* Metric.incrementBy(
|
523
|
-
Metric.tagged(ClusterMetrics.unassignedShards, "runner_address", address.toString()),
|
524
|
-
unassignments.length
|
525
|
-
)
|
526
531
|
yield* PubSub.publish(events, ShardingEvent.RunnerUnregistered({ address }))
|
527
532
|
}
|
528
533
|
|
@@ -639,18 +644,8 @@ export const make = Effect.gen(function*() {
|
|
639
644
|
MutableHashMap.remove(assignments, address)
|
640
645
|
return Effect.void
|
641
646
|
},
|
642
|
-
onSuccess: () =>
|
643
|
-
|
644
|
-
return Metric.incrementBy(
|
645
|
-
Metric.tagged(ClusterMetrics.assignedShards, "runner_address", address.toString()),
|
646
|
-
-shardCount
|
647
|
-
).pipe(
|
648
|
-
Effect.zipRight(Metric.incrementBy(ClusterMetrics.unassignedShards, shardCount)),
|
649
|
-
Effect.zipRight(
|
650
|
-
PubSub.publish(events, ShardingEvent.ShardsUnassigned({ address, shards: Array.from(shards) }))
|
651
|
-
)
|
652
|
-
)
|
653
|
-
}
|
647
|
+
onSuccess: () =>
|
648
|
+
PubSub.publish(events, ShardingEvent.ShardsUnassigned({ address, shards: Array.from(shards) }))
|
654
649
|
})
|
655
650
|
)
|
656
651
|
)
|
@@ -677,24 +672,16 @@ export const make = Effect.gen(function*() {
|
|
677
672
|
MutableHashSet.add(failedRunners, address)
|
678
673
|
return Effect.void
|
679
674
|
},
|
680
|
-
onSuccess: () =>
|
681
|
-
|
682
|
-
return Metric.incrementBy(
|
683
|
-
Metric.tagged(ClusterMetrics.assignedShards, "runner_address", address.toString()),
|
684
|
-
-shardCount
|
685
|
-
).pipe(
|
686
|
-
Effect.zipRight(Metric.incrementBy(ClusterMetrics.unassignedShards, -shardCount)),
|
687
|
-
Effect.zipRight(
|
688
|
-
PubSub.publish(events, ShardingEvent.ShardsAssigned({ address, shards: Array.from(shards) }))
|
689
|
-
)
|
690
|
-
)
|
691
|
-
}
|
675
|
+
onSuccess: () =>
|
676
|
+
PubSub.publish(events, ShardingEvent.ShardsAssigned({ address, shards: Array.from(shards) }))
|
692
677
|
})
|
693
678
|
)
|
694
679
|
)
|
695
680
|
}
|
696
681
|
yield* FiberSet.awaitEmpty(rebalanceFibers)
|
697
682
|
|
683
|
+
updateShardMetrics()
|
684
|
+
|
698
685
|
const wereFailures = MutableHashSet.size(failedRunners) > 0
|
699
686
|
if (wereFailures) {
|
700
687
|
// Check if the failing runners are still reachable
|
@@ -162,6 +162,25 @@ export class State {
|
|
162
162
|
)
|
163
163
|
}
|
164
164
|
|
165
|
+
get shardStats(): {
|
166
|
+
readonly perRunner: Map<string, number>
|
167
|
+
readonly unassigned: number
|
168
|
+
} {
|
169
|
+
const perRunner = new Map<string, number>()
|
170
|
+
let unassigned = 0
|
171
|
+
for (const [, address] of this.assignments) {
|
172
|
+
if (Option.isNone(address)) {
|
173
|
+
unassigned++
|
174
|
+
continue
|
175
|
+
}
|
176
|
+
const runner = address.value.toString()
|
177
|
+
const count = perRunner.get(runner) ?? 0
|
178
|
+
perRunner.set(runner, count + 1)
|
179
|
+
}
|
180
|
+
|
181
|
+
return { perRunner, unassigned }
|
182
|
+
}
|
183
|
+
|
165
184
|
shardsPerRunner(group: string): MutableHashMap.MutableHashMap<RunnerAddress, Set<number>> {
|
166
185
|
const groupRunners = this.runners.get(group)
|
167
186
|
const shards = MutableHashMap.empty<RunnerAddress, Set<number>>()
|