@axinom/mosaic-db-common 0.25.1-rc.8 → 0.25.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.
@@ -43,6 +43,17 @@ export interface LogicalReplicationServiceConfig {
43
43
  * passed, console logs are used instead.
44
44
  */
45
45
  logger?: DbLogger;
46
+ /**
47
+ * When logical replication service establishes a connection with a
48
+ * replication slot - it can already be in use, e.g. by another instance of
49
+ * the service. The logical replication service will then try to reconnect
50
+ * once every 10 seconds for a total duration of 5 minutes.
51
+ *
52
+ * After the first 5 minutes, the delay of reconnection attempts will be
53
+ * increased from 10 seconds to the value of this parameter. Default value is
54
+ * 300000 (every 5 minutes).
55
+ */
56
+ reconnectionIntervalInMs?: number;
46
57
  }
47
58
  /**
48
59
  * Creates a logical replication service and keeps it running until stopped.
@@ -62,7 +73,7 @@ export interface LogicalReplicationServiceConfig {
62
73
  * service.
63
74
  * @returns a function to stop the logical replication service.
64
75
  */
65
- export declare const createLogicalReplicationService: ({ connectionString, publicationNames, replicationSlotName, messageHandler, operationsToWatch, logger: passedLogger, }: LogicalReplicationServiceConfig) => Promise<{
76
+ export declare const createLogicalReplicationService: ({ connectionString, publicationNames, replicationSlotName, messageHandler, operationsToWatch, logger: passedLogger, reconnectionIntervalInMs, }: LogicalReplicationServiceConfig) => Promise<{
66
77
  (): Promise<void>;
67
78
  }>;
68
79
  //# sourceMappingURL=create-logical-replication-service.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"create-logical-replication-service.d.ts","sourceRoot":"","sources":["../../src/replication/create-logical-replication-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,QAAQ,EAET,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAKrC,MAAM,MAAM,2BAA2B,GACnC,OAAO,GACP,QAAQ,GACR,QAAQ,GACR,QAAQ,GACR,SAAS,GACT,QAAQ,GACR,UAAU,GACV,UAAU,GACV,MAAM,GACN,QAAQ,CAAC;AAEb,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,2BAA2B,CAAC;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAE1B,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC3B;AAED,MAAM,WAAW,qCAAqC;IACpD,aAAa,EAAE,qBAAqB,CAAC;IACrC,WAAW,EAAE,QAAQ,CAAC,OAAO,CAAC;CAC/B;AAED,MAAM,MAAM,gCAAgC,GAAG,CAC7C,MAAM,EAAE,qCAAqC,KAC1C,OAAO,CAAC,IAAI,CAAC,CAAC;AAEnB;;GAEG;AACH,MAAM,WAAW,+BAA+B;IAI9C,gBAAgB,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B;;;OAGG;IACH,mBAAmB,EAAE,MAAM,CAAC;IAE5B;;OAEG;IACH,cAAc,EAAE,gCAAgC,CAAC;IAEjD;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,2BAA2B,EAAE,CAAC;IAClD;;;OAGG;IACH,MAAM,CAAC,EAAE,QAAQ,CAAC;CACnB;AAmBD;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,+BAA+B,0HAOzC,+BAA+B,KAAG,QAAQ;IAAE,IAAI,QAAQ,IAAI,CAAC,CAAA;CAAE,CA2GjE,CAAC"}
1
+ {"version":3,"file":"create-logical-replication-service.d.ts","sourceRoot":"","sources":["../../src/replication/create-logical-replication-service.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,QAAQ,EAET,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAKrC,MAAM,MAAM,2BAA2B,GACnC,OAAO,GACP,QAAQ,GACR,QAAQ,GACR,QAAQ,GACR,SAAS,GACT,QAAQ,GACR,UAAU,GACV,UAAU,GACV,MAAM,GACN,QAAQ,CAAC;AAEb,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,2BAA2B,CAAC;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAE1B,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC3B;AAED,MAAM,WAAW,qCAAqC;IACpD,aAAa,EAAE,qBAAqB,CAAC;IACrC,WAAW,EAAE,QAAQ,CAAC,OAAO,CAAC;CAC/B;AAED,MAAM,MAAM,gCAAgC,GAAG,CAC7C,MAAM,EAAE,qCAAqC,KAC1C,OAAO,CAAC,IAAI,CAAC,CAAC;AAEnB;;GAEG;AACH,MAAM,WAAW,+BAA+B;IAI9C,gBAAgB,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B;;;OAGG;IACH,mBAAmB,EAAE,MAAM,CAAC;IAE5B;;OAEG;IACH,cAAc,EAAE,gCAAgC,CAAC;IAEjD;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,2BAA2B,EAAE,CAAC;IAClD;;;OAGG;IACH,MAAM,CAAC,EAAE,QAAQ,CAAC;IAClB;;;;;;;;;OASG;IACH,wBAAwB,CAAC,EAAE,MAAM,CAAC;CACnC;AAoED;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,+BAA+B,oJAQzC,+BAA+B,KAAG,QAAQ;IAAE,IAAI,QAAQ,IAAI,CAAC,CAAA;CAAE,CAoHjE,CAAC"}
@@ -15,6 +15,33 @@ const getScopedMessage = (message, operationsToWatch) => {
15
15
  old: 'old' in message && message.old ? message.old : undefined,
16
16
  };
17
17
  };
18
+ const handleReconnection = async (error, reconnectionAttempts, replicationSlotName, reconnectionIntervalInMs, logger) => {
19
+ if ((error === null || error === void 0 ? void 0 : error.code) === '55006' && (error === null || error === void 0 ? void 0 : error.routine) === 'ReplicationSlotAcquire') {
20
+ const isFirstFiveMinutesAfterStartup = reconnectionAttempts < 30;
21
+ const log = isFirstFiveMinutesAfterStartup
22
+ ? logger.trace.bind(logger)
23
+ : logger.log.bind(logger);
24
+ const waitingTimeInMs = isFirstFiveMinutesAfterStartup
25
+ ? 10000
26
+ : reconnectionIntervalInMs;
27
+ log(`The logical replication slot '${replicationSlotName}' is already in use. Waiting for ${waitingTimeInMs / 1000} seconds to try connecting again. (Attempt ${reconnectionAttempts + 1})`);
28
+ await sleep(waitingTimeInMs);
29
+ return true;
30
+ }
31
+ return false;
32
+ };
33
+ const handleFailure = async (error, failedAttempts, failedAttemptsResetTimer, logger) => {
34
+ if (failedAttempts > 20) {
35
+ throw error;
36
+ }
37
+ logger.error(error, `Logical replication service failure has occurred. (Attempt ${failedAttempts})`);
38
+ await sleep(1500 * failedAttempts);
39
+ clearTimeout(failedAttemptsResetTimer);
40
+ return setTimeout(async () => {
41
+ logger.trace(`No errors have occurred within 5 minutes. Resetting failures counter after ${failedAttempts} failure(s).`);
42
+ failedAttempts = 0;
43
+ }, 300000);
44
+ };
18
45
  /**
19
46
  * Creates a logical replication service and keeps it running until stopped.
20
47
  * Watches the table changes based on passed publication names and replication
@@ -33,7 +60,7 @@ const getScopedMessage = (message, operationsToWatch) => {
33
60
  * service.
34
61
  * @returns a function to stop the logical replication service.
35
62
  */
36
- const createLogicalReplicationService = async ({ connectionString, publicationNames, replicationSlotName, messageHandler, operationsToWatch = ['insert', 'update', 'delete'], logger: passedLogger, }) => {
63
+ const createLogicalReplicationService = async ({ connectionString, publicationNames, replicationSlotName, messageHandler, operationsToWatch = ['insert', 'update', 'delete'], logger: passedLogger, reconnectionIntervalInMs = 300000, }) => {
37
64
  if (operationsToWatch.length === 0) {
38
65
  throw new Error('Unable to start the logical replication service when operationsToWatch is an empty array.');
39
66
  }
@@ -42,6 +69,7 @@ const createLogicalReplicationService = async ({ connectionString, publicationNa
42
69
  let service;
43
70
  let stopped = false;
44
71
  let failedAttempts = 0;
72
+ let reconnectionAttempts = 0;
45
73
  let failedAttemptsResetTimer = undefined;
46
74
  // Run the service in an endless background loop until it gets stopped
47
75
  (async () => {
@@ -50,6 +78,9 @@ const createLogicalReplicationService = async ({ connectionString, publicationNa
50
78
  await new Promise((resolve, reject) => {
51
79
  let heartbeatAckTimer = undefined;
52
80
  service = new pg_logical_replication_1.LogicalReplicationService({ connectionString }, { acknowledge: { auto: false, timeoutSeconds: 0 } });
81
+ service.on('start', async () => {
82
+ logger.debug(`Started the logical replication service to watch the database operations (${operationsToWatch.join(', ')}) on table(s) associated with the following publication(s): ${publicationNames.join(', ')}`);
83
+ });
53
84
  service.on('data', async (lsn, message) => {
54
85
  try {
55
86
  if (service.isStop()) {
@@ -95,21 +126,16 @@ const createLogicalReplicationService = async ({ connectionString, publicationNa
95
126
  });
96
127
  }
97
128
  catch (err) {
98
- failedAttempts++;
99
- if (failedAttempts > 20) {
100
- throw err;
129
+ const error = err;
130
+ if (await handleReconnection(error, reconnectionAttempts, replicationSlotName, reconnectionIntervalInMs, logger)) {
131
+ reconnectionAttempts++;
132
+ continue;
101
133
  }
102
- logger.error(err, `Logical replication service failure has occurred. (Attempt ${failedAttempts})`);
103
- await sleep(1500 * failedAttempts);
104
- clearTimeout(failedAttemptsResetTimer);
105
- failedAttemptsResetTimer = setTimeout(async () => {
106
- logger.trace(`No errors have occurred within 5 minutes. Resetting failures counter after ${failedAttempts} failure(s).`);
107
- failedAttempts = 0;
108
- }, 300000);
134
+ failedAttempts++;
135
+ failedAttemptsResetTimer = await handleFailure(error, failedAttempts, failedAttemptsResetTimer, logger);
109
136
  }
110
137
  }
111
138
  })();
112
- logger.debug(`Started the logical replication service to watch the database operations (${operationsToWatch.join(', ')}) on table(s) associated with the following publication(s): ${publicationNames.join(', ')}`);
113
139
  return async () => {
114
140
  logger.debug('Shutting down the logical replication service.');
115
141
  stopped = true;
@@ -1 +1 @@
1
- {"version":3,"file":"create-logical-replication-service.js","sourceRoot":"","sources":["../../src/replication/create-logical-replication-service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"create-logical-replication-service.js","sourceRoot":"","sources":["../../src/replication/create-logical-replication-service.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axinom/mosaic-db-common",
3
- "version": "0.25.1-rc.8",
3
+ "version": "0.25.1",
4
4
  "description": "This library encapsulates database-related functionality to develop Mosaic based services.",
5
5
  "author": "Axinom",
6
6
  "license": "PROPRIETARY",
@@ -52,5 +52,5 @@
52
52
  "publishConfig": {
53
53
  "access": "public"
54
54
  },
55
- "gitHead": "2fe8eef9a9c327e0f80e31b2d4c40f879b76c044"
55
+ "gitHead": "4e4a3aa0d5a0efa7dc2da0e52fa5610d2a771498"
56
56
  }
@@ -1,3 +1,4 @@
1
+ import { DatabaseError } from 'pg';
1
2
  import {
2
3
  LogicalReplicationService,
3
4
  Pgoutput,
@@ -74,6 +75,17 @@ export interface LogicalReplicationServiceConfig {
74
75
  * passed, console logs are used instead.
75
76
  */
76
77
  logger?: DbLogger;
78
+ /**
79
+ * When logical replication service establishes a connection with a
80
+ * replication slot - it can already be in use, e.g. by another instance of
81
+ * the service. The logical replication service will then try to reconnect
82
+ * once every 10 seconds for a total duration of 5 minutes.
83
+ *
84
+ * After the first 5 minutes, the delay of reconnection attempts will be
85
+ * increased from 10 seconds to the value of this parameter. Default value is
86
+ * 300000 (every 5 minutes).
87
+ */
88
+ reconnectionIntervalInMs?: number;
77
89
  }
78
90
 
79
91
  const getScopedMessage = (
@@ -93,6 +105,55 @@ const getScopedMessage = (
93
105
  };
94
106
  };
95
107
 
108
+ const handleReconnection = async (
109
+ error: DatabaseError,
110
+ reconnectionAttempts: number,
111
+ replicationSlotName: string,
112
+ reconnectionIntervalInMs: number,
113
+ logger: DbLogger,
114
+ ): Promise<boolean> => {
115
+ if (error?.code === '55006' && error?.routine === 'ReplicationSlotAcquire') {
116
+ const isFirstFiveMinutesAfterStartup = reconnectionAttempts < 30;
117
+ const log = isFirstFiveMinutesAfterStartup
118
+ ? logger.trace.bind(logger)
119
+ : logger.log.bind(logger);
120
+ const waitingTimeInMs = isFirstFiveMinutesAfterStartup
121
+ ? 10000
122
+ : reconnectionIntervalInMs;
123
+ log(
124
+ `The logical replication slot '${replicationSlotName}' is already in use. Waiting for ${
125
+ waitingTimeInMs / 1000
126
+ } seconds to try connecting again. (Attempt ${reconnectionAttempts + 1})`,
127
+ );
128
+ await sleep(waitingTimeInMs);
129
+ return true;
130
+ }
131
+ return false;
132
+ };
133
+
134
+ const handleFailure = async (
135
+ error: Error,
136
+ failedAttempts: number,
137
+ failedAttemptsResetTimer: NodeJS.Timeout | undefined,
138
+ logger: DbLogger,
139
+ ): Promise<NodeJS.Timeout | undefined> => {
140
+ if (failedAttempts > 20) {
141
+ throw error;
142
+ }
143
+ logger.error(
144
+ error,
145
+ `Logical replication service failure has occurred. (Attempt ${failedAttempts})`,
146
+ );
147
+ await sleep(1500 * failedAttempts);
148
+ clearTimeout(failedAttemptsResetTimer);
149
+ return setTimeout(async () => {
150
+ logger.trace(
151
+ `No errors have occurred within 5 minutes. Resetting failures counter after ${failedAttempts} failure(s).`,
152
+ );
153
+ failedAttempts = 0;
154
+ }, 300000);
155
+ };
156
+
96
157
  /**
97
158
  * Creates a logical replication service and keeps it running until stopped.
98
159
  * Watches the table changes based on passed publication names and replication
@@ -118,6 +179,7 @@ export const createLogicalReplicationService = async ({
118
179
  messageHandler,
119
180
  operationsToWatch = ['insert', 'update', 'delete'],
120
181
  logger: passedLogger,
182
+ reconnectionIntervalInMs = 300000,
121
183
  }: LogicalReplicationServiceConfig): Promise<{ (): Promise<void> }> => {
122
184
  if (operationsToWatch.length === 0) {
123
185
  throw new Error(
@@ -130,6 +192,7 @@ export const createLogicalReplicationService = async ({
130
192
  let service: LogicalReplicationService;
131
193
  let stopped = false;
132
194
  let failedAttempts = 0;
195
+ let reconnectionAttempts = 0;
133
196
  let failedAttemptsResetTimer: NodeJS.Timeout | undefined = undefined;
134
197
  // Run the service in an endless background loop until it gets stopped
135
198
  (async () => {
@@ -141,6 +204,15 @@ export const createLogicalReplicationService = async ({
141
204
  { connectionString },
142
205
  { acknowledge: { auto: false, timeoutSeconds: 0 } },
143
206
  );
207
+ service.on('start', async () => {
208
+ logger.debug(
209
+ `Started the logical replication service to watch the database operations (${operationsToWatch.join(
210
+ ', ',
211
+ )}) on table(s) associated with the following publication(s): ${publicationNames.join(
212
+ ', ',
213
+ )}`,
214
+ );
215
+ });
144
216
  service.on('data', async (lsn: string, message: Pgoutput.Message) => {
145
217
  try {
146
218
  if (service.isStop()) {
@@ -189,32 +261,31 @@ export const createLogicalReplicationService = async ({
189
261
  });
190
262
  });
191
263
  } catch (err) {
192
- failedAttempts++;
193
- if (failedAttempts > 20) {
194
- throw err;
264
+ const error = err as DatabaseError;
265
+
266
+ if (
267
+ await handleReconnection(
268
+ error,
269
+ reconnectionAttempts,
270
+ replicationSlotName,
271
+ reconnectionIntervalInMs,
272
+ logger,
273
+ )
274
+ ) {
275
+ reconnectionAttempts++;
276
+ continue;
195
277
  }
196
- logger.error(
197
- err as Error,
198
- `Logical replication service failure has occurred. (Attempt ${failedAttempts})`,
278
+
279
+ failedAttempts++;
280
+ failedAttemptsResetTimer = await handleFailure(
281
+ error,
282
+ failedAttempts,
283
+ failedAttemptsResetTimer,
284
+ logger,
199
285
  );
200
- await sleep(1500 * failedAttempts);
201
- clearTimeout(failedAttemptsResetTimer);
202
- failedAttemptsResetTimer = setTimeout(async () => {
203
- logger.trace(
204
- `No errors have occurred within 5 minutes. Resetting failures counter after ${failedAttempts} failure(s).`,
205
- );
206
- failedAttempts = 0;
207
- }, 300000);
208
286
  }
209
287
  }
210
288
  })();
211
- logger.debug(
212
- `Started the logical replication service to watch the database operations (${operationsToWatch.join(
213
- ', ',
214
- )}) on table(s) associated with the following publication(s): ${publicationNames.join(
215
- ', ',
216
- )}`,
217
- );
218
289
  return async () => {
219
290
  logger.debug('Shutting down the logical replication service.');
220
291
  stopped = true;