@shivaedev/sql 0.33.0 → 0.34.0
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/CHANGELOG.md +26 -0
- package/README.md +102 -9
- package/dist/MigrationStartedAt.d.ts.map +1 -0
- package/dist/{internal/MigrationStartedAt.js → MigrationStartedAt.js} +1 -1
- package/dist/MigrationStartedAt.js.map +1 -0
- package/dist/batchWrites.d.ts +1 -1
- package/dist/batchWrites.d.ts.map +1 -1
- package/dist/batchWrites.js.map +1 -1
- package/dist/batchedDelete.d.ts +9 -2
- package/dist/batchedDelete.d.ts.map +1 -1
- package/dist/batchedDelete.js +34 -35
- package/dist/batchedDelete.js.map +1 -1
- package/dist/changes.d.ts +8 -0
- package/dist/changes.d.ts.map +1 -0
- package/dist/changes.js +7 -0
- package/dist/changes.js.map +1 -0
- package/dist/deleteGroupsBatched.d.ts +13 -0
- package/dist/deleteGroupsBatched.d.ts.map +1 -0
- package/dist/deleteGroupsBatched.js +23 -0
- package/dist/deleteGroupsBatched.js.map +1 -0
- package/dist/insertFromSelect.d.ts +1 -1
- package/dist/insertFromSelect.d.ts.map +1 -1
- package/dist/insertFromSelect.js.map +1 -1
- package/dist/invalidationKeys.d.ts +8 -0
- package/dist/invalidationKeys.d.ts.map +1 -0
- package/dist/invalidationKeys.js +15 -0
- package/dist/invalidationKeys.js.map +1 -0
- package/dist/keysetPage.d.ts +26 -0
- package/dist/keysetPage.d.ts.map +1 -0
- package/dist/keysetPage.js +23 -0
- package/dist/keysetPage.js.map +1 -0
- package/dist/makeConflictClause.d.ts +1 -1
- package/dist/makeConflictClause.d.ts.map +1 -1
- package/dist/makeConflictClause.js +24 -2
- package/dist/makeConflictClause.js.map +1 -1
- package/dist/migrationLock.d.ts.map +1 -0
- package/dist/migrationLock.js.map +1 -0
- package/dist/migrations.d.ts +1 -1
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js +18 -9
- package/dist/migrations.js.map +1 -1
- package/dist/postgresChanges.d.ts.map +1 -1
- package/dist/postgresChanges.js +8 -4
- package/dist/postgresChanges.js.map +1 -1
- package/dist/postgresLayer.d.ts +2 -1
- package/dist/postgresLayer.d.ts.map +1 -1
- package/dist/postgresLayer.js +1 -1
- package/dist/postgresLayer.js.map +1 -1
- package/dist/postgresRelease.js +1 -1
- package/dist/postgresRelease.js.map +1 -1
- package/dist/repositoryChanges.d.ts.map +1 -1
- package/dist/repositoryChanges.js +4 -3
- package/dist/repositoryChanges.js.map +1 -1
- package/dist/transact.d.ts +13 -5
- package/dist/transact.d.ts.map +1 -1
- package/dist/transact.js +45 -19
- package/dist/transact.js.map +1 -1
- package/dist/versionedStatements.d.ts +1 -1
- package/package.json +7 -6
- package/src/{internal/MigrationStartedAt.ts → MigrationStartedAt.ts} +1 -1
- package/src/batchWrites.ts +2 -1
- package/src/batchedDelete.ts +53 -40
- package/src/changes.ts +12 -0
- package/src/deleteGroupsBatched.ts +41 -0
- package/src/insertFromSelect.ts +1 -1
- package/src/invalidationKeys.ts +24 -0
- package/src/keysetPage.ts +54 -0
- package/src/makeConflictClause.ts +31 -2
- package/src/migrations.ts +21 -10
- package/src/postgresChanges.ts +13 -4
- package/src/postgresLayer.ts +1 -1
- package/src/postgresRelease.ts +1 -1
- package/src/repositoryChanges.ts +4 -3
- package/src/transact.ts +61 -24
- package/dist/internal/CommandRecovery.d.ts +0 -3
- package/dist/internal/CommandRecovery.d.ts.map +0 -1
- package/dist/internal/CommandRecovery.js +0 -5
- package/dist/internal/CommandRecovery.js.map +0 -1
- package/dist/internal/MigrationStartedAt.d.ts.map +0 -1
- package/dist/internal/MigrationStartedAt.js.map +0 -1
- package/dist/internal/migrationLock.d.ts.map +0 -1
- package/dist/internal/migrationLock.js.map +0 -1
- package/src/internal/CommandRecovery.ts +0 -5
- /package/dist/{internal/MigrationStartedAt.d.ts → MigrationStartedAt.d.ts} +0 -0
- /package/dist/{internal/migrationLock.d.ts → migrationLock.d.ts} +0 -0
- /package/dist/{internal/migrationLock.js → migrationLock.js} +0 -0
- /package/src/{internal/migrationLock.ts → migrationLock.ts} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.34.0 - 2026-11-03
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- The `postgresChanges` binding implements `CommandPublication` with PostgreSQL publication and recovery through local resynchronization and a Resync broadcast.
|
|
8
|
+
- `keysetPage` supplies native SQL seek and ordering fragments, validates positive integer page sizes, and returns rows with an optional continuation from one row of lookahead. Callers supply a unique tie-breaker, predicates, result decoding and cursor values, including exact PostgreSQL timestamp text for microsecond continuation.
|
|
9
|
+
- `sql`'s `postgresLayer` provides its own Reactivity service alongside `PgPool`, `PgClient` and `SqlClient`.
|
|
10
|
+
- Declare raw SQL writers' table, record and bucket keys once with `changes(covers)` from `@shivaedev/sql/changes.ts`. Record the declaration with `record()` and pass it directly to `withChangeCoverage` to check announcements against PostgreSQL writes.
|
|
11
|
+
- Keep a recording local with the typed `{ publication: "local" }` option on `record()` or `invalidateOnCommit`. Local readers refresh after commit without cross-process publication; rollback discards the recording. Shared recordings in the same transaction still publish.
|
|
12
|
+
- `deleteGroupsBatched` selects an ordered prefix of whole groups within per-batch and total row budgets, then deletes each batch in an independent transaction. Group selection and deletion use native Effects and SQL; repository deletes publish item and bucket refreshes after commit. A failed batch rolls back its deletes and announcements while preserving earlier batches.
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- Omit database causes from PostgreSQL change listener outage warnings and reconnect debug logs while preserving reconnection and live-stream resynchronization.
|
|
17
|
+
- Reject a migration ID recorded under a different name with a typed `BadState` migration error before running new migrations, schema components or release publication, matching readiness's ID and name checks.
|
|
18
|
+
- Match partial unique indexes in conflict writes under PostgreSQL generic plans as well as the default plan mode. Encoded conflict-target predicates use PostgreSQL-quoted constants, with one preliminary quoting query per write; full-index targets and bound write values are unchanged.
|
|
19
|
+
- PostgreSQL retention purges refresh item and authorized bucket query results after each committed batch without requiring collection-wide read permission. Batches remain bounded and commit independently, with constant-size refresh hints.
|
|
20
|
+
- Repository writes from workers refresh matching session-scoped queries over the application's change channel. Every subscriber rechecks its own read policy before receiving hints or results, including rerun-and-compare queries. Client caches retain their registry isolation.
|
|
21
|
+
- Refresh matching subscribers when `invalidateOnCommit` receives typed collection keys. List, item and bucket keys use the shared invalidation encoding before commit buffering; arrays may mix typed and raw Reactivity keys, and record-form invalidations retain their behavior.
|
|
22
|
+
|
|
23
|
+
### Breaking
|
|
24
|
+
|
|
25
|
+
- Replace `CommandRecovery` from `@shivaedev/sql/internal/CommandRecovery.ts` with `CommandPublication` from `@shivaedev/changes/commandPublication.ts`: yield the port and run its `recover` Effect. Custom providers now supply `{ publish, recover }`; both operations fail with `CommandPublicationError`, so consumers must handle recovery failures. `postgresChanges` provides both operations.
|
|
26
|
+
- Import `MigrationStartedAt` from `@shivaedev/sql/MigrationStartedAt.ts` instead of `@shivaedev/sql/internal/MigrationStartedAt.ts`. Its service identifier is now `@shivaedev/sql/MigrationStartedAt`; provide the exported reference.
|
|
27
|
+
- Import `migrationLock` from `@shivaedev/sql/migrationLock.ts` instead of `@shivaedev/sql/internal/migrationLock.ts`. The SQL `internal` import paths are removed without aliases.
|
|
28
|
+
|
|
3
29
|
## 0.33.0 - 2026-11-02
|
|
4
30
|
|
|
5
31
|
### Added
|
package/README.md
CHANGED
|
@@ -54,7 +54,7 @@ Pair this preset with server's [cache trim and connection lifetime](https://gith
|
|
|
54
54
|
|
|
55
55
|
### One PostgreSQL pool per application
|
|
56
56
|
|
|
57
|
-
`postgresLayer(url, options)` from `@shivaedev/sql/postgresLayer.ts` provides the native `PgPool` service from `@effect/sql-pg/PgPool`, `PgClient
|
|
57
|
+
`postgresLayer(url, options)` from `@shivaedev/sql/postgresLayer.ts` provides the native `PgPool` service from `@effect/sql-pg/PgPool`, `PgClient`, `SqlClient` and a database-local Reactivity service together. `options` accepts `applicationName`, `maxConnections`, `minConnections`, `connectionTTL`, `idleTimeout`, `transformQueryNames` and `transformResultNames`. Queries, transactions and raw connections reserved with `yield* pool.reserve` share the same `maxConnections` budget. A reservation holds its connection until its scope closes; release it before waiting for work that needs that slot.
|
|
58
58
|
|
|
59
59
|
```ts
|
|
60
60
|
import * as PgPool from "@effect/sql-pg/PgPool";
|
|
@@ -366,6 +366,39 @@ const rows = yield* paymentsBySequence([]);
|
|
|
366
366
|
|
|
367
367
|
`rows` is empty even when the table contains payments. The helper takes its column explicitly and leaves native `SqlSchema` visible. Use native SQL with composed schemas for joins; this helper adds no query layer or typed join.
|
|
368
368
|
|
|
369
|
+
### Keyset pages over native SQL
|
|
370
|
+
|
|
371
|
+
`keysetPage` builds seek and ordering fragments and asks your `read` callback for one extra row. It returns `{ rows, next }`, where `next` is an `Option` containing the last returned row's cursor only when another row exists. Your query keeps its predicates, joins and result schema:
|
|
372
|
+
|
|
373
|
+
```ts
|
|
374
|
+
import { Effect, Schema } from "effect";
|
|
375
|
+
import { SqlClient } from "effect/sql";
|
|
376
|
+
import { keysetPage } from "@shivaedev/sql/keysetPage.ts";
|
|
377
|
+
|
|
378
|
+
const Cursor = Schema.Struct({ at: Schema.String, id: Schema.Number });
|
|
379
|
+
const PageRow = Schema.Struct({ id: Schema.Number, cursorAt: Schema.String, updatedAt: Schema.Date });
|
|
380
|
+
|
|
381
|
+
const listNotes = Effect.fnUntraced(function* (owner: string, after?: typeof Cursor.Type) {
|
|
382
|
+
const sql = yield* SqlClient.SqlClient;
|
|
383
|
+
return yield* keysetPage({
|
|
384
|
+
limit: 20,
|
|
385
|
+
after,
|
|
386
|
+
orderBy: [{ column: sql`n.updated_at`, direction: "desc", value: (cursor: typeof Cursor.Type) => cursor.at }],
|
|
387
|
+
tieBreaker: { column: sql`n.id`, direction: "desc", value: (cursor) => cursor.id },
|
|
388
|
+
cursorFor: (row: typeof PageRow.Type) => ({ at: row.cursorAt, id: row.id }),
|
|
389
|
+
read: ({ seek, orderBy, limit }) =>
|
|
390
|
+
sql`select n.id, n.updated_at as "updatedAt", to_json(n.updated_at) #>> '{}' as "cursorAt"
|
|
391
|
+
from notes n where n.owner = ${owner} and ${seek} order by ${orderBy} limit ${limit}`.pipe(
|
|
392
|
+
Effect.flatMap(Schema.decodeUnknownEffect(Schema.Array(PageRow))),
|
|
393
|
+
),
|
|
394
|
+
});
|
|
395
|
+
});
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
All ordering expressions must be non-null. The required `tieBreaker` must be unique across the query's rows, normally a primary key; the caller owns that database constraint. It is appended to `orderBy`, which can be empty when the unique key alone orders the list. Each column can be a name or a native fragment, and each direction can be `asc` or `desc`, including mixed directions. `read` must apply the supplied seek predicate, ordering and lookahead limit. Page sizes must be positive safe integers smaller than `Number.MAX_SAFE_INTEGER`; invalid sizes fail with `SchemaError` before `read` runs.
|
|
399
|
+
|
|
400
|
+
Keep timestamp continuation values separate from decoded dates: the example projects the PostgreSQL `timestamptz` value as exact text with `to_json`, retaining microseconds while the display field uses `Schema.Date`. Order and compare the original timestamp column, and return the text unchanged through `cursorFor`. Cursor values accept strings, numbers, bigints and booleans, excluding `Date` and null. Cursor encoding and request validation stay with the caller. Reuse the same predicates and ordering on subsequent requests. This is a page read, with no snapshot or live infinite-list behavior.
|
|
401
|
+
|
|
369
402
|
### Composite keys and idempotent writes
|
|
370
403
|
|
|
371
404
|
A generated app can ingest the same reading more than once. Declare the table's primary key with an `idColumn` tuple and its unique conflict key with `conflictColumns`:
|
|
@@ -419,7 +452,7 @@ const entries = yield* makeRepository(Entry, {
|
|
|
419
452
|
const inserted = yield* entries.insertManyOrIgnore(inputs);
|
|
420
453
|
```
|
|
421
454
|
|
|
422
|
-
All four conflict methods use this target, producing `ON CONFLICT (a, b) WHERE kind =
|
|
455
|
+
All four conflict methods use this target, producing `ON CONFLICT (a, b) WHERE kind = 'x'` before `DO NOTHING` or `DO UPDATE SET ...`, followed by `RETURNING`. `conflictWhere` uses typed equality entries like `findMany.where`: decoded field values pass through their field codecs. PostgreSQL quotes the encoded target values in one preliminary query so the conflict target contains constants and matches the partial index under both default and generic plans. Insert values and `updateWhere` operands remain bound parameters. Rows outside the index predicate can insert with the same key. A target that matches no unique index fails with native `SqlError` in the error channel. Use native `SqlClient` and `SqlSchema` for SQL expressions or other conflict policies.
|
|
423
456
|
|
|
424
457
|
Declare conflict behavior per field in the same `updateColumns` list:
|
|
425
458
|
|
|
@@ -476,7 +509,7 @@ Each batch call starts `transact`, so a later chunk failure rolls back earlier c
|
|
|
476
509
|
|
|
477
510
|
On PostgreSQL, `deleteBatched({ where, batchSize, maxRows, pause })` removes matching rows in independently committed transactions and returns the number removed. `where` uses the same encoded filters as `findMany`, including comparisons. Each batch deletes at most `batchSize` rows; the last batch shrinks to respect `maxRows`. `batchSize` is a positive safe integer and `maxRows` is a nonnegative safe integer. A zero cap or no matching rows returns zero without announcing changes.
|
|
478
511
|
|
|
479
|
-
Each nonempty batch publishes one
|
|
512
|
+
Each nonempty batch publishes one collection-wide change hint after commit, so table, item and bucket subscribers refresh after each batch. The hint has a constant size and does not enumerate deleted records or bucket values. Viewers keep their existing query permissions; a refresh does not require access to the whole table. A later failure rolls back only its batch and its hint; earlier batches remain committed. SQL failures remain typed `SqlError` and invalid limits fail with `SchemaError`.
|
|
480
513
|
|
|
481
514
|
`pause` is an Effect duration between full batches while the cap permits more work. The method holds a PostgreSQL session advisory lock for the run; another purge of that table skips and returns zero, even when called through another repository instance. The lock is released on completion, failure or interruption. The run reserves one connection for the lock and uses another for batch transactions: allow at least two pool connections, plus capacity for other work. Use a direct PostgreSQL connection or session pooling, since transaction pooling cannot preserve session locks. Rows locked by other transactions are skipped and can be removed by a later run. Purges have no ordering guarantee.
|
|
482
515
|
|
|
@@ -514,6 +547,41 @@ export const PurgeAudit = defineSchedule({
|
|
|
514
547
|
|
|
515
548
|
Here the application's `AuditEntry` model has a `createdAt` field decoded as `Date`. Register `PurgeAudit` in the jobs Layer's `jobs` list and provide the database and change-feed Layers to the worker.
|
|
516
549
|
|
|
550
|
+
#### Whole groups across tables
|
|
551
|
+
|
|
552
|
+
Use `deleteGroupsBatched` from `@shivaedev/sql/deleteGroupsBatched.ts` when a parent and its children must fit together. Keep `repository.deleteBatched` for single-table purges with constant-size collection hints. The grouped helper takes the same `batchSize`, `maxRows` and `pause`, plus a parent `tableName`, `select(limit)` and `delete(value)` callbacks. It shares the parent table's purge lock with single-table runs and requires PostgreSQL, at least two pool connections and a call outside transactions.
|
|
553
|
+
|
|
554
|
+
`select` runs inside each batch transaction and returns an ordered array of `{ value, rowCount }`. Return at most `limit` candidates, with a positive safe-integer count including every parent and child row that `delete` will remove. Lock candidate parents with native SQL, and have concurrent group writers take the same parent lock before changing children, so counts remain accurate through deletion. Count rows in a separate statement after acquiring the parent locks, as below, to include changes committed before lock acquisition. The helper selects the longest prefix that fits the smaller of the batch budget and the remaining total budget; it never splits a group or skips past an oversized first group. If no group fits, the run returns the count already removed. A partially filled batch can be followed by another batch after `pause`.
|
|
555
|
+
|
|
556
|
+
```ts
|
|
557
|
+
import { Effect } from "effect";
|
|
558
|
+
import { deleteGroupsBatched } from "@shivaedev/sql/deleteGroupsBatched.ts";
|
|
559
|
+
import { inValues } from "@shivaedev/sql/inValues.ts";
|
|
560
|
+
|
|
561
|
+
const removed = yield* deleteGroupsBatched({
|
|
562
|
+
tableName: "runs",
|
|
563
|
+
batchSize: 1_000,
|
|
564
|
+
maxRows: 50_000,
|
|
565
|
+
pause: "100 millis",
|
|
566
|
+
select: (limit) => Effect.gen(function* () {
|
|
567
|
+
const locked = yield* sql<{ id: number }>`
|
|
568
|
+
select id from runs where finished_at < ${cutoff}
|
|
569
|
+
order by id limit ${limit} for update skip locked`;
|
|
570
|
+
const selected = yield* inValues(sql`r.id`, locked.map(({ id }) => id));
|
|
571
|
+
return yield* sql<{ value: number; rowCount: number }>`
|
|
572
|
+
select r.id as value,
|
|
573
|
+
(1 + (select count(*) from run_steps s where s.run_id = r.id))::int as "rowCount"
|
|
574
|
+
from runs r where ${selected} order by r.id`;
|
|
575
|
+
}),
|
|
576
|
+
delete: (runId) => Effect.gen(function* () {
|
|
577
|
+
yield* steps.delete({ runId });
|
|
578
|
+
yield* runs.delete(runId);
|
|
579
|
+
}),
|
|
580
|
+
});
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
Here `sql` is the native client and `steps` and `runs` are repositories. The callbacks own eligibility, group membership, locking and accurate row counts; the helper owns budget selection and the independent commit boundary. Use repository deletes for each table, including children, so deleted items and declared owner buckets refresh after each committed batch. Unaffected owner readers receive no deleted identities. Raw SQL deletes instead need explicit `invalidateOnCommit` keys. A failure anywhere in a batch discards all of that batch's deletes and announcements, including earlier groups in the same batch; previous committed batches and refreshes remain. Callback failures and required services remain in the returned Effect, alongside SQL, schema and purge-availability errors.
|
|
584
|
+
|
|
517
585
|
### Optimistic concurrency
|
|
518
586
|
|
|
519
587
|
Declare a numeric version field in the model's select and update variants, leave it out of insert, and select it with `versionColumn`:
|
|
@@ -576,6 +644,25 @@ const saveInvoiceLine = (input: typeof InvoiceLine.update.Type) =>
|
|
|
576
644
|
|
|
577
645
|
`transact` wraps native `sql.withTransaction`. It maps typed `SqlError` failures through `onSqlError` and keeps other typed failures and service requirements. `invalidateOnCommit` records derived reader keys that a repository or command declaration does not already name; after the owning outermost commit, they are deduplicated and passed to native `Reactivity.invalidate` once. The record `{ invoiceLines: [1] }` marks `"invoiceLines"` and `"invoiceLines:1"`. An array such as `["invoiceLines:1"]` marks only those keys.
|
|
578
646
|
|
|
647
|
+
Arrays also accept typed keys from `collection`: a list key refreshes list subscribers, an item key refreshes that item and its list, and a bucket key refreshes that bucket. Subscribers register these keys through `readKeys` from `@shivaedev/changes/keys.ts`. Typed keys may share an array with raw Reactivity keys; raw keys keep their native identity.
|
|
648
|
+
|
|
649
|
+
For a raw SQL writer, declare affected records and buckets once with `changes` from `@shivaedev/sql/changes.ts`, grouped by their SQL table name. The returned declaration's `record()` action uses the same transaction boundary as repository writes:
|
|
650
|
+
|
|
651
|
+
```ts
|
|
652
|
+
import { changes } from "@shivaedev/sql/changes.ts";
|
|
653
|
+
|
|
654
|
+
const moved = changes({
|
|
655
|
+
notes: [notes.item(id), notes.bucket.listId(oldList), notes.bucket.listId(newList), notes.bucket.owner(owner)],
|
|
656
|
+
});
|
|
657
|
+
|
|
658
|
+
yield* Effect.gen(function* () {
|
|
659
|
+
yield* sql`update notes set "listId" = ${newList} where id = ${id}`;
|
|
660
|
+
yield* moved.record();
|
|
661
|
+
}).pipe(transact({ onSqlError: (error) => error }));
|
|
662
|
+
```
|
|
663
|
+
|
|
664
|
+
Use `moved.record({ publication: "local" })` to refresh local readers after commit without cross-process publication. `invalidateOnCommit(keys, { publication: "local" })` accepts the same typed option. The default is `"shared"`. Local and shared keys can coexist in one transaction; a key recorded both ways publishes once. Rollback discards both kinds. The option applies to that recording, so other writers in the transaction can still publish shared keys.
|
|
665
|
+
|
|
579
666
|
Mark bucket fields on the model with `bucket` from `@shivaedev/changes/buckets.ts`:
|
|
580
667
|
|
|
581
668
|
```ts
|
|
@@ -672,7 +759,7 @@ Use sequential numbers, not timestamps. Effect's Migrator skips any migration wh
|
|
|
672
759
|
|
|
673
760
|
`migrationConvention.ts` exports `MIGRATIONS_DIRECTORY` and `MIGRATION_FILE_PATTERN` so template packaging and repository quality configuration share the folder and filename convention.
|
|
674
761
|
|
|
675
|
-
`migratePostgres` rejects loaded migration IDs at or below the latest applied ID that are missing from the ledger with a typed native `MigrationError` (`kind: "BadState"`) naming those IDs.
|
|
762
|
+
`migratePostgres` rejects loaded migration IDs at or below the latest applied ID that are missing from the ledger with a typed native `MigrationError` (`kind: "BadState"`) naming those IDs. It also rejects a loaded ID recorded under a different name, matching readiness's ID and name checks. Both checks run before any new migration body, schema component or release publication. Later ledger entries absent from the loader are allowed for older releases.
|
|
676
763
|
|
|
677
764
|
`migratePostgres` discovers migrations, then takes an advisory lock before creating the ledger and running native Migrator. Runs with only numbered migrations use a transaction lock and work with a one-connection pool. Runs with schema components or a `release` use a session lock that stays held through the ledger transaction, all schema components and release publication, and releases on completion, failure or interruption. Both lock modes coordinate on the same ledger key. Concurrent runners on a fresh or existing ledger wait, then see the committed ledger and avoid repeating migrations; their schema components never overlap. Every runner of that ledger must use this helper and the same ledger name. The session lock reserves one pool connection while migration and component SQL use other connections: allow at least two connections for an active runner with components or a release, plus room for waiting runners and other work. Those runs require a direct PostgreSQL connection or session pooling. The required `lockTimeout` bounds acquisition of the advisory lock and lock waits in the ledger transaction; `Duration.infinity` disables those lock timeouts. Choose deployment and execution timeouts in the application.
|
|
678
765
|
|
|
@@ -702,6 +789,8 @@ const delivery = postgresChanges({
|
|
|
702
789
|
|
|
703
790
|
`pg` is the application's `@effect/sql-pg` client; the binding accepts its native SQL and `listen` methods without creating another pool. Optional `listenerClient` supplies a different native client for LISTEN; the application client still owns write notifications and heartbeat publication. Both clients must reach the same database. LISTEN requires a direct or session-mode connection. With one shared client, reserve one session connection for LISTEN and another for writes and heartbeats; use a pool client with at least two connections. With separate clients, the listener needs one connection of its own. Compose `delivery` into the Layer that provides feature services using `transact`, alongside the existing SQL and Reactivity services. The [reference server](https://github.com/ShivaeDev/platform/blob/main/packages/react/src/test-support/postgres-example/server.ts) shows the complete composition. The binding sends `select pg_notify(...)` through the writing transaction's `SqlClient`, so a rollback wakes no process. Its transaction sink leaves delivery to the listener, including in the writing process. Unframed `invalidateOnCommit` calls retain local invalidation; cross-process writes must use `transact`.
|
|
704
791
|
|
|
792
|
+
The Layer also provides `CommandPublication` from `@shivaedev/changes/commandPublication.ts`. Call `publish(keys)` after a command's write commits to announce its declared keys through the bound client. An enclosing `transact` delays these keys until its outer commit and discards them on rollback. Publication failures use the typed `CommandPublicationError`; `recover` resynchronizes local streams and broadcasts Resync through PostgreSQL. Recovery failures use the same typed error, so the command boundary can log a delivery gap while preserving the committed result.
|
|
793
|
+
|
|
705
794
|
`PostgresChangesOptions` takes `channel`, `client`, `resync`, optional `listenerClient` and `connectionSetting` (a diagnostic label, default `"client"`), and optional `heartbeatInterval`, `heartbeatTimeout` and `retryDelay` Effect durations. The defaults are 5 seconds, 10 seconds and 1 second. Acquisition waits for LISTEN and then a heartbeat received through it, each within `heartbeatTimeout`. A deadline without a heartbeat fails startup with `PostgresListenerError` from `@shivaedev/sql/PostgresListenerError.ts`, whose `connectionSetting` and message identify the connection to fix. Native SQL failures still fail startup with `SqlError`. Established listeners reconnect after a loss. Loss warnings and reconnect debug logs omit database causes. A missing heartbeat or a stream failure resyncs streams before listener cleanup; a successful reconnection resyncs again. Listener cleanup runs in a parallel sub-scope bounded by `heartbeatTimeout`, so stalled listener cleanup cannot delay native connection release, a fresh subscription or scope shutdown. The heartbeat detects a listener that stops delivering without failing. `resync` is an infallible `Effect.Effect<number>` returning the number of open live streams it marked for reconciliation. `makeLiveServer.resync` provides this count and stops counting a stream when its scope closes. Direct bindings with another stream owner must return that owner's count. Provide the Layer for the process lifetime so scope release closes the listener.
|
|
706
795
|
|
|
707
796
|
NOTIFY is a wake-up signal, not a durable log. Transaction payloads contain only string or finite-number invalidation keys, with no row values. PostgreSQL requires each payload to be smaller than 8,000 bytes in the default configuration. The binding splits JSON arrays by their UTF-8 byte length; one key that cannot fit, or a nonserializable key type, fails with `SqlError` before commit and rolls back the write. Heartbeats use a separate control payload on the same channel. Applications choose a channel shared only by their cooperating processes. LISTEN requires a session connection; PgBouncer transaction pooling needs another delivery adapter. Missed notifications are reconciled by querying again on resync, with no replay guarantee.
|
|
@@ -711,6 +800,7 @@ NOTIFY is a wake-up signal, not a durable log. Transaction payloads contain only
|
|
|
711
800
|
Import the module that defines the name; there is no root entry.
|
|
712
801
|
- `@shivaedev/sql/clientOptions.ts`: `clientOptions(options, { snakeCase? })`, `SqlNameTransforms` — Add default native name transforms to driver or app host options; opt out with `snakeCase: false`.
|
|
713
802
|
- `@shivaedev/sql/inValues.ts`: `inValues(column, values)` — Native list predicate with match-nothing semantics for an empty list; requires `SqlClient`.
|
|
803
|
+
- `@shivaedev/sql/keysetPage.ts`: `keysetPage(options)`, `KeysetOrder`, `KeysetPageQuery`, `KeysetPage` — Native seek and order fragments with a required unique tie-breaker, validated page size and one-row lookahead; predicates, result schemas and cursor formats stay with the caller.
|
|
714
804
|
- `@shivaedev/sql/int8.ts`: `int8(field)` — PostgreSQL bigint storage codec using the supplied field schema.
|
|
715
805
|
- `@shivaedev/sql/int4Array.ts`: `int4Array(field)` — PostgreSQL integer-array storage codec, including typed empty arrays.
|
|
716
806
|
- `@shivaedev/sql/repository.ts`: `makeRepository(model, options)` — `tableName`, scalar or composite `idColumn`, `spanPrefix`, optional `versionColumn`, `conflictColumns` and `conflictWhere`, and `collection` required for bucket models; requires native `SqlClient`.
|
|
@@ -720,7 +810,7 @@ Import the module that defines the name; there is no root entry.
|
|
|
720
810
|
- `@shivaedev/sql/conditionalUpsert.ts`: `UpsertFilter`, `UpsertResult` — The `updateWhere` filter shape and the tagged `Inserted`, `Updated` or `Unchanged` result of a conditional upsert.
|
|
721
811
|
- `@shivaedev/sql/mapSqlError.ts`: `mapSqlError({ constraints, fallback })` — Mapper from named constraint violations to application errors, with a fallback for every other `SqlError`.
|
|
722
812
|
- `@shivaedev/sql/transact.ts`: `transact(effect, options)` or `effect.pipe(transact(options))` — Required `onSqlError` mapper; requires `SqlClient` and `Reactivity`.
|
|
723
|
-
- `@shivaedev/sql/transact.ts`: `invalidateOnCommit(keys)` — Records keys for its client, or invalidates immediately outside a transaction.
|
|
813
|
+
- `@shivaedev/sql/transact.ts`: `invalidateOnCommit(keys, options?)` — Records keys for its client, or invalidates immediately outside a transaction.
|
|
724
814
|
- `@shivaedev/sql/advisoryXactLock.ts`: `advisoryXactLock(key)`, `AdvisoryLockOutsideTransaction` — PostgreSQL transaction lock on a bigint or string hashed with `hashtextextended(key, 0)`; requires an active transaction on the current `SqlClient`.
|
|
725
815
|
- `@shivaedev/sql/migrationConvention.ts`: `MIGRATIONS_DIRECTORY`, `MIGRATION_FILE_PATTERN` — Application folder and four-digit sequential snake_case TypeScript filename pattern.
|
|
726
816
|
- `@shivaedev/sql/migrationsFromDirectory.ts`: `migrationsFromDirectory(root)` — Discover migrations under the application root with native `FileSystem` and `Path`.
|
|
@@ -730,6 +820,7 @@ Import the module that defines the name; there is no root entry.
|
|
|
730
820
|
- `@shivaedev/sql/postgresRelease.ts`: `postgresRelease(release, table?)`, `readPostgresRelease(table?)` — Release marker schema component and its reader.
|
|
731
821
|
- `@shivaedev/sql/postgresChanges.ts`: `postgresChanges(options)` — Scoped PostgreSQL LISTEN/NOTIFY Layer for commit-bound invalidation.
|
|
732
822
|
- `@shivaedev/sql/repositoryCollection.ts`: `BucketRegistrationError` — Registration failure with table, collection and bucket field names.
|
|
823
|
+
- `@shivaedev/sql/changes.ts`: `changes(covers)`, `ChangeDeclaration` — Declares a raw writer's table, record and bucket keys once, with a `record(options?)` action.
|
|
733
824
|
- `@shivaedev/sql/testing.ts`: `withChangeCoverage(body, { covers })`, `expectPublished(keys)`, `UnannouncedWrites`, `PublishedKeysMismatch` — Test assertions that every write announced its keys and that the expected keys were published.
|
|
734
825
|
|
|
735
826
|
### Install and database setup
|
|
@@ -792,17 +883,19 @@ JSON and JSONB columns are not supported. A repository write of a plain object t
|
|
|
792
883
|
|
|
793
884
|
The package's PostgreSQL tests use a disposable database through `PLATFORM_SQL_TEST_DATABASE_URL`. Without the URL they skip locally; under `CI`, collection fails at the shared gate.
|
|
794
885
|
|
|
795
|
-
The sql story kit exposes `withChangeCoverage` and `expectPublished`, also exported from `@shivaedev/sql/testing.ts`. `withChangeCoverage(body, { covers })` checks PostgreSQL transaction write counters before commit and requires every declared record and bucket key to be recorded. Repository writes supply these declarations automatically. Raw
|
|
886
|
+
The sql story kit exposes `withChangeCoverage` and `expectPublished`, also exported from `@shivaedev/sql/testing.ts`. `withChangeCoverage(body, { covers })` checks PostgreSQL transaction write counters before commit and requires every declared record and bucket key to be recorded. Repository writes supply these declarations automatically. Raw writers can pass the declaration returned by `changes` directly to `withChangeCoverage` and record it through `record()`; a table key alone covers nothing. Local recordings satisfy coverage too. Missing announcements fail with `UnannouncedWrites` and roll back the transaction.
|
|
796
887
|
|
|
797
888
|
```ts
|
|
798
|
-
const changed =
|
|
889
|
+
const changed = changes({
|
|
890
|
+
notes: [notes.item(id), notes.bucket.listId(oldList), notes.bucket.listId(newList), notes.bucket.owner(owner)],
|
|
891
|
+
});
|
|
799
892
|
|
|
800
893
|
yield* withChangeCoverage(
|
|
801
894
|
Effect.gen(function* () {
|
|
802
895
|
yield* sql`update notes set "listId" = ${newList} where id = ${id}`;
|
|
803
|
-
yield*
|
|
896
|
+
yield* changed.record();
|
|
804
897
|
}),
|
|
805
|
-
|
|
898
|
+
changed,
|
|
806
899
|
);
|
|
807
900
|
```
|
|
808
901
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MigrationStartedAt.d.ts","sourceRoot":"","sources":["../src/MigrationStartedAt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAEjC,eAAO,MAAM,kBAAkB,uCAE7B,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Context } from "effect";
|
|
2
|
-
export const MigrationStartedAt = Context.Reference("@shivaedev/sql/
|
|
2
|
+
export const MigrationStartedAt = Context.Reference("@shivaedev/sql/MigrationStartedAt", {
|
|
3
3
|
defaultValue: () => undefined,
|
|
4
4
|
});
|
|
5
5
|
//# sourceMappingURL=MigrationStartedAt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MigrationStartedAt.js","sourceRoot":"","sources":["../src/MigrationStartedAt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAEjC,MAAM,CAAC,MAAM,kBAAkB,GAAG,OAAO,CAAC,SAAS,CAAqB,mCAAmC,EAAE;IAC5G,YAAY,EAAE,GAAG,EAAE,CAAC,SAAS;CAC7B,CAAC,CAAC"}
|
package/dist/batchWrites.d.ts
CHANGED
|
@@ -12,5 +12,5 @@ export declare function makeInsertMany<TServices = never>(model: Model.Any, sql:
|
|
|
12
12
|
readonly tableName: string;
|
|
13
13
|
readonly spanPrefix: string;
|
|
14
14
|
readonly changes: RepositoryChanges;
|
|
15
|
-
}, operation: string, prepare?: (row: Readonly<Record<string, unknown>>) => Effect.Effect<Fragment, Schema.SchemaError, TServices>, upsert?: boolean): (inputs: readonly unknown[]) => Effect.Effect<unknown[], Schema.SchemaError | SqlError, unknown>;
|
|
15
|
+
}, operation: string, prepare?: (row: Readonly<Record<string, unknown>>) => Effect.Effect<Fragment, SqlError | Schema.SchemaError, TServices>, upsert?: boolean): (inputs: readonly unknown[]) => Effect.Effect<unknown[], Schema.SchemaError | SqlError, unknown>;
|
|
16
16
|
//# sourceMappingURL=batchWrites.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"batchWrites.d.ts","sourceRoot":"","sources":["../src/batchWrites.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAGhE,MAAM,MAAM,UAAU,CAAC,MAAM,SAAS,KAAK,CAAC,GAAG,EAAE,SAAS,GAAG,KAAK,IAAI,CACrE,MAAM,EAAE,SAAS,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,EAAE,KACvC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,QAAQ,GAAG,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,kBAAkB,CAAC,GAAG,MAAM,CAAC,kBAAkB,CAAC,GAAG,SAAS,CAAC,CAAC;AAEnJ,MAAM,WAAW,WAAW,CAAC,MAAM,SAAS,KAAK,CAAC,GAAG;IACpD,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;CACxC;AAED,wBAAgB,cAAc,CAAC,SAAS,GAAG,KAAK,EAC/C,KAAK,EAAE,KAAK,CAAC,GAAG,EAChB,GAAG,EAAE,SAAS,CAAC,SAAS,EACxB,OAAO,EAAE;IAAE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,iBAAiB,CAAA;CAAE,EACzG,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,CAAC,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,WAAW,EAAE,SAAS,
|
|
1
|
+
{"version":3,"file":"batchWrites.d.ts","sourceRoot":"","sources":["../src/batchWrites.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAGhE,MAAM,MAAM,UAAU,CAAC,MAAM,SAAS,KAAK,CAAC,GAAG,EAAE,SAAS,GAAG,KAAK,IAAI,CACrE,MAAM,EAAE,SAAS,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,EAAE,KACvC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,QAAQ,GAAG,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,kBAAkB,CAAC,GAAG,MAAM,CAAC,kBAAkB,CAAC,GAAG,SAAS,CAAC,CAAC;AAEnJ,MAAM,WAAW,WAAW,CAAC,MAAM,SAAS,KAAK,CAAC,GAAG;IACpD,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;CACxC;AAED,wBAAgB,cAAc,CAAC,SAAS,GAAG,KAAK,EAC/C,KAAK,EAAE,KAAK,CAAC,GAAG,EAChB,GAAG,EAAE,SAAS,CAAC,SAAS,EACxB,OAAO,EAAE;IAAE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,iBAAiB,CAAA;CAAE,EACzG,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,CAAC,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAAC,WAAW,EAAE,SAAS,CAC/F,EACtB,MAAM,UAAQ,YAOE,SAAS,OAAO,EAAE,sEA2ClC"}
|
package/dist/batchWrites.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"batchWrites.js","sourceRoot":"","sources":["../src/batchWrites.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAMxC,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAUvE,MAAM,UAAU,cAAc,CAC7B,KAAgB,EAChB,GAAwB,EACxB,OAAyG,EACzG,SAAiB,EACjB,OAAO,
|
|
1
|
+
{"version":3,"file":"batchWrites.js","sourceRoot":"","sources":["../src/batchWrites.ts"],"names":[],"mappings":"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"}
|
package/dist/batchedDelete.d.ts
CHANGED
|
@@ -4,11 +4,10 @@ import { SqlClient } from "effect/sql";
|
|
|
4
4
|
import type { SqlError } from "effect/sql/SqlError";
|
|
5
5
|
import type { Fragment } from "effect/sql/Statement";
|
|
6
6
|
import { type FindManyFilter } from "#repositoryFilter.ts";
|
|
7
|
-
export interface
|
|
7
|
+
export interface PurgeLimits {
|
|
8
8
|
readonly batchSize: number;
|
|
9
9
|
readonly maxRows: number;
|
|
10
10
|
readonly pause: Duration.Input;
|
|
11
|
-
readonly where: FindManyFilter<TModel>;
|
|
12
11
|
}
|
|
13
12
|
declare const BatchedDeleteUnavailable_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
14
13
|
readonly _tag: "BatchedDeleteUnavailable";
|
|
@@ -17,6 +16,14 @@ export declare class BatchedDeleteUnavailable extends BatchedDeleteUnavailable_b
|
|
|
17
16
|
readonly reason: "transaction" | "dialect";
|
|
18
17
|
}> {
|
|
19
18
|
}
|
|
19
|
+
export declare const PositiveRowCount: Schema.Number;
|
|
20
|
+
export declare const runPurgeBatches: <E, R>(tableName: string, options: PurgeLimits, batch: (limit: number) => Effect.Effect<{
|
|
21
|
+
readonly count: number;
|
|
22
|
+
readonly done: boolean;
|
|
23
|
+
}, E, R>, prepare?: Effect.Effect<unknown, E, R> | undefined) => Effect.Effect<number, E | BatchedDeleteUnavailable | Schema.SchemaError | SqlError, R | import("effect/Scope").Scope | SqlClient.SqlClient>;
|
|
24
|
+
export interface BatchedDeleteOptions<TModel extends Model.Any> extends PurgeLimits {
|
|
25
|
+
readonly where: FindManyFilter<TModel>;
|
|
26
|
+
}
|
|
20
27
|
export interface BatchedDelete<TModel extends Model.Any> {
|
|
21
28
|
readonly deleteBatched: (options: BatchedDeleteOptions<TModel>) => Effect.Effect<number, SqlError | Schema.SchemaError | BatchedDeleteUnavailable, TModel["fields"][keyof TModel["fields"]]["EncodingServices"]>;
|
|
22
29
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"batchedDelete.d.ts","sourceRoot":"","sources":["../src/batchedDelete.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,QAAQ,EAAE,MAAM,EAAU,MAAM,EAAE,MAAM,QAAQ,CAAC;AACrE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAa,MAAM,YAAY,CAAC;AAClD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAErD,OAAO,EAAE,KAAK,cAAc,EAAc,MAAM,sBAAsB,CAAC;AAGvE,MAAM,WAAW,
|
|
1
|
+
{"version":3,"file":"batchedDelete.d.ts","sourceRoot":"","sources":["../src/batchedDelete.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,QAAQ,EAAE,MAAM,EAAU,MAAM,EAAE,MAAM,QAAQ,CAAC;AACrE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAa,MAAM,YAAY,CAAC;AAClD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAErD,OAAO,EAAE,KAAK,cAAc,EAAc,MAAM,sBAAsB,CAAC;AAGvE,MAAM,WAAW,WAAW;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,KAAK,CAAC;CAC/B;;;;AAED,qBAAa,wBAAyB,SAAQ,8BAA6C;IAC1F,QAAQ,CAAC,MAAM,EAAE,aAAa,GAAG,SAAS,CAAC;CAC3C,CAAC;CAAG;AAEL,eAAO,MAAM,gBAAgB,eAAoH,CAAC;AAIlJ,eAAO,MAAM,eAAe,GAAgC,CAAC,EAAE,CAAC,0DAGhD,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;CAAE,EAAE,CAAC,EAAE,CAAC,CAAC,oMA2BhG,CAAC;AAEH,MAAM,WAAW,oBAAoB,CAAC,MAAM,SAAS,KAAK,CAAC,GAAG,CAAE,SAAQ,WAAW;IAClF,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;CACvC;AAED,MAAM,WAAW,aAAa,CAAC,MAAM,SAAS,KAAK,CAAC,GAAG;IACtD,QAAQ,CAAC,aAAa,EAAE,CACvB,OAAO,EAAE,oBAAoB,CAAC,MAAM,CAAC,KACjC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,GAAG,MAAM,CAAC,WAAW,GAAG,wBAAwB,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC;CACnJ;AAED,eAAO,MAAM,iBAAiB;wBAEoB,MAAM;yBAAuB,MAAM;;;;;;8BAwBnE,oBAAoB,CAAC,KAAK,CAAC,GAAG,CAAC,0HAc/C,CAAC"}
|
package/dist/batchedDelete.js
CHANGED
|
@@ -4,20 +4,38 @@ import { makeFilter } from "#repositoryFilter.ts";
|
|
|
4
4
|
import { tryPurgeLock } from "#tryPurgeLock.ts";
|
|
5
5
|
export class BatchedDeleteUnavailable extends Data.TaggedError("BatchedDeleteUnavailable") {
|
|
6
6
|
}
|
|
7
|
-
const
|
|
7
|
+
export const PositiveRowCount = Schema.Number.check(Schema.isInt(), Schema.isGreaterThan(0), Schema.isLessThanOrEqualTo(Number.MAX_SAFE_INTEGER));
|
|
8
8
|
const Nonnegative = Schema.Number.check(Schema.isInt(), Schema.isGreaterThanOrEqualTo(0), Schema.isLessThanOrEqualTo(Number.MAX_SAFE_INTEGER));
|
|
9
|
-
const Limits = Schema.Struct({ batchSize:
|
|
9
|
+
const Limits = Schema.Struct({ batchSize: PositiveRowCount, maxRows: Nonnegative });
|
|
10
|
+
export const runPurgeBatches = Effect.fnUntraced(function* (tableName, options, batch, prepare) {
|
|
11
|
+
const sql = yield* SqlClient.SqlClient;
|
|
12
|
+
if (sql.driver !== "pg") {
|
|
13
|
+
return yield* new BatchedDeleteUnavailable({ reason: "dialect" });
|
|
14
|
+
}
|
|
15
|
+
if (Option.isSome(yield* Effect.serviceOption(sql.transactionService))) {
|
|
16
|
+
return yield* new BatchedDeleteUnavailable({ reason: "transaction" });
|
|
17
|
+
}
|
|
18
|
+
const { batchSize, maxRows } = yield* Schema.decodeUnknownEffect(Limits)(options);
|
|
19
|
+
if (prepare !== undefined) {
|
|
20
|
+
yield* prepare;
|
|
21
|
+
}
|
|
22
|
+
if (maxRows === 0 || !(yield* tryPurgeLock(tableName))) {
|
|
23
|
+
return 0;
|
|
24
|
+
}
|
|
25
|
+
let removed = 0;
|
|
26
|
+
while (removed < maxRows) {
|
|
27
|
+
const { count, done } = yield* batch(Math.min(batchSize, maxRows - removed));
|
|
28
|
+
removed += count;
|
|
29
|
+
if (done || count === 0 || removed === maxRows) {
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
32
|
+
yield* Effect.sleep(options.pause);
|
|
33
|
+
}
|
|
34
|
+
return removed;
|
|
35
|
+
});
|
|
10
36
|
export const makeBatchedDelete = Effect.fnUntraced(function* (model, { tableName, spanPrefix }, changes) {
|
|
11
37
|
const sql = yield* SqlClient.SqlClient;
|
|
12
38
|
const { where } = makeFilter(model, sql);
|
|
13
|
-
const available = Effect.gen(function* () {
|
|
14
|
-
if (sql.driver !== "pg") {
|
|
15
|
-
return yield* new BatchedDeleteUnavailable({ reason: "dialect" });
|
|
16
|
-
}
|
|
17
|
-
if (Option.isSome(yield* Effect.serviceOption(sql.transactionService))) {
|
|
18
|
-
return yield* new BatchedDeleteUnavailable({ reason: "transaction" });
|
|
19
|
-
}
|
|
20
|
-
});
|
|
21
39
|
function deleteBatch(predicate, limit) {
|
|
22
40
|
const count = SqlSchema.findOne({
|
|
23
41
|
execute: (batchLimit) => sql `with removed as (
|
|
@@ -34,31 +52,12 @@ export const makeBatchedDelete = Effect.fnUntraced(function* (model, { tableName
|
|
|
34
52
|
return result.count;
|
|
35
53
|
}));
|
|
36
54
|
}
|
|
37
|
-
const runBatches = Effect.fnUntraced(function* (options, predicate) {
|
|
38
|
-
const { batchSize, maxRows } = options;
|
|
39
|
-
let removed = 0;
|
|
40
|
-
while (removed < maxRows) {
|
|
41
|
-
const limit = Math.min(batchSize, maxRows - removed);
|
|
42
|
-
const count = yield* deleteBatch(predicate, limit);
|
|
43
|
-
removed += count;
|
|
44
|
-
if (count < limit || removed === maxRows) {
|
|
45
|
-
break;
|
|
46
|
-
}
|
|
47
|
-
yield* Effect.sleep(options.pause);
|
|
48
|
-
}
|
|
49
|
-
return removed;
|
|
50
|
-
});
|
|
51
55
|
return (options) => Effect.gen(function* () {
|
|
52
|
-
yield*
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
if (!(yield* tryPurgeLock(tableName).pipe(Effect.provideService(SqlClient.SqlClient, sql)))) {
|
|
59
|
-
return 0;
|
|
60
|
-
}
|
|
61
|
-
return yield* runBatches(options, predicate);
|
|
62
|
-
}).pipe(Effect.scoped, Effect.withSpan(`${spanPrefix}.deleteBatched`));
|
|
56
|
+
const predicate = yield* Effect.cached(where(options.where, true, true));
|
|
57
|
+
return yield* runPurgeBatches(tableName, options, (limit) => Effect.gen(function* () {
|
|
58
|
+
const count = yield* deleteBatch(yield* predicate, limit);
|
|
59
|
+
return { count, done: count < limit };
|
|
60
|
+
}), predicate);
|
|
61
|
+
}).pipe(Effect.provideService(SqlClient.SqlClient, sql), Effect.scoped, Effect.withSpan(`${spanPrefix}.deleteBatched`));
|
|
63
62
|
});
|
|
64
63
|
//# sourceMappingURL=batchedDelete.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"batchedDelete.js","sourceRoot":"","sources":["../src/batchedDelete.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAiB,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAErE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAIlD,OAAO,EAAuB,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvE,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"batchedDelete.js","sourceRoot":"","sources":["../src/batchedDelete.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAiB,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAErE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAIlD,OAAO,EAAuB,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvE,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAQhD,MAAM,OAAO,wBAAyB,SAAQ,IAAI,CAAC,WAAW,CAAC,0BAA0B,CAEvF;CAAG;AAEL,MAAM,CAAC,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,mBAAmB,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,CAAC;AAClJ,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,MAAM,CAAC,sBAAsB,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,mBAAmB,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,CAAC;AAC/I,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,gBAAgB,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC,CAAC;AAEpF,MAAM,CAAC,MAAM,eAAe,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,EACzD,SAAiB,EACjB,OAAoB,EACpB,KAAiG,EACjG,OAAsC;IAEtC,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC;IACvC,IAAI,GAAG,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC,CAAC,IAAI,wBAAwB,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;IACnE,CAAC;IACD,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,EAAE,CAAC;QACxE,OAAO,KAAK,CAAC,CAAC,IAAI,wBAAwB,CAAC,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC,CAAC;IACvE,CAAC;IACD,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,mBAAmB,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC;IAClF,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC3B,KAAK,CAAC,CAAC,OAAO,CAAC;IAChB,CAAC;IACD,IAAI,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC;QACxD,OAAO,CAAC,CAAC;IACV,CAAC;IACD,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,OAAO,OAAO,GAAG,OAAO,EAAE,CAAC;QAC1B,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;QAC7E,OAAO,IAAI,KAAK,CAAC;QACjB,IAAI,IAAI,IAAI,KAAK,KAAK,CAAC,IAAI,OAAO,KAAK,OAAO,EAAE,CAAC;YAChD,MAAM;QACP,CAAC;QACD,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACpC,CAAC;IACD,OAAO,OAAO,CAAC;AAChB,CAAC,CAAC,CAAC;AAYH,MAAM,CAAC,MAAM,iBAAiB,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,EAC3D,KAAgB,EAChB,EAAE,SAAS,EAAE,UAAU,EAA+D,EACtF,OAA0B;IAE1B,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC;IACvC,MAAM,EAAE,KAAK,EAAE,GAAG,UAAU,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IACzC,SAAS,WAAW,CAAC,SAAmB,EAAE,KAAa;QACtD,MAAM,KAAK,GAAG,SAAS,CAAC,OAAO,CAAC;YAC/B,OAAO,EAAE,CAAC,UAAU,EAAE,EAAE,CAAC,GAAG,CAAA;kBACb,GAAG,CAAC,SAAS,CAAC;kCACE,GAAG,CAAC,SAAS,CAAC,UAAU,SAAS,UAAU,UAAU;;iDAEtC;YAC9C,OAAO,EAAE,MAAM,CAAC,MAAM;YACtB,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,gBAAgB,EAAE,CAAC;SACzD,CAAC,CAAC;QACH,OAAO,OAAO,CAAC,KAAK,CACnB,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YACnB,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;YAC3F,KAAK,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACzC,OAAO,MAAM,CAAC,KAAK,CAAC;QACrB,CAAC,CAAC,CACF,CAAC;IACH,CAAC;IAED,OAAO,CAAC,OAAwC,EAAE,EAAE,CACnD,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QACnB,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;QACzE,OAAO,KAAK,CAAC,CAAC,eAAe,CAC5B,SAAS,EACT,OAAO,EACP,CAAC,KAAK,EAAE,EAAE,CACT,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YACnB,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;YAC1D,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,GAAG,KAAK,EAAE,CAAC;QACvC,CAAC,CAAC,EACH,SAAS,CACT,CAAC;IACH,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,SAAS,CAAC,SAAS,EAAE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,GAAG,UAAU,gBAAgB,CAAC,CAAC,CAAC;AAC1H,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type BucketKey, type ItemKey } from "@shivaedev/changes/keys.ts";
|
|
2
|
+
import { type AnnouncementOptions, invalidateOnCommit } from "#transact.ts";
|
|
3
|
+
export interface ChangeDeclaration {
|
|
4
|
+
readonly covers: Readonly<Record<string, readonly (ItemKey | BucketKey)[]>>;
|
|
5
|
+
readonly record: (options?: AnnouncementOptions) => ReturnType<typeof invalidateOnCommit>;
|
|
6
|
+
}
|
|
7
|
+
export declare function changes(covers: ChangeDeclaration["covers"]): ChangeDeclaration;
|
|
8
|
+
//# sourceMappingURL=changes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"changes.d.ts","sourceRoot":"","sources":["../src/changes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,OAAO,EAAoB,MAAM,4BAA4B,CAAC;AAC5F,OAAO,EAAE,KAAK,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAE5E,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,OAAO,GAAG,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC;IAC5E,QAAQ,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,mBAAmB,KAAK,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC;CAC1F;AAED,wBAAgB,OAAO,CAAC,MAAM,EAAE,iBAAiB,CAAC,QAAQ,CAAC,GAAG,iBAAiB,CAG9E"}
|
package/dist/changes.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { invalidationKeys } from "@shivaedev/changes/keys.ts";
|
|
2
|
+
import { invalidateOnCommit } from "#transact.ts";
|
|
3
|
+
export function changes(covers) {
|
|
4
|
+
const keys = invalidationKeys(Object.values(covers).flat());
|
|
5
|
+
return { covers, record: (options) => invalidateOnCommit(keys, options) };
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=changes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"changes.js","sourceRoot":"","sources":["../src/changes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgC,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAC5F,OAAO,EAA4B,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAO5E,MAAM,UAAU,OAAO,CAAC,MAAmC;IAC1D,MAAM,IAAI,GAAG,gBAAgB,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IAC5D,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,kBAAkB,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,CAAC;AAC3E,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Effect, Schema } from "effect";
|
|
2
|
+
import { type PurgeLimits } from "#batchedDelete.ts";
|
|
3
|
+
export interface DeleteGroup<T> {
|
|
4
|
+
readonly rowCount: number;
|
|
5
|
+
readonly value: T;
|
|
6
|
+
}
|
|
7
|
+
export interface DeleteGroupsBatchedOptions<T, TSelectError, TSelectServices, TDeleteError, TDeleteServices> extends PurgeLimits {
|
|
8
|
+
readonly delete: (group: T) => Effect.Effect<unknown, TDeleteError, TDeleteServices>;
|
|
9
|
+
readonly select: (limit: number) => Effect.Effect<readonly DeleteGroup<T>[], TSelectError, TSelectServices>;
|
|
10
|
+
readonly tableName: string;
|
|
11
|
+
}
|
|
12
|
+
export declare const deleteGroupsBatched: <T, TSelectError, TSelectServices, TDeleteError, TDeleteServices>(options: DeleteGroupsBatchedOptions<T, TSelectError, TSelectServices, TDeleteError, TDeleteServices>) => Effect.Effect<number, import("#batchedDelete.ts").BatchedDeleteUnavailable | Schema.SchemaError | import("effect/sql/SqlError").SqlError | Exclude<TDeleteError, import("effect/sql/SqlError").SqlError> | Exclude<TSelectError, import("effect/sql/SqlError").SqlError>, import("effect/reactivity/Reactivity").Reactivity | import("effect/sql/SqlClient").SqlClient | Exclude<TDeleteServices, import("effect/Scope").Scope> | Exclude<TSelectServices, import("effect/Scope").Scope>>;
|
|
13
|
+
//# sourceMappingURL=deleteGroupsBatched.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deleteGroupsBatched.d.ts","sourceRoot":"","sources":["../src/deleteGroupsBatched.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAoB,KAAK,WAAW,EAAmB,MAAM,mBAAmB,CAAC;AAGxF,MAAM,WAAW,WAAW,CAAC,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;CAClB;AAED,MAAM,WAAW,0BAA0B,CAAC,CAAC,EAAE,YAAY,EAAE,eAAe,EAAE,YAAY,EAAE,eAAe,CAAE,SAAQ,WAAW;IAC/H,QAAQ,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,YAAY,EAAE,eAAe,CAAC,CAAC;IACrF,QAAQ,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,SAAS,WAAW,CAAC,CAAC,CAAC,EAAE,EAAE,YAAY,EAAE,eAAe,CAAC,CAAC;IAC5G,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC3B;AAED,eAAO,MAAM,mBAAmB,GAAmD,CAAC,EAAE,YAAY,EAAE,eAAe,EAAE,YAAY,EAAE,eAAe,okBAyBhJ,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Effect, Schema } from "effect";
|
|
2
|
+
import { PositiveRowCount, runPurgeBatches } from "#batchedDelete.ts";
|
|
3
|
+
import { transact } from "#transact.ts";
|
|
4
|
+
export const deleteGroupsBatched = Effect.fn("Sql.deleteGroupsBatched")(function* (options) {
|
|
5
|
+
return yield* runPurgeBatches(options.tableName, options, (limit) => transact(Effect.gen(function* () {
|
|
6
|
+
const candidates = yield* options.select(limit);
|
|
7
|
+
const selected = [];
|
|
8
|
+
let count = 0;
|
|
9
|
+
for (const candidate of candidates) {
|
|
10
|
+
const size = yield* Schema.decodeUnknownEffect(PositiveRowCount)(candidate.rowCount);
|
|
11
|
+
if (size > limit - count) {
|
|
12
|
+
break;
|
|
13
|
+
}
|
|
14
|
+
selected.push(candidate.value);
|
|
15
|
+
count += size;
|
|
16
|
+
}
|
|
17
|
+
for (const group of selected) {
|
|
18
|
+
yield* options.delete(group);
|
|
19
|
+
}
|
|
20
|
+
return { count, done: candidates.length === 0 };
|
|
21
|
+
}), { onSqlError: (error) => error })).pipe(Effect.scoped);
|
|
22
|
+
});
|
|
23
|
+
//# sourceMappingURL=deleteGroupsBatched.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deleteGroupsBatched.js","sourceRoot":"","sources":["../src/deleteGroupsBatched.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAE,gBAAgB,EAAoB,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACxF,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAaxC,MAAM,CAAC,MAAM,mBAAmB,GAAG,MAAM,CAAC,EAAE,CAAC,yBAAyB,CAAC,CAAC,QAAQ,CAAC,EAChF,OAAoG;IAEpG,OAAO,KAAK,CAAC,CAAC,eAAe,CAAC,OAAO,CAAC,SAAS,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CACnE,QAAQ,CACP,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QACnB,MAAM,UAAU,GAAG,KAAK,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAChD,MAAM,QAAQ,GAAQ,EAAE,CAAC;QACzB,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;YACpC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;YACrF,IAAI,IAAI,GAAG,KAAK,GAAG,KAAK,EAAE,CAAC;gBAC1B,MAAM;YACP,CAAC;YACD,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;YAC/B,KAAK,IAAI,IAAI,CAAC;QACf,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,QAAQ,EAAE,CAAC;YAC9B,KAAK,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC9B,CAAC;QACD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;IACjD,CAAC,CAAC,EACF,EAAE,UAAU,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,EAAE,CAChC,CACD,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;AACvB,CAAC,CAAC,CAAC"}
|
|
@@ -19,6 +19,6 @@ export declare function makeInsertFromSelect<TServices = never>(model: Model.Any
|
|
|
19
19
|
readonly tableName: string;
|
|
20
20
|
readonly spanPrefix: string;
|
|
21
21
|
readonly changes: RepositoryChanges;
|
|
22
|
-
}, operation: string, prepare?: (columns: readonly string[]) => Effect.Effect<Fragment, Schema.SchemaError, TServices>): (source: RepositorySelect<Schema.Struct.Fields, unknown>, columns: Readonly<Record<string, string>>) => Effect.Effect<readonly unknown[], Schema.SchemaError | SqlError, unknown>;
|
|
22
|
+
}, operation: string, prepare?: (columns: readonly string[]) => Effect.Effect<Fragment, SqlError | Schema.SchemaError, TServices>): (source: RepositorySelect<Schema.Struct.Fields, unknown>, columns: Readonly<Record<string, string>>) => Effect.Effect<readonly unknown[], Schema.SchemaError | SqlError, unknown>;
|
|
23
23
|
export {};
|
|
24
24
|
//# sourceMappingURL=insertFromSelect.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"insertFromSelect.d.ts","sourceRoot":"","sources":["../src/insertFromSelect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAE9D,KAAK,gBAAgB,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,SAAS,MAAM,CAAC,MAAM,CAAC,MAAM,IAAI;KAC7E,IAAI,IAAI,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,SAAS,KAAK,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,SAAS,QAAQ,GAAG,IAAI,GAAG,KAAK,CAAC,GAAG,KAAK;CACjI,CAAC,MAAM,OAAO,CAAC,GACf,MAAM,CAAC;AAER,KAAK,aAAa,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,SAAS,MAAM,CAAC,MAAM,CAAC,MAAM,IAAI;KAC3E,IAAI,IAAI,MAAM,MAAM,GAAG,gBAAgB,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,QAAQ,CAAC,CAAC,EAAE,OAAO,CAAC;CACxG,CAAC;AAEF,MAAM,MAAM,gBAAgB,CAAC,MAAM,SAAS,KAAK,CAAC,GAAG,EAAE,SAAS,GAAG,KAAK,IAAI,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,eAAe,EACjI,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,eAAe,CAAC,EAClD,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC,KAClF,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,QAAQ,GAAG,MAAM,CAAC,WAAW,EAAE,eAAe,GAAG,MAAM,CAAC,kBAAkB,CAAC,GAAG,SAAS,CAAC,CAAC;AAE9H,MAAM,WAAW,YAAY,CAAC,MAAM,SAAS,KAAK,CAAC,GAAG;IACrD,QAAQ,CAAC,gBAAgB,EAAE,gBAAgB,CAAC,MAAM,CAAC,CAAC;CACpD;AAED,wBAAgB,oBAAoB,CAAC,SAAS,GAAG,KAAK,EACrD,KAAK,EAAE,KAAK,CAAC,GAAG,EAChB,GAAG,EAAE,SAAS,CAAC,SAAS,EACxB,OAAO,EAAE;IAAE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,iBAAiB,CAAA;CAAE,EACzG,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,KAAK,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,WAAW,EAAE,SAAS,CAA+B,
|
|
1
|
+
{"version":3,"file":"insertFromSelect.d.ts","sourceRoot":"","sources":["../src/insertFromSelect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAE9D,KAAK,gBAAgB,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,SAAS,MAAM,CAAC,MAAM,CAAC,MAAM,IAAI;KAC7E,IAAI,IAAI,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,SAAS,KAAK,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,SAAS,QAAQ,GAAG,IAAI,GAAG,KAAK,CAAC,GAAG,KAAK;CACjI,CAAC,MAAM,OAAO,CAAC,GACf,MAAM,CAAC;AAER,KAAK,aAAa,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,SAAS,MAAM,CAAC,MAAM,CAAC,MAAM,IAAI;KAC3E,IAAI,IAAI,MAAM,MAAM,GAAG,gBAAgB,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,QAAQ,CAAC,CAAC,EAAE,OAAO,CAAC;CACxG,CAAC;AAEF,MAAM,MAAM,gBAAgB,CAAC,MAAM,SAAS,KAAK,CAAC,GAAG,EAAE,SAAS,GAAG,KAAK,IAAI,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,eAAe,EACjI,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,eAAe,CAAC,EAClD,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC,KAClF,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,QAAQ,GAAG,MAAM,CAAC,WAAW,EAAE,eAAe,GAAG,MAAM,CAAC,kBAAkB,CAAC,GAAG,SAAS,CAAC,CAAC;AAE9H,MAAM,WAAW,YAAY,CAAC,MAAM,SAAS,KAAK,CAAC,GAAG;IACrD,QAAQ,CAAC,gBAAgB,EAAE,gBAAgB,CAAC,MAAM,CAAC,CAAC;CACpD;AAED,wBAAgB,oBAAoB,CAAC,SAAS,GAAG,KAAK,EACrD,KAAK,EAAE,KAAK,CAAC,GAAG,EAChB,GAAG,EAAE,SAAS,CAAC,SAAS,EACxB,OAAO,EAAE;IAAE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,iBAAiB,CAAA;CAAE,EACzG,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,KAAK,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAAC,WAAW,EAAE,SAAS,CAA+B,YAGxH,gBAAgB,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,WAAW,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,+EAY1G"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"insertFromSelect.js","sourceRoot":"","sources":["../src/insertFromSelect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AA0BxC,MAAM,UAAU,oBAAoB,CACnC,KAAgB,EAChB,GAAwB,EACxB,OAAyG,EACzG,SAAiB,EACjB,OAAO,
|
|
1
|
+
{"version":3,"file":"insertFromSelect.js","sourceRoot":"","sources":["../src/insertFromSelect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AA0BxC,MAAM,UAAU,oBAAoB,CACnC,KAAgB,EAChB,GAAwB,EACxB,OAAyG,EACzG,SAAiB,EACjB,OAAO,GAAsG,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAA,EAAE,CAAC;IAExI,MAAM,MAAM,GAAG,MAAM,CAAC,mBAAmB,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;IAC/D,OAAO,CAAC,MAAuD,EAAE,OAAyC,EAAE,EAAE,CAC7G,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QACnB,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACnC,MAAM,QAAQ,GAAG,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,GAAG,CAAA,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;QACtF,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,GAAG,CAAA,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;QACnE,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QACrC,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC;QAC1C,MAAM,IAAI,GACT,KAAK,CAAC,CAAC,GAAG,CAAA,eAAe,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,KAAK,MAAM,YAAY,QAAQ,UAAU,SAAS,QAAQ,GAAG,CAAC,QAAQ,CAAC,eAAe,MAAM,cAAc,OAAO,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC;QACxL,KAAK,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACpC,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC,UAAU,IAAI,SAAS,EAAE,CAAC,CAAC,CAAC;AACxF,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { InvalidationKeys } from "#transact.ts";
|
|
2
|
+
export interface Invalidation {
|
|
3
|
+
readonly key: unknown;
|
|
4
|
+
readonly publication: "local" | "shared";
|
|
5
|
+
}
|
|
6
|
+
export declare function flatten(keys: InvalidationKeys): readonly unknown[];
|
|
7
|
+
export declare function keysOf(changes: readonly Invalidation[]): unknown[];
|
|
8
|
+
//# sourceMappingURL=invalidationKeys.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"invalidationKeys.d.ts","sourceRoot":"","sources":["../src/invalidationKeys.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD,MAAM,WAAW,YAAY;IAC5B,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,OAAO,GAAG,QAAQ,CAAC;CACzC;AAQD,wBAAgB,OAAO,CAAC,IAAI,EAAE,gBAAgB,GAAG,SAAS,OAAO,EAAE,CAIlE;AAED,wBAAgB,MAAM,CAAC,OAAO,EAAE,SAAS,YAAY,EAAE,aAEtD"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
import { invalidationKeys, Key, recordKey } from "@shivaedev/changes/keys.ts";
|
|
3
|
+
const isKey = Schema.is(Key);
|
|
4
|
+
function isList(keys) {
|
|
5
|
+
return Array.isArray(keys);
|
|
6
|
+
}
|
|
7
|
+
export function flatten(keys) {
|
|
8
|
+
return isList(keys)
|
|
9
|
+
? keys.flatMap((key) => (isKey(key) ? invalidationKeys([key]) : [key]))
|
|
10
|
+
: Object.entries(keys).flatMap(([name, ids]) => [name, ...ids.map((id) => recordKey(name, id))]);
|
|
11
|
+
}
|
|
12
|
+
export function keysOf(changes) {
|
|
13
|
+
return [...new Set(changes.map((change) => change.key))];
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=invalidationKeys.js.map
|