@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.
@@ -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(function*({ activity, attempt }) {
296
- const context = yield* Effect.context<WorkflowInstance>()
297
- const instance = Context.get(context, WorkflowInstance)
298
- const activityId = `${instance.executionId}/${activity.name}`
299
- activities.set(activityId, { activity, context })
300
- const latch = activityLatches.get(activityId)
301
- if (latch) {
302
- yield* latch.release
303
- activityLatches.delete(activityId)
304
- }
305
- const client = (yield* RcMap.get(clients, instance.workflow.name))(instance.executionId)
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
- activities.delete(activityId)
320
- return result
321
- }
322
- }, Effect.scoped),
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(function*({ deferred, executionId, exit, workflowName }) {
345
- const client = yield* RcMap.get(clients, workflowName)
346
- return yield* Effect.orDie(
347
- client(executionId).deferred({
348
- name: deferred.name,
349
- exit
350
- }, { discard: true })
351
- )
352
- }, Effect.scoped),
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)
@@ -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
- yield* Metric.incrementBy(ClusterMetrics.runners, MutableHashMap.size(state.allRunners))
435
+ function updateRunnerMetrics() {
436
+ ClusterMetrics.runners.unsafeUpdate(MutableHashMap.size(state.allRunners), [])
437
+ }
435
438
 
436
- for (const [, address] of state.assignments) {
437
- const metric = Option.isSome(address) ?
438
- Metric.tagged(ClusterMetrics.assignedShards, "address", address.toString()) :
439
- ClusterMetrics.unassignedShards
440
- yield* Metric.increment(metric)
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
- yield* Metric.increment(ClusterMetrics.runners)
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
- yield* Metric.incrementBy(ClusterMetrics.runners, -1)
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
- const shardCount = MutableHashSet.size(shards)
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
- const shardCount = MutableHashSet.size(shards)
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>>()