@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.
- package/CHANGELOG.md +22 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/@types/migrations/scripts/1771424826685-current-data-pending-deletes.d.ts +3 -0
- package/dist/@types/storage/PostgresBucketStorageFactory.d.ts +4 -0
- package/dist/@types/storage/PostgresCompactor.d.ts +8 -2
- 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/table-id.d.ts +2 -0
- package/dist/@types/types/models/CurrentData.d.ts +18 -3
- package/dist/@types/utils/bson.d.ts +1 -1
- package/dist/@types/utils/test-utils.d.ts +1 -1
- 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/storage/PostgresBucketStorageFactory.js +41 -4
- package/dist/storage/PostgresBucketStorageFactory.js.map +1 -1
- package/dist/storage/PostgresCompactor.js +14 -6
- 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/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/utils/bson.js.map +1 -1
- package/dist/utils/db.js +9 -0
- package/dist/utils/db.js.map +1 -1
- package/dist/utils/test-utils.js +13 -6
- package/dist/utils/test-utils.js.map +1 -1
- package/package.json +8 -8
- package/src/migrations/scripts/1771424826685-current-data-pending-deletes.ts +10 -0
- package/src/storage/PostgresBucketStorageFactory.ts +53 -5
- package/src/storage/PostgresCompactor.ts +17 -8
- 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/table-id.ts +9 -0
- package/src/types/models/CurrentData.ts +17 -4
- package/src/utils/bson.ts +1 -1
- package/src/utils/db.ts +10 -0
- package/src/utils/test-utils.ts +14 -7
- package/test/src/__snapshots__/storage.test.ts.snap +151 -0
- package/test/src/__snapshots__/storage_compacting.test.ts.snap +17 -0
- package/test/src/__snapshots__/storage_sync.test.ts.snap +1095 -0
- package/test/src/migrations.test.ts +1 -1
- package/test/src/storage.test.ts +136 -130
- package/test/src/storage_compacting.test.ts +65 -3
- package/test/src/storage_sync.test.ts +11 -9
- 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
|
|
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;
|
|
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"}
|
package/dist/utils/bson.js.map
CHANGED
|
@@ -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,
|
|
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
|
package/dist/utils/db.js.map
CHANGED
|
@@ -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"}
|
package/dist/utils/test-utils.js
CHANGED
|
@@ -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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,OAAO,EAAE,SAAS,EAAiE,MAAM,yBAAyB,CAAC;AACnH,OAAO,EAAE,sBAAsB,EAAE,MAAM,yCAAyC,CAAC;AACjF,OAAO,EAAE,8BAA8B,EAAgC,MAAM,mBAAmB,CAAC;AACjG,OAAO,EAAE,qBAAqB,EAAE,MAAM,qCAAqC,CAAC;AAC5E,OAAO,EAAE,4BAA4B,EAAE,MAAM,4CAA4C,CAAC;AAC1F,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAWzC,MAAM,UAAU,iBAAiB,CAAC,cAA0C;IAC1E,MAAM,WAAW,GAAG;QAClB,IAAI,EAAE,YAAqB;QAC3B,GAAG,EAAE,cAAc,CAAC,GAAG;QACvB,OAAO,EAAE,SAAkB;KAC5B,CAAC;IAEF,MAAM,uBAAuB,GAAG,8BAA8B,CAAC,WAAW,CAAC,CAAC;IAE5E,MAAM,aAAa,GAAG,KAAK,EAAE,OAAuC,EAAE,EAAE;;;YACtE,MAAY,gBAAgB,kCAA8B,IAAI,SAAS,CAAC,gBAAgB,EAAE,OAAA,CAAC;YAC3F,MAAY,cAAc,kCAAG,cAAc,CAAC,cAAc;gBACxD,CAAC,CAAC,cAAc,CAAC,cAAc,CAAC,WAAW,CAAC;gBAC5C,CAAC,CAAC,IAAI,sBAAsB,CAAC,WAAW,CAAC,OAAA,CAAC;YAC5C,gBAAgB,CAAC,sBAAsB,CAAC,cAAc,CAAC,CAAC;YAExD,MAAM,kBAAkB,GAAG,EAAE,aAAa,EAAE,EAAE,OAAO,EAAE,WAAW,EAAE,EAA+B,CAAC;YAEpG,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;gBACjB,MAAM,gBAAgB,CAAC,OAAO,CAAC;oBAC7B,SAAS,EAAE,SAAS,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI;oBAC9C,gBAAgB,EAAE;wBAChB,eAAe,EAAE,kBAAkB;qBACpC;
|
|
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.
|
|
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.
|
|
33
|
-
"@powersync/lib-services-framework": "0.
|
|
34
|
-
"@powersync/service-core": "1.20.
|
|
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.
|
|
36
|
+
"@powersync/service-jpgwire": "0.21.14",
|
|
37
37
|
"@powersync/service-jsonbig": "0.17.12",
|
|
38
|
-
"@powersync/service-sync-rules": "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.
|
|
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 ./
|
|
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
|
-
|
|
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<{
|
|
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!.
|
|
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
|
|
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
|
|
60
|
+
options: PostgresCompactOptions
|
|
61
61
|
) {
|
|
62
|
-
this.idLimitBytes = (options
|
|
63
|
-
this.moveBatchLimit = options
|
|
64
|
-
this.moveBatchQueryLimit = options
|
|
65
|
-
this.clearBatchLimit = options
|
|
66
|
-
this.maxOpId = options
|
|
67
|
-
this.buckets = options
|
|
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
|
*
|