@powersync/service-module-postgres-storage 0.13.4 → 0.15.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 +70 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/@types/storage/PostgresSyncRulesStorage.d.ts +4 -4
- package/dist/@types/storage/batch/PostgresBucketBatch.d.ts +12 -3
- package/dist/migrations/scripts/1771424826685-current-data-pending-deletes.js +1 -1
- package/dist/storage/PostgresBucketStorageFactory.js +5 -5
- package/dist/storage/PostgresBucketStorageFactory.js.map +1 -1
- package/dist/storage/PostgresSyncRulesStorage.js +78 -197
- package/dist/storage/PostgresSyncRulesStorage.js.map +1 -1
- package/dist/storage/batch/PostgresBucketBatch.js +265 -15
- package/dist/storage/batch/PostgresBucketBatch.js.map +1 -1
- package/dist/storage/checkpoints/PostgresWriteCheckpointAPI.js +1 -1
- package/dist/storage/checkpoints/PostgresWriteCheckpointAPI.js.map +1 -1
- package/dist/storage/sync-rules/PostgresPersistedSyncRulesContent.js +3 -3
- package/dist/storage/sync-rules/PostgresPersistedSyncRulesContent.js.map +1 -1
- package/package.json +11 -11
- package/src/migrations/scripts/1771424826685-current-data-pending-deletes.ts +1 -1
- package/src/storage/PostgresBucketStorageFactory.ts +6 -5
- package/src/storage/PostgresSyncRulesStorage.ts +90 -209
- package/src/storage/batch/PostgresBucketBatch.ts +308 -26
- package/src/storage/checkpoints/PostgresWriteCheckpointAPI.ts +3 -1
- package/src/storage/sync-rules/PostgresPersistedSyncRulesContent.ts +3 -6
- package/test/tsconfig.json +0 -1
- package/dist/@types/storage/current-data-table.d.ts +0 -9
- package/dist/storage/current-data-table.js +0 -22
- package/dist/storage/current-data-table.js.map +0 -1
- package/src/storage/current-data-table.ts +0 -26
|
@@ -20,6 +20,7 @@ export declare class PostgresSyncRulesStorage extends framework.BaseObserver<sto
|
|
|
20
20
|
readonly slot_name: string;
|
|
21
21
|
readonly factory: PostgresBucketStorageFactory;
|
|
22
22
|
readonly storageConfig: StorageVersionConfig;
|
|
23
|
+
readonly logger: framework.Logger;
|
|
23
24
|
private sharedIterator;
|
|
24
25
|
protected db: lib_postgres.DatabaseClient;
|
|
25
26
|
protected writeCheckpointAPI: PostgresWriteCheckpointAPI;
|
|
@@ -34,21 +35,20 @@ export declare class PostgresSyncRulesStorage extends framework.BaseObserver<sto
|
|
|
34
35
|
*/
|
|
35
36
|
private get checksumCache();
|
|
36
37
|
get writeCheckpointMode(): storage.WriteCheckpointMode;
|
|
37
|
-
getParsedSyncRules(options: storage.ParseSyncRulesOptions): sync_rules.
|
|
38
|
+
getParsedSyncRules(options: storage.ParseSyncRulesOptions): sync_rules.HydratedSyncConfig;
|
|
38
39
|
reportError(e: any): Promise<void>;
|
|
39
40
|
compact(options?: storage.CompactOptions): Promise<void>;
|
|
40
|
-
populatePersistentChecksumCache(
|
|
41
|
+
populatePersistentChecksumCache(_options: PopulateChecksumCacheOptions): Promise<PopulateChecksumCacheResults>;
|
|
41
42
|
lastWriteCheckpoint(filters: storage.SyncStorageLastWriteCheckpointFilters): Promise<bigint | null>;
|
|
42
43
|
setWriteCheckpointMode(mode: storage.WriteCheckpointMode): void;
|
|
43
44
|
createManagedWriteCheckpoint(checkpoint: storage.ManagedWriteCheckpointOptions): Promise<bigint>;
|
|
44
45
|
getCheckpoint(): Promise<storage.ReplicationCheckpoint>;
|
|
45
|
-
resolveTable(options: storage.ResolveTableOptions): Promise<storage.ResolveTableResult>;
|
|
46
46
|
createWriter(options: storage.CreateWriterOptions): Promise<storage.BucketStorageBatch>;
|
|
47
47
|
/**
|
|
48
48
|
* @deprecated Use `createWriter()` with `await using` instead.
|
|
49
49
|
*/
|
|
50
50
|
startBatch(options: storage.CreateWriterOptions, callback: (batch: storage.BucketStorageBatch) => Promise<void>): Promise<storage.FlushedResult | null>;
|
|
51
|
-
getParameterSets(checkpoint: ReplicationCheckpoint, lookups: sync_rules.ScopedParameterLookup[]): Promise<sync_rules.
|
|
51
|
+
getParameterSets(checkpoint: ReplicationCheckpoint, lookups: sync_rules.ScopedParameterLookup[], limit: number): Promise<sync_rules.ParameterLookupRows[]>;
|
|
52
52
|
getBucketDataBatch(checkpoint: InternalOpId, dataBuckets: storage.BucketDataRequest[], options?: storage.BucketDataBatchOptions): AsyncIterable<storage.SyncBucketDataChunk>;
|
|
53
53
|
getChecksums(checkpoint: utils.InternalOpId, buckets: storage.BucketChecksumRequest[]): Promise<utils.ChecksumMap>;
|
|
54
54
|
clearChecksumCache(): void;
|
|
@@ -10,7 +10,7 @@ import { PostgresPersistedBatch } from './PostgresPersistedBatch.js';
|
|
|
10
10
|
export interface PostgresBucketBatchOptions {
|
|
11
11
|
logger: Logger;
|
|
12
12
|
db: lib_postgres.DatabaseClient;
|
|
13
|
-
sync_rules: sync_rules.
|
|
13
|
+
sync_rules: sync_rules.HydratedSyncConfig;
|
|
14
14
|
group_id: number;
|
|
15
15
|
slot_name: string;
|
|
16
16
|
last_checkpoint_lsn: string | null;
|
|
@@ -23,10 +23,11 @@ export interface PostgresBucketBatchOptions {
|
|
|
23
23
|
skip_existing_rows: boolean;
|
|
24
24
|
batch_limits: RequiredOperationBatchLimits;
|
|
25
25
|
markRecordUnavailable: BucketStorageMarkRecordUnavailable | undefined;
|
|
26
|
+
hooks: storage.StorageHooks | undefined;
|
|
26
27
|
storageConfig: storage.StorageVersionConfig;
|
|
27
28
|
}
|
|
28
29
|
/**
|
|
29
|
-
* Intermediate type which helps for only watching the active
|
|
30
|
+
* Intermediate type which helps for only watching the active replication stream
|
|
30
31
|
* via the Postgres NOTIFY protocol.
|
|
31
32
|
*/
|
|
32
33
|
declare const StatefulCheckpoint: t.Intersection<t.Codec<{
|
|
@@ -46,15 +47,17 @@ export declare class PostgresBucketBatch extends BaseObserver<storage.BucketBatc
|
|
|
46
47
|
private logger;
|
|
47
48
|
last_flushed_op: InternalOpId | null;
|
|
48
49
|
resumeFromLsn: string | null;
|
|
50
|
+
readonly skipExistingRows: boolean;
|
|
49
51
|
protected db: lib_postgres.DatabaseClient;
|
|
50
52
|
protected group_id: number;
|
|
51
53
|
protected last_checkpoint_lsn: string | null;
|
|
52
54
|
protected persisted_op: InternalOpId | null;
|
|
53
55
|
protected write_checkpoint_batch: storage.CustomWriteCheckpointOptions[];
|
|
54
|
-
protected readonly sync_rules: sync_rules.
|
|
56
|
+
protected readonly sync_rules: sync_rules.HydratedSyncConfig;
|
|
55
57
|
protected batch: OperationBatch | null;
|
|
56
58
|
private lastWaitingLogThrottled;
|
|
57
59
|
private markRecordUnavailable;
|
|
60
|
+
private hooks;
|
|
58
61
|
private needsActivation;
|
|
59
62
|
private clearedError;
|
|
60
63
|
private readonly storageConfig;
|
|
@@ -63,6 +66,9 @@ export declare class PostgresBucketBatch extends BaseObserver<storage.BucketBatc
|
|
|
63
66
|
get lastCheckpointLsn(): string | null;
|
|
64
67
|
[Symbol.asyncDispose](): Promise<void>;
|
|
65
68
|
dispose(): Promise<void>;
|
|
69
|
+
resolveTables(options: storage.ResolveTablesOptions): Promise<storage.ResolveTablesResult>;
|
|
70
|
+
getSourceTableStatus(table: storage.SourceTable): Promise<storage.SourceTable | null>;
|
|
71
|
+
private sourceTableFromRow;
|
|
66
72
|
save(record: storage.SaveOptions): Promise<storage.FlushedResult | null>;
|
|
67
73
|
truncate(sourceTables: storage.SourceTable[]): Promise<storage.FlushedResult | null>;
|
|
68
74
|
protected truncateSingle(sourceTable: storage.SourceTable): Promise<bigint | null>;
|
|
@@ -73,6 +79,9 @@ export declare class PostgresBucketBatch extends BaseObserver<storage.BucketBatc
|
|
|
73
79
|
keepalive(lsn: string): Promise<CheckpointResult>;
|
|
74
80
|
setResumeLsn(lsn: string): Promise<void>;
|
|
75
81
|
markAllSnapshotDone(no_checkpoint_before_lsn: string): Promise<void>;
|
|
82
|
+
markSnapshotDone(no_checkpoint_before_lsn: string, options?: {
|
|
83
|
+
throwOnConflict?: boolean;
|
|
84
|
+
}): Promise<void>;
|
|
76
85
|
markTableSnapshotRequired(table: storage.SourceTable): Promise<void>;
|
|
77
86
|
markTableSnapshotDone(tables: storage.SourceTable[], no_checkpoint_before_lsn?: string): Promise<storage.SourceTable[]>;
|
|
78
87
|
updateTableProgress(table: storage.SourceTable, progress: Partial<storage.TableSnapshotStatus>): Promise<storage.SourceTable>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export const up = async (_context) => {
|
|
2
2
|
// No-op.
|
|
3
|
-
// Pending-delete support is now storage-version specific and initialized when v3 sync
|
|
3
|
+
// Pending-delete support is now storage-version specific and initialized when v3 sync config is deployed.
|
|
4
4
|
};
|
|
5
5
|
export const down = async (_context) => {
|
|
6
6
|
// No-op.
|
|
@@ -124,7 +124,7 @@ export class PostgresBucketStorageFactory extends storage.BucketStorageFactory {
|
|
|
124
124
|
};
|
|
125
125
|
}
|
|
126
126
|
async updateSyncRules(options) {
|
|
127
|
-
const storageVersion = options.storageVersion ?? storage.CURRENT_STORAGE_VERSION;
|
|
127
|
+
const storageVersion = options.storageVersion ?? options.config.parsed.config.storageVersion ?? storage.CURRENT_STORAGE_VERSION;
|
|
128
128
|
const storageConfig = storage.STORAGE_VERSION_CONFIG[storageVersion];
|
|
129
129
|
if (storageConfig == null) {
|
|
130
130
|
throw new framework.ServiceError(framework.ErrorCode.PSYNC_S1005, `Unsupported storage version ${storageVersion}`);
|
|
@@ -214,10 +214,10 @@ export class PostgresBucketStorageFactory extends storage.BucketStorageFactory {
|
|
|
214
214
|
async restartReplication(sync_rules_group_id) {
|
|
215
215
|
const next = await this.getNextSyncRulesContent();
|
|
216
216
|
const active = await this.getActiveSyncRulesContent();
|
|
217
|
-
// In both the below cases, we create a new
|
|
217
|
+
// In both the below cases, we create a new replication stream.
|
|
218
218
|
// The current one will continue serving sync requests until the next one has finished processing.
|
|
219
219
|
if (next != null && next.id == sync_rules_group_id) {
|
|
220
|
-
// We need to redo the "next"
|
|
220
|
+
// We need to redo the "next" replication stream
|
|
221
221
|
await this.updateSyncRules(next.asUpdateOptions());
|
|
222
222
|
// Pro-actively stop replicating
|
|
223
223
|
await this.db.sql `
|
|
@@ -230,7 +230,7 @@ export class PostgresBucketStorageFactory extends storage.BucketStorageFactory {
|
|
|
230
230
|
`.execute();
|
|
231
231
|
}
|
|
232
232
|
else if (next == null && active?.id == sync_rules_group_id) {
|
|
233
|
-
// Slot removed for "active"
|
|
233
|
+
// Slot removed for "active" replication stream, while there is no "next" one.
|
|
234
234
|
await this.updateSyncRules(active.asUpdateOptions());
|
|
235
235
|
// Pro-actively stop replicating, but still serve clients with existing data
|
|
236
236
|
await this.db.sql `
|
|
@@ -243,7 +243,7 @@ export class PostgresBucketStorageFactory extends storage.BucketStorageFactory {
|
|
|
243
243
|
`.execute();
|
|
244
244
|
}
|
|
245
245
|
else if (next != null && active?.id == sync_rules_group_id) {
|
|
246
|
-
// Already have "next"
|
|
246
|
+
// Already have "next" replication stream - don't update any.
|
|
247
247
|
// Pro-actively stop replicating, but still serve clients with existing data
|
|
248
248
|
await this.db.sql `
|
|
249
249
|
UPDATE sync_rules
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PostgresBucketStorageFactory.js","sourceRoot":"","sources":["../../src/storage/PostgresBucketStorageFactory.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"PostgresBucketStorageFactory.js","sourceRoot":"","sources":["../../src/storage/PostgresBucketStorageFactory.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { BroadcastIterable, CHECKPOINT_INVALIDATE_ALL, internalToExternalOpId, LastValueSink, maxLsn, storage } from '@powersync/service-core';
|
|
1
|
+
import { BroadcastIterable, CHECKPOINT_INVALIDATE_ALL, internalToExternalOpId, LastValueSink, maxLsn, ParameterSetLimitExceededError, storage } from '@powersync/service-core';
|
|
2
2
|
import { JSONBig } from '@powersync/service-jsonbig';
|
|
3
3
|
import * as timers from 'timers/promises';
|
|
4
|
-
import
|
|
5
|
-
import { BIGINT_MAX } from '../types/codecs.js';
|
|
4
|
+
import { bigint, BIGINT_MAX } from '../types/codecs.js';
|
|
6
5
|
import { models } from '../types/types.js';
|
|
7
6
|
import { replicaIdToSubkey } from '../utils/bson.js';
|
|
8
7
|
import { mapOpEntry } from '../utils/bucket-data.js';
|
|
9
8
|
import * as framework from '@powersync/lib-services-framework';
|
|
10
9
|
import { wrapWithAbort } from 'ix/asynciterable/operators/withabort.js';
|
|
10
|
+
import * as t from 'ts-codec';
|
|
11
11
|
import { pick } from '../utils/ts-codec.js';
|
|
12
12
|
import { PostgresBucketBatch } from './batch/PostgresBucketBatch.js';
|
|
13
13
|
import { PostgresWriteCheckpointAPI } from './checkpoints/PostgresWriteCheckpointAPI.js';
|
|
@@ -21,6 +21,7 @@ export class PostgresSyncRulesStorage extends framework.BaseObserver {
|
|
|
21
21
|
slot_name;
|
|
22
22
|
factory;
|
|
23
23
|
storageConfig;
|
|
24
|
+
logger;
|
|
24
25
|
sharedIterator = new BroadcastIterable((signal) => this.watchActiveCheckpoint(signal));
|
|
25
26
|
db;
|
|
26
27
|
writeCheckpointAPI;
|
|
@@ -38,6 +39,7 @@ export class PostgresSyncRulesStorage extends framework.BaseObserver {
|
|
|
38
39
|
this.factory = options.factory;
|
|
39
40
|
this.storageConfig = options.sync_rules.getStorageConfig();
|
|
40
41
|
this.currentDataStore = new PostgresCurrentDataStore(this.storageConfig);
|
|
42
|
+
this.logger = options.sync_rules.logger;
|
|
41
43
|
this.writeCheckpointAPI = new PostgresWriteCheckpointAPI({
|
|
42
44
|
db: this.db,
|
|
43
45
|
mode: options.write_checkpoint_mode ?? storage.WriteCheckpointMode.MANAGED
|
|
@@ -63,11 +65,11 @@ export class PostgresSyncRulesStorage extends framework.BaseObserver {
|
|
|
63
65
|
getParsedSyncRules(options) {
|
|
64
66
|
const { parsed, options: cachedOptions } = this.parsedSyncRulesCache ?? {};
|
|
65
67
|
/**
|
|
66
|
-
* Check if the cached sync
|
|
67
|
-
* Parse sync
|
|
68
|
+
* Check if the cached sync config, if present, had the same options.
|
|
69
|
+
* Parse sync config if the options are different or if there is no cached value.
|
|
68
70
|
*/
|
|
69
71
|
if (!parsed || options.defaultSchema != cachedOptions?.defaultSchema) {
|
|
70
|
-
this.parsedSyncRulesCache = { parsed: this.sync_rules.parsed(options).
|
|
72
|
+
this.parsedSyncRulesCache = { parsed: this.sync_rules.parsed(options).hydratedSyncConfig(), options };
|
|
71
73
|
}
|
|
72
74
|
return this.parsedSyncRulesCache.parsed;
|
|
73
75
|
}
|
|
@@ -90,10 +92,11 @@ export class PostgresSyncRulesStorage extends framework.BaseObserver {
|
|
|
90
92
|
}
|
|
91
93
|
return new PostgresCompactor(this.db, this.group_id, {
|
|
92
94
|
...options,
|
|
93
|
-
maxOpId
|
|
95
|
+
maxOpId,
|
|
96
|
+
logger: this.logger
|
|
94
97
|
}).compact();
|
|
95
98
|
}
|
|
96
|
-
async populatePersistentChecksumCache(
|
|
99
|
+
async populatePersistentChecksumCache(_options) {
|
|
97
100
|
// no-op - checksum cache is not implemented for Postgres yet
|
|
98
101
|
return { buckets: 0 };
|
|
99
102
|
}
|
|
@@ -123,159 +126,6 @@ export class PostgresSyncRulesStorage extends framework.BaseObserver {
|
|
|
123
126
|
.first();
|
|
124
127
|
return new PostgresReplicationCheckpoint(this, checkpointRow?.last_checkpoint ?? 0n, checkpointRow?.last_checkpoint_lsn ?? null);
|
|
125
128
|
}
|
|
126
|
-
async resolveTable(options) {
|
|
127
|
-
const { group_id, connection_id, connection_tag, entity_descriptor } = options;
|
|
128
|
-
const { schema, name: table, objectId, replicaIdColumns } = entity_descriptor;
|
|
129
|
-
const normalizedReplicaIdColumns = replicaIdColumns.map((column) => ({
|
|
130
|
-
name: column.name,
|
|
131
|
-
type: column.type,
|
|
132
|
-
// The PGWire returns this as a BigInt. We want to store this as JSONB
|
|
133
|
-
type_oid: typeof column.typeId !== 'undefined' ? Number(column.typeId) : column.typeId
|
|
134
|
-
}));
|
|
135
|
-
return this.db.transaction(async (db) => {
|
|
136
|
-
let sourceTableRow;
|
|
137
|
-
if (objectId != null) {
|
|
138
|
-
sourceTableRow = await db.sql `
|
|
139
|
-
SELECT
|
|
140
|
-
*
|
|
141
|
-
FROM
|
|
142
|
-
source_tables
|
|
143
|
-
WHERE
|
|
144
|
-
group_id = ${{ type: 'int4', value: group_id }}
|
|
145
|
-
AND connection_id = ${{ type: 'int4', value: connection_id }}
|
|
146
|
-
AND relation_id = ${{ type: 'jsonb', value: { object_id: objectId } }}
|
|
147
|
-
AND schema_name = ${{ type: 'varchar', value: schema }}
|
|
148
|
-
AND table_name = ${{ type: 'varchar', value: table }}
|
|
149
|
-
AND replica_id_columns = ${{ type: 'jsonb', value: normalizedReplicaIdColumns }}
|
|
150
|
-
`
|
|
151
|
-
.decoded(models.SourceTable)
|
|
152
|
-
.first();
|
|
153
|
-
}
|
|
154
|
-
else {
|
|
155
|
-
sourceTableRow = await db.sql `
|
|
156
|
-
SELECT
|
|
157
|
-
*
|
|
158
|
-
FROM
|
|
159
|
-
source_tables
|
|
160
|
-
WHERE
|
|
161
|
-
group_id = ${{ type: 'int4', value: group_id }}
|
|
162
|
-
AND connection_id = ${{ type: 'int4', value: connection_id }}
|
|
163
|
-
AND schema_name = ${{ type: 'varchar', value: schema }}
|
|
164
|
-
AND table_name = ${{ type: 'varchar', value: table }}
|
|
165
|
-
AND replica_id_columns = ${{ type: 'jsonb', value: normalizedReplicaIdColumns }}
|
|
166
|
-
`
|
|
167
|
-
.decoded(models.SourceTable)
|
|
168
|
-
.first();
|
|
169
|
-
}
|
|
170
|
-
if (sourceTableRow == null) {
|
|
171
|
-
const row = await db.sql `
|
|
172
|
-
INSERT INTO
|
|
173
|
-
source_tables (
|
|
174
|
-
id,
|
|
175
|
-
group_id,
|
|
176
|
-
connection_id,
|
|
177
|
-
relation_id,
|
|
178
|
-
schema_name,
|
|
179
|
-
table_name,
|
|
180
|
-
replica_id_columns
|
|
181
|
-
)
|
|
182
|
-
VALUES
|
|
183
|
-
(
|
|
184
|
-
${{ type: 'varchar', value: uuid.v4() }},
|
|
185
|
-
${{ type: 'int4', value: group_id }},
|
|
186
|
-
${{ type: 'int4', value: connection_id }},
|
|
187
|
-
--- The objectId can be string | number | undefined, we store it as jsonb value
|
|
188
|
-
${{ type: 'jsonb', value: { object_id: objectId } }},
|
|
189
|
-
${{ type: 'varchar', value: schema }},
|
|
190
|
-
${{ type: 'varchar', value: table }},
|
|
191
|
-
${{ type: 'jsonb', value: normalizedReplicaIdColumns }}
|
|
192
|
-
)
|
|
193
|
-
RETURNING
|
|
194
|
-
*
|
|
195
|
-
`
|
|
196
|
-
.decoded(models.SourceTable)
|
|
197
|
-
.first();
|
|
198
|
-
sourceTableRow = row;
|
|
199
|
-
}
|
|
200
|
-
const sourceTable = new storage.SourceTable({
|
|
201
|
-
id: sourceTableRow.id,
|
|
202
|
-
connectionTag: connection_tag,
|
|
203
|
-
objectId: objectId,
|
|
204
|
-
schema: schema,
|
|
205
|
-
name: table,
|
|
206
|
-
replicaIdColumns: replicaIdColumns,
|
|
207
|
-
snapshotComplete: sourceTableRow.snapshot_done ?? true
|
|
208
|
-
});
|
|
209
|
-
if (!sourceTable.snapshotComplete) {
|
|
210
|
-
sourceTable.snapshotStatus = {
|
|
211
|
-
totalEstimatedCount: Number(sourceTableRow.snapshot_total_estimated_count ?? -1n),
|
|
212
|
-
replicatedCount: Number(sourceTableRow.snapshot_replicated_count ?? 0n),
|
|
213
|
-
lastKey: sourceTableRow.snapshot_last_key
|
|
214
|
-
};
|
|
215
|
-
}
|
|
216
|
-
sourceTable.syncEvent = options.sync_rules.tableTriggersEvent(sourceTable);
|
|
217
|
-
sourceTable.syncData = options.sync_rules.tableSyncsData(sourceTable);
|
|
218
|
-
sourceTable.syncParameters = options.sync_rules.tableSyncsParameters(sourceTable);
|
|
219
|
-
let truncatedTables = [];
|
|
220
|
-
if (objectId != null) {
|
|
221
|
-
// relation_id present - check for renamed tables
|
|
222
|
-
truncatedTables = await db.sql `
|
|
223
|
-
SELECT
|
|
224
|
-
*
|
|
225
|
-
FROM
|
|
226
|
-
source_tables
|
|
227
|
-
WHERE
|
|
228
|
-
group_id = ${{ type: 'int4', value: group_id }}
|
|
229
|
-
AND connection_id = ${{ type: 'int4', value: connection_id }}
|
|
230
|
-
AND id != ${{ type: 'varchar', value: sourceTableRow.id }}
|
|
231
|
-
AND (
|
|
232
|
-
relation_id = ${{ type: 'jsonb', value: { object_id: objectId } }}
|
|
233
|
-
OR (
|
|
234
|
-
schema_name = ${{ type: 'varchar', value: schema }}
|
|
235
|
-
AND table_name = ${{ type: 'varchar', value: table }}
|
|
236
|
-
)
|
|
237
|
-
)
|
|
238
|
-
`
|
|
239
|
-
.decoded(models.SourceTable)
|
|
240
|
-
.rows();
|
|
241
|
-
}
|
|
242
|
-
else {
|
|
243
|
-
// relation_id not present - only check for changed replica_id_columns
|
|
244
|
-
truncatedTables = await db.sql `
|
|
245
|
-
SELECT
|
|
246
|
-
*
|
|
247
|
-
FROM
|
|
248
|
-
source_tables
|
|
249
|
-
WHERE
|
|
250
|
-
group_id = ${{ type: 'int4', value: group_id }}
|
|
251
|
-
AND connection_id = ${{ type: 'int4', value: connection_id }}
|
|
252
|
-
AND id != ${{ type: 'varchar', value: sourceTableRow.id }}
|
|
253
|
-
AND (
|
|
254
|
-
schema_name = ${{ type: 'varchar', value: schema }}
|
|
255
|
-
AND table_name = ${{ type: 'varchar', value: table }}
|
|
256
|
-
)
|
|
257
|
-
`
|
|
258
|
-
.decoded(models.SourceTable)
|
|
259
|
-
.rows();
|
|
260
|
-
}
|
|
261
|
-
return {
|
|
262
|
-
table: sourceTable,
|
|
263
|
-
dropTables: truncatedTables.map((doc) => new storage.SourceTable({
|
|
264
|
-
id: doc.id,
|
|
265
|
-
connectionTag: connection_tag,
|
|
266
|
-
objectId: doc.relation_id?.object_id ?? 0,
|
|
267
|
-
schema: doc.schema_name,
|
|
268
|
-
name: doc.table_name,
|
|
269
|
-
replicaIdColumns: doc.replica_id_columns?.map((c) => ({
|
|
270
|
-
name: c.name,
|
|
271
|
-
typeOid: c.typeId,
|
|
272
|
-
type: c.type
|
|
273
|
-
})) ?? [],
|
|
274
|
-
snapshotComplete: doc.snapshot_done ?? true
|
|
275
|
-
}))
|
|
276
|
-
};
|
|
277
|
-
});
|
|
278
|
-
}
|
|
279
129
|
async createWriter(options) {
|
|
280
130
|
const syncRules = await this.db.sql `
|
|
281
131
|
SELECT
|
|
@@ -292,9 +142,9 @@ export class PostgresSyncRulesStorage extends framework.BaseObserver {
|
|
|
292
142
|
.first();
|
|
293
143
|
const checkpoint_lsn = syncRules?.last_checkpoint_lsn ?? null;
|
|
294
144
|
const writer = new PostgresBucketBatch({
|
|
295
|
-
logger: options.logger ??
|
|
145
|
+
logger: options.logger ?? this.logger,
|
|
296
146
|
db: this.db,
|
|
297
|
-
sync_rules: this.sync_rules.parsed(options).
|
|
147
|
+
sync_rules: this.sync_rules.parsed(options).hydratedSyncConfig(),
|
|
298
148
|
group_id: this.group_id,
|
|
299
149
|
slot_name: this.slot_name,
|
|
300
150
|
last_checkpoint_lsn: checkpoint_lsn,
|
|
@@ -304,6 +154,7 @@ export class PostgresSyncRulesStorage extends framework.BaseObserver {
|
|
|
304
154
|
skip_existing_rows: options.skipExistingRows ?? false,
|
|
305
155
|
batch_limits: this.options.batchLimits,
|
|
306
156
|
markRecordUnavailable: options.markRecordUnavailable,
|
|
157
|
+
hooks: options.hooks,
|
|
307
158
|
storageConfig: this.storageConfig
|
|
308
159
|
});
|
|
309
160
|
this.iterateListeners((cb) => cb.batchStarted?.(writer));
|
|
@@ -318,40 +169,64 @@ export class PostgresSyncRulesStorage extends framework.BaseObserver {
|
|
|
318
169
|
await writer.flush();
|
|
319
170
|
return writer.last_flushed_op != null ? { flushed_op: writer.last_flushed_op } : null;
|
|
320
171
|
}
|
|
321
|
-
async getParameterSets(checkpoint, lookups) {
|
|
172
|
+
async getParameterSets(checkpoint, lookups, limit) {
|
|
322
173
|
const rows = await this.db.sql `
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
bucket_parameters
|
|
329
|
-
FROM
|
|
330
|
-
bucket_parameters
|
|
331
|
-
WHERE
|
|
332
|
-
group_id = ${{ type: 'int4', value: this.group_id }}
|
|
333
|
-
AND lookup = ANY (
|
|
334
|
-
SELECT
|
|
335
|
-
decode((FILTER ->> 0)::text, 'hex') -- Decode the hex string to bytea
|
|
174
|
+
WITH
|
|
175
|
+
rows AS (
|
|
176
|
+
SELECT DISTINCT
|
|
177
|
+
ON (lookup, source_table, source_key) requested.index - 1 AS index,
|
|
178
|
+
bucket_parameters
|
|
336
179
|
FROM
|
|
180
|
+
bucket_parameters,
|
|
337
181
|
jsonb_array_elements(${{
|
|
338
182
|
type: 'jsonb',
|
|
339
183
|
value: lookups.map((l) => storage.serializeLookupBuffer(l).toString('hex'))
|
|
340
|
-
}}) AS
|
|
184
|
+
}}) WITH ORDINALITY AS requested (value, index)
|
|
185
|
+
WHERE
|
|
186
|
+
group_id = ${{ type: 'int4', value: this.group_id }}
|
|
187
|
+
AND lookup = decode((requested.value ->> 0)::text, 'hex') -- Decode the hex string to bytea
|
|
188
|
+
AND id <= ${{ type: 'int8', value: checkpoint.checkpoint }}
|
|
189
|
+
ORDER BY
|
|
190
|
+
lookup,
|
|
191
|
+
source_table,
|
|
192
|
+
source_key,
|
|
193
|
+
id DESC
|
|
341
194
|
)
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
195
|
+
SELECT
|
|
196
|
+
index,
|
|
197
|
+
bucket_parameters
|
|
198
|
+
FROM
|
|
199
|
+
rows
|
|
200
|
+
WHERE
|
|
201
|
+
bucket_parameters != '[]'
|
|
202
|
+
LIMIT
|
|
203
|
+
${{ type: 'int4', value: limit + 1 }}
|
|
348
204
|
`
|
|
349
|
-
.decoded(
|
|
205
|
+
.decoded(parameterSetsRow)
|
|
350
206
|
.rows();
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
207
|
+
let totalRows = 0;
|
|
208
|
+
const resultsByLookup = new Map();
|
|
209
|
+
for (const row of rows) {
|
|
210
|
+
const parameterRows = JSONBig.parse(row.bucket_parameters);
|
|
211
|
+
const lookup = lookups[Number(row.index)];
|
|
212
|
+
totalRows += parameterRows.length;
|
|
213
|
+
const existingResults = resultsByLookup.get(lookup);
|
|
214
|
+
if (existingResults != null) {
|
|
215
|
+
existingResults.push(...parameterRows);
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
resultsByLookup.set(lookup, parameterRows);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
if (totalRows > limit) {
|
|
222
|
+
// Note that the LIMIT in the query allows more rows than parameters (because each row stores an array of
|
|
223
|
+
// parameter results). That array is very small though, and it doesn't allow fewer rows (due to the != []), so
|
|
224
|
+
// the SQL limit is good enough.
|
|
225
|
+
throw new ParameterSetLimitExceededError(limit);
|
|
226
|
+
}
|
|
227
|
+
const results = [];
|
|
228
|
+
resultsByLookup.forEach((rows, lookup) => results.push({ lookup, rows }));
|
|
229
|
+
return results;
|
|
355
230
|
}
|
|
356
231
|
async *getBucketDataBatch(checkpoint, dataBuckets, options) {
|
|
357
232
|
if (dataBuckets.length == 0) {
|
|
@@ -528,22 +403,24 @@ export class PostgresSyncRulesStorage extends framework.BaseObserver {
|
|
|
528
403
|
snapshot_done,
|
|
529
404
|
snapshot_lsn,
|
|
530
405
|
last_checkpoint_lsn,
|
|
531
|
-
state
|
|
406
|
+
state,
|
|
407
|
+
keepalive_op
|
|
532
408
|
FROM
|
|
533
409
|
sync_rules
|
|
534
410
|
WHERE
|
|
535
411
|
id = ${{ type: 'int4', value: this.group_id }}
|
|
536
412
|
`
|
|
537
|
-
.decoded(pick(models.SyncRules, ['snapshot_done', 'last_checkpoint_lsn', 'state', 'snapshot_lsn']))
|
|
413
|
+
.decoded(pick(models.SyncRules, ['snapshot_done', 'last_checkpoint_lsn', 'state', 'snapshot_lsn', 'keepalive_op']))
|
|
538
414
|
.first();
|
|
539
415
|
if (syncRulesRow == null) {
|
|
540
|
-
throw new Error('Cannot find
|
|
416
|
+
throw new Error('Cannot find replication stream status');
|
|
541
417
|
}
|
|
542
418
|
return {
|
|
543
419
|
snapshot_done: syncRulesRow.snapshot_done,
|
|
544
420
|
active: syncRulesRow.state == storage.SyncRuleState.ACTIVE,
|
|
545
421
|
checkpoint_lsn: syncRulesRow.last_checkpoint_lsn ?? null,
|
|
546
|
-
snapshot_lsn: syncRulesRow.snapshot_lsn ?? null
|
|
422
|
+
snapshot_lsn: syncRulesRow.snapshot_lsn ?? null,
|
|
423
|
+
keepalive_op: syncRulesRow.keepalive_op ?? null
|
|
547
424
|
};
|
|
548
425
|
}
|
|
549
426
|
async clear(options) {
|
|
@@ -705,7 +582,7 @@ export class PostgresSyncRulesStorage extends framework.BaseObserver {
|
|
|
705
582
|
.first();
|
|
706
583
|
if (doc == null) {
|
|
707
584
|
// Abort the connections - clients will have to retry later.
|
|
708
|
-
throw new framework.ServiceError(framework.ErrorCode.PSYNC_S2302, 'No active
|
|
585
|
+
throw new framework.ServiceError(framework.ErrorCode.PSYNC_S2302, 'No active replication stream available');
|
|
709
586
|
}
|
|
710
587
|
const sink = new LastValueSink(undefined);
|
|
711
588
|
const disposeListener = this.db.registerListener({
|
|
@@ -726,7 +603,7 @@ export class PostgresSyncRulesStorage extends framework.BaseObserver {
|
|
|
726
603
|
continue;
|
|
727
604
|
}
|
|
728
605
|
if (Number(notification.active_checkpoint.id) != doc.id) {
|
|
729
|
-
// Active
|
|
606
|
+
// Active replication stream changed - abort and restart the stream
|
|
730
607
|
break;
|
|
731
608
|
}
|
|
732
609
|
const activeCheckpoint = this.makeActiveCheckpoint(notification.active_checkpoint);
|
|
@@ -753,8 +630,12 @@ class PostgresReplicationCheckpoint {
|
|
|
753
630
|
this.checkpoint = checkpoint;
|
|
754
631
|
this.lsn = lsn;
|
|
755
632
|
}
|
|
756
|
-
getParameterSets(lookups) {
|
|
757
|
-
return this.storage.getParameterSets(this, lookups);
|
|
633
|
+
getParameterSets(lookups, limit) {
|
|
634
|
+
return this.storage.getParameterSets(this, lookups, limit);
|
|
758
635
|
}
|
|
759
636
|
}
|
|
637
|
+
const parameterSetsRow = t.object({
|
|
638
|
+
index: bigint,
|
|
639
|
+
bucket_parameters: t.string
|
|
640
|
+
});
|
|
760
641
|
//# sourceMappingURL=PostgresSyncRulesStorage.js.map
|