@powersync/service-module-postgres-storage 0.12.0 → 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.
Files changed (65) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/@types/migrations/scripts/1771424826685-current-data-pending-deletes.d.ts +3 -0
  4. package/dist/@types/storage/PostgresBucketStorageFactory.d.ts +4 -0
  5. package/dist/@types/storage/PostgresCompactor.d.ts +8 -2
  6. package/dist/@types/storage/PostgresSyncRulesStorage.d.ts +5 -3
  7. package/dist/@types/storage/batch/OperationBatch.d.ts +2 -2
  8. package/dist/@types/storage/batch/PostgresBucketBatch.d.ts +12 -9
  9. package/dist/@types/storage/batch/PostgresPersistedBatch.d.ts +17 -5
  10. package/dist/@types/storage/current-data-store.d.ts +85 -0
  11. package/dist/@types/storage/current-data-table.d.ts +9 -0
  12. package/dist/@types/storage/table-id.d.ts +2 -0
  13. package/dist/@types/types/models/CurrentData.d.ts +18 -3
  14. package/dist/@types/utils/bson.d.ts +1 -1
  15. package/dist/@types/utils/test-utils.d.ts +1 -1
  16. package/dist/migrations/scripts/1771424826685-current-data-pending-deletes.js +8 -0
  17. package/dist/migrations/scripts/1771424826685-current-data-pending-deletes.js.map +1 -0
  18. package/dist/storage/PostgresBucketStorageFactory.js +41 -4
  19. package/dist/storage/PostgresBucketStorageFactory.js.map +1 -1
  20. package/dist/storage/PostgresCompactor.js +14 -6
  21. package/dist/storage/PostgresCompactor.js.map +1 -1
  22. package/dist/storage/PostgresSyncRulesStorage.js +23 -15
  23. package/dist/storage/PostgresSyncRulesStorage.js.map +1 -1
  24. package/dist/storage/batch/OperationBatch.js +2 -1
  25. package/dist/storage/batch/OperationBatch.js.map +1 -1
  26. package/dist/storage/batch/PostgresBucketBatch.js +286 -213
  27. package/dist/storage/batch/PostgresBucketBatch.js.map +1 -1
  28. package/dist/storage/batch/PostgresPersistedBatch.js +86 -81
  29. package/dist/storage/batch/PostgresPersistedBatch.js.map +1 -1
  30. package/dist/storage/current-data-store.js +270 -0
  31. package/dist/storage/current-data-store.js.map +1 -0
  32. package/dist/storage/current-data-table.js +22 -0
  33. package/dist/storage/current-data-table.js.map +1 -0
  34. package/dist/storage/table-id.js +8 -0
  35. package/dist/storage/table-id.js.map +1 -0
  36. package/dist/types/models/CurrentData.js +11 -2
  37. package/dist/types/models/CurrentData.js.map +1 -1
  38. package/dist/utils/bson.js.map +1 -1
  39. package/dist/utils/db.js +9 -0
  40. package/dist/utils/db.js.map +1 -1
  41. package/dist/utils/test-utils.js +13 -6
  42. package/dist/utils/test-utils.js.map +1 -1
  43. package/package.json +8 -8
  44. package/src/migrations/scripts/1771424826685-current-data-pending-deletes.ts +10 -0
  45. package/src/storage/PostgresBucketStorageFactory.ts +53 -5
  46. package/src/storage/PostgresCompactor.ts +17 -8
  47. package/src/storage/PostgresSyncRulesStorage.ts +30 -17
  48. package/src/storage/batch/OperationBatch.ts +4 -3
  49. package/src/storage/batch/PostgresBucketBatch.ts +306 -238
  50. package/src/storage/batch/PostgresPersistedBatch.ts +92 -84
  51. package/src/storage/current-data-store.ts +326 -0
  52. package/src/storage/current-data-table.ts +26 -0
  53. package/src/storage/table-id.ts +9 -0
  54. package/src/types/models/CurrentData.ts +17 -4
  55. package/src/utils/bson.ts +1 -1
  56. package/src/utils/db.ts +10 -0
  57. package/src/utils/test-utils.ts +14 -7
  58. package/test/src/__snapshots__/storage.test.ts.snap +151 -0
  59. package/test/src/__snapshots__/storage_compacting.test.ts.snap +17 -0
  60. package/test/src/__snapshots__/storage_sync.test.ts.snap +1095 -0
  61. package/test/src/migrations.test.ts +1 -1
  62. package/test/src/storage.test.ts +136 -130
  63. package/test/src/storage_compacting.test.ts +65 -3
  64. package/test/src/storage_sync.test.ts +11 -9
  65. package/test/src/util.ts +4 -4
