@effect/cluster 0.53.4 → 0.53.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/EntityResource.js +1 -1
- package/dist/cjs/EntityResource.js.map +1 -1
- package/dist/cjs/ShardingConfig.js +2 -0
- package/dist/cjs/ShardingConfig.js.map +1 -1
- package/dist/cjs/SqlRunnerStorage.js +149 -65
- package/dist/cjs/SqlRunnerStorage.js.map +1 -1
- package/dist/dts/ShardingConfig.d.ts +8 -0
- package/dist/dts/ShardingConfig.d.ts.map +1 -1
- package/dist/dts/SqlRunnerStorage.d.ts.map +1 -1
- package/dist/esm/EntityResource.js +1 -1
- package/dist/esm/EntityResource.js.map +1 -1
- package/dist/esm/ShardingConfig.js +2 -0
- package/dist/esm/ShardingConfig.js.map +1 -1
- package/dist/esm/SqlRunnerStorage.js +149 -65
- package/dist/esm/SqlRunnerStorage.js.map +1 -1
- package/package.json +4 -4
- package/src/EntityResource.ts +2 -2
- package/src/ShardingConfig.ts +9 -0
- package/src/SqlRunnerStorage.ts +134 -22
package/src/SqlRunnerStorage.ts
CHANGED
|
@@ -25,6 +25,7 @@ export const make = Effect.fnUntraced(function*(options: {
|
|
|
25
25
|
readonly prefix?: string | undefined
|
|
26
26
|
}) {
|
|
27
27
|
const config = yield* ShardingConfig.ShardingConfig
|
|
28
|
+
const disableAdvisoryLocks = config.shardLockDisableAdvisory
|
|
28
29
|
const sql = (yield* SqlClient.SqlClient).withoutTransforms()
|
|
29
30
|
const prefix = options?.prefix ?? "cluster"
|
|
30
31
|
const table = (name: string) => `${prefix}_${name}`
|
|
@@ -138,8 +139,22 @@ export const make = Effect.fnUntraced(function*(options: {
|
|
|
138
139
|
acquired_at DATETIME NOT NULL
|
|
139
140
|
)
|
|
140
141
|
`,
|
|
141
|
-
mysql: () =>
|
|
142
|
-
|
|
142
|
+
mysql: () =>
|
|
143
|
+
sql`
|
|
144
|
+
CREATE TABLE IF NOT EXISTS ${locksTableSql} (
|
|
145
|
+
shard_id VARCHAR(50) PRIMARY KEY,
|
|
146
|
+
address VARCHAR(255) NOT NULL,
|
|
147
|
+
acquired_at DATETIME NOT NULL
|
|
148
|
+
)
|
|
149
|
+
`,
|
|
150
|
+
pg: () =>
|
|
151
|
+
sql`
|
|
152
|
+
CREATE TABLE IF NOT EXISTS ${locksTableSql} (
|
|
153
|
+
shard_id VARCHAR(50) PRIMARY KEY,
|
|
154
|
+
address VARCHAR(255) NOT NULL,
|
|
155
|
+
acquired_at TIMESTAMP NOT NULL
|
|
156
|
+
)
|
|
157
|
+
`,
|
|
143
158
|
orElse: () =>
|
|
144
159
|
// sqlite
|
|
145
160
|
sql`
|
|
@@ -232,6 +247,16 @@ export const make = Effect.fnUntraced(function*(options: {
|
|
|
232
247
|
Effect.onError(() => lockConn.unsafeRebuild())
|
|
233
248
|
)
|
|
234
249
|
}
|
|
250
|
+
const execWithLockConnUnprepared = <A>(
|
|
251
|
+
effect: Statement.Statement<A>
|
|
252
|
+
): Effect.Effect<ReadonlyArray<ReadonlyArray<any>>, SqlError> => {
|
|
253
|
+
if (!lockConn) return effect.values
|
|
254
|
+
const [query, params] = effect.compile()
|
|
255
|
+
return lockConn.await.pipe(
|
|
256
|
+
Effect.flatMap(([conn]) => conn.executeUnprepared(query, params, undefined)),
|
|
257
|
+
Effect.onError(() => lockConn.unsafeRebuild())
|
|
258
|
+
)
|
|
259
|
+
}
|
|
235
260
|
const execWithLockConnValues = <A>(
|
|
236
261
|
effect: Statement.Statement<A>
|
|
237
262
|
): Effect.Effect<ReadonlyArray<ReadonlyArray<any>>, SqlError> => {
|
|
@@ -244,8 +269,24 @@ export const make = Effect.fnUntraced(function*(options: {
|
|
|
244
269
|
}
|
|
245
270
|
|
|
246
271
|
const acquireLock = sql.onDialectOrElse({
|
|
247
|
-
pg: () =>
|
|
248
|
-
|
|
272
|
+
pg: () => {
|
|
273
|
+
if (disableAdvisoryLocks) {
|
|
274
|
+
return (address: string, shardIds: ReadonlyArray<string>) => {
|
|
275
|
+
const values = shardIds.map((shardId) =>
|
|
276
|
+
sql`(${stringLiteral(shardId)}, ${stringLiteral(address)}, ${sqlNow})`
|
|
277
|
+
)
|
|
278
|
+
return sql`
|
|
279
|
+
INSERT INTO ${locksTableSql} (shard_id, address, acquired_at) VALUES ${sql.csv(values)}
|
|
280
|
+
ON CONFLICT (shard_id) DO UPDATE
|
|
281
|
+
SET address = ${address}, acquired_at = ${sqlNow}
|
|
282
|
+
WHERE ${locksTableSql}.address = ${address}
|
|
283
|
+
OR ${locksTableSql}.acquired_at < ${lockExpiresAt}
|
|
284
|
+
`.pipe(
|
|
285
|
+
Effect.andThen(acquiredLocks(address, shardIds))
|
|
286
|
+
)
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return Effect.fnUntraced(function*(_address: string, shardIds: ReadonlyArray<string>) {
|
|
249
290
|
const [conn, pid] = yield* lockConn!.await
|
|
250
291
|
const acquiredShardIds: Array<string> = []
|
|
251
292
|
const toAcquire = new Map(shardIds.map((shardId) => [lockNumbers.get(shardId)!, shardId]))
|
|
@@ -269,10 +310,26 @@ export const make = Effect.fnUntraced(function*(options: {
|
|
|
269
310
|
}
|
|
270
311
|
}
|
|
271
312
|
return acquiredShardIds
|
|
272
|
-
}, Effect.onError(() => lockConn!.unsafeRebuild()))
|
|
313
|
+
}, Effect.onError(() => lockConn!.unsafeRebuild()))
|
|
314
|
+
},
|
|
273
315
|
|
|
274
|
-
mysql: () =>
|
|
275
|
-
|
|
316
|
+
mysql: () => {
|
|
317
|
+
if (disableAdvisoryLocks) {
|
|
318
|
+
return (address: string, shardIds: ReadonlyArray<string>) => {
|
|
319
|
+
const values = shardIds.map((shardId) =>
|
|
320
|
+
sql`(${stringLiteral(shardId)}, ${stringLiteral(address)}, ${sqlNow})`
|
|
321
|
+
)
|
|
322
|
+
return sql`
|
|
323
|
+
INSERT INTO ${locksTableSql} (shard_id, address, acquired_at) VALUES ${sql.csv(values)}
|
|
324
|
+
ON DUPLICATE KEY UPDATE
|
|
325
|
+
address = IF(address = VALUES(address) OR acquired_at < ${lockExpiresAt}, VALUES(address), address),
|
|
326
|
+
acquired_at = IF(address = VALUES(address) OR acquired_at < ${lockExpiresAt}, VALUES(acquired_at), acquired_at)
|
|
327
|
+
`.unprepared.pipe(
|
|
328
|
+
Effect.andThen(acquiredLocks(address, shardIds))
|
|
329
|
+
)
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
return Effect.fnUntraced(function*(_address: string, shardIds: ReadonlyArray<string>) {
|
|
276
333
|
const [conn, pid] = yield* lockConn!.await
|
|
277
334
|
const takenLocks = (yield* conn.executeValues(`SELECT ${allMySqlTakenLocks}`, []))[0] as Array<number | null>
|
|
278
335
|
const acquiredShardIds: Array<string> = []
|
|
@@ -296,7 +353,8 @@ export const make = Effect.fnUntraced(function*(options: {
|
|
|
296
353
|
}
|
|
297
354
|
}
|
|
298
355
|
return acquiredShardIds
|
|
299
|
-
}, Effect.onError(() => lockConn!.unsafeRebuild()))
|
|
356
|
+
}, Effect.onError(() => lockConn!.unsafeRebuild()))
|
|
357
|
+
},
|
|
300
358
|
|
|
301
359
|
mssql: () => (address: string, shardIds: ReadonlyArray<string>) => {
|
|
302
360
|
const values = shardIds.map((shardId) => sql`(${stringLiteral(shardId)}, ${stringLiteral(address)}, ${sqlNow})`)
|
|
@@ -399,8 +457,34 @@ export const make = Effect.fnUntraced(function*(options: {
|
|
|
399
457
|
const stringLiteralArr = (arr: ReadonlyArray<string>) => sql.literal(`(${arr.map(wrapString).join(",")})`)
|
|
400
458
|
|
|
401
459
|
const refreshShards = sql.onDialectOrElse({
|
|
402
|
-
pg: () =>
|
|
403
|
-
|
|
460
|
+
pg: () => {
|
|
461
|
+
if (!disableAdvisoryLocks) return acquireLock
|
|
462
|
+
return (address: string, shardIds: ReadonlyArray<string>) =>
|
|
463
|
+
sql`
|
|
464
|
+
UPDATE ${locksTableSql}
|
|
465
|
+
SET acquired_at = ${sqlNow}
|
|
466
|
+
WHERE address = ${address} AND shard_id IN ${stringLiteralArr(shardIds)}
|
|
467
|
+
RETURNING shard_id
|
|
468
|
+
`.pipe(
|
|
469
|
+
execWithLockConnValues,
|
|
470
|
+
Effect.map((rows) => rows.map((row) => row[0] as string))
|
|
471
|
+
)
|
|
472
|
+
},
|
|
473
|
+
mysql: () => {
|
|
474
|
+
if (!disableAdvisoryLocks) return acquireLock
|
|
475
|
+
return (address: string, shardIds: ReadonlyArray<string>) => {
|
|
476
|
+
const shardIdsStr = stringLiteralArr(shardIds)
|
|
477
|
+
return sql<Array<{ shard_id: string }>>`
|
|
478
|
+
UPDATE ${locksTableSql}
|
|
479
|
+
SET acquired_at = ${sqlNow}
|
|
480
|
+
WHERE address = ${address} AND shard_id IN ${shardIdsStr};
|
|
481
|
+
SELECT shard_id FROM ${locksTableSql} WHERE address = ${address} AND shard_id IN ${shardIdsStr}
|
|
482
|
+
`.pipe(
|
|
483
|
+
execWithLockConnUnprepared,
|
|
484
|
+
Effect.map((rows) => rows[1].map((row) => row.shard_id))
|
|
485
|
+
)
|
|
486
|
+
}
|
|
487
|
+
},
|
|
404
488
|
mssql: () => (address: string, shardIds: ReadonlyArray<string>) =>
|
|
405
489
|
sql`
|
|
406
490
|
UPDATE ${locksTableSql}
|
|
@@ -462,8 +546,14 @@ export const make = Effect.fnUntraced(function*(options: {
|
|
|
462
546
|
),
|
|
463
547
|
|
|
464
548
|
release: sql.onDialectOrElse({
|
|
465
|
-
pg: () =>
|
|
466
|
-
|
|
549
|
+
pg: () => {
|
|
550
|
+
if (disableAdvisoryLocks) {
|
|
551
|
+
return (address: string, shardId: string) =>
|
|
552
|
+
sql`DELETE FROM ${locksTableSql} WHERE address = ${address} AND shard_id = ${shardId}`.pipe(
|
|
553
|
+
PersistenceError.refail
|
|
554
|
+
)
|
|
555
|
+
}
|
|
556
|
+
return Effect.fnUntraced(
|
|
467
557
|
function*(_address, shardId) {
|
|
468
558
|
const lockNum = lockNumbers.get(shardId)!
|
|
469
559
|
for (let i = 0; i < 5; i++) {
|
|
@@ -481,9 +571,16 @@ export const make = Effect.fnUntraced(function*(options: {
|
|
|
481
571
|
Effect.onError(() => lockConn!.unsafeRebuild()),
|
|
482
572
|
Effect.asVoid,
|
|
483
573
|
PersistenceError.refail
|
|
484
|
-
)
|
|
485
|
-
|
|
486
|
-
|
|
574
|
+
)
|
|
575
|
+
},
|
|
576
|
+
mysql: () => {
|
|
577
|
+
if (disableAdvisoryLocks) {
|
|
578
|
+
return (address: string, shardId: string) =>
|
|
579
|
+
sql`DELETE FROM ${locksTableSql} WHERE address = ${address} AND shard_id = ${shardId}`.pipe(
|
|
580
|
+
PersistenceError.refail
|
|
581
|
+
)
|
|
582
|
+
}
|
|
583
|
+
return Effect.fnUntraced(
|
|
487
584
|
function*(_address, shardId) {
|
|
488
585
|
const lockName = lockNames.get(shardId)!
|
|
489
586
|
while (true) {
|
|
@@ -499,7 +596,8 @@ export const make = Effect.fnUntraced(function*(options: {
|
|
|
499
596
|
Effect.onError(() => lockConn!.unsafeRebuild()),
|
|
500
597
|
Effect.asVoid,
|
|
501
598
|
PersistenceError.refail
|
|
502
|
-
)
|
|
599
|
+
)
|
|
600
|
+
},
|
|
503
601
|
orElse: () => (address, shardId) =>
|
|
504
602
|
sql`DELETE FROM ${locksTableSql} WHERE address = ${address} AND shard_id = ${shardId}`.pipe(
|
|
505
603
|
PersistenceError.refail
|
|
@@ -507,20 +605,34 @@ export const make = Effect.fnUntraced(function*(options: {
|
|
|
507
605
|
}),
|
|
508
606
|
|
|
509
607
|
releaseAll: sql.onDialectOrElse({
|
|
510
|
-
pg: () => (
|
|
511
|
-
|
|
608
|
+
pg: () => (address) => {
|
|
609
|
+
if (disableAdvisoryLocks) {
|
|
610
|
+
return sql`DELETE FROM ${locksTableSql} WHERE address = ${address}`.pipe(
|
|
611
|
+
PersistenceError.refail,
|
|
612
|
+
withTracerDisabled
|
|
613
|
+
)
|
|
614
|
+
}
|
|
615
|
+
return sql`SELECT pg_advisory_unlock_all()`.pipe(
|
|
512
616
|
execWithLockConn,
|
|
513
617
|
Effect.asVoid,
|
|
514
618
|
PersistenceError.refail,
|
|
515
619
|
withTracerDisabled
|
|
516
|
-
)
|
|
517
|
-
|
|
518
|
-
|
|
620
|
+
)
|
|
621
|
+
},
|
|
622
|
+
mysql: () => (address) => {
|
|
623
|
+
if (disableAdvisoryLocks) {
|
|
624
|
+
return sql`DELETE FROM ${locksTableSql} WHERE address = ${address}`.pipe(
|
|
625
|
+
PersistenceError.refail,
|
|
626
|
+
withTracerDisabled
|
|
627
|
+
)
|
|
628
|
+
}
|
|
629
|
+
return sql`SELECT RELEASE_ALL_LOCKS()`.pipe(
|
|
519
630
|
execWithLockConn,
|
|
520
631
|
Effect.asVoid,
|
|
521
632
|
PersistenceError.refail,
|
|
522
633
|
withTracerDisabled
|
|
523
|
-
)
|
|
634
|
+
)
|
|
635
|
+
},
|
|
524
636
|
orElse: () => (address) =>
|
|
525
637
|
sql`DELETE FROM ${locksTableSql} WHERE address = ${address}`.pipe(
|
|
526
638
|
PersistenceError.refail,
|