@powersync/service-module-mssql 0.4.0 → 0.6.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 +45 -0
- package/dist/common/CaptureInstance.d.ts +14 -0
- package/dist/common/CaptureInstance.js +2 -0
- package/dist/common/CaptureInstance.js.map +1 -0
- package/dist/common/MSSQLSourceTable.d.ts +16 -14
- package/dist/common/MSSQLSourceTable.js +35 -16
- package/dist/common/MSSQLSourceTable.js.map +1 -1
- package/dist/replication/CDCPoller.d.ts +42 -20
- package/dist/replication/CDCPoller.js +200 -60
- package/dist/replication/CDCPoller.js.map +1 -1
- package/dist/replication/CDCReplicationJob.js +9 -1
- package/dist/replication/CDCReplicationJob.js.map +1 -1
- package/dist/replication/CDCStream.d.ts +35 -4
- package/dist/replication/CDCStream.js +188 -77
- package/dist/replication/CDCStream.js.map +1 -1
- package/dist/replication/MSSQLConnectionManager.js +16 -5
- package/dist/replication/MSSQLConnectionManager.js.map +1 -1
- package/dist/types/types.d.ts +4 -56
- package/dist/types/types.js +5 -24
- package/dist/types/types.js.map +1 -1
- package/dist/utils/deadlock.d.ts +9 -0
- package/dist/utils/deadlock.js +40 -0
- package/dist/utils/deadlock.js.map +1 -0
- package/dist/utils/mssql.d.ts +33 -15
- package/dist/utils/mssql.js +101 -99
- package/dist/utils/mssql.js.map +1 -1
- package/dist/utils/schema.d.ts +9 -0
- package/dist/utils/schema.js +34 -0
- package/dist/utils/schema.js.map +1 -1
- package/package.json +8 -8
- package/src/common/CaptureInstance.ts +15 -0
- package/src/common/MSSQLSourceTable.ts +33 -24
- package/src/replication/CDCPoller.ts +272 -72
- package/src/replication/CDCReplicationJob.ts +8 -1
- package/src/replication/CDCStream.ts +245 -96
- package/src/replication/MSSQLConnectionManager.ts +15 -5
- package/src/types/types.ts +5 -28
- package/src/utils/deadlock.ts +44 -0
- package/src/utils/mssql.ts +159 -124
- package/src/utils/schema.ts +43 -0
- package/test/src/CDCStream.test.ts +3 -1
- package/test/src/CDCStreamTestContext.ts +28 -7
- package/test/src/CDCStream_resumable_snapshot.test.ts +9 -7
- package/test/src/env.ts +1 -1
- package/test/src/mssql-to-sqlite.test.ts +18 -10
- package/test/src/schema-changes.test.ts +470 -0
- package/test/src/util.ts +84 -15
- package/tsconfig.tsbuildinfo +1 -1
package/test/src/util.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import * as types from '@module/types/types.js';
|
|
2
2
|
import { logger } from '@powersync/lib-services-framework';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
BucketStorageFactory,
|
|
5
|
+
InternalOpId,
|
|
6
|
+
ReplicationCheckpoint,
|
|
7
|
+
TestStorageConfig,
|
|
8
|
+
TestStorageFactory
|
|
9
|
+
} from '@powersync/service-core';
|
|
4
10
|
|
|
5
11
|
import * as mongo_storage from '@powersync/service-module-mongodb-storage';
|
|
6
12
|
import * as postgres_storage from '@powersync/service-module-postgres-storage';
|
|
@@ -8,7 +14,7 @@ import * as postgres_storage from '@powersync/service-module-postgres-storage';
|
|
|
8
14
|
import { describe, TestOptions } from 'vitest';
|
|
9
15
|
import { env } from './env.js';
|
|
10
16
|
import { MSSQLConnectionManager } from '@module/replication/MSSQLConnectionManager.js';
|
|
11
|
-
import { createCheckpoint,
|
|
17
|
+
import { createCheckpoint, escapeIdentifier, getLatestLSN, toQualifiedTableName } from '@module/utils/mssql.js';
|
|
12
18
|
import sql from 'mssql';
|
|
13
19
|
import { v4 as uuid } from 'uuid';
|
|
14
20
|
import { LSN } from '@module/common/LSN.js';
|
|
@@ -20,11 +26,11 @@ export const INITIALIZED_MONGO_STORAGE_FACTORY = mongo_storage.test_utils.mongoT
|
|
|
20
26
|
isCI: env.CI
|
|
21
27
|
});
|
|
22
28
|
|
|
23
|
-
export const INITIALIZED_POSTGRES_STORAGE_FACTORY = postgres_storage.test_utils.
|
|
29
|
+
export const INITIALIZED_POSTGRES_STORAGE_FACTORY = postgres_storage.test_utils.postgresTestSetup({
|
|
24
30
|
url: env.PG_STORAGE_TEST_URL
|
|
25
31
|
});
|
|
26
32
|
|
|
27
|
-
export function describeWithStorage(options: TestOptions, fn: (
|
|
33
|
+
export function describeWithStorage(options: TestOptions, fn: (config: TestStorageConfig) => void) {
|
|
28
34
|
describe.skipIf(!env.TEST_MONGO_STORAGE)(`mongodb storage`, options, function () {
|
|
29
35
|
fn(INITIALIZED_MONGO_STORAGE_FACTORY);
|
|
30
36
|
});
|
|
@@ -39,7 +45,7 @@ export const TEST_CONNECTION_OPTIONS = types.normalizeConnectionConfig({
|
|
|
39
45
|
uri: TEST_URI,
|
|
40
46
|
additionalConfig: {
|
|
41
47
|
pollingBatchSize: 10,
|
|
42
|
-
pollingIntervalMs:
|
|
48
|
+
pollingIntervalMs: 100,
|
|
43
49
|
trustServerCertificate: true
|
|
44
50
|
}
|
|
45
51
|
});
|
|
@@ -66,13 +72,12 @@ export async function clearTestDb(connectionManager: MSSQLConnectionManager) {
|
|
|
66
72
|
}
|
|
67
73
|
}
|
|
68
74
|
|
|
69
|
-
export async function
|
|
75
|
+
export async function dropTestTable(connectionManager: MSSQLConnectionManager, tableName: string) {
|
|
70
76
|
await connectionManager.execute('sys.sp_cdc_disable_table', [
|
|
71
77
|
{ name: 'source_schema', value: connectionManager.schema },
|
|
72
78
|
{ name: 'source_name', value: tableName },
|
|
73
79
|
{ name: 'capture_instance', value: 'all' }
|
|
74
80
|
]);
|
|
75
|
-
|
|
76
81
|
await connectionManager.query(`DROP TABLE [${tableName}]`);
|
|
77
82
|
}
|
|
78
83
|
|
|
@@ -92,19 +97,26 @@ export async function createTestDb(connectionManager: MSSQLConnectionManager, db
|
|
|
92
97
|
GO`);
|
|
93
98
|
}
|
|
94
99
|
|
|
95
|
-
export async function createTestTable(
|
|
100
|
+
export async function createTestTable(
|
|
101
|
+
connectionManager: MSSQLConnectionManager,
|
|
102
|
+
tableName: string,
|
|
103
|
+
withCaptureInstance: boolean = true
|
|
104
|
+
): Promise<void> {
|
|
96
105
|
await connectionManager.query(`
|
|
97
106
|
CREATE TABLE ${escapeIdentifier(connectionManager.schema)}.${escapeIdentifier(tableName)} (
|
|
98
107
|
id UNIQUEIDENTIFIER PRIMARY KEY,
|
|
99
108
|
description VARCHAR(MAX)
|
|
100
109
|
)
|
|
101
110
|
`);
|
|
102
|
-
|
|
111
|
+
if (withCaptureInstance) {
|
|
112
|
+
await enableCDCForTable({ connectionManager, table: tableName });
|
|
113
|
+
}
|
|
103
114
|
}
|
|
104
115
|
|
|
105
116
|
export async function createTestTableWithBasicId(
|
|
106
117
|
connectionManager: MSSQLConnectionManager,
|
|
107
|
-
tableName: string
|
|
118
|
+
tableName: string,
|
|
119
|
+
withCaptureInstance: boolean = true
|
|
108
120
|
): Promise<void> {
|
|
109
121
|
await connectionManager.query(`
|
|
110
122
|
CREATE TABLE ${escapeIdentifier(connectionManager.schema)}.${escapeIdentifier(tableName)} (
|
|
@@ -112,7 +124,9 @@ export async function createTestTableWithBasicId(
|
|
|
112
124
|
description VARCHAR(MAX)
|
|
113
125
|
)
|
|
114
126
|
`);
|
|
115
|
-
|
|
127
|
+
if (withCaptureInstance) {
|
|
128
|
+
await enableCDCForTable({ connectionManager, table: tableName });
|
|
129
|
+
}
|
|
116
130
|
}
|
|
117
131
|
|
|
118
132
|
export interface TestData {
|
|
@@ -135,6 +149,24 @@ export async function insertTestData(connectionManager: MSSQLConnectionManager,
|
|
|
135
149
|
return { id, description };
|
|
136
150
|
}
|
|
137
151
|
|
|
152
|
+
export async function insertBasicIdTestData(
|
|
153
|
+
connectionManager: MSSQLConnectionManager,
|
|
154
|
+
tableName: string
|
|
155
|
+
): Promise<TestData> {
|
|
156
|
+
const description = `description_${Math.floor(Math.random() * 1000000)}`;
|
|
157
|
+
const { recordset: result } = await connectionManager.query(
|
|
158
|
+
`
|
|
159
|
+
INSERT INTO ${toQualifiedTableName(connectionManager.schema, tableName)} (description)
|
|
160
|
+
OUTPUT INSERTED.id
|
|
161
|
+
VALUES (@description)
|
|
162
|
+
`,
|
|
163
|
+
[{ name: 'description', type: sql.NVarChar(sql.MAX), value: description }]
|
|
164
|
+
);
|
|
165
|
+
const id = result[0].id;
|
|
166
|
+
|
|
167
|
+
return { id, description };
|
|
168
|
+
}
|
|
169
|
+
|
|
138
170
|
export async function waitForPendingCDCChanges(
|
|
139
171
|
beforeLSN: LSN,
|
|
140
172
|
connectionManager: MSSQLConnectionManager
|
|
@@ -151,10 +183,14 @@ export async function waitForPendingCDCChanges(
|
|
|
151
183
|
);
|
|
152
184
|
|
|
153
185
|
if (result.length === 0) {
|
|
154
|
-
logger.info(
|
|
186
|
+
logger.info(
|
|
187
|
+
`Test Assertion: CDC changes pending. Waiting for a transaction newer than: ${beforeLSN.toString()} for 200ms...`
|
|
188
|
+
);
|
|
155
189
|
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
156
190
|
} else {
|
|
157
|
-
logger.info(
|
|
191
|
+
logger.info(
|
|
192
|
+
`Test Assertion: Expected CDC change found with LSN: ${LSN.fromBinary(result[0].start_lsn).toString()}`
|
|
193
|
+
);
|
|
158
194
|
return;
|
|
159
195
|
}
|
|
160
196
|
}
|
|
@@ -176,14 +212,14 @@ export async function getClientCheckpoint(
|
|
|
176
212
|
const timeout = options?.timeout ?? 50_000;
|
|
177
213
|
let lastCp: ReplicationCheckpoint | null = null;
|
|
178
214
|
|
|
179
|
-
logger.info(`Waiting for LSN checkpoint: ${lsn}`);
|
|
215
|
+
logger.info(`Test Assertion: Waiting for LSN checkpoint: ${lsn}`);
|
|
180
216
|
while (Date.now() - start < timeout) {
|
|
181
217
|
const storage = await storageFactory.getActiveStorage();
|
|
182
218
|
const cp = await storage?.getCheckpoint();
|
|
183
219
|
if (cp != null) {
|
|
184
220
|
lastCp = cp;
|
|
185
221
|
if (cp.lsn != null && cp.lsn >= lsn.toString()) {
|
|
186
|
-
logger.info(`Got write checkpoint: ${lsn} : ${cp.checkpoint}`);
|
|
222
|
+
logger.info(`Test Assertion: Got write checkpoint: ${lsn} : ${cp.checkpoint}`);
|
|
187
223
|
return cp.checkpoint;
|
|
188
224
|
}
|
|
189
225
|
}
|
|
@@ -200,3 +236,36 @@ export async function getClientCheckpoint(
|
|
|
200
236
|
export function createUpperCaseUUID(): string {
|
|
201
237
|
return uuid().toUpperCase();
|
|
202
238
|
}
|
|
239
|
+
|
|
240
|
+
export async function renameTable(connectionManager: MSSQLConnectionManager, fromTable: string, toTable: string) {
|
|
241
|
+
await connectionManager.execute('sp_rename', [
|
|
242
|
+
{ name: 'objname', value: toQualifiedTableName(connectionManager.schema, fromTable) },
|
|
243
|
+
{ name: 'newname', value: toTable }
|
|
244
|
+
]);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export interface EnableCDCForTableOptions {
|
|
248
|
+
connectionManager: MSSQLConnectionManager;
|
|
249
|
+
table: string;
|
|
250
|
+
captureInstance?: string;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export async function enableCDCForTable(options: EnableCDCForTableOptions): Promise<void> {
|
|
254
|
+
const { connectionManager, table, captureInstance } = options;
|
|
255
|
+
|
|
256
|
+
await connectionManager.execute('sys.sp_cdc_enable_table', [
|
|
257
|
+
{ name: 'source_schema', value: connectionManager.schema },
|
|
258
|
+
{ name: 'source_name', value: table },
|
|
259
|
+
{ name: 'role_name', value: 'cdc_reader' },
|
|
260
|
+
{ name: 'supports_net_changes', value: 0 },
|
|
261
|
+
...(captureInstance !== undefined ? [{ name: 'capture_instance', value: captureInstance }] : [])
|
|
262
|
+
]);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
export async function disableCDCForTable(connectionManager: MSSQLConnectionManager, tableName: string) {
|
|
266
|
+
await connectionManager.execute('sys.sp_cdc_disable_table', [
|
|
267
|
+
{ name: 'source_schema', value: connectionManager.schema },
|
|
268
|
+
{ name: 'source_name', value: tableName },
|
|
269
|
+
{ name: 'capture_instance', value: 'all' }
|
|
270
|
+
]);
|
|
271
|
+
}
|