@@ -0,0 +1,270 @@
1
+ import { pick } from '../utils/ts-codec.js';
2
+ import * as models from '../types/models/CurrentData.js';
3
+ const TruncateCurrentDataCodec = pick(models.V1CurrentData, ['buckets', 'lookups', 'source_key']);
4
+ const LookupKeyCodec = pick(models.V1CurrentData, ['source_key', 'source_table']);
5
+ export const V1_CURRENT_DATA_TABLE = 'current_data';
6
+ export const V3_CURRENT_DATA_TABLE = 'v3_current_data';
7
+ export class PostgresCurrentDataStore {
8
+ table;
9
+ softDeleteEnabled;
10
+ constructor(storageConfig) {
11
+ this.softDeleteEnabled = storageConfig.softDeleteCurrentData;
12
+ this.table = storageConfig.softDeleteCurrentData ? V3_CURRENT_DATA_TABLE : V1_CURRENT_DATA_TABLE;
13
+ }
14
+ streamTruncateRows(db, options) {
15
+ return db.streamRows({
16
+ statement: `
17
+ SELECT
18
+ buckets,
19
+ lookups,
20
+ source_key
21
+ FROM
22
+ ${this.table}
23
+ WHERE
24
+ group_id = $1
25
+ AND source_table = $2
26
+ ${this.wherePendingDelete({ onlyLiveRows: true })}
27
+ LIMIT
28
+ $3
29
+ FOR NO KEY UPDATE
30
+ `,
31
+ params: [
32
+ { type: 'int4', value: options.groupId },
33
+ { type: 'varchar', value: options.sourceTableId },
34
+ { type: 'int4', value: options.limit }
35
+ ]
36
+ });
37
+ }
38
+ decodeTruncateRow(row) {
39
+ return TruncateCurrentDataCodec.decode(row);
40
+ }
41
+ streamSizeRows(db, options) {
42
+ return db.streamRows({
43
+ statement: `
44
+ WITH
45
+ filter_data AS (
46
+ SELECT
47
+ decode(FILTER ->> 'source_key', 'hex') AS source_key,
48
+ (FILTER ->> 'source_table') AS source_table_id
49
+ FROM
50
+ jsonb_array_elements($1::jsonb) AS FILTER
51
+ )
52
+ SELECT
53
+ octet_length(c.data) AS data_size,
54
+ c.source_table,
55
+ c.source_key
56
+ FROM
57
+ ${this.table} c
58
+ JOIN filter_data f ON c.source_table = f.source_table_id
59
+ AND c.source_key = f.source_key
60
+ WHERE
61
+ c.group_id = $2
62
+ FOR NO KEY UPDATE
63
+ `,
64
+ params: [
65
+ { type: 'jsonb', value: options.lookups },
66
+ { type: 'int4', value: options.groupId }
67
+ ]
68
+ });
69
+ }
70
+ streamLookupRows(db, options) {
71
+ const selectColumns = options.skipExistingRows ? `c.source_table, c.source_key` : `c.*`;
72
+ return db.streamRows({
73
+ statement: `
74
+ SELECT
75
+ ${selectColumns}
76
+ FROM
77
+ ${this.table} c
78
+ JOIN (
79
+ SELECT
80
+ decode(FILTER ->> 'source_key', 'hex') AS source_key,
81
+ FILTER ->> 'source_table' AS source_table_id
82
+ FROM
83
+ jsonb_array_elements($1::jsonb) AS FILTER
84
+ ) f ON c.source_table = f.source_table_id
85
+ AND c.source_key = f.source_key
86
+ WHERE
87
+ c.group_id = $2
88
+ FOR NO KEY UPDATE;
89
+ `,
90
+ params: [
91
+ { type: 'jsonb', value: options.lookups },
92
+ { type: 'int4', value: options.groupId }
93
+ ]
94
+ });
95
+ }
96
+ decodeLookupRow(row, skipExistingRows) {
97
+ if (skipExistingRows) {
98
+ return LookupKeyCodec.decode(row);
99
+ }
100
+ return this.softDeleteEnabled ? models.V3CurrentData.decode(row) : models.V1CurrentData.decode(row);
101
+ }
102
+ async flushUpserts(db, updates) {
103
+ if (updates.length == 0) {
104
+ return;
105
+ }
106
+ if (this.softDeleteEnabled) {
107
+ await db.sql `
108
+ INSERT INTO
109
+ v3_current_data (
110
+ group_id,
111
+ source_table,
112
+ source_key,
113
+ buckets,
114
+ data,
115
+ lookups,
116
+ pending_delete
117
+ )
118
+ SELECT
119
+ group_id,
120
+ source_table,
121
+ decode(source_key, 'hex') AS source_key,
122
+ buckets::jsonb AS buckets,
123
+ decode(data, 'hex') AS data,
124
+ array(
125
+ SELECT
126
+ decode(element, 'hex')
127
+ FROM
128
+ unnest(lookups) AS element
129
+ ) AS lookups,
130
+ CASE
131
+ WHEN pending_delete IS NOT NULL THEN nextval('op_id_sequence')
132
+ ELSE NULL
133
+ END AS pending_delete
134
+ FROM
135
+ json_to_recordset(${{ type: 'json', value: updates }}::json) AS t (
136
+ group_id integer,
137
+ source_table text,
138
+ source_key text,
139
+ buckets text,
140
+ data text,
141
+ lookups TEXT[],
142
+ pending_delete bigint
143
+ )
144
+ ON CONFLICT (group_id, source_table, source_key) DO UPDATE
145
+ SET
146
+ buckets = EXCLUDED.buckets,
147
+ data = EXCLUDED.data,
148
+ lookups = EXCLUDED.lookups,
149
+ pending_delete = EXCLUDED.pending_delete;
150
+ `.execute();
151
+ return;
152
+ }
153
+ await db.sql `
154
+ INSERT INTO
155
+ current_data (
156
+ group_id,
157
+ source_table,
158
+ source_key,
159
+ buckets,
160
+ data,
161
+ lookups
162
+ )
163
+ SELECT
164
+ group_id,
165
+ source_table,
166
+ decode(source_key, 'hex') AS source_key,
167
+ buckets::jsonb AS buckets,
168
+ decode(data, 'hex') AS data,
169
+ array(
170
+ SELECT
171
+ decode(element, 'hex')
172
+ FROM
173
+ unnest(lookups) AS element
174
+ ) AS lookups
175
+ FROM
176
+ json_to_recordset(${{ type: 'json', value: updates }}::json) AS t (
177
+ group_id integer,
178
+ source_table text,
179
+ source_key text,
180
+ buckets text,
181
+ data text,
182
+ lookups TEXT[]
183
+ )
184
+ ON CONFLICT (group_id, source_table, source_key) DO UPDATE
185
+ SET
186
+ buckets = EXCLUDED.buckets,
187
+ data = EXCLUDED.data,
188
+ lookups = EXCLUDED.lookups;
189
+ `.execute();
190
+ }
191
+ async flushDeletes(db, options) {
192
+ if (options.deletes.length == 0) {
193
+ return;
194
+ }
195
+ if (this.softDeleteEnabled) {
196
+ await db.sql `
197
+ WITH
198
+ conditions AS (
199
+ SELECT
200
+ source_table,
201
+ decode(source_key_hex, 'hex') AS source_key
202
+ FROM
203
+ jsonb_to_recordset(${{
204
+ type: 'jsonb',
205
+ value: options.deletes
206
+ }}::jsonb) AS t (source_table text, source_key_hex text)
207
+ )
208
+ DELETE FROM v3_current_data USING conditions
209
+ WHERE
210
+ v3_current_data.group_id = ${{ type: 'int4', value: options.groupId }}
211
+ AND v3_current_data.source_table = conditions.source_table
212
+ AND v3_current_data.source_key = conditions.source_key;
213
+ `.execute();
214
+ return;
215
+ }
216
+ await db.sql `
217
+ WITH
218
+ conditions AS (
219
+ SELECT
220
+ source_table,
221
+ decode(source_key_hex, 'hex') AS source_key
222
+ FROM
223
+ jsonb_to_recordset(${{
224
+ type: 'jsonb',
225
+ value: options.deletes
226
+ }}::jsonb) AS t (source_table text, source_key_hex text)
227
+ )
228
+ DELETE FROM current_data USING conditions
229
+ WHERE
230
+ current_data.group_id = ${{ type: 'int4', value: options.groupId }}
231
+ AND current_data.source_table = conditions.source_table
232
+ AND current_data.source_key = conditions.source_key;
233
+ `.execute();
234
+ }
235
+ async cleanupPendingDeletes(db, options) {
236
+ if (!this.softDeleteEnabled) {
237
+ return;
238
+ }
239
+ await db.sql `
240
+ DELETE FROM v3_current_data
241
+ WHERE
242
+ group_id = ${{ type: 'int4', value: options.groupId }}
243
+ AND pending_delete IS NOT NULL
244
+ AND pending_delete <= ${{ type: 'int8', value: options.lastCheckpoint }}
245
+ `.execute();
246
+ }
247
+ async deleteGroupRows(db, options) {
248
+ if (this.softDeleteEnabled) {
249
+ await db.sql `
250
+ DELETE FROM v3_current_data
251
+ WHERE
252
+ group_id = ${{ type: 'int4', value: options.groupId }}
253
+ `.execute();
254
+ }
255
+ else {
256
+ await db.sql `
257
+ DELETE FROM current_data
258
+ WHERE
259
+ group_id = ${{ type: 'int4', value: options.groupId }}
260
+ `.execute();
261
+ }
262
+ }
263
+ wherePendingDelete(options) {
264
+ if (this.softDeleteEnabled && options.onlyLiveRows) {
265
+ return `AND pending_delete IS NULL`;
266
+ }
267
+ return ``;
268
+ }
269
+ }
270
+ //# sourceMappingURL=current-data-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"current-data-store.js","sourceRoot":"","sources":["../../src/storage/current-data-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import { ServiceAssertionError } from '@powersync/lib-services-framework';
2
+ export const V1_CURRENT_DATA_TABLE = 'current_data';
3
+ export const V3_CURRENT_DATA_TABLE = 'v3_current_data';
4
+ /**
5
+ * The table used by a specific storage version for general current_data access.
6
+ */
7
+ export function getCommonCurrentDataTable(storageConfig) {
8
+ return storageConfig.softDeleteCurrentData ? V3_CURRENT_DATA_TABLE : V1_CURRENT_DATA_TABLE;
9
+ }
10
+ export function getV1CurrentDataTable(storageConfig) {
11
+ if (storageConfig.softDeleteCurrentData) {
12
+ throw new ServiceAssertionError('current_data table cannot be used when softDeleteCurrentData is enabled');
13
+ }
14
+ return V1_CURRENT_DATA_TABLE;
15
+ }
16
+ export function getV3CurrentDataTable(storageConfig) {
17
+ if (!storageConfig.softDeleteCurrentData) {
18
+ throw new ServiceAssertionError('v3_current_data table cannot be used when softDeleteCurrentData is disabled');
19
+ }
20
+ return V3_CURRENT_DATA_TABLE;
21
+ }
22
+ //# sourceMappingURL=current-data-table.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"current-data-table.js","sourceRoot":"","sources":["../../src/storage/current-data-table.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,mCAAmC,CAAC;AAG1E,MAAM,CAAC,MAAM,qBAAqB,GAAG,cAAc,CAAC;AACpD,MAAM,CAAC,MAAM,qBAAqB,GAAG,iBAAiB,CAAC;AAEvD;;GAEG;AACH,MAAM,UAAU,yBAAyB,CAAC,aAA2C;IACnF,OAAO,aAAa,CAAC,qBAAqB,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,qBAAqB,CAAC;AAC7F,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,aAA2C;IAC/E,IAAI,aAAa,CAAC,qBAAqB,EAAE,CAAC;QACxC,MAAM,IAAI,qBAAqB,CAAC,yEAAyE,CAAC,CAAC;IAC7G,CAAC;IACD,OAAO,qBAAqB,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,aAA2C;IAC/E,IAAI,CAAC,aAAa,CAAC,qBAAqB,EAAE,CAAC;QACzC,MAAM,IAAI,qBAAqB,CAAC,6EAA6E,CAAC,CAAC;IACjH,CAAC;IACD,OAAO,qBAAqB,CAAC;AAC/B,CAAC"}
@@ -0,0 +1,8 @@
1
+ import { ServiceAssertionError } from '@powersync/lib-services-framework';
2
+ export function postgresTableId(id) {
3
+ if (typeof id == 'string') {
4
+ return id;
5
+ }
6
+ throw new ServiceAssertionError(`Expected string table id, got ObjectId`);
7
+ }
8
+ //# sourceMappingURL=table-id.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"table-id.js","sourceRoot":"","sources":["../../src/storage/table-id.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,mCAAmC,CAAC;AAG1E,MAAM,UAAU,eAAe,CAAC,EAAyB;IACvD,IAAI,OAAO,EAAE,IAAI,QAAQ,EAAE,CAAC;QAC1B,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,MAAM,IAAI,qBAAqB,CAAC,wCAAwC,CAAC,CAAC;AAC5E,CAAC"}
@@ -1,11 +1,11 @@
1
1
  import * as t from 'ts-codec';
