@powersync/service-module-postgres-storage 0.16.3 → 0.17.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.
Files changed (44) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/dist/migrations/scripts/1782950400000-checkpoint-requested-at.d.ts +3 -0
  3. package/dist/migrations/scripts/1782950400000-checkpoint-requested-at.js +42 -0
  4. package/dist/migrations/scripts/1782950400000-checkpoint-requested-at.js.map +1 -0
  5. package/dist/storage/PostgresBucketStorageFactory.js +0 -2
  6. package/dist/storage/PostgresBucketStorageFactory.js.map +1 -1
  7. package/dist/storage/PostgresCompactor.d.ts +2 -0
  8. package/dist/storage/PostgresCompactor.js +20 -0
  9. package/dist/storage/PostgresCompactor.js.map +1 -1
  10. package/dist/storage/PostgresSyncRulesStorage.d.ts +16 -1
  11. package/dist/storage/PostgresSyncRulesStorage.js +200 -131
  12. package/dist/storage/PostgresSyncRulesStorage.js.map +1 -1
  13. package/dist/storage/batch/PostgresBucketBatch.d.ts +2 -20
  14. package/dist/storage/batch/PostgresBucketBatch.js +162 -151
  15. package/dist/storage/batch/PostgresBucketBatch.js.map +1 -1
  16. package/dist/storage/checkpoints/PostgresWriteCheckpointAPI.d.ts +1 -1
  17. package/dist/storage/checkpoints/PostgresWriteCheckpointAPI.js +156 -36
  18. package/dist/storage/checkpoints/PostgresWriteCheckpointAPI.js.map +1 -1
  19. package/dist/storage/current-data-store.d.ts +8 -2
  20. package/dist/storage/current-data-store.js +66 -11
  21. package/dist/storage/current-data-store.js.map +1 -1
  22. package/dist/types/models/WriteCheckpoint.d.ts +2 -0
  23. package/dist/types/models/WriteCheckpoint.js +5 -2
  24. package/dist/types/models/WriteCheckpoint.js.map +1 -1
  25. package/dist/utils/checkpoints.d.ts +9 -0
  26. package/dist/utils/checkpoints.js +26 -0
  27. package/dist/utils/checkpoints.js.map +1 -0
  28. package/package.json +8 -8
  29. package/src/migrations/scripts/1782950400000-checkpoint-requested-at.ts +51 -0
  30. package/src/storage/PostgresBucketStorageFactory.ts +0 -3
  31. package/src/storage/PostgresCompactor.ts +24 -0
  32. package/src/storage/PostgresSyncRulesStorage.ts +219 -137
  33. package/src/storage/batch/PostgresBucketBatch.ts +169 -154
  34. package/src/storage/checkpoints/PostgresWriteCheckpointAPI.ts +165 -37
  35. package/src/storage/current-data-store.ts +66 -11
  36. package/src/types/models/WriteCheckpoint.ts +5 -2
  37. package/src/utils/checkpoints.ts +31 -0
  38. package/test/src/__snapshots__/storage_sync.test.ts.snap +0 -582
  39. package/test/src/checkpoint_notifications.test.ts +522 -0
  40. package/test/src/storage.test.ts +214 -5
  41. package/test/src/storage_compacting.test.ts +3 -3
  42. package/test/src/storage_sync.test.ts +4 -4
  43. package/test/tsconfig.json +1 -1
  44. package/tsconfig.tsbuildinfo +1 -1
