@powersync/service-module-postgres-storage 0.0.0-dev-20250116115804
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 +32 -0
- package/LICENSE +67 -0
- package/README.md +67 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/@types/index.d.ts +7 -0
- package/dist/@types/migrations/PostgresMigrationAgent.d.ts +12 -0
- package/dist/@types/migrations/PostgresMigrationStore.d.ts +14 -0
- package/dist/@types/migrations/migration-utils.d.ts +3 -0
- package/dist/@types/migrations/scripts/1684951997326-init.d.ts +3 -0
- package/dist/@types/module/PostgresStorageModule.d.ts +6 -0
- package/dist/@types/storage/PostgresBucketStorageFactory.d.ts +42 -0
- package/dist/@types/storage/PostgresCompactor.d.ts +40 -0
- package/dist/@types/storage/PostgresStorageProvider.d.ts +5 -0
- package/dist/@types/storage/PostgresSyncRulesStorage.d.ts +46 -0
- package/dist/@types/storage/PostgresTestStorageFactoryGenerator.d.ts +13 -0
- package/dist/@types/storage/batch/OperationBatch.d.ts +47 -0
- package/dist/@types/storage/batch/PostgresBucketBatch.d.ts +90 -0
- package/dist/@types/storage/batch/PostgresPersistedBatch.d.ts +64 -0
- package/dist/@types/storage/checkpoints/PostgresWriteCheckpointAPI.d.ts +20 -0
- package/dist/@types/storage/storage-index.d.ts +5 -0
- package/dist/@types/storage/sync-rules/PostgresPersistedSyncRulesContent.d.ts +17 -0
- package/dist/@types/types/codecs.d.ts +61 -0
- package/dist/@types/types/models/ActiveCheckpoint.d.ts +12 -0
- package/dist/@types/types/models/ActiveCheckpointNotification.d.ts +19 -0
- package/dist/@types/types/models/BucketData.d.ts +22 -0
- package/dist/@types/types/models/BucketParameters.d.ts +11 -0
- package/dist/@types/types/models/CurrentData.d.ts +22 -0
- package/dist/@types/types/models/Instance.d.ts +6 -0
- package/dist/@types/types/models/Migration.d.ts +12 -0
- package/dist/@types/types/models/SourceTable.d.ts +31 -0
- package/dist/@types/types/models/SyncRules.d.ts +47 -0
- package/dist/@types/types/models/WriteCheckpoint.d.ts +15 -0
- package/dist/@types/types/models/models-index.d.ts +10 -0
- package/dist/@types/types/types.d.ts +96 -0
- package/dist/@types/utils/bson.d.ts +6 -0
- package/dist/@types/utils/bucket-data.d.ts +18 -0
- package/dist/@types/utils/db.d.ts +8 -0
- package/dist/@types/utils/ts-codec.d.ts +5 -0
- package/dist/@types/utils/utils-index.d.ts +4 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/migrations/PostgresMigrationAgent.js +36 -0
- package/dist/migrations/PostgresMigrationAgent.js.map +1 -0
- package/dist/migrations/PostgresMigrationStore.js +60 -0
- package/dist/migrations/PostgresMigrationStore.js.map +1 -0
- package/dist/migrations/migration-utils.js +13 -0
- package/dist/migrations/migration-utils.js.map +1 -0
- package/dist/migrations/scripts/1684951997326-init.js +196 -0
- package/dist/migrations/scripts/1684951997326-init.js.map +1 -0
- package/dist/module/PostgresStorageModule.js +23 -0
- package/dist/module/PostgresStorageModule.js.map +1 -0
- package/dist/storage/PostgresBucketStorageFactory.js +433 -0
- package/dist/storage/PostgresBucketStorageFactory.js.map +1 -0
- package/dist/storage/PostgresCompactor.js +298 -0
- package/dist/storage/PostgresCompactor.js.map +1 -0
- package/dist/storage/PostgresStorageProvider.js +35 -0
- package/dist/storage/PostgresStorageProvider.js.map +1 -0
- package/dist/storage/PostgresSyncRulesStorage.js +619 -0
- package/dist/storage/PostgresSyncRulesStorage.js.map +1 -0
- package/dist/storage/PostgresTestStorageFactoryGenerator.js +110 -0
- package/dist/storage/PostgresTestStorageFactoryGenerator.js.map +1 -0
- package/dist/storage/batch/OperationBatch.js +93 -0
- package/dist/storage/batch/OperationBatch.js.map +1 -0
- package/dist/storage/batch/PostgresBucketBatch.js +732 -0
- package/dist/storage/batch/PostgresBucketBatch.js.map +1 -0
- package/dist/storage/batch/PostgresPersistedBatch.js +367 -0
- package/dist/storage/batch/PostgresPersistedBatch.js.map +1 -0
- package/dist/storage/checkpoints/PostgresWriteCheckpointAPI.js +148 -0
- package/dist/storage/checkpoints/PostgresWriteCheckpointAPI.js.map +1 -0
- package/dist/storage/storage-index.js +6 -0
- package/dist/storage/storage-index.js.map +1 -0
- package/dist/storage/sync-rules/PostgresPersistedSyncRulesContent.js +58 -0
- package/dist/storage/sync-rules/PostgresPersistedSyncRulesContent.js.map +1 -0
- package/dist/types/codecs.js +97 -0
- package/dist/types/codecs.js.map +1 -0
- package/dist/types/models/ActiveCheckpoint.js +12 -0
- package/dist/types/models/ActiveCheckpoint.js.map +1 -0
- package/dist/types/models/ActiveCheckpointNotification.js +8 -0
- package/dist/types/models/ActiveCheckpointNotification.js.map +1 -0
- package/dist/types/models/BucketData.js +23 -0
- package/dist/types/models/BucketData.js.map +1 -0
- package/dist/types/models/BucketParameters.js +11 -0
- package/dist/types/models/BucketParameters.js.map +1 -0
- package/dist/types/models/CurrentData.js +16 -0
- package/dist/types/models/CurrentData.js.map +1 -0
- package/dist/types/models/Instance.js +5 -0
- package/dist/types/models/Instance.js.map +1 -0
- package/dist/types/models/Migration.js +12 -0
- package/dist/types/models/Migration.js.map +1 -0
- package/dist/types/models/SourceTable.js +24 -0
- package/dist/types/models/SourceTable.js.map +1 -0
- package/dist/types/models/SyncRules.js +47 -0
- package/dist/types/models/SyncRules.js.map +1 -0
- package/dist/types/models/WriteCheckpoint.js +13 -0
- package/dist/types/models/WriteCheckpoint.js.map +1 -0
- package/dist/types/models/models-index.js +11 -0
- package/dist/types/models/models-index.js.map +1 -0
- package/dist/types/types.js +46 -0
- package/dist/types/types.js.map +1 -0
- package/dist/utils/bson.js +16 -0
- package/dist/utils/bson.js.map +1 -0
- package/dist/utils/bucket-data.js +25 -0
- package/dist/utils/bucket-data.js.map +1 -0
- package/dist/utils/db.js +24 -0
- package/dist/utils/db.js.map +1 -0
- package/dist/utils/ts-codec.js +11 -0
- package/dist/utils/ts-codec.js.map +1 -0
- package/dist/utils/utils-index.js +5 -0
- package/dist/utils/utils-index.js.map +1 -0
- package/package.json +50 -0
- package/src/index.ts +10 -0
- package/src/migrations/PostgresMigrationAgent.ts +46 -0
- package/src/migrations/PostgresMigrationStore.ts +70 -0
- package/src/migrations/migration-utils.ts +14 -0
- package/src/migrations/scripts/1684951997326-init.ts +141 -0
- package/src/module/PostgresStorageModule.ts +30 -0
- package/src/storage/PostgresBucketStorageFactory.ts +496 -0
- package/src/storage/PostgresCompactor.ts +366 -0
- package/src/storage/PostgresStorageProvider.ts +42 -0
- package/src/storage/PostgresSyncRulesStorage.ts +666 -0
- package/src/storage/PostgresTestStorageFactoryGenerator.ts +61 -0
- package/src/storage/batch/OperationBatch.ts +101 -0
- package/src/storage/batch/PostgresBucketBatch.ts +885 -0
- package/src/storage/batch/PostgresPersistedBatch.ts +441 -0
- package/src/storage/checkpoints/PostgresWriteCheckpointAPI.ts +176 -0
- package/src/storage/storage-index.ts +5 -0
- package/src/storage/sync-rules/PostgresPersistedSyncRulesContent.ts +67 -0
- package/src/types/codecs.ts +136 -0
- package/src/types/models/ActiveCheckpoint.ts +15 -0
- package/src/types/models/ActiveCheckpointNotification.ts +14 -0
- package/src/types/models/BucketData.ts +26 -0
- package/src/types/models/BucketParameters.ts +14 -0
- package/src/types/models/CurrentData.ts +23 -0
- package/src/types/models/Instance.ts +8 -0
- package/src/types/models/Migration.ts +19 -0
- package/src/types/models/SourceTable.ts +32 -0
- package/src/types/models/SyncRules.ts +50 -0
- package/src/types/models/WriteCheckpoint.ts +20 -0
- package/src/types/models/models-index.ts +10 -0
- package/src/types/types.ts +73 -0
- package/src/utils/bson.ts +17 -0
- package/src/utils/bucket-data.ts +25 -0
- package/src/utils/db.ts +27 -0
- package/src/utils/ts-codec.ts +14 -0
- package/src/utils/utils-index.ts +4 -0
- package/test/src/__snapshots__/storage.test.ts.snap +9 -0
- package/test/src/__snapshots__/storage_sync.test.ts.snap +332 -0
- package/test/src/env.ts +6 -0
- package/test/src/migrations.test.ts +34 -0
- package/test/src/setup.ts +16 -0
- package/test/src/storage.test.ts +131 -0
- package/test/src/storage_compacting.test.ts +5 -0
- package/test/src/storage_sync.test.ts +12 -0
- package/test/src/util.ts +34 -0
- package/test/tsconfig.json +20 -0
- package/tsconfig.json +36 -0
- package/vitest.config.ts +13 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { framework, PowerSyncMigrationManager, ServiceContext, TestStorageOptions } from '@powersync/service-core';
|
|
2
|
+
import { PostgresMigrationAgent } from '../migrations/PostgresMigrationAgent.js';
|
|
3
|
+
import { normalizePostgresStorageConfig, PostgresStorageConfigDecoded } from '../types/types.js';
|
|
4
|
+
import { PostgresBucketStorageFactory } from './PostgresBucketStorageFactory.js';
|
|
5
|
+
|
|
6
|
+
export type PostgresTestStorageOptions = {
|
|
7
|
+
url: string;
|
|
8
|
+
/**
|
|
9
|
+
* Vitest can cause issues when loading .ts files for migrations.
|
|
10
|
+
* This allows for providing a custom PostgresMigrationAgent.
|
|
11
|
+
*/
|
|
12
|
+
migrationAgent?: (config: PostgresStorageConfigDecoded) => PostgresMigrationAgent;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const PostgresTestStorageFactoryGenerator = (factoryOptions: PostgresTestStorageOptions) => {
|
|
16
|
+
return async (options?: TestStorageOptions) => {
|
|
17
|
+
try {
|
|
18
|
+
const migrationManager: PowerSyncMigrationManager = new framework.MigrationManager();
|
|
19
|
+
|
|
20
|
+
const BASE_CONFIG = {
|
|
21
|
+
type: 'postgresql' as const,
|
|
22
|
+
uri: factoryOptions.url,
|
|
23
|
+
sslmode: 'disable' as const
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const TEST_CONNECTION_OPTIONS = normalizePostgresStorageConfig(BASE_CONFIG);
|
|
27
|
+
|
|
28
|
+
await using migrationAgent = factoryOptions.migrationAgent
|
|
29
|
+
? factoryOptions.migrationAgent(BASE_CONFIG)
|
|
30
|
+
: new PostgresMigrationAgent(BASE_CONFIG);
|
|
31
|
+
migrationManager.registerMigrationAgent(migrationAgent);
|
|
32
|
+
|
|
33
|
+
const mockServiceContext = { configuration: { storage: BASE_CONFIG } } as unknown as ServiceContext;
|
|
34
|
+
|
|
35
|
+
if (!options?.doNotClear) {
|
|
36
|
+
await migrationManager.migrate({
|
|
37
|
+
direction: framework.migrations.Direction.Down,
|
|
38
|
+
migrationContext: {
|
|
39
|
+
service_context: mockServiceContext
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
await migrationManager.migrate({
|
|
45
|
+
direction: framework.migrations.Direction.Up,
|
|
46
|
+
migrationContext: {
|
|
47
|
+
service_context: mockServiceContext
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
return new PostgresBucketStorageFactory({
|
|
52
|
+
config: TEST_CONNECTION_OPTIONS,
|
|
53
|
+
slot_name_prefix: 'test_'
|
|
54
|
+
});
|
|
55
|
+
} catch (ex) {
|
|
56
|
+
// Vitest does not display these errors nicely when using the `await using` syntx
|
|
57
|
+
console.error(ex, ex.cause);
|
|
58
|
+
throw ex;
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TODO share this implementation better in the core package.
|
|
3
|
+
* There are some subtle differences in this implementation.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { storage, utils } from '@powersync/service-core';
|
|
7
|
+
import { RequiredOperationBatchLimits } from '../../types/types.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Batch of input operations.
|
|
11
|
+
*
|
|
12
|
+
* We accumulate operations up to MAX_RECORD_BATCH_SIZE,
|
|
13
|
+
* then further split into sub-batches if MAX_CURRENT_DATA_BATCH_SIZE is exceeded.
|
|
14
|
+
*/
|
|
15
|
+
export class OperationBatch {
|
|
16
|
+
batch: RecordOperation[] = [];
|
|
17
|
+
currentSize: number = 0;
|
|
18
|
+
|
|
19
|
+
readonly maxBatchCount: number;
|
|
20
|
+
readonly maxRecordSize: number;
|
|
21
|
+
readonly maxCurrentDataBatchSize: number;
|
|
22
|
+
|
|
23
|
+
get length() {
|
|
24
|
+
return this.batch.length;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
constructor(protected options: RequiredOperationBatchLimits) {
|
|
28
|
+
this.maxBatchCount = options.max_record_count;
|
|
29
|
+
this.maxRecordSize = options.max_estimated_size;
|
|
30
|
+
this.maxCurrentDataBatchSize = options.max_current_data_batch_size;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
push(op: RecordOperation) {
|
|
34
|
+
this.batch.push(op);
|
|
35
|
+
this.currentSize += op.estimatedSize;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
shouldFlush() {
|
|
39
|
+
return this.batch.length >= this.maxBatchCount || this.currentSize > this.maxCurrentDataBatchSize;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
*
|
|
44
|
+
* @param sizes Map of source key to estimated size of the current_data document, or undefined if current_data is not persisted.
|
|
45
|
+
*
|
|
46
|
+
*/
|
|
47
|
+
*batched(sizes: Map<string, number> | undefined): Generator<RecordOperation[]> {
|
|
48
|
+
if (sizes == null) {
|
|
49
|
+
yield this.batch;
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
let currentBatch: RecordOperation[] = [];
|
|
53
|
+
let currentBatchSize = 0;
|
|
54
|
+
for (let op of this.batch) {
|
|
55
|
+
const key = op.internalBeforeKey;
|
|
56
|
+
const size = sizes.get(key) ?? 0;
|
|
57
|
+
if (currentBatchSize + size > this.maxCurrentDataBatchSize && currentBatch.length > 0) {
|
|
58
|
+
yield currentBatch;
|
|
59
|
+
currentBatch = [];
|
|
60
|
+
currentBatchSize = 0;
|
|
61
|
+
}
|
|
62
|
+
currentBatchSize += size;
|
|
63
|
+
currentBatch.push(op);
|
|
64
|
+
}
|
|
65
|
+
if (currentBatch.length > 0) {
|
|
66
|
+
yield currentBatch;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export class RecordOperation {
|
|
72
|
+
public readonly afterId: storage.ReplicaId | null;
|
|
73
|
+
public readonly beforeId: storage.ReplicaId;
|
|
74
|
+
public readonly internalBeforeKey: string;
|
|
75
|
+
public readonly internalAfterKey: string | null;
|
|
76
|
+
public readonly estimatedSize: number;
|
|
77
|
+
|
|
78
|
+
constructor(public readonly record: storage.SaveOptions) {
|
|
79
|
+
const afterId = record.afterReplicaId ?? null;
|
|
80
|
+
const beforeId = record.beforeReplicaId ?? record.afterReplicaId;
|
|
81
|
+
this.afterId = afterId;
|
|
82
|
+
this.beforeId = beforeId;
|
|
83
|
+
this.internalBeforeKey = cacheKey(record.sourceTable.id, beforeId);
|
|
84
|
+
this.internalAfterKey = afterId ? cacheKey(record.sourceTable.id, afterId) : null;
|
|
85
|
+
this.estimatedSize = utils.estimateRowSize(record.before) + utils.estimateRowSize(record.after);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* In-memory cache key - must not be persisted.
|
|
91
|
+
*/
|
|
92
|
+
export function cacheKey(sourceTableId: string, id: storage.ReplicaId) {
|
|
93
|
+
return encodedCacheKey(sourceTableId, storage.serializeReplicaId(id));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Calculates a cache key for a stored ReplicaId. This is usually stored as a bytea/Buffer.
|
|
98
|
+
*/
|
|
99
|
+
export function encodedCacheKey(sourceTableId: string, storedKey: Buffer) {
|
|
100
|
+
return `${sourceTableId}.${storedKey.toString('base64')}`;
|
|
101
|
+
}
|