@powersync/service-module-postgres-storage 0.11.2 → 0.13.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 +60 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/@types/migrations/scripts/1771232439485-storage-version.d.ts +3 -0
- package/dist/@types/migrations/scripts/1771424826685-current-data-pending-deletes.d.ts +3 -0
- package/dist/@types/migrations/scripts/1771491856000-sync-plan.d.ts +3 -0
- package/dist/@types/storage/PostgresBucketStorageFactory.d.ts +6 -10
- package/dist/@types/storage/PostgresCompactor.d.ts +10 -3
- package/dist/@types/storage/PostgresSyncRulesStorage.d.ts +5 -3
- package/dist/@types/storage/batch/OperationBatch.d.ts +2 -2
- package/dist/@types/storage/batch/PostgresBucketBatch.d.ts +12 -9
- package/dist/@types/storage/batch/PostgresPersistedBatch.d.ts +17 -5
- package/dist/@types/storage/current-data-store.d.ts +85 -0
- package/dist/@types/storage/current-data-table.d.ts +9 -0
- package/dist/@types/storage/sync-rules/PostgresPersistedSyncRulesContent.d.ts +1 -10
- package/dist/@types/storage/table-id.d.ts +2 -0
- package/dist/@types/types/models/CurrentData.d.ts +18 -3
- package/dist/@types/types/models/SyncRules.d.ts +12 -2
- package/dist/@types/types/models/json.d.ts +11 -0
- package/dist/@types/types/types.d.ts +2 -0
- package/dist/@types/utils/bson.d.ts +1 -1
- package/dist/@types/utils/db.d.ts +9 -0
- package/dist/@types/utils/test-utils.d.ts +1 -1
- package/dist/migrations/scripts/1771232439485-storage-version.js +111 -0
- package/dist/migrations/scripts/1771232439485-storage-version.js.map +1 -0
- package/dist/migrations/scripts/1771424826685-current-data-pending-deletes.js +8 -0
- package/dist/migrations/scripts/1771424826685-current-data-pending-deletes.js.map +1 -0
- package/dist/migrations/scripts/1771491856000-sync-plan.js +91 -0
- package/dist/migrations/scripts/1771491856000-sync-plan.js.map +1 -0
- package/dist/storage/PostgresBucketStorageFactory.js +56 -58
- package/dist/storage/PostgresBucketStorageFactory.js.map +1 -1
- package/dist/storage/PostgresCompactor.js +55 -66
- package/dist/storage/PostgresCompactor.js.map +1 -1
- package/dist/storage/PostgresSyncRulesStorage.js +23 -15
- package/dist/storage/PostgresSyncRulesStorage.js.map +1 -1
- package/dist/storage/batch/OperationBatch.js +2 -1
- package/dist/storage/batch/OperationBatch.js.map +1 -1
- package/dist/storage/batch/PostgresBucketBatch.js +286 -213
- package/dist/storage/batch/PostgresBucketBatch.js.map +1 -1
- package/dist/storage/batch/PostgresPersistedBatch.js +86 -81
- package/dist/storage/batch/PostgresPersistedBatch.js.map +1 -1
- package/dist/storage/current-data-store.js +270 -0
- package/dist/storage/current-data-store.js.map +1 -0
- package/dist/storage/current-data-table.js +22 -0
- package/dist/storage/current-data-table.js.map +1 -0
- package/dist/storage/sync-rules/PostgresPersistedSyncRulesContent.js +14 -30
- package/dist/storage/sync-rules/PostgresPersistedSyncRulesContent.js.map +1 -1
- package/dist/storage/table-id.js +8 -0
- package/dist/storage/table-id.js.map +1 -0
- package/dist/types/models/CurrentData.js +11 -2
- package/dist/types/models/CurrentData.js.map +1 -1
- package/dist/types/models/SyncRules.js +12 -1
- package/dist/types/models/SyncRules.js.map +1 -1
- package/dist/types/models/json.js +21 -0
- package/dist/types/models/json.js.map +1 -0
- package/dist/utils/bson.js.map +1 -1
- package/dist/utils/db.js +41 -0
- package/dist/utils/db.js.map +1 -1
- package/dist/utils/test-utils.js +50 -14
- package/dist/utils/test-utils.js.map +1 -1
- package/package.json +9 -9
- package/src/migrations/scripts/1771232439485-storage-version.ts +44 -0
- package/src/migrations/scripts/1771424826685-current-data-pending-deletes.ts +10 -0
- package/src/migrations/scripts/1771491856000-sync-plan.ts +21 -0
- package/src/storage/PostgresBucketStorageFactory.ts +69 -68
- package/src/storage/PostgresCompactor.ts +63 -72
- package/src/storage/PostgresSyncRulesStorage.ts +30 -17
- package/src/storage/batch/OperationBatch.ts +4 -3
- package/src/storage/batch/PostgresBucketBatch.ts +306 -238
- package/src/storage/batch/PostgresPersistedBatch.ts +92 -84
- package/src/storage/current-data-store.ts +326 -0
- package/src/storage/current-data-table.ts +26 -0
- package/src/storage/sync-rules/PostgresPersistedSyncRulesContent.ts +13 -33
- package/src/storage/table-id.ts +9 -0
- package/src/types/models/CurrentData.ts +17 -4
- package/src/types/models/SyncRules.ts +16 -1
- package/src/types/models/json.ts +26 -0
- package/src/utils/bson.ts +1 -1
- package/src/utils/db.ts +47 -0
- package/src/utils/test-utils.ts +42 -15
- package/test/src/__snapshots__/storage.test.ts.snap +148 -6
- package/test/src/__snapshots__/storage_compacting.test.ts.snap +17 -0
- package/test/src/__snapshots__/storage_sync.test.ts.snap +2211 -21
- package/test/src/migrations.test.ts +9 -2
- package/test/src/storage.test.ts +137 -131
- package/test/src/storage_compacting.test.ts +113 -2
- package/test/src/storage_sync.test.ts +148 -4
- package/test/src/util.ts +5 -2
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
PopulateChecksumCacheResults,
|
|
15
15
|
ReplicationCheckpoint,
|
|
16
16
|
storage,
|
|
17
|
+
StorageVersionConfig,
|
|
17
18
|
utils,
|
|
18
19
|
WatchWriteCheckpointOptions
|
|
19
20
|
} from '@powersync/service-core';
|
|
@@ -35,6 +36,7 @@ import { PostgresBucketBatch } from './batch/PostgresBucketBatch.js';
|
|
|
35
36
|
import { PostgresWriteCheckpointAPI } from './checkpoints/PostgresWriteCheckpointAPI.js';
|
|
36
37
|
import { PostgresBucketStorageFactory } from './PostgresBucketStorageFactory.js';
|
|
37
38
|
import { PostgresCompactor } from './PostgresCompactor.js';
|
|
39
|
+
import { PostgresCurrentDataStore } from './current-data-store.js';
|
|
38
40
|
|
|
39
41
|
export type PostgresSyncRulesStorageOptions = {
|
|
40
42
|
factory: PostgresBucketStorageFactory;
|
|
@@ -52,11 +54,13 @@ export class PostgresSyncRulesStorage
|
|
|
52
54
|
public readonly sync_rules: storage.PersistedSyncRulesContent;
|
|
53
55
|
public readonly slot_name: string;
|
|
54
56
|
public readonly factory: PostgresBucketStorageFactory;
|
|
57
|
+
public readonly storageConfig: StorageVersionConfig;
|
|
55
58
|
|
|
56
59
|
private sharedIterator = new BroadcastIterable((signal) => this.watchActiveCheckpoint(signal));
|
|
57
60
|
|
|
58
61
|
protected db: lib_postgres.DatabaseClient;
|
|
59
62
|
protected writeCheckpointAPI: PostgresWriteCheckpointAPI;
|
|
63
|
+
private readonly currentDataStore: PostgresCurrentDataStore;
|
|
60
64
|
|
|
61
65
|
// TODO we might be able to share this in an abstract class
|
|
62
66
|
private parsedSyncRulesCache:
|
|
@@ -71,6 +75,8 @@ export class PostgresSyncRulesStorage
|
|
|
71
75
|
this.sync_rules = options.sync_rules;
|
|
72
76
|
this.slot_name = options.sync_rules.slot_name;
|
|
73
77
|
this.factory = options.factory;
|
|
78
|
+
this.storageConfig = options.sync_rules.getStorageConfig();
|
|
79
|
+
this.currentDataStore = new PostgresCurrentDataStore(this.storageConfig);
|
|
74
80
|
|
|
75
81
|
this.writeCheckpointAPI = new PostgresWriteCheckpointAPI({
|
|
76
82
|
db: this.db,
|
|
@@ -121,8 +127,18 @@ export class PostgresSyncRulesStorage
|
|
|
121
127
|
`.execute();
|
|
122
128
|
}
|
|
123
129
|
|
|
124
|
-
compact(options?: storage.CompactOptions): Promise<void> {
|
|
125
|
-
|
|
130
|
+
async compact(options?: storage.CompactOptions): Promise<void> {
|
|
131
|
+
let maxOpId = options?.maxOpId;
|
|
132
|
+
if (maxOpId == null) {
|
|
133
|
+
const checkpoint = await this.getCheckpoint();
|
|
134
|
+
// Note: If there is no active checkpoint, this will be 0, in which case no compacting is performed
|
|
135
|
+
maxOpId = checkpoint.checkpoint;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return new PostgresCompactor(this.db, this.group_id, {
|
|
139
|
+
...options,
|
|
140
|
+
maxOpId
|
|
141
|
+
}).compact();
|
|
126
142
|
}
|
|
127
143
|
|
|
128
144
|
async populatePersistentChecksumCache(options: PopulateChecksumCacheOptions): Promise<PopulateChecksumCacheResults> {
|
|
@@ -355,12 +371,12 @@ export class PostgresSyncRulesStorage
|
|
|
355
371
|
slot_name: this.slot_name,
|
|
356
372
|
last_checkpoint_lsn: checkpoint_lsn,
|
|
357
373
|
keep_alive_op: syncRules?.keepalive_op,
|
|
358
|
-
no_checkpoint_before_lsn: syncRules?.no_checkpoint_before ?? options.zeroLSN,
|
|
359
374
|
resumeFromLsn: maxLsn(syncRules?.snapshot_lsn, checkpoint_lsn),
|
|
360
375
|
store_current_data: options.storeCurrentData,
|
|
361
376
|
skip_existing_rows: options.skipExistingRows ?? false,
|
|
362
377
|
batch_limits: this.options.batchLimits,
|
|
363
|
-
markRecordUnavailable: options.markRecordUnavailable
|
|
378
|
+
markRecordUnavailable: options.markRecordUnavailable,
|
|
379
|
+
storageConfig: this.storageConfig
|
|
364
380
|
});
|
|
365
381
|
this.iterateListeners((cb) => cb.batchStarted?.(batch));
|
|
366
382
|
|
|
@@ -415,10 +431,10 @@ export class PostgresSyncRulesStorage
|
|
|
415
431
|
|
|
416
432
|
async *getBucketDataBatch(
|
|
417
433
|
checkpoint: InternalOpId,
|
|
418
|
-
dataBuckets:
|
|
434
|
+
dataBuckets: storage.BucketDataRequest[],
|
|
419
435
|
options?: storage.BucketDataBatchOptions
|
|
420
436
|
): AsyncIterable<storage.SyncBucketDataChunk> {
|
|
421
|
-
if (dataBuckets.
|
|
437
|
+
if (dataBuckets.length == 0) {
|
|
422
438
|
return;
|
|
423
439
|
}
|
|
424
440
|
|
|
@@ -430,10 +446,8 @@ export class PostgresSyncRulesStorage
|
|
|
430
446
|
// not match up with chunks.
|
|
431
447
|
|
|
432
448
|
const end = checkpoint ?? BIGINT_MAX;
|
|
433
|
-
const filters =
|
|
434
|
-
|
|
435
|
-
start: start
|
|
436
|
-
}));
|
|
449
|
+
const filters = dataBuckets.map((request) => ({ bucket_name: request.bucket, start: request.start }));
|
|
450
|
+
const startOpByBucket = new Map(dataBuckets.map((request) => [request.bucket, request.start]));
|
|
437
451
|
|
|
438
452
|
const batchRowLimit = options?.limit ?? storage.DEFAULT_DOCUMENT_BATCH_LIMIT;
|
|
439
453
|
const chunkSizeLimitBytes = options?.chunkLimitBytes ?? storage.DEFAULT_DOCUMENT_CHUNK_LIMIT_BYTES;
|
|
@@ -533,7 +547,7 @@ export class PostgresSyncRulesStorage
|
|
|
533
547
|
}
|
|
534
548
|
|
|
535
549
|
if (start == null) {
|
|
536
|
-
const startOpId =
|
|
550
|
+
const startOpId = startOpByBucket.get(bucket_name);
|
|
537
551
|
if (startOpId == null) {
|
|
538
552
|
throw new framework.ServiceAssertionError(`data for unexpected bucket: ${bucket_name}`);
|
|
539
553
|
}
|
|
@@ -588,7 +602,10 @@ export class PostgresSyncRulesStorage
|
|
|
588
602
|
}
|
|
589
603
|
}
|
|
590
604
|
|
|
591
|
-
async getChecksums(
|
|
605
|
+
async getChecksums(
|
|
606
|
+
checkpoint: utils.InternalOpId,
|
|
607
|
+
buckets: storage.BucketChecksumRequest[]
|
|
608
|
+
): Promise<utils.ChecksumMap> {
|
|
592
609
|
return this.checksumCache.getChecksumMap(checkpoint, buckets);
|
|
593
610
|
}
|
|
594
611
|
|
|
@@ -662,11 +679,7 @@ export class PostgresSyncRulesStorage
|
|
|
662
679
|
group_id = ${{ type: 'int4', value: this.group_id }}
|
|
663
680
|
`.execute();
|
|
664
681
|
|
|
665
|
-
await this.db.
|
|
666
|
-
DELETE FROM current_data
|
|
667
|
-
WHERE
|
|
668
|
-
group_id = ${{ type: 'int4', value: this.group_id }}
|
|
669
|
-
`.execute();
|
|
682
|
+
await this.currentDataStore.deleteGroupRows(this.db, { groupId: this.group_id });
|
|
670
683
|
|
|
671
684
|
await this.db.sql`
|
|
672
685
|
DELETE FROM source_tables
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { storage, utils } from '@powersync/service-core';
|
|
7
7
|
import { RequiredOperationBatchLimits } from '../../types/types.js';
|
|
8
|
+
import { postgresTableId } from '../table-id.js';
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Batch of input operations.
|
|
@@ -89,13 +90,13 @@ export class RecordOperation {
|
|
|
89
90
|
/**
|
|
90
91
|
* In-memory cache key - must not be persisted.
|
|
91
92
|
*/
|
|
92
|
-
export function cacheKey(sourceTableId:
|
|
93
|
+
export function cacheKey(sourceTableId: storage.SourceTableId, id: storage.ReplicaId) {
|
|
93
94
|
return encodedCacheKey(sourceTableId, storage.serializeReplicaId(id));
|
|
94
95
|
}
|
|
95
96
|
|
|
96
97
|
/**
|
|
97
98
|
* Calculates a cache key for a stored ReplicaId. This is usually stored as a bytea/Buffer.
|
|
98
99
|
*/
|
|
99
|
-
export function encodedCacheKey(sourceTableId:
|
|
100
|
-
return `${sourceTableId}.${storedKey.toString('base64')}`;
|
|
100
|
+
export function encodedCacheKey(sourceTableId: storage.SourceTableId, storedKey: Buffer) {
|
|
101
|
+
return `${postgresTableId(sourceTableId)}.${storedKey.toString('base64')}`;
|
|
101
102
|
}
|