@@ -301,19 +301,74 @@ export class PostgresCurrentDataStore {
301
301
  `.execute();
302
302
  }
303
303
 
304
- async deleteGroupRows(db: Queryable, options: { groupId: number }) {
304
+ /**
305
+ * Delete up to `limit` rows for the group, returning the number of candidate
306
+ * rows found by the scan (see PostgresSyncRulesStorage#deleteGroupBatch for
307
+ * why the scan is counted rather than the delete).
308
+ */
309
+ async deleteGroupRowsBatch(db: Queryable, options: { groupId: number; limit: number }): Promise<bigint> {
305
310
  if (this.softDeleteEnabled) {
306
- await db.sql`
307
- DELETE FROM v3_current_data
308
- WHERE
309
- group_id = ${{ type: 'int4', value: options.groupId }}
310
- `.execute();
311
+ const result = await db.sql`
312
+ WITH
313
+ batch AS (
314
+ SELECT
315
+ ctid
316
+ FROM
317
+ v3_current_data
318
+ WHERE
319
+ group_id = ${{ type: 'int4', value: options.groupId }}
320
+ LIMIT
321
+ ${{ type: 'int4', value: options.limit }}
322
+ ),
323
+ deleted AS (
324
+ DELETE FROM v3_current_data
325
+ WHERE
326
+ ctid IN (
327
+ SELECT
328
+ ctid
329
+ FROM
330
+ batch
331
+ )
332
+ RETURNING
333
+ 1
334
+ )
335
+ SELECT
336
+ COUNT(*) AS count
337
+ FROM
338
+ batch
339
+ `.first<{ count: bigint }>();
340
+ return result?.count ?? 0n;
311
341
  } else {
312
- await db.sql`
313
- DELETE FROM current_data
314
- WHERE
315
- group_id = ${{ type: 'int4', value: options.groupId }}
316
- `.execute();
342
+ const result = await db.sql`
343
+ WITH
344
+ batch AS (
345
+ SELECT
346
+ ctid
347
+ FROM
348
+ current_data
349
+ WHERE
350
+ group_id = ${{ type: 'int4', value: options.groupId }}
351
+ LIMIT
352
+ ${{ type: 'int4', value: options.limit }}
353
+ ),
354
+ deleted AS (
355
+ DELETE FROM current_data
356
+ WHERE
357
+ ctid IN (
358
+ SELECT
359
+ ctid
360
+ FROM
361
+ batch
362
+ )
363
+ RETURNING
364
+ 1
365
+ )
366
+ SELECT
367
+ COUNT(*) AS count
368
+ FROM
369
+ batch
370
+ `.first<{ count: bigint }>();
371
+ return result?.count ?? 0n;
317
372
  }
318
373
  }
319
374
 
@@ -1,10 +1,12 @@
1
+ import { framework } from '@powersync/service-core';
1
2
  import * as t from 'ts-codec';
2
3
  import { bigint, jsonb } from '../codecs.js';
3
4
 
4
5
  export const WriteCheckpoint = t.object({
5
6
  user_id: t.string,
6
7
  lsns: jsonb(t.record(t.string)),
7
- write_checkpoint: bigint
8
+ write_checkpoint: bigint,
9
+ checkpoint_requested_at: t.Null.or(framework.codecs.date)
8
10
  });
9
11
 
10
12
  export type WriteCheckpoint = t.Encoded<typeof WriteCheckpoint>;
@@ -13,7 +15,8 @@ export type WriteCheckpointDecoded = t.Decoded<typeof WriteCheckpoint>;
13
15
  export const CustomWriteCheckpoint = t.object({
14
16
  user_id: t.string,
15
17
  write_checkpoint: bigint,
16
- sync_rules_id: bigint
18
+ sync_rules_id: bigint,
19
+ checkpoint_requested_at: t.Null.or(framework.codecs.date)
17
20
  });
18
21
 
19
22
  export type CustomWriteCheckpoint = t.Encoded<typeof CustomWriteCheckpoint>;
@@ -0,0 +1,31 @@
1
+ import * as lib_postgres from '@powersync/lib-service-postgres';
2
+ import { storage } from '@powersync/service-core';
3
+ import { models } from '../types/types.js';
4
+
5
+ /**
6
+ * Gets the latest active checkpoint document.
7
+ * This is mainly exported for mocking in tests.
8
+ */
9
+ export async function getActiveCheckpointDocument({
10
+ db
11
+ }: {
12
+ db: lib_postgres.DatabaseClient;
13
+ }): Promise<models.ActiveCheckpointDecoded | null> {
14
+ return db.sql`
15
+ SELECT
16
+ id,
17
+ last_checkpoint,
18
+ last_checkpoint_lsn
19
+ FROM
20
+ sync_rules
21
+ WHERE
22
+ state = ${{ value: storage.SyncRuleState.ACTIVE, type: 'varchar' }}
23
+ OR state = ${{ value: storage.SyncRuleState.ERRORED, type: 'varchar' }}
24
+ ORDER BY
25
+ id DESC
26
+ LIMIT
27
+ 1
28
+ `
29
+ .decoded(models.ActiveCheckpoint)
30
+ .first();
31
+ }