@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.
- package/dist/cjs/Sharding.js +49 -28
- package/dist/cjs/Sharding.js.map +1 -1
- package/dist/cjs/SqlRunnerStorage.js +29 -30
- package/dist/cjs/SqlRunnerStorage.js.map +1 -1
- package/dist/cjs/internal/entityManager.js +6 -2
- package/dist/cjs/internal/entityManager.js.map +1 -1
- package/dist/dts/Sharding.d.ts.map +1 -1
- package/dist/dts/SqlRunnerStorage.d.ts +2 -1
- package/dist/dts/SqlRunnerStorage.d.ts.map +1 -1
- package/dist/esm/Sharding.js +49 -28
- package/dist/esm/Sharding.js.map +1 -1
- package/dist/esm/SqlRunnerStorage.js +29 -30
- package/dist/esm/SqlRunnerStorage.js.map +1 -1
- package/dist/esm/internal/entityManager.js +6 -2
- package/dist/esm/internal/entityManager.js.map +1 -1
- package/package.json +4 -4
- package/src/Sharding.ts +73 -35
- package/src/SqlRunnerStorage.ts +44 -47
- package/src/internal/entityManager.ts +12 -2
package/src/SqlRunnerStorage.ts
CHANGED
|
@@ -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
|
|
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.
|
|
33
|
-
const conn = yield* Effect.orDie(sql.reserve)
|
|
34
|
-
|
|
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.
|
|
39
|
-
const conn = yield* Effect.orDie(sql.reserve)
|
|
40
|
-
|
|
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
|
|
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 (!
|
|
215
|
+
if (!lockConn) return effect
|
|
210
216
|
const [query, params] = effect.compile()
|
|
211
|
-
return
|
|
217
|
+
return lockConn.await.pipe(
|
|
212
218
|
Effect.flatMap((conn) => conn.executeRaw(query, params)),
|
|
213
|
-
Effect.onError(() =>
|
|
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 (!
|
|
225
|
+
if (!lockConn) return effect.values
|
|
220
226
|
const [query, params] = effect.compile()
|
|
221
|
-
return
|
|
227
|
+
return lockConn.await.pipe(
|
|
222
228
|
Effect.flatMap((conn) => conn.executeValues(query, params)),
|
|
223
|
-
Effect.onError(() =>
|
|
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*
|
|
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(() =>
|
|
259
|
+
}, Effect.onError(() => lockConn!.unsafeRebuild())),
|
|
254
260
|
|
|
255
261
|
mysql: () =>
|
|
256
262
|
Effect.fnUntraced(function*(_address: string, shardIds: ReadonlyArray<string>) {
|
|
257
|
-
const conn = yield*
|
|
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(() =>
|
|
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
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
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.
|
|
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*
|
|
481
|
-
|
|
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.
|
|
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.
|
|
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(
|
|
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) => {
|