@powersync/service-module-mssql 0.0.1

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 (92) hide show
  1. package/LICENSE +67 -0
  2. package/README.md +3 -0
  3. package/ci/init-mssql.sql +50 -0
  4. package/dist/api/MSSQLRouteAPIAdapter.d.ts +21 -0
  5. package/dist/api/MSSQLRouteAPIAdapter.js +248 -0
  6. package/dist/api/MSSQLRouteAPIAdapter.js.map +1 -0
  7. package/dist/common/LSN.d.ts +37 -0
  8. package/dist/common/LSN.js +64 -0
  9. package/dist/common/LSN.js.map +1 -0
  10. package/dist/common/MSSQLSourceTable.d.ts +27 -0
  11. package/dist/common/MSSQLSourceTable.js +35 -0
  12. package/dist/common/MSSQLSourceTable.js.map +1 -0
  13. package/dist/common/MSSQLSourceTableCache.d.ts +14 -0
  14. package/dist/common/MSSQLSourceTableCache.js +28 -0
  15. package/dist/common/MSSQLSourceTableCache.js.map +1 -0
  16. package/dist/common/mssqls-to-sqlite.d.ts +18 -0
  17. package/dist/common/mssqls-to-sqlite.js +143 -0
  18. package/dist/common/mssqls-to-sqlite.js.map +1 -0
  19. package/dist/index.d.ts +1 -0
  20. package/dist/index.js +2 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/module/MSSQLModule.d.ts +15 -0
  23. package/dist/module/MSSQLModule.js +68 -0
  24. package/dist/module/MSSQLModule.js.map +1 -0
  25. package/dist/replication/CDCPoller.d.ts +67 -0
  26. package/dist/replication/CDCPoller.js +183 -0
  27. package/dist/replication/CDCPoller.js.map +1 -0
  28. package/dist/replication/CDCReplicationJob.d.ts +17 -0
  29. package/dist/replication/CDCReplicationJob.js +76 -0
  30. package/dist/replication/CDCReplicationJob.js.map +1 -0
  31. package/dist/replication/CDCReplicator.d.ts +18 -0
  32. package/dist/replication/CDCReplicator.js +55 -0
  33. package/dist/replication/CDCReplicator.js.map +1 -0
  34. package/dist/replication/CDCStream.d.ts +106 -0
  35. package/dist/replication/CDCStream.js +536 -0
  36. package/dist/replication/CDCStream.js.map +1 -0
  37. package/dist/replication/MSSQLConnectionManager.d.ts +23 -0
  38. package/dist/replication/MSSQLConnectionManager.js +97 -0
  39. package/dist/replication/MSSQLConnectionManager.js.map +1 -0
  40. package/dist/replication/MSSQLConnectionManagerFactory.d.ts +10 -0
  41. package/dist/replication/MSSQLConnectionManagerFactory.js +28 -0
  42. package/dist/replication/MSSQLConnectionManagerFactory.js.map +1 -0
  43. package/dist/replication/MSSQLErrorRateLimiter.d.ts +10 -0
  44. package/dist/replication/MSSQLErrorRateLimiter.js +34 -0
  45. package/dist/replication/MSSQLErrorRateLimiter.js.map +1 -0
  46. package/dist/replication/MSSQLSnapshotQuery.d.ts +71 -0
  47. package/dist/replication/MSSQLSnapshotQuery.js +190 -0
  48. package/dist/replication/MSSQLSnapshotQuery.js.map +1 -0
  49. package/dist/types/mssql-data-types.d.ts +66 -0
  50. package/dist/types/mssql-data-types.js +62 -0
  51. package/dist/types/mssql-data-types.js.map +1 -0
  52. package/dist/types/types.d.ts +177 -0
  53. package/dist/types/types.js +141 -0
  54. package/dist/types/types.js.map +1 -0
  55. package/dist/utils/mssql.d.ts +80 -0
  56. package/dist/utils/mssql.js +329 -0
  57. package/dist/utils/mssql.js.map +1 -0
  58. package/dist/utils/schema.d.ts +21 -0
  59. package/dist/utils/schema.js +131 -0
  60. package/dist/utils/schema.js.map +1 -0
  61. package/package.json +51 -0
  62. package/src/api/MSSQLRouteAPIAdapter.ts +283 -0
  63. package/src/common/LSN.ts +77 -0
  64. package/src/common/MSSQLSourceTable.ts +54 -0
  65. package/src/common/MSSQLSourceTableCache.ts +36 -0
  66. package/src/common/mssqls-to-sqlite.ts +151 -0
  67. package/src/index.ts +1 -0
  68. package/src/module/MSSQLModule.ts +82 -0
  69. package/src/replication/CDCPoller.ts +241 -0
  70. package/src/replication/CDCReplicationJob.ts +87 -0
  71. package/src/replication/CDCReplicator.ts +70 -0
  72. package/src/replication/CDCStream.ts +688 -0
  73. package/src/replication/MSSQLConnectionManager.ts +113 -0
  74. package/src/replication/MSSQLConnectionManagerFactory.ts +33 -0
  75. package/src/replication/MSSQLErrorRateLimiter.ts +36 -0
  76. package/src/replication/MSSQLSnapshotQuery.ts +230 -0
  77. package/src/types/mssql-data-types.ts +79 -0
  78. package/src/types/types.ts +224 -0
  79. package/src/utils/mssql.ts +420 -0
  80. package/src/utils/schema.ts +172 -0
  81. package/test/src/CDCStream.test.ts +206 -0
  82. package/test/src/CDCStreamTestContext.ts +212 -0
  83. package/test/src/CDCStream_resumable_snapshot.test.ts +152 -0
  84. package/test/src/env.ts +11 -0
  85. package/test/src/mssql-to-sqlite.test.ts +474 -0
  86. package/test/src/setup.ts +12 -0
  87. package/test/src/util.ts +189 -0
  88. package/test/tsconfig.json +28 -0
  89. package/test/tsconfig.tsbuildinfo +1 -0
  90. package/tsconfig.json +26 -0
  91. package/tsconfig.tsbuildinfo +1 -0
  92. package/vitest.config.ts +15 -0
