@powersync/service-module-postgres-storage 0.1.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 (157) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/LICENSE +67 -0
  3. package/README.md +67 -0
  4. package/dist/.tsbuildinfo +1 -0
  5. package/dist/@types/index.d.ts +7 -0
  6. package/dist/@types/migrations/PostgresMigrationAgent.d.ts +12 -0
  7. package/dist/@types/migrations/PostgresMigrationStore.d.ts +14 -0
  8. package/dist/@types/migrations/migration-utils.d.ts +3 -0
  9. package/dist/@types/migrations/scripts/1684951997326-init.d.ts +3 -0
  10. package/dist/@types/module/PostgresStorageModule.d.ts +6 -0
  11. package/dist/@types/storage/PostgresBucketStorageFactory.d.ts +42 -0
  12. package/dist/@types/storage/PostgresCompactor.d.ts +40 -0
  13. package/dist/@types/storage/PostgresStorageProvider.d.ts +5 -0
  14. package/dist/@types/storage/PostgresSyncRulesStorage.d.ts +46 -0
  15. package/dist/@types/storage/PostgresTestStorageFactoryGenerator.d.ts +13 -0
  16. package/dist/@types/storage/batch/OperationBatch.d.ts +47 -0
  17. package/dist/@types/storage/batch/PostgresBucketBatch.d.ts +90 -0
  18. package/dist/@types/storage/batch/PostgresPersistedBatch.d.ts +64 -0
  19. package/dist/@types/storage/checkpoints/PostgresWriteCheckpointAPI.d.ts +20 -0
  20. package/dist/@types/storage/storage-index.d.ts +5 -0
  21. package/dist/@types/storage/sync-rules/PostgresPersistedSyncRulesContent.d.ts +17 -0
  22. package/dist/@types/types/codecs.d.ts +61 -0
  23. package/dist/@types/types/models/ActiveCheckpoint.d.ts +12 -0
  24. package/dist/@types/types/models/ActiveCheckpointNotification.d.ts +19 -0
  25. package/dist/@types/types/models/BucketData.d.ts +22 -0
  26. package/dist/@types/types/models/BucketParameters.d.ts +11 -0
  27. package/dist/@types/types/models/CurrentData.d.ts +22 -0
  28. package/dist/@types/types/models/Instance.d.ts +6 -0
  29. package/dist/@types/types/models/Migration.d.ts +12 -0
  30. package/dist/@types/types/models/SourceTable.d.ts +31 -0
  31. package/dist/@types/types/models/SyncRules.d.ts +47 -0
  32. package/dist/@types/types/models/WriteCheckpoint.d.ts +15 -0
  33. package/dist/@types/types/models/models-index.d.ts +10 -0
  34. package/dist/@types/types/types.d.ts +94 -0
  35. package/dist/@types/utils/bson.d.ts +6 -0
  36. package/dist/@types/utils/bucket-data.d.ts +18 -0
  37. package/dist/@types/utils/db.d.ts +8 -0
  38. package/dist/@types/utils/ts-codec.d.ts +5 -0
  39. package/dist/@types/utils/utils-index.d.ts +4 -0
  40. package/dist/index.js +8 -0
  41. package/dist/index.js.map +1 -0
  42. package/dist/migrations/PostgresMigrationAgent.js +36 -0
  43. package/dist/migrations/PostgresMigrationAgent.js.map +1 -0
  44. package/dist/migrations/PostgresMigrationStore.js +60 -0
  45. package/dist/migrations/PostgresMigrationStore.js.map +1 -0
  46. package/dist/migrations/migration-utils.js +13 -0
  47. package/dist/migrations/migration-utils.js.map +1 -0
  48. package/dist/migrations/scripts/1684951997326-init.js +196 -0
  49. package/dist/migrations/scripts/1684951997326-init.js.map +1 -0
  50. package/dist/module/PostgresStorageModule.js +23 -0
  51. package/dist/module/PostgresStorageModule.js.map +1 -0
  52. package/dist/storage/PostgresBucketStorageFactory.js +433 -0
  53. package/dist/storage/PostgresBucketStorageFactory.js.map +1 -0
  54. package/dist/storage/PostgresCompactor.js +298 -0
  55. package/dist/storage/PostgresCompactor.js.map +1 -0
  56. package/dist/storage/PostgresStorageProvider.js +35 -0
  57. package/dist/storage/PostgresStorageProvider.js.map +1 -0
  58. package/dist/storage/PostgresSyncRulesStorage.js +619 -0
  59. package/dist/storage/PostgresSyncRulesStorage.js.map +1 -0
  60. package/dist/storage/PostgresTestStorageFactoryGenerator.js +110 -0
  61. package/dist/storage/PostgresTestStorageFactoryGenerator.js.map +1 -0
  62. package/dist/storage/batch/OperationBatch.js +93 -0
  63. package/dist/storage/batch/OperationBatch.js.map +1 -0
  64. package/dist/storage/batch/PostgresBucketBatch.js +732 -0
  65. package/dist/storage/batch/PostgresBucketBatch.js.map +1 -0
  66. package/dist/storage/batch/PostgresPersistedBatch.js +367 -0
  67. package/dist/storage/batch/PostgresPersistedBatch.js.map +1 -0
  68. package/dist/storage/checkpoints/PostgresWriteCheckpointAPI.js +148 -0
  69. package/dist/storage/checkpoints/PostgresWriteCheckpointAPI.js.map +1 -0
  70. package/dist/storage/storage-index.js +6 -0
  71. package/dist/storage/storage-index.js.map +1 -0
  72. package/dist/storage/sync-rules/PostgresPersistedSyncRulesContent.js +58 -0
  73. package/dist/storage/sync-rules/PostgresPersistedSyncRulesContent.js.map +1 -0
  74. package/dist/types/codecs.js +97 -0
  75. package/dist/types/codecs.js.map +1 -0
  76. package/dist/types/models/ActiveCheckpoint.js +12 -0
  77. package/dist/types/models/ActiveCheckpoint.js.map +1 -0
  78. package/dist/types/models/ActiveCheckpointNotification.js +8 -0
  79. package/dist/types/models/ActiveCheckpointNotification.js.map +1 -0
  80. package/dist/types/models/BucketData.js +23 -0
  81. package/dist/types/models/BucketData.js.map +1 -0
  82. package/dist/types/models/BucketParameters.js +11 -0
  83. package/dist/types/models/BucketParameters.js.map +1 -0
  84. package/dist/types/models/CurrentData.js +16 -0
  85. package/dist/types/models/CurrentData.js.map +1 -0
  86. package/dist/types/models/Instance.js +5 -0
  87. package/dist/types/models/Instance.js.map +1 -0
  88. package/dist/types/models/Migration.js +12 -0
  89. package/dist/types/models/Migration.js.map +1 -0
  90. package/dist/types/models/SourceTable.js +24 -0
  91. package/dist/types/models/SourceTable.js.map +1 -0
  92. package/dist/types/models/SyncRules.js +47 -0
  93. package/dist/types/models/SyncRules.js.map +1 -0
  94. package/dist/types/models/WriteCheckpoint.js +13 -0
  95. package/dist/types/models/WriteCheckpoint.js.map +1 -0
  96. package/dist/types/models/models-index.js +11 -0
  97. package/dist/types/models/models-index.js.map +1 -0
  98. package/dist/types/types.js +46 -0
  99. package/dist/types/types.js.map +1 -0
  100. package/dist/utils/bson.js +16 -0
  101. package/dist/utils/bson.js.map +1 -0
  102. package/dist/utils/bucket-data.js +25 -0
  103. package/dist/utils/bucket-data.js.map +1 -0
  104. package/dist/utils/db.js +24 -0
  105. package/dist/utils/db.js.map +1 -0
  106. package/dist/utils/ts-codec.js +11 -0
  107. package/dist/utils/ts-codec.js.map +1 -0
  108. package/dist/utils/utils-index.js +5 -0
  109. package/dist/utils/utils-index.js.map +1 -0
  110. package/package.json +50 -0
  111. package/src/index.ts +10 -0
  112. package/src/migrations/PostgresMigrationAgent.ts +46 -0
  113. package/src/migrations/PostgresMigrationStore.ts +70 -0
  114. package/src/migrations/migration-utils.ts +14 -0
  115. package/src/migrations/scripts/1684951997326-init.ts +141 -0
  116. package/src/module/PostgresStorageModule.ts +30 -0
  117. package/src/storage/PostgresBucketStorageFactory.ts +496 -0
  118. package/src/storage/PostgresCompactor.ts +366 -0
  119. package/src/storage/PostgresStorageProvider.ts +42 -0
  120. package/src/storage/PostgresSyncRulesStorage.ts +666 -0
  121. package/src/storage/PostgresTestStorageFactoryGenerator.ts +61 -0
  122. package/src/storage/batch/OperationBatch.ts +101 -0
  123. package/src/storage/batch/PostgresBucketBatch.ts +885 -0
  124. package/src/storage/batch/PostgresPersistedBatch.ts +441 -0
  125. package/src/storage/checkpoints/PostgresWriteCheckpointAPI.ts +176 -0
  126. package/src/storage/storage-index.ts +5 -0
  127. package/src/storage/sync-rules/PostgresPersistedSyncRulesContent.ts +67 -0
  128. package/src/types/codecs.ts +136 -0
  129. package/src/types/models/ActiveCheckpoint.ts +15 -0
  130. package/src/types/models/ActiveCheckpointNotification.ts +14 -0
  131. package/src/types/models/BucketData.ts +26 -0
  132. package/src/types/models/BucketParameters.ts +14 -0
  133. package/src/types/models/CurrentData.ts +23 -0
  134. package/src/types/models/Instance.ts +8 -0
  135. package/src/types/models/Migration.ts +19 -0
  136. package/src/types/models/SourceTable.ts +32 -0
  137. package/src/types/models/SyncRules.ts +50 -0
  138. package/src/types/models/WriteCheckpoint.ts +20 -0
  139. package/src/types/models/models-index.ts +10 -0
  140. package/src/types/types.ts +73 -0
  141. package/src/utils/bson.ts +17 -0
  142. package/src/utils/bucket-data.ts +25 -0
  143. package/src/utils/db.ts +27 -0
  144. package/src/utils/ts-codec.ts +14 -0
  145. package/src/utils/utils-index.ts +4 -0
  146. package/test/src/__snapshots__/storage.test.ts.snap +9 -0
  147. package/test/src/__snapshots__/storage_sync.test.ts.snap +332 -0
  148. package/test/src/env.ts +6 -0
  149. package/test/src/migrations.test.ts +34 -0
  150. package/test/src/setup.ts +16 -0
  151. package/test/src/storage.test.ts +131 -0
  152. package/test/src/storage_compacting.test.ts +5 -0
  153. package/test/src/storage_sync.test.ts +12 -0
  154. package/test/src/util.ts +34 -0
  155. package/test/tsconfig.json +20 -0
  156. package/tsconfig.json +36 -0
  157. package/vitest.config.ts +13 -0
