@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.
@@ -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: () => Effect.void,
142
- pg: () => Effect.void,
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
- Effect.fnUntraced(function*(_address: string, shardIds: ReadonlyArray<string>) {
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
- Effect.fnUntraced(function*(_address: string, shardIds: ReadonlyArray<string>) {
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: () => acquireLock,
403
- mysql: () => acquireLock,
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
- Effect.fnUntraced(
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
- mysql: () =>
486
- Effect.fnUntraced(
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: () => (_address) =>
511
- sql`SELECT pg_advisory_unlock_all()`.pipe(
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
- mysql: () => (_address) =>
518
- sql`SELECT RELEASE_ALL_LOCKS()`.pipe(
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,