2
- import { hexBuffer, jsonb, pgwire_number } from '../codecs.js';
2
+ import { bigint, hexBuffer, jsonb, pgwire_number } from '../codecs.js';
3
3
  export const CurrentBucket = t.object({
4
4
  bucket: t.string,
5
5
  table: t.string,
6
6
  id: t.string
7
7
  });
8
- export const CurrentData = t.object({
8
+ export const V1CurrentData = t.object({
9
9
  buckets: jsonb(t.array(CurrentBucket)),
10
10
  data: hexBuffer,
11
11
  group_id: pgwire_number,
@@ -13,4 +13,13 @@ export const CurrentData = t.object({
13
13
  source_key: hexBuffer,
14
14
  source_table: t.string
15
15
  });
16
+ export const V3CurrentData = t.object({
17
+ buckets: jsonb(t.array(CurrentBucket)),
18
+ data: hexBuffer,
19
+ group_id: pgwire_number,
20
+ lookups: t.array(hexBuffer),
21
+ source_key: hexBuffer,
22
+ source_table: t.string,
23
+ pending_delete: t.Null.or(bigint)
24
+ });
16
25
  //# sourceMappingURL=CurrentData.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"CurrentData.js","sourceRoot":"","sources":["../../../src/types/models/CurrentData.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,UAAU,CAAC;AAC9B,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE/D,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC;IACpC,MAAM,EAAE,CAAC,CAAC,MAAM;IAChB,KAAK,EAAE,CAAC,CAAC,MAAM;IACf,EAAE,EAAE,CAAC,CAAC,MAAM;CACb,CAAC,CAAC;AAKH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,CAAC,MAAM,CAAC;IAClC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;IACtC,IAAI,EAAE,SAAS;IACf,QAAQ,EAAE,aAAa;IACvB,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC;IAC3B,UAAU,EAAE,SAAS;IACrB,YAAY,EAAE,CAAC,CAAC,MAAM;CACvB,CAAC,CAAC"}
1
+ {"version":3,"file":"CurrentData.js","sourceRoot":"","sources":["../../../src/types/models/CurrentData.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,UAAU,CAAC;AAC9B,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAEvE,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC;IACpC,MAAM,EAAE,CAAC,CAAC,MAAM;IAChB,KAAK,EAAE,CAAC,CAAC,MAAM;IACf,EAAE,EAAE,CAAC,CAAC,MAAM;CACb,CAAC,CAAC;AAKH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC;IACpC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;IACtC,IAAI,EAAE,SAAS;IACf,QAAQ,EAAE,aAAa;IACvB,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC;IAC3B,UAAU,EAAE,SAAS;IACrB,YAAY,EAAE,CAAC,CAAC,MAAM;CACvB,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC;IACpC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;IACtC,IAAI,EAAE,SAAS;IACf,QAAQ,EAAE,aAAa;IACvB,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC;IAC3B,UAAU,EAAE,SAAS;IACrB,YAAY,EAAE,CAAC,CAAC,MAAM;IACtB,cAAc,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC;CAClC,CAAC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"bson.js","sourceRoot":"","sources":["../../src/utils/bson.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAE7B;;;GAGG;AAEH,MAAM,UAAU,iBAAiB,CAAC,OAAe,EAAE,EAAqB;IACtE,oCAAoC;IACpC,IAAI,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;QACvB,mDAAmD;QACnD,OAAO,GAAG,OAAO,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC;IAC1C,CAAC;IACD,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC;IAC3D,OAAO,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC;AAC3C,CAAC"}
1
+ {"version":3,"file":"bson.js","sourceRoot":"","sources":["../../src/utils/bson.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAE7B;;;GAGG;AAEH,MAAM,UAAU,iBAAiB,CAAC,OAA8B,EAAE,EAAqB;IACrF,oCAAoC;IACpC,IAAI,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;QACvB,mDAAmD;QACnD,OAAO,GAAG,OAAO,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC;IAC1C,CAAC;IACD,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC;IAC3D,OAAO,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC;AAC3C,CAAC"}
package/dist/utils/db.js CHANGED
@@ -17,6 +17,7 @@ export const dropTables = async (client) => {
17
17
  await db.sql `DROP TABLE IF EXISTS instance`.execute();
18
18
  await db.sql `DROP TABLE IF EXISTS bucket_data`.execute();
19
19
  await db.sql `DROP TABLE IF EXISTS current_data`.execute();
20
+ await db.sql `DROP TABLE IF EXISTS v3_current_data`.execute();
20
21
  await db.sql `DROP TABLE IF EXISTS source_tables`.execute();
21
22
  await db.sql `DROP TABLE IF EXISTS write_checkpoints`.execute();
22
23
  await db.sql `DROP TABLE IF EXISTS custom_write_checkpoints`.execute();
@@ -43,6 +44,14 @@ export const truncateTables = async (db) => {
43
44
  custom_write_checkpoints,
44
45
  connection_report_events RESTART IDENTITY CASCADE
45
46
  `
47
+ }, {
48
+ // TRUNCATE if v3_current_data exists
49
+ statement: `DO $$
50
+ BEGIN
51
+ IF to_regclass('v3_current_data') IS NOT NULL THEN
52
+ EXECUTE 'TRUNCATE TABLE v3_current_data RESTART IDENTITY CASCADE';
53
+ END IF;
54
+ END $$;`
46
55
  }, {
47
56
  statement: `ALTER SEQUENCE IF EXISTS op_id_sequence RESTART
48
57
  WITH
@@ -1 +1 @@
1
- {"version":3,"file":"db.js","sourceRoot":"","sources":["../../src/utils/db.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,YAAY,MAAM,iCAAiC,CAAC;AAEhE,MAAM,CAAC,MAAM,mBAAmB,GAAG,WAAW,CAAC;AAE/C,MAAM,CAAC,MAAM,oBAAoB,GAAG,sBAAsB,CAAC;AAE3D;;GAEG;AACH,MAAM,CAAC,MAAM,GAAG,GAAG,YAAY,CAAC,GAAG,CAAC;AAEpC;;GAEG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,KAAK,EAAE,MAAmC,EAAE,EAAE;IACtE,sDAAsD;IACtD,MAAM,MAAM,CAAC,cAAc,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE;QACvC,MAAM,EAAE,CAAC,GAAG,CAAA,kCAAkC,CAAC,OAAO,EAAE,CAAC;QACzD,MAAM,EAAE,CAAC,GAAG,CAAA,wCAAwC,CAAC,OAAO,EAAE,CAAC;QAC/D,MAAM,EAAE,CAAC,GAAG,CAAA,iCAAiC,CAAC,OAAO,EAAE,CAAC;QACxD,MAAM,EAAE,CAAC,GAAG,CAAA,+BAA+B,CAAC,OAAO,EAAE,CAAC;QACtD,MAAM,EAAE,CAAC,GAAG,CAAA,kCAAkC,CAAC,OAAO,EAAE,CAAC;QACzD,MAAM,EAAE,CAAC,GAAG,CAAA,mCAAmC,CAAC,OAAO,EAAE,CAAC;QAC1D,MAAM,EAAE,CAAC,GAAG,CAAA,oCAAoC,CAAC,OAAO,EAAE,CAAC;QAC3D,MAAM,EAAE,CAAC,GAAG,CAAA,wCAAwC,CAAC,OAAO,EAAE,CAAC;QAC/D,MAAM,EAAE,CAAC,GAAG,CAAA,+CAA+C,CAAC,OAAO,EAAE,CAAC;QACtE,MAAM,EAAE,CAAC,GAAG,CAAA,wCAAwC,CAAC,OAAO,EAAE,CAAC;QAC/D,MAAM,EAAE,CAAC,GAAG,CAAA,gDAAgD,CAAC,OAAO,EAAE,CAAC;QACvE,MAAM,EAAE,CAAC,GAAG,CAAA,iCAAiC,CAAC,OAAO,EAAE,CAAC;IAC1D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,KAAK,EAAE,EAA+B,EAAE,EAAE;IACtE,sDAAsD;IACtD,MAAM,EAAE,CAAC,KAAK,CACZ;QACE,SAAS,EAAE;;;;;;;;;KASZ;KACA,EACD;QACE,SAAS,EAAE;;UAEP;KACL,EACD;QACE,SAAS,EAAE;;UAEP;KACL,CACF,CAAC;AACJ,CAAC,CAAC"}
1
+ {"version":3,"file":"db.js","sourceRoot":"","sources":["../../src/utils/db.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,YAAY,MAAM,iCAAiC,CAAC;AAEhE,MAAM,CAAC,MAAM,mBAAmB,GAAG,WAAW,CAAC;AAE/C,MAAM,CAAC,MAAM,oBAAoB,GAAG,sBAAsB,CAAC;AAE3D;;GAEG;AACH,MAAM,CAAC,MAAM,GAAG,GAAG,YAAY,CAAC,GAAG,CAAC;AAEpC;;GAEG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,KAAK,EAAE,MAAmC,EAAE,EAAE;IACtE,sDAAsD;IACtD,MAAM,MAAM,CAAC,cAAc,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE;QACvC,MAAM,EAAE,CAAC,GAAG,CAAA,kCAAkC,CAAC,OAAO,EAAE,CAAC;QACzD,MAAM,EAAE,CAAC,GAAG,CAAA,wCAAwC,CAAC,OAAO,EAAE,CAAC;QAC/D,MAAM,EAAE,CAAC,GAAG,CAAA,iCAAiC,CAAC,OAAO,EAAE,CAAC;QACxD,MAAM,EAAE,CAAC,GAAG,CAAA,+BAA+B,CAAC,OAAO,EAAE,CAAC;QACtD,MAAM,EAAE,CAAC,GAAG,CAAA,kCAAkC,CAAC,OAAO,EAAE,CAAC;QACzD,MAAM,EAAE,CAAC,GAAG,CAAA,mCAAmC,CAAC,OAAO,EAAE,CAAC;QAC1D,MAAM,EAAE,CAAC,GAAG,CAAA,sCAAsC,CAAC,OAAO,EAAE,CAAC;QAC7D,MAAM,EAAE,CAAC,GAAG,CAAA,oCAAoC,CAAC,OAAO,EAAE,CAAC;QAC3D,MAAM,EAAE,CAAC,GAAG,CAAA,wCAAwC,CAAC,OAAO,EAAE,CAAC;QAC/D,MAAM,EAAE,CAAC,GAAG,CAAA,+CAA+C,CAAC,OAAO,EAAE,CAAC;QACtE,MAAM,EAAE,CAAC,GAAG,CAAA,wCAAwC,CAAC,OAAO,EAAE,CAAC;QAC/D,MAAM,EAAE,CAAC,GAAG,CAAA,gDAAgD,CAAC,OAAO,EAAE,CAAC;QACvE,MAAM,EAAE,CAAC,GAAG,CAAA,iCAAiC,CAAC,OAAO,EAAE,CAAC;IAC1D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,KAAK,EAAE,EAA+B,EAAE,EAAE;IACtE,sDAAsD;IACtD,MAAM,EAAE,CAAC,KAAK,CACZ;QACE,SAAS,EAAE;;;;;;;;;KASZ;KACA,EACD;QACE,qCAAqC;QACrC,SAAS,EAAE;;;;;gBAKD;KACX,EACD;QACE,SAAS,EAAE;;UAEP;KACL,EACD;QACE,SAAS,EAAE;;UAEP;KACL,CACF,CAAC;AACJ,CAAC,CAAC"}
@@ -55,6 +55,7 @@ import { PostgresMigrationAgent } from '../migrations/PostgresMigrationAgent.js'
55
55
  import { normalizePostgresStorageConfig } from '../types/types.js';
56
56
  import { PostgresReportStorage } from '../storage/PostgresReportStorage.js';
57
57
  import { PostgresBucketStorageFactory } from '../storage/PostgresBucketStorageFactory.js';
58
+ import { logger as defaultLogger, createLogger, transports } from '@powersync/lib-services-framework';
58
59
  import { truncateTables } from './db.js';
59
60
  export function postgresTestSetup(factoryOptions) {
60
61
  const BASE_CONFIG = {
@@ -72,12 +73,19 @@ export function postgresTestSetup(factoryOptions) {
72
73
  : new PostgresMigrationAgent(BASE_CONFIG), true);
73
74
  migrationManager.registerMigrationAgent(migrationAgent);
74
75
  const mockServiceContext = { configuration: { storage: BASE_CONFIG } };
76
+ // Migration logs can get really verbose in tests, so only log warnings and up.
77
+ const logger = createLogger({
78
+ level: 'warn',
79
+ format: defaultLogger.format,
80
+ transports: [new transports.Console()]
81
+ });
75
82
  if (options.down) {
76
83
  await migrationManager.migrate({
77
84
  direction: framework.migrations.Direction.Down,
78
85
  migrationContext: {
79
86
  service_context: mockServiceContext
80
- }
87
+ },
88
+ logger
81
89
  });
82
90
  }
83
91
  if (options.up) {
@@ -85,7 +93,8 @@ export function postgresTestSetup(factoryOptions) {
85
93
  direction: framework.migrations.Direction.Up,
86
94
  migrationContext: {
87
95
  service_context: mockServiceContext
88
- }
96
+ },
97
+ logger
89
98
  });
90
99
  }
91
100
  }
@@ -157,10 +166,8 @@ export function postgresTestSetup(factoryOptions) {
157
166
  throw ex;
158
167
  }
159
168
  },
160
- migrate
169
+ migrate,
170
+ tableIdStrings: true
161
171
  };
162
172
  }
163
- export function postgresTestStorageFactoryGenerator(factoryOptions) {
164
- return postgresTestSetup(factoryOptions).factory;
165
- }
166
173
  //# sourceMappingURL=test-utils.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"test-utils.js","sourceRoot":"","sources":["../../src/utils/test-utils.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"test-utils.js","sourceRoot":"","sources":["../../src/utils/test-utils.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@powersync/service-module-postgres-storage",
3
3
  "repository": "https://github.com/powersync-ja/powersync-service",
4
4
  "types": "dist/@types/index.d.ts",
5
- "version": "0.12.0",
5
+ "version": "0.13.0",
6
6
  "main": "dist/index.js",
7
7
  "license": "FSL-1.1-ALv2",
8
8
  "type": "module",
@@ -29,22 +29,22 @@
29
29
  "p-defer": "^4.0.1",
30
30
  "ts-codec": "^1.3.0",
31
31
  "uuid": "^11.1.0",
32
- "@powersync/lib-service-postgres": "0.4.22",
33
- "@powersync/lib-services-framework": "0.8.3",
34
- "@powersync/service-core": "1.20.0",
32
+ "@powersync/lib-service-postgres": "0.4.23",
33
+ "@powersync/lib-services-framework": "0.9.0",
34
+ "@powersync/service-core": "1.20.1",
35
35
  "@powersync/service-types": "0.15.0",
36
- "@powersync/service-jpgwire": "0.21.13",
36
+ "@powersync/service-jpgwire": "0.21.14",
37
37
  "@powersync/service-jsonbig": "0.17.12",
38
- "@powersync/service-sync-rules": "0.32.0"
38
+ "@powersync/service-sync-rules": "0.33.0"
39
39
  },
40
40
  "devDependencies": {
41
41
  "typescript": "^5.7.3",
42
- "@powersync/service-core-tests": "0.14.0"
42
+ "@powersync/service-core-tests": "0.15.0"
43
43
  },
44
44
  "scripts": {
45
45
  "build": "tsc -b",
46
46
  "build:tests": "tsc -b test/tsconfig.json",
47
- "clean": "rm -rf ./lib && tsc -b --clean",
47
+ "clean": "rm -rf ./dist && tsc -b --clean",
48
48
  "test": "vitest"
49
49
  }
50
50
  }
@@ -0,0 +1,10 @@
1
+ import { migrations } from '@powersync/service-core';
2
+
3
+ export const up: migrations.PowerSyncMigrationFunction = async (_context) => {
4
+ // No-op.
5
+ // Pending-delete support is now storage-version specific and initialized when v3 sync rules are deployed.
6
+ };
7
+
8
+ export const down: migrations.PowerSyncMigrationFunction = async (_context) => {
9
+ // No-op.
10
+ };
@@ -1,4 +1,4 @@
1
- import { GetIntanceOptions, storage, SyncRulesBucketStorage } from '@powersync/service-core';
1
+ import { framework, GetIntanceOptions, storage, SyncRulesBucketStorage } from '@powersync/service-core';
2
2
  import * as pg_wire from '@powersync/service-jpgwire';
3
3
  import crypto from 'crypto';
4
4
  import * as uuid from 'uuid';
@@ -6,11 +6,11 @@ import * as uuid from 'uuid';
6
6
  import * as lib_postgres from '@powersync/lib-service-postgres';
7
7
  import { models, NormalizedPostgresStorageConfig } from '../types/types.js';
8
8
 
9
+ import { getStorageApplicationName } from '../utils/application-name.js';
9
10
  import { NOTIFICATION_CHANNEL, STORAGE_SCHEMA_NAME } from '../utils/db.js';
10
11
  import { notifySyncRulesUpdate } from './batch/PostgresBucketBatch.js';
11
12
  import { PostgresSyncRulesStorage } from './PostgresSyncRulesStorage.js';
12
13
  import { PostgresPersistedSyncRulesContent } from './sync-rules/PostgresPersistedSyncRulesContent.js';
13
- import { getStorageApplicationName } from '../utils/application-name.js';
14
14
 
15
15
  export type PostgresBucketStorageOptions = {
16
16
  config: NormalizedPostgresStorageConfig;
@@ -82,15 +82,27 @@ export class PostgresBucketStorageFactory extends storage.BucketStorageFactory {
82
82
 
83
83
  const sizes = await this.db.sql`
84
84
  SELECT
85
- pg_total_relation_size('current_data') AS current_size_bytes,
85
+ COALESCE(
86
+ pg_total_relation_size(to_regclass('current_data')),
87
+ 0
88
+ ) AS v1_current_size_bytes,
89
+ COALESCE(
90
+ pg_total_relation_size(to_regclass('v3_current_data')),
91
+ 0
92
+ ) AS v3_current_size_bytes,
86
93
  pg_total_relation_size('bucket_parameters') AS parameter_size_bytes,
87
94
  pg_total_relation_size('bucket_data') AS operations_size_bytes;
88
- `.first<{ current_size_bytes: bigint; parameter_size_bytes: bigint; operations_size_bytes: bigint }>();
95
+ `.first<{
96
+ v1_current_size_bytes: bigint;
97
+ v3_current_size_bytes: bigint;
98
+ parameter_size_bytes: bigint;
99
+ operations_size_bytes: bigint;
100
+ }>();
89
101
 
90
102
  return {
91
103
  operations_size_bytes: Number(sizes!.operations_size_bytes),
92
104
  parameters_size_bytes: Number(sizes!.parameter_size_bytes),
93
- replication_size_bytes: Number(sizes!.current_size_bytes)
105
+ replication_size_bytes: Number(sizes!.v1_current_size_bytes) + Number(sizes!.v3_current_size_bytes)
94
106
  };
95
107
  }
96
108
 
@@ -142,6 +154,14 @@ export class PostgresBucketStorageFactory extends storage.BucketStorageFactory {
142
154
 
143
155
  async updateSyncRules(options: storage.UpdateSyncRulesOptions): Promise<PostgresPersistedSyncRulesContent> {
144
156
  const storageVersion = options.storageVersion ?? storage.CURRENT_STORAGE_VERSION;
157
+ const storageConfig = storage.STORAGE_VERSION_CONFIG[storageVersion];
158
+ if (storageConfig == null) {
159
+ throw new framework.ServiceError(
160
+ framework.ErrorCode.PSYNC_S1005,
161
+ `Unsupported storage version ${storageVersion}`
162
+ );
163
+ }
164
+ await this.initializeStorageVersion(storageConfig);
145
165
  return this.db.transaction(async (db) => {
146
166
  await db.sql`
147
167
  UPDATE sync_rules
@@ -202,6 +222,34 @@ export class PostgresBucketStorageFactory extends storage.BucketStorageFactory {
202
222
  });
203
223
  }
204
224
 
225
+ /**
226
+ * Lazy-initializes storage-version-specific structures, if needed.
227
+ */
228
+ private async initializeStorageVersion(storageConfig: storage.StorageVersionConfig) {
229
+ if (!storageConfig.softDeleteCurrentData) {
230
+ return;
231
+ }
232
+
233
+ await this.db.sql`
234
+ CREATE TABLE IF NOT EXISTS v3_current_data (
235
+ group_id integer NOT NULL,
236
+ source_table TEXT NOT NULL,
237
+ source_key bytea NOT NULL,
238
+ CONSTRAINT unique_v3_current_data_id PRIMARY KEY (group_id, source_table, source_key),
239
+ buckets jsonb NOT NULL,
240
+ data bytea NOT NULL,
241
+ lookups bytea[] NOT NULL,
242
+ pending_delete BIGINT NULL
243
+ )
244
+ `.execute();
245
+
246
+ await this.db.sql`
247
+ CREATE INDEX IF NOT EXISTS v3_current_data_pending_deletes ON v3_current_data (group_id, pending_delete)
248
+ WHERE
249
+ pending_delete IS NOT NULL
250
+ `.execute();
251
+ }
252
+
205
253
  async restartReplication(sync_rules_group_id: number): Promise<void> {
206
254
  const next = await this.getNextSyncRulesContent();
207
255
  const active = await this.getActiveSyncRulesContent();
@@ -51,20 +51,20 @@ export class PostgresCompactor {
51
51
  private moveBatchLimit: number;
52
52
  private moveBatchQueryLimit: number;
53
53
  private clearBatchLimit: number;
54
- private maxOpId: InternalOpId | undefined;
54
+ private maxOpId: InternalOpId;
55
55
  private buckets: string[] | undefined;
56
56
 
57
57
  constructor(
58
58
  private db: lib_postgres.DatabaseClient,
59
59
  private group_id: number,
60
- options?: PostgresCompactOptions
60
+ options: PostgresCompactOptions
61
61
  ) {
62
- this.idLimitBytes = (options?.memoryLimitMB ?? DEFAULT_MEMORY_LIMIT_MB) * 1024 * 1024;
63
- this.moveBatchLimit = options?.moveBatchLimit ?? DEFAULT_MOVE_BATCH_LIMIT;
64
- this.moveBatchQueryLimit = options?.moveBatchQueryLimit ?? DEFAULT_MOVE_BATCH_QUERY_LIMIT;
65
- this.clearBatchLimit = options?.clearBatchLimit ?? DEFAULT_CLEAR_BATCH_LIMIT;
66
- this.maxOpId = options?.maxOpId;
67
- this.buckets = options?.compactBuckets;
62
+ this.idLimitBytes = (options.memoryLimitMB ?? DEFAULT_MEMORY_LIMIT_MB) * 1024 * 1024;
63
+ this.moveBatchLimit = options.moveBatchLimit ?? DEFAULT_MOVE_BATCH_LIMIT;
64
+ this.moveBatchQueryLimit = options.moveBatchQueryLimit ?? DEFAULT_MOVE_BATCH_QUERY_LIMIT;
65
+ this.clearBatchLimit = options.clearBatchLimit ?? DEFAULT_CLEAR_BATCH_LIMIT;
66
+ this.maxOpId = options.maxOpId ?? 0n;
67
+ this.buckets = options.compactBuckets;
68
68
  }
69
69
 
70
70
  /**
@@ -240,6 +240,15 @@ export class PostgresCompactor {
240
240
  }
241
241
  }
242
242
 
243
+ /**
244
+ * Expose the internal clearBucket() method to tests.
245
+ *
246
+ * @deprecated Only for tests
247
+ */
248
+ clearBucketForTests(bucket: string, op: InternalOpId) {
249
+ return this.clearBucket(bucket, op);
250
+ }
251
+
243
252
  /**
244
253
  * Perform a CLEAR compact for a bucket.
245
254
  *