@effect/cluster 0.52.5 → 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,48 +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})`, [])
451
- const check = conn.executeValues(
452
- `SELECT 1 FROM pg_locks WHERE locktype = 'advisory' AND granted = true AND pid = pg_backend_pid() AND objid = ${lockNum}`,
453
- []
454
- )
455
- while (true) {
456
- yield* release
457
- 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
+ )
458
462
  if (takenLocks.length === 0) return
459
463
  }
464
+ const conn = yield* lockConn!.await
465
+ yield* conn.executeRaw(`SELECT pg_advisory_unlock_all()`, [])
460
466
  },
461
- Effect.onError(() => Resource.refresh(lockConnRef!)),
467
+ Effect.onError(() => lockConn!.unsafeRebuild()),
462
468
  Effect.asVoid,
463
- PersistenceError.refail,
464
- withTracerDisabled
469
+ PersistenceError.refail
465
470
  ),
466
471
  mysql: () =>
467
472
  Effect.fnUntraced(
468
473
  function*(_address, shardId) {
469
- const conn = yield* Resource.get(lockConnRef!)
470
474
  const lockName = lockNames.get(shardId)!
471
- const release = conn.executeRaw(`SELECT RELEASE_LOCK('${lockName}')`, [])
472
- const check = conn.executeValues(
473
- `SELECT IS_USED_LOCK('${lockName}') = CONNECTION_ID() AS is_taken`,
474
- []
475
- )
476
475
  while (true) {
477
- yield* release
478
- 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
+ )
479
482
  if (takenLocks.length === 0 || takenLocks[0][0] !== 1) return
480
483
  }
481
484
  },
482
- Effect.onError(() => Resource.refresh(lockConnRef!)),
485
+ Effect.onError(() => lockConn!.unsafeRebuild()),
483
486
  Effect.asVoid,
484
- PersistenceError.refail,
485
- withTracerDisabled
487
+ PersistenceError.refail
486
488
  ),
487
489
  orElse: () => (address, shardId) =>
488
490
  sql`DELETE FROM ${locksTableSql} WHERE address = ${address} AND shard_id = ${shardId}`.pipe(
489
- PersistenceError.refail,
490
- withTracerDisabled
491
+ PersistenceError.refail
491
492
  )
492
493
  }),
493
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"
@@ -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) => {