@@ -0,0 +1,332 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`sync - postgres > compacting data - invalidate checkpoint 1`] = `
4
+ [
5
+ {
6
+ "checkpoint": {
7
+ "buckets": [
8
+ {
9
+ "bucket": "mybucket[]",
10
+ "checksum": -93886621,
11
+ "count": 2,
12
+ },
13
+ ],
14
+ "last_op_id": "2",
15
+ "write_checkpoint": undefined,
16
+ },
17
+ },
18
+ ]
19
+ `;
20
+
21
+ exports[`sync - postgres > compacting data - invalidate checkpoint 2`] = `
22
+ [
23
+ {
24
+ "data": {
25
+ "after": "0",
26
+ "bucket": "mybucket[]",
27
+ "data": [
28
+ {
29
+ "checksum": -93886621n,
30
+ "op": "CLEAR",
31
+ "op_id": "2",
32
+ },
33
+ ],
34
+ "has_more": false,
35
+ "next_after": "2",
36
+ },
37
+ },
38
+ {
39
+ "checkpoint_diff": {
40
+ "last_op_id": "4",
41
+ "removed_buckets": [],
42
+ "updated_buckets": [
43
+ {
44
+ "bucket": "mybucket[]",
45
+ "checksum": 499012468,
46
+ "count": 4,
47
+ },
48
+ ],
49
+ "write_checkpoint": undefined,
50
+ },
51
+ },
52
+ {
53
+ "data": {
54
+ "after": "2",
55
+ "bucket": "mybucket[]",
56
+ "data": [
57
+ {
58
+ "checksum": 1859363232n,
59
+ "data": "{"id":"t1","description":"Test 1b"}",
60
+ "object_id": "t1",
61
+ "object_type": "test",
62
+ "op": "PUT",
63
+ "op_id": "3",
64
+ "subkey": "02d285ac-4f96-5124-8fba-c6d1df992dd1",
65
+ },
66
+ {
67
+ "checksum": 3028503153n,
68
+ "data": "{"id":"t2","description":"Test 2b"}",
69
+ "object_id": "t2",
70
+ "object_type": "test",
71
+ "op": "PUT",
72
+ "op_id": "4",
73
+ "subkey": "a17e6883-d5d2-599d-a805-d60528127dbd",
74
+ },
75
+ ],
76
+ "has_more": false,
77
+ "next_after": "4",
78
+ },
79
+ },
80
+ {
81
+ "checkpoint_complete": {
82
+ "last_op_id": "4",
83
+ },
84
+ },
85
+ ]
86
+ `;
87
+
88
+ exports[`sync - postgres > expired token 1`] = `
89
+ [
90
+ {
91
+ "token_expires_in": 0,
92
+ },
93
+ ]
94
+ `;
95
+
96
+ exports[`sync - postgres > expiring token 1`] = `
97
+ [
98
+ {
99
+ "checkpoint": {
100
+ "buckets": [
101
+ {
102
+ "bucket": "mybucket[]",
103
+ "checksum": 0,
104
+ "count": 0,
105
+ },
106
+ ],
107
+ "last_op_id": "0",
108
+ "write_checkpoint": undefined,
109
+ },
110
+ },
111
+ {
112
+ "checkpoint_complete": {
113
+ "last_op_id": "0",
114
+ },
115
+ },
116
+ ]
117
+ `;
118
+
119
+ exports[`sync - postgres > expiring token 2`] = `
120
+ [
121
+ {
122
+ "token_expires_in": 0,
123
+ },
124
+ ]
125
+ `;
126
+
127
+ exports[`sync - postgres > sync global data 1`] = `
128
+ [
129
+ {
130
+ "checkpoint": {
131
+ "buckets": [
132
+ {
133
+ "bucket": "mybucket[]",
134
+ "checksum": -93886621,
135
+ "count": 2,
136
+ },
137
+ ],
138
+ "last_op_id": "2",
139
+ "write_checkpoint": undefined,
140
+ },
141
+ },
142
+ {
143
+ "data": {
144
+ "after": "0",
145
+ "bucket": "mybucket[]",
146
+ "data": [
147
+ {
148
+ "checksum": 920318466n,
149
+ "data": "{"id":"t1","description":"Test 1"}",
150
+ "object_id": "t1",
151
+ "object_type": "test",
152
+ "op": "PUT",
153
+ "op_id": "1",
154
+ "subkey": "02d285ac-4f96-5124-8fba-c6d1df992dd1",
155
+ },
156
+ {
157
+ "checksum": 3280762209n,
158
+ "data": "{"id":"t2","description":"Test 2"}",
159
+ "object_id": "t2",
160
+ "object_type": "test",
161
+ "op": "PUT",
162
+ "op_id": "2",
163
+ "subkey": "a17e6883-d5d2-599d-a805-d60528127dbd",
164
+ },
165
+ ],
166
+ "has_more": false,
167
+ "next_after": "2",
168
+ },
169
+ },
170
+ {
171
+ "checkpoint_complete": {
172
+ "last_op_id": "2",
173
+ },
174
+ },
175
+ ]
176
+ `;
177
+
178
+ exports[`sync - postgres > sync legacy non-raw data 1`] = `
179
+ [
180
+ {
181
+ "checkpoint": {
182
+ "buckets": [
183
+ {
184
+ "bucket": "mybucket[]",
185
+ "checksum": -852817836,
186
+ "count": 1,
187
+ },
188
+ ],
189
+ "last_op_id": "1",
190
+ "write_checkpoint": undefined,
191
+ },
192
+ },
193
+ {
194
+ "data": {
195
+ "after": "0",
196
+ "bucket": "mybucket[]",
197
+ "data": [
198
+ {
199
+ "checksum": 3442149460n,
200
+ "data": {
201
+ "description": "Test
202
+ "string"",
203
+ "id": "t1",
204
+ "large_num": 12345678901234567890n,
205
+ },
206
+ "object_id": "t1",
207
+ "object_type": "test",
208
+ "op": "PUT",
209
+ "op_id": "1",
210
+ "subkey": "02d285ac-4f96-5124-8fba-c6d1df992dd1",
211
+ },
212
+ ],
213
+ "has_more": false,
214
+ "next_after": "1",
215
+ },
216
+ },
217
+ {
218
+ "checkpoint_complete": {
219
+ "last_op_id": "1",
220
+ },
221
+ },
222
+ ]
223
+ `;
224
+
225
+ exports[`sync - postgres > sync updates to global data 1`] = `
226
+ [
227
+ {
228
+ "checkpoint": {
229
+ "buckets": [
230
+ {
231
+ "bucket": "mybucket[]",
232
+ "checksum": 0,
233
+ "count": 0,
234
+ },
235
+ ],
236
+ "last_op_id": "0",
237
+ "write_checkpoint": undefined,
238
+ },
239
+ },
240
+ {
241
+ "checkpoint_complete": {
242
+ "last_op_id": "0",
243
+ },
244
+ },
245
+ ]
246
+ `;
247
+
248
+ exports[`sync - postgres > sync updates to global data 2`] = `
249
+ [
250
+ {
251
+ "checkpoint_diff": {
252
+ "last_op_id": "1",
253
+ "removed_buckets": [],
254
+ "updated_buckets": [
255
+ {
256
+ "bucket": "mybucket[]",
257
+ "checksum": 920318466,
258
+ "count": 1,
259
+ },
260
+ ],
261
+ "write_checkpoint": undefined,
262
+ },
263
+ },
264
+ {
265
+ "data": {
266
+ "after": "0",
267
+ "bucket": "mybucket[]",
268
+ "data": [
269
+ {
270
+ "checksum": 920318466n,
271
+ "data": "{"id":"t1","description":"Test 1"}",
272
+ "object_id": "t1",
273
+ "object_type": "test",
274
+ "op": "PUT",
275
+ "op_id": "1",
276
+ "subkey": "02d285ac-4f96-5124-8fba-c6d1df992dd1",
277
+ },
278
+ ],
279
+ "has_more": false,
280
+ "next_after": "1",
281
+ },
282
+ },
283
+ {
284
+ "checkpoint_complete": {
285
+ "last_op_id": "1",
286
+ },
287
+ },
288
+ ]
289
+ `;
290
+
291
+ exports[`sync - postgres > sync updates to global data 3`] = `
292
+ [
293
+ {
294
+ "checkpoint_diff": {
295
+ "last_op_id": "2",
296
+ "removed_buckets": [],
297
+ "updated_buckets": [
298
+ {
299
+ "bucket": "mybucket[]",
300
+ "checksum": -93886621,
301
+ "count": 2,
302
+ },
303
+ ],
304
+ "write_checkpoint": undefined,
305
+ },
306
+ },
307
+ {
308
+ "data": {
309
+ "after": "1",
310
+ "bucket": "mybucket[]",
311
+ "data": [
312
+ {
313
+ "checksum": 3280762209n,
314
+ "data": "{"id":"t2","description":"Test 2"}",
315
+ "object_id": "t2",
316
+ "object_type": "test",
317
+ "op": "PUT",
318
+ "op_id": "2",
319
+ "subkey": "a17e6883-d5d2-599d-a805-d60528127dbd",
320
+ },
321
+ ],
322
+ "has_more": false,
323
+ "next_after": "2",
324
+ },
325
+ },
326
+ {
327
+ "checkpoint_complete": {
328
+ "last_op_id": "2",
329
+ },
330
+ },
331
+ ]
332
+ `;
@@ -0,0 +1,6 @@
1
+ import { utils } from '@powersync/lib-services-framework';
2
+
3
+ export const env = utils.collectEnvironmentVariables({
4
+ PG_STORAGE_TEST_URL: utils.type.string.default('postgres://postgres:postgres@localhost:5431/powersync_storage_test'),
5
+ CI: utils.type.boolean.default('false')
6
+ });
@@ -0,0 +1,34 @@
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ import { register } from '@powersync/service-core-tests';
4
+ import { PostgresMigrationAgent } from '../../src/migrations/PostgresMigrationAgent.js';
5
+ import { env } from './env.js';
6
+ import { POSTGRES_STORAGE_FACTORY } from './util.js';
7
+
8
+ const MIGRATION_AGENT_FACTORY = () => {
9
+ return new PostgresMigrationAgent({ type: 'postgresql', uri: env.PG_STORAGE_TEST_URL, sslmode: 'disable' });
10
+ };
11
+
12
+ describe('Migrations', () => {
13
+ register.registerMigrationTests(MIGRATION_AGENT_FACTORY);
14
+
15
+ it('Should have tables declared', async () => {
16
+ const { db } = await POSTGRES_STORAGE_FACTORY();
17
+
18
+ const tables = await db.sql`
19
+ SELECT
20
+ table_schema,
21
+ table_name
22
+ FROM
23
+ information_schema.tables
24
+ WHERE
25
+ table_type = 'BASE TABLE'
26
+ AND table_schema NOT IN ('pg_catalog', 'information_schema')
27
+ ORDER BY
28
+ table_schema,
29
+ table_name;
30
+ `.rows<{ table_schema: string; table_name: string }>();
31
+
32
+ expect(tables.find((t) => t.table_name == 'sync_rules')).exist;
33
+ });
34
+ });
@@ -0,0 +1,16 @@
1
+ import { container } from '@powersync/lib-services-framework';
2
+ import { Metrics } from '@powersync/service-core';
3
+ import { beforeAll } from 'vitest';
4
+
5
+ beforeAll(async () => {
6
+ // Executes for every test file
7
+ container.registerDefaults();
8
+
9
+ // The metrics need to be initialized before they can be used
10
+ await Metrics.initialise({
11
+ disable_telemetry_sharing: true,
12
+ powersync_instance_id: 'test',
13
+ internal_metrics_endpoint: 'unused.for.tests.com'
14
+ });
15
+ Metrics.getInstance().resetCounters();
16
+ });
@@ -0,0 +1,131 @@
1
+ import { storage } from '@powersync/service-core';
2
+ import { register, TEST_TABLE, test_utils } from '@powersync/service-core-tests';
3
+ import { describe, expect, test } from 'vitest';
4
+ import { POSTGRES_STORAGE_FACTORY } from './util.js';
5
+
6
+ describe('Sync Bucket Validation', register.registerBucketValidationTests);
7
+
8
+ describe('Postgres Sync Bucket Storage', () => {
9
+ register.registerDataStorageTests(POSTGRES_STORAGE_FACTORY);
10
+
11
+ /**
12
+ * The split of returned results can vary depending on storage drivers.
13
+ * The large rows here are 2MB large while the default chunk limit is 1mb.
14
+ * The Postgres storage driver will detect if the next row will increase the batch
15
+ * over the limit and separate that row into a new batch (or single row batch) if applicable.
16
+ */
17
+ test('large batch (2)', async () => {
18
+ // Test syncing a batch of data that is small in count,
19
+ // but large enough in size to be split over multiple returned chunks.
20
+ // Similar to the above test, but splits over 1MB chunks.
21
+ const sync_rules = test_utils.testRules(
22
+ `
23
+ bucket_definitions:
24
+ global:
25
+ data:
26
+ - SELECT id, description FROM "%"
27
+ `
28
+ );
29
+ using factory = await POSTGRES_STORAGE_FACTORY();
30
+ const bucketStorage = factory.getInstance(sync_rules);
31
+
32
+ const result = await bucketStorage.startBatch(test_utils.BATCH_OPTIONS, async (batch) => {
33
+ const sourceTable = TEST_TABLE;
34
+
35
+ const largeDescription = '0123456789'.repeat(2_000_00);
36
+
37
+ await batch.save({
38
+ sourceTable,
39
+ tag: storage.SaveOperationTag.INSERT,
40
+ after: {
41
+ id: 'test1',
42
+ description: 'test1'
43
+ },
44
+ afterReplicaId: test_utils.rid('test1')
45
+ });
46
+
47
+ await batch.save({
48
+ sourceTable,
49
+ tag: storage.SaveOperationTag.INSERT,
50
+ after: {
51
+ id: 'large1',
52
+ description: largeDescription
53
+ },
54
+ afterReplicaId: test_utils.rid('large1')
55
+ });
56
+
57
+ // Large enough to split the returned batch
58
+ await batch.save({
59
+ sourceTable,
60
+ tag: storage.SaveOperationTag.INSERT,
61
+ after: {
62
+ id: 'large2',
63
+ description: largeDescription
64
+ },
65
+ afterReplicaId: test_utils.rid('large2')
66
+ });
67
+
68
+ await batch.save({
69
+ sourceTable,
70
+ tag: storage.SaveOperationTag.INSERT,
71
+ after: {
72
+ id: 'test3',
73
+ description: 'test3'
74
+ },
75
+ afterReplicaId: test_utils.rid('test3')
76
+ });
77
+ });
78
+
79
+ const checkpoint = result!.flushed_op;
80
+
81
+ const options: storage.BucketDataBatchOptions = {};
82
+
83
+ const batch1 = await test_utils.fromAsync(
84
+ bucketStorage.getBucketDataBatch(checkpoint, new Map([['global[]', '0']]), options)
85
+ );
86
+ expect(test_utils.getBatchData(batch1)).toEqual([
87
+ { op_id: '1', op: 'PUT', object_id: 'test1', checksum: 2871785649 }
88
+ ]);
89
+ expect(test_utils.getBatchMeta(batch1)).toEqual({
90
+ after: '0',
91
+ has_more: true,
92
+ next_after: '1'
93
+ });
94
+
95
+ const batch2 = await test_utils.fromAsync(
96
+ bucketStorage.getBucketDataBatch(checkpoint, new Map([['global[]', batch1[0].batch.next_after]]), options)
97
+ );
98
+ expect(test_utils.getBatchData(batch2)).toEqual([
99
+ { op_id: '2', op: 'PUT', object_id: 'large1', checksum: 1178768505 }
100
+ ]);
101
+ expect(test_utils.getBatchMeta(batch2)).toEqual({
102
+ after: '1',
103
+ has_more: true,
104
+ next_after: '2'
105
+ });
106
+
107
+ const batch3 = await test_utils.fromAsync(
108
+ bucketStorage.getBucketDataBatch(checkpoint, new Map([['global[]', batch2[0].batch.next_after]]), options)
109
+ );
110
+ expect(test_utils.getBatchData(batch3)).toEqual([
111
+ { op_id: '3', op: 'PUT', object_id: 'large2', checksum: 1607205872 }
112
+ ]);
113
+ expect(test_utils.getBatchMeta(batch3)).toEqual({
114
+ after: '2',
115
+ has_more: true,
116
+ next_after: '3'
117
+ });
118
+
119
+ const batch4 = await test_utils.fromAsync(
120
+ bucketStorage.getBucketDataBatch(checkpoint, new Map([['global[]', batch3[0].batch.next_after]]), options)
121
+ );
122
+ expect(test_utils.getBatchData(batch4)).toEqual([
123
+ { op_id: '4', op: 'PUT', object_id: 'test3', checksum: 1359888332 }
124
+ ]);
125
+ expect(test_utils.getBatchMeta(batch4)).toEqual({
126
+ after: '3',
127
+ has_more: false,
128
+ next_after: '4'
129
+ });
130
+ });
131
+ });
@@ -0,0 +1,5 @@
1
+ import { register } from '@powersync/service-core-tests';
2
+ import { describe } from 'vitest';
3
+ import { POSTGRES_STORAGE_FACTORY } from './util.js';
4
+
5
+ describe('Postgres Sync Bucket Storage Compact', () => register.registerCompactTests(POSTGRES_STORAGE_FACTORY, {}));
@@ -0,0 +1,12 @@
1
+ import { register } from '@powersync/service-core-tests';
2
+ import { describe } from 'vitest';
3
+ import { POSTGRES_STORAGE_FACTORY } from './util.js';
4
+
5
+ /**
6
+ * Bucket compacting is not yet implemented.
7
+ * This causes the internal compacting test to fail.
8
+ * Other tests have been verified manually.
9
+ */
10
+ describe('sync - postgres', () => {
11
+ register.registerSyncTests(POSTGRES_STORAGE_FACTORY);
12
+ });
@@ -0,0 +1,34 @@
1
+ import path from 'path';
2
+ import { fileURLToPath } from 'url';
3
+ import { normalizePostgresStorageConfig } from '../../src//types/types.js';
4
+ import { PostgresMigrationAgent } from '../../src/migrations/PostgresMigrationAgent.js';
5
+ import { PostgresTestStorageFactoryGenerator } from '../../src/storage/PostgresTestStorageFactoryGenerator.js';
6
+ import { env } from './env.js';
7
+
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = path.dirname(__filename);
10
+
11
+ export const TEST_URI = env.PG_STORAGE_TEST_URL;
12
+
13
+ const BASE_CONFIG = {
14
+ type: 'postgresql' as const,
15
+ uri: TEST_URI,
16
+ sslmode: 'disable' as const
17
+ };
18
+
19
+ export const TEST_CONNECTION_OPTIONS = normalizePostgresStorageConfig(BASE_CONFIG);
20
+
21
+ /**
22
+ * Vitest tries to load the migrations via .ts files which fails.
23
+ * For tests this links to the relevant .js files correctly
24
+ */
25
+ class TestPostgresMigrationAgent extends PostgresMigrationAgent {
26
+ getInternalScriptsDir(): string {
27
+ return path.resolve(__dirname, '../../dist/migrations/scripts');
28
+ }
29
+ }
30
+
31
+ export const POSTGRES_STORAGE_FACTORY = PostgresTestStorageFactoryGenerator({
32
+ url: env.PG_STORAGE_TEST_URL,
33
+ migrationAgent: (config) => new TestPostgresMigrationAgent(config)
34
+ });
@@ -0,0 +1,20 @@
1
+ {
2
+ "extends": "../tsconfig.json",
3
+ "compilerOptions": {
4
+ "rootDir": "src",
5
+ "baseUrl": "./",
6
+ "noEmit": true,
7
+ "esModuleInterop": true,
8
+ "declarationDir": "dist/@types",
9
+ "tsBuildInfoFile": "dist/.tsbuildinfo",
10
+ "lib": ["ES2022", "esnext.disposable"],
11
+ "skipLibCheck": true,
12
+ "sourceMap": true
13
+ },
14
+ "include": ["src"],
15
+ "references": [
16
+ {
17
+ "path": "../"
18
+ }
19
+ ]
20
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "outDir": "dist",
5
+ "declarationDir": "dist/@types",
6
+ "tsBuildInfoFile": "dist/.tsbuildinfo",
7
+ "rootDir": "src",
8
+ "target": "ES2022",
9
+ "lib": ["ES2022", "esnext.disposable"],
10
+ "skipLibCheck": true
11
+ },
12
+ "include": ["src"],
13
+ "references": [
14
+ {
15
+ "path": "../../packages/types"
16
+ },
17
+ {
18
+ "path": "../../packages/jsonbig"
19
+ },
20
+ {
21
+ "path": "../../packages/jpgwire"
22
+ },
23
+ {
24
+ "path": "../../packages/sync-rules"
25
+ },
26
+ {
27
+ "path": "../../packages/service-core"
28
+ },
29
+ {
30
+ "path": "../../libs/lib-services"
31
+ },
32
+ {
33
+ "path": "../../libs/lib-postgres"
34
+ }
35
+ ]
36
+ }
@@ -0,0 +1,13 @@
1
+ import { defineConfig } from 'vitest/config';
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ setupFiles: './test/src/setup.ts',
6
+ poolOptions: {
7
+ threads: {
8
+ singleThread: true
9
+ }
10
+ },
11
+ pool: 'threads'
12
+ }
13
+ });