@effect/cluster 0.52.4 → 0.52.6

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.
@@ -8,8 +8,9 @@ import * as Arr from "effect/Array"
8
8
  import * as Duration from "effect/Duration"
9
9
  import * as Effect from "effect/Effect"
10
10
  import * as Layer from "effect/Layer"
11
- import * as Resource from "effect/Resource"
11
+ import * as Scope from "effect/Scope"
12
12
  import { PersistenceError } from "./ClusterError.js"
13
+ import { ResourceRef } from "./internal/resourceRef.js"
13
14
  import * as RunnerStorage from "./RunnerStorage.js"
14
15
  import * as ShardId from "./ShardId.js"
15
16
  import * as ShardingConfig from "./ShardingConfig.js"
@@ -27,22 +28,27 @@ export const make = Effect.fnUntraced(function*(options: {
27
28
  const sql = (yield* SqlClient.SqlClient).withoutTransforms()
28
29
  const prefix = options?.prefix ?? "cluster"
29
30
  const table = (name: string) => `${prefix}_${name}`
31
+
30
32
  const acquireLockConn = sql.onDialectOrElse({
31
33
  pg: () =>
32
- Effect.gen(function*() {
33
- const conn = yield* Effect.orDie(sql.reserve)
34
- yield* Effect.addFinalizer(() => Effect.orDie(conn.executeRaw("SELECT pg_advisory_unlock_all()", [])))
34
+ Effect.fnUntraced(function*(scope: Scope.Scope) {
35
+ const conn = yield* Effect.orDie(sql.reserve).pipe(
36
+ Scope.extend(scope)
37
+ )
38
+ yield* Scope.addFinalizerExit(scope, () => Effect.orDie(conn.executeRaw("SELECT pg_advisory_unlock_all()", [])))
35
39
  return conn
36
40
  }),
37
41
  mysql: () =>
38
- Effect.gen(function*() {
39
- const conn = yield* Effect.orDie(sql.reserve)
40
- yield* Effect.addFinalizer(() => Effect.orDie(conn.executeRaw("SELECT RELEASE_ALL_LOCKS()", [])))
42
+ Effect.fnUntraced(function*(scope: Scope.Scope) {
43
+ const conn = yield* Effect.orDie(sql.reserve).pipe(
44
+ Scope.extend(scope)
45
+ )
46
+ yield* Scope.addFinalizerExit(scope, () => Effect.orDie(conn.executeRaw("SELECT RELEASE_ALL_LOCKS()", [])))
41
47
  return conn
42
48
  }),
43
49
  orElse: () => undefined
44
50
  })
45
- const lockConnRef = acquireLockConn && (yield* Resource.manual(acquireLockConn))
51
+ const lockConn = acquireLockConn && (yield* ResourceRef.from(yield* Effect.scope, acquireLockConn))
46
52
 
47
53
  const runnersTable = table("runners")
48
54
  const runnersTableSql = sql(runnersTable)
@@ -206,28 +212,28 @@ export const make = Effect.fnUntraced(function*(options: {
206
212
  })
207
213
 
208
214
  const execWithLockConn = <A>(effect: Statement.Statement<A>): Effect.Effect<unknown, SqlError> => {
209
- if (!lockConnRef) return effect
215
+ if (!lockConn) return effect
210
216
  const [query, params] = effect.compile()
211
- return Resource.get(lockConnRef).pipe(
217
+ return lockConn.await.pipe(
212
218
  Effect.flatMap((conn) => conn.executeRaw(query, params)),
213
- Effect.onError(() => Resource.refresh(lockConnRef!))
219
+ Effect.onError(() => lockConn.unsafeRebuild())
214
220
  )
215
221
  }
216
222
  const execWithLockConnValues = <A>(
217
223
  effect: Statement.Statement<A>
218
224
  ): Effect.Effect<ReadonlyArray<ReadonlyArray<any>>, SqlError> => {
219
- if (!lockConnRef) return effect.values
225
+ if (!lockConn) return effect.values
220
226
  const [query, params] = effect.compile()
221
- return Resource.get(lockConnRef).pipe(
227
+ return lockConn.await.pipe(
222
228
  Effect.flatMap((conn) => conn.executeValues(query, params)),
223
- Effect.onError(() => Resource.refresh(lockConnRef!))
229
+ Effect.onError(() => lockConn.unsafeRebuild())
224
230
  )
225
231
  }
226
232
 
227
233
  const acquireLock = sql.onDialectOrElse({
228
234
  pg: () =>
229
235
  Effect.fnUntraced(function*(_address: string, shardIds: ReadonlyArray<string>) {
230
- const conn = yield* Resource.get(lockConnRef!)
236
+ const conn = yield* lockConn!.await
231
237
  const acquiredShardIds: Array<string> = []
232
238
  const toAcquire = new Map(shardIds.map((shardId) => [lockNumbers.get(shardId)!, shardId]))
233
239
  const takenLocks = yield* conn.executeValues(
@@ -250,11 +256,11 @@ export const make = Effect.fnUntraced(function*(options: {
250
256
  }
251
257
  }
252
258
  return acquiredShardIds
253
- }, Effect.onError(() => Resource.refresh(lockConnRef!))),
259
+ }, Effect.onError(() => lockConn!.unsafeRebuild())),
254
260
 
255
261
  mysql: () =>
256
262
  Effect.fnUntraced(function*(_address: string, shardIds: ReadonlyArray<string>) {
257
- const conn = yield* Resource.get(lockConnRef!)
263
+ const conn = yield* lockConn!.await
258
264
  const takenLocks = (yield* conn.executeUnprepared(`SELECT ${allMySqlTakenLocks}`, [], undefined))[0] as Record<
259
265
  string,
260
266
  1 | null
@@ -281,7 +287,7 @@ export const make = Effect.fnUntraced(function*(options: {
281
287
  }
282
288
  }
283
289
  return acquiredShardIds
284
- }, Effect.onError(() => Resource.refresh(lockConnRef!))),
290
+ }, Effect.onError(() => lockConn!.unsafeRebuild())),
285
291
 
286
292
  mssql: () => (address: string, shardIds: ReadonlyArray<string>) => {
287
293
  const values = shardIds.map((shardId) => sql`(${stringLiteral(shardId)}, ${stringLiteral(address)}, ${sqlNow})`)
@@ -446,52 +452,43 @@ export const make = Effect.fnUntraced(function*(options: {
446
452
  Effect.fnUntraced(
447
453
  function*(_address, shardId) {
448
454
  const lockNum = lockNumbers.get(shardId)!
449
- const conn = yield* Resource.get(lockConnRef!)
450
- const release = conn.executeRaw(`SELECT pg_advisory_unlock(${lockNum})`, []).pipe(
451
- Effect.timeoutOption(Duration.seconds(5))
452
- )
453
- const check = conn.executeValues(
454
- `SELECT 1 FROM pg_locks WHERE locktype = 'advisory' AND granted = true AND pid = pg_backend_pid() AND objid = ${lockNum}`,
455
- []
456
- )
457
- while (true) {
458
- yield* release
459
- const takenLocks = yield* check
455
+ for (let i = 0; i < 5; i++) {
456
+ const conn = yield* lockConn!.await
457
+ yield* conn.executeRaw(`SELECT pg_advisory_unlock(${lockNum})`, [])
458
+ const takenLocks = yield* conn.executeValues(
459
+ `SELECT 1 FROM pg_locks WHERE locktype = 'advisory' AND granted = true AND pid = pg_backend_pid() AND objid = ${lockNum}`,
460
+ []
461
+ )
460
462
  if (takenLocks.length === 0) return
461
463
  }
464
+ const conn = yield* lockConn!.await
465
+ yield* conn.executeRaw(`SELECT pg_advisory_unlock_all()`, [])
462
466
  },
463
- Effect.timeout(config.shardLockExpiration),
464
- Effect.onError(() => Resource.refresh(lockConnRef!)),
467
+ Effect.onError(() => lockConn!.unsafeRebuild()),
465
468
  Effect.asVoid,
466
- PersistenceError.refail,
467
- withTracerDisabled
469
+ PersistenceError.refail
468
470
  ),
469
471
  mysql: () =>
470
472
  Effect.fnUntraced(
471
473
  function*(_address, shardId) {
472
- const conn = yield* Resource.get(lockConnRef!)
473
474
  const lockName = lockNames.get(shardId)!
474
- const release = conn.executeRaw(`SELECT RELEASE_LOCK('${lockName}')`, [])
475
- const check = conn.executeValues(
476
- `SELECT IS_USED_LOCK('${lockName}') = CONNECTION_ID() AS is_taken`,
477
- []
478
- )
479
475
  while (true) {
480
- yield* release
481
- const takenLocks = yield* check
476
+ const conn = yield* lockConn!.await
477
+ yield* conn.executeRaw(`SELECT RELEASE_LOCK('${lockName}')`, [])
478
+ const takenLocks = yield* conn.executeValues(
479
+ `SELECT IS_USED_LOCK('${lockName}') = CONNECTION_ID() AS is_taken`,
480
+ []
481
+ )
482
482
  if (takenLocks.length === 0 || takenLocks[0][0] !== 1) return
483
483
  }
484
484
  },
485
- Effect.timeout(config.shardLockExpiration),
486
- Effect.onError(() => Resource.refresh(lockConnRef!)),
485
+ Effect.onError(() => lockConn!.unsafeRebuild()),
487
486
  Effect.asVoid,
488
- PersistenceError.refail,
489
- withTracerDisabled
487
+ PersistenceError.refail
490
488
  ),
491
489
  orElse: () => (address, shardId) =>
492
490
  sql`DELETE FROM ${locksTableSql} WHERE address = ${address} AND shard_id = ${shardId}`.pipe(
493
- PersistenceError.refail,
494
- withTracerDisabled
491
+ PersistenceError.refail
495
492
  )
496
493
  }),
497
494
 
@@ -9,6 +9,7 @@ import type { DurationInput } from "effect/Duration"
9
9
  import * as Effect from "effect/Effect"
10
10
  import * as Equal from "effect/Equal"
11
11
  import * as Exit from "effect/Exit"
12
+ import * as FiberMap from "effect/FiberMap"
12
13
  import * as FiberRef from "effect/FiberRef"
13
14
  import { identity } from "effect/Function"
14
15
  import * as HashMap from "effect/HashMap"
@@ -127,7 +128,7 @@ export const make = Effect.fnUntraced(function*<
127
128
  () => {
128
129
  serverCloseLatches.get(address)?.unsafeOpen()
129
130
  serverCloseLatches.delete(address)
130
- return Effect.ignore(options.storage.resetAddress(address))
131
+ return Effect.void
131
132
  }
132
133
  )
133
134
 
@@ -434,12 +435,21 @@ export const make = Effect.fnUntraced(function*<
434
435
  )
435
436
  }
436
437
 
438
+ const entityRemovalMap = yield* FiberMap.make<EntityAddress>()
439
+
437
440
  const interruptShard = (shardId: ShardId) =>
438
441
  Effect.suspend(function loop(): Effect.Effect<void> {
439
442
  const toAwait = Arr.empty<Effect.Effect<void>>()
440
443
  activeServers.forEach((state) => {
441
444
  if (shardId[Equal.symbol](state.address.shardId)) {
442
- toAwait.push(entities.removeIgnore(state.address))
445
+ toAwait.push(
446
+ FiberMap.run(
447
+ entityRemovalMap,
448
+ state.address,
449
+ entities.removeIgnore(state.address),
450
+ { onlyIfMissing: true }
451
+ )
452
+ )
443
453
  }
444
454
  })
445
455
  serverCloseLatches.forEach((latch, address) => {