@@ -0,0 +1,143 @@
1
+ import sql from 'mssql';
2
+ import { ExpressionType, toSyncRulesRow } from '@powersync/service-sync-rules';
3
+ import { MSSQLUserDefinedType } from '../types/mssql-data-types.js';
4
+ export function toSqliteInputRow(row, columns) {
5
+ let result = {};
6
+ for (const key in row) {
7
+ // We are very much expecting the column to be there
8
+ const columnMetadata = columns[key];
9
+ if (row[key] !== null) {
10
+ switch (columnMetadata.type) {
11
+ case sql.TYPES.BigInt:
12
+ // MSSQL returns BIGINT as a string to avoid precision loss
13
+ if (typeof row[key] === 'string') {
14
+ result[key] = BigInt(row[key]);
15
+ }
16
+ break;
17
+ case sql.TYPES.Bit:
18
+ // MSSQL returns BIT as boolean
19
+ result[key] = row[key] ? 1 : 0;
20
+ break;
21
+ // Convert Dates to string
22
+ case sql.TYPES.Date:
23
+ result[key] = toISODateString(row[key]);
24
+ break;
25
+ case sql.TYPES.Time:
26
+ result[key] = toISOTimeString(row[key]);
27
+ break;
28
+ case sql.TYPES.DateTime:
29
+ case sql.TYPES.DateTime2:
30
+ case sql.TYPES.SmallDateTime:
31
+ case sql.TYPES.DateTimeOffset: // The offset is lost when the driver converts to Date. This needs to be handled in the sql query.
32
+ const date = row[key];
33
+ result[key] = isNaN(date.getTime()) ? null : date.toISOString();
34
+ break;
35
+ case sql.TYPES.Binary:
36
+ case sql.TYPES.VarBinary:
37
+ case sql.TYPES.Image:
38
+ result[key] = new Uint8Array(Object.values(row[key]));
39
+ break;
40
+ // TODO: Spatial types need to be converted to binary WKB, they are returned as a non standard object currently
41
+ case sql.TYPES.Geometry:
42
+ case sql.TYPES.Geography:
43
+ result[key] = JSON.stringify(row[key]);
44
+ break;
45
+ case sql.TYPES.UDT:
46
+ if (columnMetadata.udt.name === MSSQLUserDefinedType.HIERARCHYID) {
47
+ result[key] = new Uint8Array(Object.values(row[key]));
48
+ break;
49
+ }
50
+ else {
51
+ result[key] = row[key];
52
+ }
53
+ break;
54
+ default:
55
+ result[key] = row[key];
56
+ }
57
+ }
58
+ else {
59
+ // If the value is null, we just set it to null
60
+ result[key] = null;
61
+ }
62
+ }
63
+ return toSyncRulesRow(result);
64
+ }
65
+ function toISODateString(date) {
66
+ return isNaN(date.getTime()) ? null : date.toISOString().split('T')[0];
67
+ }
68
+ /**
69
+ * MSSQL time format is HH:mm:ss[.nnnnnnn]
70
+ * @param date
71
+ * @returns
72
+ */
73
+ function toISOTimeString(date) {
74
+ return isNaN(date.getTime()) ? null : date.toISOString().split('T')[1].replace('Z', '');
75
+ }
76
+ /**
77
+ * Converts MSSQL type names to SQLite ExpressionType
78
+ * @param mssqlType - The MSSQL type name (e.g., 'int', 'varchar', 'datetime2')
79
+ */
80
+ export function toExpressionTypeFromMSSQLType(mssqlType) {
81
+ if (!mssqlType) {
82
+ return ExpressionType.TEXT;
83
+ }
84
+ const baseType = mssqlType.toUpperCase();
85
+ switch (baseType) {
86
+ case 'BIT':
87
+ case 'TINYINT':
88
+ case 'SMALLINT':
89
+ case 'INT':
90
+ case 'INTEGER':
91
+ case 'BIGINT':
92
+ return ExpressionType.INTEGER;
93
+ case 'BINARY':
94
+ case 'VARBINARY':
95
+ case 'IMAGE':
96
+ case 'TIMESTAMP':
97
+ return ExpressionType.BLOB;
98
+ case 'FLOAT':
99
+ case 'REAL':
100
+ case 'MONEY':
101
+ case 'SMALLMONEY':
102
+ case 'DECIMAL':
103
+ case 'NUMERIC':
104
+ return ExpressionType.REAL;
105
+ case 'JSON':
106
+ return ExpressionType.TEXT;
107
+ // System and extended types
108
+ case 'SYSNAME':
109
+ // SYSNAME is essentially NVARCHAR(128), map to TEXT
110
+ return ExpressionType.TEXT;
111
+ case 'HIERARCHYID':
112
+ // HIERARCHYID is a CLR UDT representing hierarchical data, stored as string representation
113
+ return ExpressionType.TEXT;
114
+ case 'GEOMETRY':
115
+ case 'GEOGRAPHY':
116
+ // Spatial CLR UDT types, typically stored as WKT (Well-Known Text) strings
117
+ return ExpressionType.TEXT;
118
+ case 'VECTOR':
119
+ // Vector type (SQL Server 2022+), stored as binary data
120
+ return ExpressionType.BLOB;
121
+ default:
122
+ // In addition to the normal text types, includes: VARCHAR, NVARCHAR, CHAR, NCHAR, TEXT, NTEXT, DATE, TIME, DATETIME, DATETIME2, SMALLDATETIME, DATETIMEOFFSET, XML, UNIQUEIDENTIFIER, SQL_VARIANT
123
+ return ExpressionType.TEXT;
124
+ }
125
+ }
126
+ // CDC metadata columns in CDCS rows that should be excluded
127
+ const CDC_METADATA_COLUMNS = ['__$operation', '__$start_lsn', '__$end_lsn', '__$seqval', '__$update_mask'];
128
+ /**
129
+ * Convert CDC row data to SqliteRow format.
130
+ * CDC rows include table columns plus CDC metadata columns (__$operation, __$start_lsn, etc.)
131
+ * which we filter out.
132
+ */
133
+ export function CDCToSqliteRow(options) {
134
+ const { row, columns } = options;
135
+ const filteredRow = {};
136
+ for (const key in row) {
137
+ if (!CDC_METADATA_COLUMNS.includes(key)) {
138
+ filteredRow[key] = row[key];
139
+ }
140
+ }
141
+ return toSqliteInputRow(filteredRow, columns);
142
+ }
143
+ //# sourceMappingURL=mssqls-to-sqlite.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mssqls-to-sqlite.js","sourceRoot":"","sources":["../../src/common/mssqls-to-sqlite.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export * from './module/MSSQLModule.js';
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export * from './module/MSSQLModule.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,yBAAyB,CAAC"}
@@ -0,0 +1,15 @@
1
+ import { api, ConnectionTestResult, replication, system, TearDownOptions } from '@powersync/service-core';
2
+ import * as types from '../types/types.js';
3
+ export declare class MSSQLModule extends replication.ReplicationModule<types.MSSQLConnectionConfig> {
4
+ constructor();
5
+ onInitialized(context: system.ServiceContextContainer): Promise<void>;
6
+ protected createRouteAPIAdapter(): api.RouteAPI;
7
+ protected createReplicator(context: system.ServiceContext): replication.AbstractReplicator;
8
+ /**
9
+ * Combines base config with normalized connection settings
10
+ */
11
+ private resolveConfig;
12
+ teardown(options: TearDownOptions): Promise<void>;
13
+ testConnection(config: types.MSSQLConnectionConfig): Promise<ConnectionTestResult>;
14
+ static testConnection(normalizedConfig: types.ResolvedMSSQLConnectionConfig): Promise<ConnectionTestResult>;
15
+ }
@@ -0,0 +1,68 @@
1
+ import { ConfigurationFileSyncRulesProvider, replication } from '@powersync/service-core';
2
+ import { MSSQLConnectionManagerFactory } from '../replication/MSSQLConnectionManagerFactory.js';
3
+ import * as types from '../types/types.js';
4
+ import { CDCReplicator } from '../replication/CDCReplicator.js';
5
+ import { MSSQLConnectionManager } from '../replication/MSSQLConnectionManager.js';
6
+ import { checkSourceConfiguration } from '../utils/mssql.js';
7
+ import { MSSQLErrorRateLimiter } from '../replication/MSSQLErrorRateLimiter.js';
8
+ import { MSSQLRouteAPIAdapter } from '../api/MSSQLRouteAPIAdapter.js';
9
+ export class MSSQLModule extends replication.ReplicationModule {
10
+ constructor() {
11
+ super({
12
+ name: 'MSSQL',
13
+ type: types.MSSQL_CONNECTION_TYPE,
14
+ configSchema: types.MSSQLConnectionConfig
15
+ });
16
+ }
17
+ async onInitialized(context) { }
18
+ createRouteAPIAdapter() {
19
+ return new MSSQLRouteAPIAdapter(this.resolveConfig(this.decodedConfig));
20
+ }
21
+ createReplicator(context) {
22
+ const normalisedConfig = this.resolveConfig(this.decodedConfig);
23
+ const syncRuleProvider = new ConfigurationFileSyncRulesProvider(context.configuration.sync_rules);
24
+ const connectionFactory = new MSSQLConnectionManagerFactory(normalisedConfig);
25
+ return new CDCReplicator({
26
+ id: this.getDefaultId(normalisedConfig.database),
27
+ syncRuleProvider: syncRuleProvider,
28
+ storageEngine: context.storageEngine,
29
+ metricsEngine: context.metricsEngine,
30
+ connectionFactory: connectionFactory,
31
+ rateLimiter: new MSSQLErrorRateLimiter(),
32
+ pollingOptions: normalisedConfig.cdcPollingOptions
33
+ });
34
+ }
35
+ /**
36
+ * Combines base config with normalized connection settings
37
+ */
38
+ resolveConfig(config) {
39
+ return {
40
+ ...config,
41
+ ...types.normalizeConnectionConfig(config)
42
+ };
43
+ }
44
+ async teardown(options) {
45
+ // No specific teardown required for MSSQL
46
+ }
47
+ async testConnection(config) {
48
+ this.decodeConfig(config);
49
+ const normalizedConfig = this.resolveConfig(this.decodedConfig);
50
+ return await MSSQLModule.testConnection(normalizedConfig);
51
+ }
52
+ static async testConnection(normalizedConfig) {
53
+ const connectionManager = new MSSQLConnectionManager(normalizedConfig, { max: 1 });
54
+ try {
55
+ const errors = await checkSourceConfiguration(connectionManager);
56
+ if (errors.length > 0) {
57
+ throw new Error(errors.join('\n'));
58
+ }
59
+ }
60
+ finally {
61
+ await connectionManager.end();
62
+ }
63
+ return {
64
+ connectionDescription: normalizedConfig.hostname
65
+ };
66
+ }
67
+ }
68
+ //# sourceMappingURL=MSSQLModule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MSSQLModule.js","sourceRoot":"","sources":["../../src/module/MSSQLModule.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,67 @@
1
+ import { Logger } from '@powersync/lib-services-framework';
2
+ import { MSSQLConnectionManager } from './MSSQLConnectionManager.js';
3
+ import { MSSQLSourceTable } from '../common/MSSQLSourceTable.js';
4
+ import { LSN } from '../common/LSN.js';
5
+ import sql from 'mssql';
6
+ import { CDCPollingOptions } from '../types/types.js';
7
+ /**
8
+ * Schema changes that are detectable by inspecting query events.
9
+ * Create table statements are not included here, since new tables are automatically detected when row events
10
+ * are received for them.
11
+ */
12
+ export declare enum SchemaChangeType {
13
+ RENAME_TABLE = "Rename Table",
14
+ DROP_TABLE = "Drop Table",
15
+ TRUNCATE_TABLE = "Truncate Table",
16
+ ALTER_TABLE_COLUMN = "Alter Table Column",
17
+ REPLICATION_IDENTITY = "Alter Replication Identity"
18
+ }
19
+ export interface SchemaChange {
20
+ type: SchemaChangeType;
21
+ /**
22
+ * The table that the schema change applies to.
23
+ */
24
+ table: string;
25
+ schema: string;
26
+ /**
27
+ * Populated for table renames if the newTable was matched by the DatabaseFilter
28
+ */
29
+ newTable?: string;
30
+ }
31
+ export interface CDCEventHandler {
32
+ onInsert: (row: any, table: MSSQLSourceTable, collumns: sql.IColumnMetadata) => Promise<void>;
33
+ onUpdate: (rowAfter: any, rowBefore: any, table: MSSQLSourceTable, collumns: sql.IColumnMetadata) => Promise<void>;
34
+ onDelete: (row: any, table: MSSQLSourceTable, collumns: sql.IColumnMetadata) => Promise<void>;
35
+ onCommit: (lsn: string, transactionCount: number) => Promise<void>;
36
+ onSchemaChange: (change: SchemaChange) => Promise<void>;
37
+ }
38
+ export interface CDCPollerOptions {
39
+ connectionManager: MSSQLConnectionManager;
40
+ eventHandler: CDCEventHandler;
41
+ sourceTables: MSSQLSourceTable[];
42
+ startLSN: LSN;
43
+ pollingOptions: CDCPollingOptions;
44
+ logger?: Logger;
45
+ }
46
+ /**
47
+ *
48
+ */
49
+ export declare class CDCPoller {
50
+ options: CDCPollerOptions;
51
+ private connectionManager;
52
+ private eventHandler;
53
+ private currentLSN;
54
+ private logger;
55
+ private listenerError;
56
+ private isStopped;
57
+ private isStopping;
58
+ private isPolling;
59
+ constructor(options: CDCPollerOptions);
60
+ private get pollingBatchSize();
61
+ private get pollingIntervalMs();
62
+ private get sourceTables();
63
+ stop(): Promise<void>;
64
+ replicateUntilStopped(): Promise<void>;
65
+ private poll;
66
+ private pollTable;
67
+ }
@@ -0,0 +1,183 @@
1
+ import { logger as defaultLogger, ReplicationAssertionError } from '@powersync/lib-services-framework';
2
+ import timers from 'timers/promises';
3
+ import { LSN } from '../common/LSN.js';
4
+ import sql from 'mssql';
5
+ import { getMinLSN, incrementLSN } from '../utils/mssql.js';
6
+ var Operation;
7
+ (function (Operation) {
8
+ Operation[Operation["DELETE"] = 1] = "DELETE";
9
+ Operation[Operation["INSERT"] = 2] = "INSERT";
10
+ Operation[Operation["UPDATE_BEFORE"] = 3] = "UPDATE_BEFORE";
11
+ Operation[Operation["UPDATE_AFTER"] = 4] = "UPDATE_AFTER";
12
+ })(Operation || (Operation = {}));
13
+ /**
14
+ * Schema changes that are detectable by inspecting query events.
15
+ * Create table statements are not included here, since new tables are automatically detected when row events
16
+ * are received for them.
17
+ */
18
+ export var SchemaChangeType;
19
+ (function (SchemaChangeType) {
20
+ SchemaChangeType["RENAME_TABLE"] = "Rename Table";
21
+ SchemaChangeType["DROP_TABLE"] = "Drop Table";
22
+ SchemaChangeType["TRUNCATE_TABLE"] = "Truncate Table";
23
+ SchemaChangeType["ALTER_TABLE_COLUMN"] = "Alter Table Column";
24
+ SchemaChangeType["REPLICATION_IDENTITY"] = "Alter Replication Identity";
25
+ })(SchemaChangeType || (SchemaChangeType = {}));
26
+ /**
27
+ *
28
+ */
29
+ export class CDCPoller {
30
+ options;
31
+ connectionManager;
32
+ eventHandler;
33
+ currentLSN;
34
+ logger;
35
+ listenerError;
36
+ isStopped = false;
37
+ isStopping = false;
38
+ isPolling = false;
39
+ constructor(options) {
40
+ this.options = options;
41
+ this.logger = options.logger ?? defaultLogger;
42
+ this.connectionManager = options.connectionManager;
43
+ this.eventHandler = options.eventHandler;
44
+ this.currentLSN = options.startLSN;
45
+ this.listenerError = null;
46
+ }
47
+ get pollingBatchSize() {
48
+ return this.options.pollingOptions.batchSize;
49
+ }
50
+ get pollingIntervalMs() {
51
+ return this.options.pollingOptions.intervalMs;
52
+ }
53
+ get sourceTables() {
54
+ return this.options.sourceTables;
55
+ }
56
+ async stop() {
57
+ if (!(this.isStopped || this.isStopping)) {
58
+ this.isStopping = true;
59
+ this.isStopped = true;
60
+ }
61
+ }
62
+ async replicateUntilStopped() {
63
+ this.logger.info(`CDC polling started with interval of ${this.pollingIntervalMs}ms...`);
64
+ this.logger.info(`Polling a maximum of [${this.pollingBatchSize}] transactions per polling cycle.`);
65
+ while (!this.isStopped) {
66
+ // Don't poll if already polling (concurrency guard)
67
+ if (this.isPolling) {
68
+ await timers.setTimeout(this.pollingIntervalMs);
69
+ continue;
70
+ }
71
+ try {
72
+ const hasChanges = await this.poll();
73
+ if (!hasChanges) {
74
+ // No changes found, wait before next poll
75
+ await timers.setTimeout(this.pollingIntervalMs);
76
+ }
77
+ // If changes were found, poll immediately again (no wait)
78
+ }
79
+ catch (error) {
80
+ if (!(this.isStopped || this.isStopping)) {
81
+ this.listenerError = error;
82
+ this.logger.error('Error during CDC polling:', error);
83
+ this.stop();
84
+ }
85
+ break;
86
+ }
87
+ }
88
+ if (this.listenerError) {
89
+ this.logger.error('CDC polling was stopped due to an error:', this.listenerError);
90
+ throw this.listenerError;
91
+ }
92
+ this.logger.info(`CDC polling stopped...`);
93
+ }
94
+ async poll() {
95
+ // Set polling flag to prevent concurrent polling cycles
96
+ this.isPolling = true;
97
+ try {
98
+ // Calculate the LSN bounds for this batch
99
+ // CDC bounds are inclusive, so the new startLSN is the currentLSN incremented by 1
100
+ const startLSN = await incrementLSN(this.currentLSN, this.connectionManager);
101
+ const { recordset: results } = await this.connectionManager.query(`SELECT TOP (${this.pollingBatchSize}) start_lsn
102
+ FROM cdc.lsn_time_mapping
103
+ WHERE start_lsn >= @startLSN
104
+ ORDER BY start_lsn ASC
105
+ `, [{ name: 'startLSN', type: sql.VarBinary, value: startLSN.toBinary() }]);
106
+ // No new LSNs found, no changes to process
107
+ if (results.length === 0) {
108
+ return false;
109
+ }
110
+ // The new endLSN is the largest LSN in the result
111
+ const endLSN = LSN.fromBinary(results[results.length - 1].start_lsn);
112
+ this.logger.info(`Polling bounds are ${startLSN} -> ${endLSN} spanning ${results.length} transaction(s).`);
113
+ let transactionCount = 0;
114
+ for (const table of this.sourceTables) {
115
+ const tableTransactionCount = await this.pollTable(table, { startLSN, endLSN });
116
+ // We poll for batch size transactions, but these include transactions not applicable to our Source Tables.
117
+ // Each Source Table may or may not have transactions that are applicable to it, so just keep track of the highest number of transactions processed for any Source Table.
118
+ if (tableTransactionCount > transactionCount) {
119
+ transactionCount = tableTransactionCount;
120
+ }
121
+ }
122
+ this.logger.info(`Processed ${results.length} transaction(s), including ${transactionCount} Source Table transaction(s).`);
123
+ // Call eventHandler.onCommit() with toLSN after processing all tables
124
+ await this.eventHandler.onCommit(endLSN.toString(), transactionCount);
125
+ this.currentLSN = endLSN;
126
+ return true;
127
+ }
128
+ finally {
129
+ // Always clear polling flag, even on error
130
+ this.isPolling = false;
131
+ }
132
+ }
133
+ async pollTable(table, bounds) {
134
+ // Ensure that the startLSN is not before the minimum LSN for the table
135
+ const minLSN = await getMinLSN(this.connectionManager, table.captureInstance);
136
+ if (minLSN > bounds.endLSN) {
137
+ return 0;
138
+ }
139
+ else if (minLSN >= bounds.startLSN) {
140
+ bounds.startLSN = minLSN;
141
+ }
142
+ const { recordset: results } = await this.connectionManager.query(`
143
+ SELECT * FROM ${table.allChangesFunction}(@from_lsn, @to_lsn, 'all update old') ORDER BY __$start_lsn, __$seqval
144
+ `, [
145
+ { name: 'from_lsn', type: sql.VarBinary, value: bounds.startLSN.toBinary() },
146
+ { name: 'to_lsn', type: sql.VarBinary, value: bounds.endLSN.toBinary() }
147
+ ]);
148
+ let transactionCount = 0;
149
+ let updateBefore = null;
150
+ for (const row of results) {
151
+ const transactionLSN = LSN.fromBinary(row.__$start_lsn);
152
+ switch (row.__$operation) {
153
+ case Operation.DELETE:
154
+ await this.eventHandler.onDelete(row, table, results.columns);
155
+ transactionCount++;
156
+ this.logger.info(`Processed DELETE row LSN: ${transactionLSN}`);
157
+ break;
158
+ case Operation.INSERT:
159
+ await this.eventHandler.onInsert(row, table, results.columns);
160
+ transactionCount++;
161
+ this.logger.info(`Processed INSERT row LSN: ${transactionLSN}`);
162
+ break;
163
+ case Operation.UPDATE_BEFORE:
164
+ updateBefore = row;
165
+ this.logger.debug(`Processed UPDATE, before row LSN: ${transactionLSN}`);
166
+ break;
167
+ case Operation.UPDATE_AFTER:
168
+ if (updateBefore === null) {
169
+ throw new ReplicationAssertionError('Missing before image for update event.');
170
+ }
171
+ await this.eventHandler.onUpdate(row, updateBefore, table, results.columns);
172
+ updateBefore = null;
173
+ transactionCount++;
174
+ this.logger.info(`Processed UPDATE row LSN: ${transactionLSN}`);
175
+ break;
176
+ default:
177
+ this.logger.warn(`Unknown operation type [${row.__$operation}] encountered in CDC changes.`);
178
+ }
179
+ }
180
+ return transactionCount;
181
+ }
182
+ }
183
+ //# sourceMappingURL=CDCPoller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CDCPoller.js","sourceRoot":"","sources":["../../src/replication/CDCPoller.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import { replication } from '@powersync/service-core';
2
+ import { MSSQLConnectionManagerFactory } from './MSSQLConnectionManagerFactory.js';
3
+ import { CDCPollingOptions } from '../types/types.js';
4
+ export interface CDCReplicationJobOptions extends replication.AbstractReplicationJobOptions {
5
+ connectionFactory: MSSQLConnectionManagerFactory;
6
+ pollingOptions: CDCPollingOptions;
7
+ }
8
+ export declare class CDCReplicationJob extends replication.AbstractReplicationJob {
9
+ private connectionFactory;
10
+ private lastStream;
11
+ private cdcReplicationJobOptions;
12
+ constructor(options: CDCReplicationJobOptions);
13
+ keepAlive(): Promise<void>;
14
+ replicate(): Promise<void>;
15
+ replicateOnce(): Promise<void>;
16
+ getReplicationLagMillis(): Promise<number | undefined>;
17
+ }
@@ -0,0 +1,76 @@
1
+ import { replication } from '@powersync/service-core';
2
+ import { container, logger as defaultLogger } from '@powersync/lib-services-framework';
3
+ import { CDCDataExpiredError, CDCStream } from './CDCStream.js';
4
+ export class CDCReplicationJob extends replication.AbstractReplicationJob {
5
+ connectionFactory;
6
+ lastStream = null;
7
+ cdcReplicationJobOptions;
8
+ constructor(options) {
9
+ super(options);
10
+ this.logger = defaultLogger.child({ prefix: `[powersync_${this.options.storage.group_id}] ` });
11
+ this.connectionFactory = options.connectionFactory;
12
+ this.cdcReplicationJobOptions = options;
13
+ }
14
+ async keepAlive() {
15
+ // TODO Might need to leverage checkpoints table as a keepAlive
16
+ }
17
+ async replicate() {
18
+ try {
19
+ await this.replicateOnce();
20
+ }
21
+ catch (e) {
22
+ // Fatal exception
23
+ if (!this.isStopped) {
24
+ // Ignore aborted errors
25
+ this.logger.error(`Replication error`, e);
26
+ if (e.cause != null) {
27
+ this.logger.error(`cause`, e.cause);
28
+ }
29
+ container.reporter.captureException(e, {
30
+ metadata: {}
31
+ });
32
+ // This sets the retry delay
33
+ this.rateLimiter.reportError(e);
34
+ }
35
+ if (e instanceof CDCDataExpiredError) {
36
+ // This stops replication and restarts with a new instance
37
+ await this.options.storage.factory.restartReplication(this.storage.group_id);
38
+ }
39
+ }
40
+ finally {
41
+ this.abortController.abort();
42
+ }
43
+ }
44
+ async replicateOnce() {
45
+ // New connections on every iteration (every error with retry),
46
+ // otherwise we risk repeating errors related to the connection,
47
+ // such as caused by cached PG schemas.
48
+ const connectionManager = this.connectionFactory.create({
49
+ idleTimeoutMillis: 30_000,
50
+ max: 2
51
+ });
52
+ try {
53
+ await this.rateLimiter?.waitUntilAllowed({ signal: this.abortController.signal });
54
+ if (this.isStopped) {
55
+ return;
56
+ }
57
+ const stream = new CDCStream({
58
+ logger: this.logger,
59
+ abortSignal: this.abortController.signal,
60
+ storage: this.options.storage,
61
+ metrics: this.options.metrics,
62
+ connections: connectionManager,
63
+ pollingOptions: this.cdcReplicationJobOptions.pollingOptions
64
+ });
65
+ this.lastStream = stream;
66
+ await stream.replicate();
67
+ }
68
+ finally {
69
+ await connectionManager.end();
70
+ }
71
+ }
72
+ async getReplicationLagMillis() {
73
+ return this.lastStream?.getReplicationLagMillis();
74
+ }
75
+ }
76
+ //# sourceMappingURL=CDCReplicationJob.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CDCReplicationJob.js","sourceRoot":"","sources":["../../src/replication/CDCReplicationJob.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAEtD,OAAO,EAAE,SAAS,EAAE,MAAM,IAAI,aAAa,EAAE,MAAM,mCAAmC,CAAC;AACvF,OAAO,EAAE,mBAAmB,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAQhE,MAAM,OAAO,iBAAkB,SAAQ,WAAW,CAAC,sBAAsB;IAC/D,iBAAiB,CAAgC;IACjD,UAAU,GAAqB,IAAI,CAAC;IACpC,wBAAwB,CAA2B;IAE3D,YAAY,OAAiC;QAC3C,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,cAAc,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC;QAC/F,IAAI,CAAC,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC;QACnD,IAAI,CAAC,wBAAwB,GAAG,OAAO,CAAC;IAC1C,CAAC;IAED,KAAK,CAAC,SAAS;QACb,+DAA+D;IACjE,CAAC;IAED,KAAK,CAAC,SAAS;QACb,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,aAAa,EAAE,CAAC;QAC7B,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,kBAAkB;YAClB,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;gBACpB,wBAAwB;gBACxB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,mBAAmB,EAAE,CAAC,CAAC,CAAC;gBAC1C,IAAI,CAAC,CAAC,KAAK,IAAI,IAAI,EAAE,CAAC;oBACpB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC;gBACtC,CAAC;gBAED,SAAS,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAC,EAAE;oBACrC,QAAQ,EAAE,EAAE;iBACb,CAAC,CAAC;gBAEH,4BAA4B;gBAC5B,IAAI,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;YAClC,CAAC;YACD,IAAI,CAAC,YAAY,mBAAmB,EAAE,CAAC;gBACrC,0DAA0D;gBAC1D,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;YAC/E,CAAC;QACH,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,CAAC;QAC/B,CAAC;IACH,CAAC;IAED,KAAK,CAAC,aAAa;QACjB,+DAA+D;QAC/D,gEAAgE;QAChE,uCAAuC;QACvC,MAAM,iBAAiB,GAAG,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC;YACtD,iBAAiB,EAAE,MAAM;YACzB,GAAG,EAAE,CAAC;SACP,CAAC,CAAC;QACH,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,WAAW,EAAE,gBAAgB,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,eAAe,CAAC,MAAM,EAAE,CAAC,CAAC;YAClF,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;gBACnB,OAAO;YACT,CAAC;YACD,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;gBAC3B,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,WAAW,EAAE,IAAI,CAAC,eAAe,CAAC,MAAM;gBACxC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO;gBAC7B,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO;gBAC7B,WAAW,EAAE,iBAAiB;gBAC9B,cAAc,EAAE,IAAI,CAAC,wBAAwB,CAAC,cAAc;aAC7D,CAAC,CAAC;YACH,IAAI,CAAC,UAAU,GAAG,MAAM,CAAC;YACzB,MAAM,MAAM,CAAC,SAAS,EAAE,CAAC;QAC3B,CAAC;gBAAS,CAAC;YACT,MAAM,iBAAiB,CAAC,GAAG,EAAE,CAAC;QAChC,CAAC;IACH,CAAC;IAED,KAAK,CAAC,uBAAuB;QAC3B,OAAO,IAAI,CAAC,UAAU,EAAE,uBAAuB,EAAE,CAAC;IACpD,CAAC;CACF"}
@@ -0,0 +1,18 @@
1
+ import { replication, storage } from '@powersync/service-core';
2
+ import { MSSQLConnectionManagerFactory } from './MSSQLConnectionManagerFactory.js';
3
+ import { CDCReplicationJob } from './CDCReplicationJob.js';
4
+ import { CDCPollingOptions } from '../types/types.js';
5
+ export interface CDCReplicatorOptions extends replication.AbstractReplicatorOptions {
6
+ connectionFactory: MSSQLConnectionManagerFactory;
7
+ pollingOptions: CDCPollingOptions;
8
+ }
9
+ export declare class CDCReplicator extends replication.AbstractReplicator<CDCReplicationJob> {
10
+ private readonly connectionFactory;
11
+ private readonly cdcReplicatorOptions;
12
+ constructor(options: CDCReplicatorOptions);
13
+ createJob(options: replication.CreateJobOptions): CDCReplicationJob;
14
+ cleanUp(syncRulesStorage: storage.SyncRulesBucketStorage): Promise<void>;
15
+ stop(): Promise<void>;
16
+ testConnection(): Promise<replication.ConnectionTestResult>;
17
+ getReplicationLagMillis(): Promise<number | undefined>;
18
+ }