@axinom/mosaic-db-common 0.26.0 → 0.27.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/dist/replication/ensure-replication-slot-and-publication-exist.d.ts +1 -1
- package/dist/replication/ensure-replication-slot-and-publication-exist.d.ts.map +1 -1
- package/dist/replication/ensure-replication-slot-and-publication-exist.js +8 -2
- package/dist/replication/ensure-replication-slot-and-publication-exist.js.map +1 -1
- package/package.json +2 -2
- package/src/replication/ensure-replication-slot-and-publication-exist.spec.ts +173 -0
- package/src/replication/ensure-replication-slot-and-publication-exist.ts +19 -3
|
@@ -52,7 +52,7 @@ export interface EnsureReplicationSlotAndPublicationExistParams {
|
|
|
52
52
|
}
|
|
53
53
|
/**
|
|
54
54
|
* Checks if specified replication slot and publication exist. If at least one
|
|
55
|
-
* does not exists - idempotently (re)creates both.
|
|
55
|
+
* does not exists, or tables have changed - idempotently (re)creates both.
|
|
56
56
|
*
|
|
57
57
|
* It is preferable to run this helper on startup instead of defining the
|
|
58
58
|
* replication slot and publication in context of migrations, because in cases
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ensure-replication-slot-and-publication-exist.d.ts","sourceRoot":"","sources":["../../src/replication/ensure-replication-slot-and-publication-exist.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC;AAE1B,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EAAE,2BAA2B,EAAE,MAAM,sCAAsC,CAAC;AAEnF;;GAEG;AACH,MAAM,WAAW,8CAA8C;IAC7D;;;;;;OAMG;IACH,mBAAmB,EAAE,MAAM,CAAC;IAC5B;;;OAGG;IACH,eAAe,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,iBAAiB,EAAE,IAAI,CAAC;IACxB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,2BAA2B,EAAE,CAAC;IAClD;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,MAAM,CAAC,EAAE,QAAQ,CAAC;CACnB;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,wCAAwC,gIASlD,8CAA8C,KAAG,QAAQ,IAAI,
|
|
1
|
+
{"version":3,"file":"ensure-replication-slot-and-publication-exist.d.ts","sourceRoot":"","sources":["../../src/replication/ensure-replication-slot-and-publication-exist.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC;AAE1B,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EAAE,2BAA2B,EAAE,MAAM,sCAAsC,CAAC;AAEnF;;GAEG;AACH,MAAM,WAAW,8CAA8C;IAC7D;;;;;;OAMG;IACH,mBAAmB,EAAE,MAAM,CAAC;IAC5B;;;OAGG;IACH,eAAe,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,iBAAiB,EAAE,IAAI,CAAC;IACxB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,2BAA2B,EAAE,CAAC;IAClD;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,MAAM,CAAC,EAAE,QAAQ,CAAC;CACnB;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,wCAAwC,gIASlD,8CAA8C,KAAG,QAAQ,IAAI,CAsE/D,CAAC"}
|
|
@@ -4,7 +4,7 @@ exports.ensureReplicationSlotAndPublicationExist = void 0;
|
|
|
4
4
|
const db_1 = require("zapatos/db");
|
|
5
5
|
/**
|
|
6
6
|
* Checks if specified replication slot and publication exist. If at least one
|
|
7
|
-
* does not exists - idempotently (re)creates both.
|
|
7
|
+
* does not exists, or tables have changed - idempotently (re)creates both.
|
|
8
8
|
*
|
|
9
9
|
* It is preferable to run this helper on startup instead of defining the
|
|
10
10
|
* replication slot and publication in context of migrations, because in cases
|
|
@@ -21,7 +21,13 @@ const ensureReplicationSlotAndPublicationExist = async ({ replicationSlotName, p
|
|
|
21
21
|
AND slot_name = ${slotName}) AS "slotExists",
|
|
22
22
|
EXISTS (SELECT FROM pg_publication
|
|
23
23
|
WHERE pubname = ${pubName}) AS "pubExists";`.run(replicationPgPool);
|
|
24
|
-
|
|
24
|
+
const pubTables = await (0, db_1.sql) `SELECT schemaname, tablename FROM pg_publication_tables
|
|
25
|
+
WHERE pubname = ${pubName}`.run(replicationPgPool);
|
|
26
|
+
const assignedTables = new Set(pubTables.map((t) => `${t.schemaname}.${t.tablename}`));
|
|
27
|
+
const expectedTables = new Set(tableNames.map((name) => `${schemaName}.${name}`));
|
|
28
|
+
const tablesRemainTheSame = assignedTables.size === expectedTables.size &&
|
|
29
|
+
[...assignedTables].every((assignedTable) => expectedTables.has(assignedTable));
|
|
30
|
+
if (slotExists && pubExists && tablesRemainTheSame) {
|
|
25
31
|
(logger !== null && logger !== void 0 ? logger : console).log(`The replication slot "${replicationSlotName}" and publication "${publicationName}" already exist.`);
|
|
26
32
|
return;
|
|
27
33
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ensure-replication-slot-and-publication-exist.js","sourceRoot":"","sources":["../../src/replication/ensure-replication-slot-and-publication-exist.ts"],"names":[],"mappings":";;;AACA,
|
|
1
|
+
{"version":3,"file":"ensure-replication-slot-and-publication-exist.js","sourceRoot":"","sources":["../../src/replication/ensure-replication-slot-and-publication-exist.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.
|
|
3
|
+
"version": "0.27.0",
|
|
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": "
|
|
55
|
+
"gitHead": "7eb762e7cec90f882c2fe11f5c294c44fc2c3bc3"
|
|
56
56
|
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { stub } from 'jest-auto-stub';
|
|
2
|
+
import { Pool } from 'pg';
|
|
3
|
+
import { SQLFragment } from 'zapatos/db';
|
|
4
|
+
import { ensureReplicationSlotAndPublicationExist } from './ensure-replication-slot-and-publication-exist';
|
|
5
|
+
|
|
6
|
+
const slotName = 'test_slot_name';
|
|
7
|
+
const pubName = 'test_pub_name';
|
|
8
|
+
let existsResult: () => unknown = () => undefined;
|
|
9
|
+
let tablesResult: () => unknown = () => undefined;
|
|
10
|
+
jest.mock('zapatos/db', () => {
|
|
11
|
+
return {
|
|
12
|
+
...jest.requireActual('zapatos/db'),
|
|
13
|
+
sql: jest.fn().mockImplementation((_query, firstParam) => {
|
|
14
|
+
return stub<SQLFragment>({
|
|
15
|
+
run: jest.fn().mockImplementation(() => {
|
|
16
|
+
if (firstParam.value === slotName) {
|
|
17
|
+
return existsResult();
|
|
18
|
+
}
|
|
19
|
+
if (firstParam.value === pubName) {
|
|
20
|
+
return tablesResult();
|
|
21
|
+
}
|
|
22
|
+
}),
|
|
23
|
+
});
|
|
24
|
+
}),
|
|
25
|
+
transaction: jest.fn().mockImplementation(() => {
|
|
26
|
+
return;
|
|
27
|
+
}),
|
|
28
|
+
};
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
describe('ensureReplicationSlotAndPublicationExist', () => {
|
|
32
|
+
let consoleOverride: jest.SpyInstance;
|
|
33
|
+
beforeEach(() => {
|
|
34
|
+
consoleOverride = jest
|
|
35
|
+
.spyOn(console, 'log')
|
|
36
|
+
.mockImplementation((log) => log);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
afterEach(() => {
|
|
40
|
+
existsResult = () => undefined;
|
|
41
|
+
tablesResult = () => undefined;
|
|
42
|
+
jest.restoreAllMocks();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it.each([
|
|
46
|
+
[true, false],
|
|
47
|
+
[false, true],
|
|
48
|
+
[false, false],
|
|
49
|
+
])(
|
|
50
|
+
'call when slot and/or publication does not exist -> pass, %p, %p',
|
|
51
|
+
async (slotExists, pubExists) => {
|
|
52
|
+
// Arrange
|
|
53
|
+
existsResult = () => [{ slotExists, pubExists }];
|
|
54
|
+
tablesResult = () =>
|
|
55
|
+
pubExists
|
|
56
|
+
? [
|
|
57
|
+
{ schemaname: 'app_public', tablename: 'table_one' },
|
|
58
|
+
{ schemaname: 'app_public', tablename: 'table_two' },
|
|
59
|
+
{ schemaname: 'app_public', tablename: 'table_three' },
|
|
60
|
+
]
|
|
61
|
+
: [];
|
|
62
|
+
|
|
63
|
+
// Act
|
|
64
|
+
await ensureReplicationSlotAndPublicationExist({
|
|
65
|
+
replicationPgPool: stub<Pool>(),
|
|
66
|
+
replicationSlotName: slotName,
|
|
67
|
+
publicationName: pubName,
|
|
68
|
+
tableNames: ['table_one', 'table_two', 'table_three'],
|
|
69
|
+
schemaName: 'app_public',
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// Assert
|
|
73
|
+
expect(consoleOverride.mock.results[0].value).toBe(
|
|
74
|
+
'The replication slot "test_slot_name" and publication "test_pub_name" successfully (re)created.',
|
|
75
|
+
);
|
|
76
|
+
},
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
it('call when slot and publication exist and new table is added -> pass', async () => {
|
|
80
|
+
// Arrange
|
|
81
|
+
existsResult = () => [{ slotExists: true, pubExists: true }];
|
|
82
|
+
tablesResult = () => [
|
|
83
|
+
{ schemaname: 'app_public', tablename: 'table_one' },
|
|
84
|
+
{ schemaname: 'app_public', tablename: 'table_two' },
|
|
85
|
+
];
|
|
86
|
+
|
|
87
|
+
// Act
|
|
88
|
+
await ensureReplicationSlotAndPublicationExist({
|
|
89
|
+
replicationPgPool: stub<Pool>(),
|
|
90
|
+
replicationSlotName: slotName,
|
|
91
|
+
publicationName: pubName,
|
|
92
|
+
tableNames: ['table_one', 'table_two', 'table_three'],
|
|
93
|
+
schemaName: 'app_public',
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// Assert
|
|
97
|
+
expect(consoleOverride.mock.results[0].value).toBe(
|
|
98
|
+
'The replication slot "test_slot_name" and publication "test_pub_name" successfully (re)created.',
|
|
99
|
+
);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('call when slot and publication exist and old table is removed -> pass', async () => {
|
|
103
|
+
// Arrange
|
|
104
|
+
existsResult = () => [{ slotExists: true, pubExists: true }];
|
|
105
|
+
tablesResult = () => [
|
|
106
|
+
{ schemaname: 'app_public', tablename: 'table_one' },
|
|
107
|
+
{ schemaname: 'app_public', tablename: 'table_two' },
|
|
108
|
+
{ schemaname: 'app_public', tablename: 'table_three' },
|
|
109
|
+
];
|
|
110
|
+
|
|
111
|
+
// Act
|
|
112
|
+
await ensureReplicationSlotAndPublicationExist({
|
|
113
|
+
replicationPgPool: stub<Pool>(),
|
|
114
|
+
replicationSlotName: slotName,
|
|
115
|
+
publicationName: pubName,
|
|
116
|
+
tableNames: ['table_one', 'table_two'],
|
|
117
|
+
schemaName: 'app_public',
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
// Assert
|
|
121
|
+
expect(consoleOverride.mock.results[0].value).toBe(
|
|
122
|
+
'The replication slot "test_slot_name" and publication "test_pub_name" successfully (re)created.',
|
|
123
|
+
);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('call when slot and publication exist and one table is replaced by another -> pass', async () => {
|
|
127
|
+
// Arrange
|
|
128
|
+
existsResult = () => [{ slotExists: true, pubExists: true }];
|
|
129
|
+
tablesResult = () => [
|
|
130
|
+
{ schemaname: 'app_public', tablename: 'table_one' },
|
|
131
|
+
{ schemaname: 'app_public', tablename: 'table_two' },
|
|
132
|
+
{ schemaname: 'app_public', tablename: 'table_three' },
|
|
133
|
+
];
|
|
134
|
+
|
|
135
|
+
// Act
|
|
136
|
+
await ensureReplicationSlotAndPublicationExist({
|
|
137
|
+
replicationPgPool: stub<Pool>(),
|
|
138
|
+
replicationSlotName: slotName,
|
|
139
|
+
publicationName: pubName,
|
|
140
|
+
tableNames: ['table_one', 'table_two', 'table_four'],
|
|
141
|
+
schemaName: 'app_public',
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// Assert
|
|
145
|
+
expect(consoleOverride.mock.results[0].value).toBe(
|
|
146
|
+
'The replication slot "test_slot_name" and publication "test_pub_name" successfully (re)created.',
|
|
147
|
+
);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('call when slot and publication exist and tables are in sync -> skip', async () => {
|
|
151
|
+
// Arrange
|
|
152
|
+
existsResult = () => [{ slotExists: true, pubExists: true }];
|
|
153
|
+
tablesResult = () => [
|
|
154
|
+
{ schemaname: 'app_public', tablename: 'table_one' },
|
|
155
|
+
{ schemaname: 'app_public', tablename: 'table_two' },
|
|
156
|
+
{ schemaname: 'app_public', tablename: 'table_three' },
|
|
157
|
+
];
|
|
158
|
+
|
|
159
|
+
// Act
|
|
160
|
+
await ensureReplicationSlotAndPublicationExist({
|
|
161
|
+
replicationPgPool: stub<Pool>(),
|
|
162
|
+
replicationSlotName: slotName,
|
|
163
|
+
publicationName: pubName,
|
|
164
|
+
tableNames: ['table_one', 'table_two', 'table_three'],
|
|
165
|
+
schemaName: 'app_public',
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// Assert
|
|
169
|
+
expect(consoleOverride.mock.results[0].value).toBe(
|
|
170
|
+
'The replication slot "test_slot_name" and publication "test_pub_name" already exist.',
|
|
171
|
+
);
|
|
172
|
+
});
|
|
173
|
+
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Pool } from 'pg';
|
|
2
|
-
import { IsolationLevel, param, sql, transaction } from 'zapatos/db';
|
|
2
|
+
import { IsolationLevel, SQL, param, sql, transaction } from 'zapatos/db';
|
|
3
3
|
import { DbLogger } from '../common';
|
|
4
4
|
import { LogicalReplicationOperation } from './create-logical-replication-service';
|
|
5
5
|
|
|
@@ -55,7 +55,7 @@ export interface EnsureReplicationSlotAndPublicationExistParams {
|
|
|
55
55
|
|
|
56
56
|
/**
|
|
57
57
|
* Checks if specified replication slot and publication exist. If at least one
|
|
58
|
-
* does not exists - idempotently (re)creates both.
|
|
58
|
+
* does not exists, or tables have changed - idempotently (re)creates both.
|
|
59
59
|
*
|
|
60
60
|
* It is preferable to run this helper on startup instead of defining the
|
|
61
61
|
* replication slot and publication in context of migrations, because in cases
|
|
@@ -83,7 +83,23 @@ export const ensureReplicationSlotAndPublicationExist = async ({
|
|
|
83
83
|
WHERE pubname = ${pubName}) AS "pubExists";`.run(
|
|
84
84
|
replicationPgPool,
|
|
85
85
|
);
|
|
86
|
-
|
|
86
|
+
const pubTables = await sql<
|
|
87
|
+
SQL,
|
|
88
|
+
{ schemaname: string; tablename: string }[]
|
|
89
|
+
>`SELECT schemaname, tablename FROM pg_publication_tables
|
|
90
|
+
WHERE pubname = ${pubName}`.run(replicationPgPool);
|
|
91
|
+
const assignedTables = new Set(
|
|
92
|
+
pubTables.map((t) => `${t.schemaname}.${t.tablename}`),
|
|
93
|
+
);
|
|
94
|
+
const expectedTables = new Set(
|
|
95
|
+
tableNames.map((name) => `${schemaName}.${name}`),
|
|
96
|
+
);
|
|
97
|
+
const tablesRemainTheSame =
|
|
98
|
+
assignedTables.size === expectedTables.size &&
|
|
99
|
+
[...assignedTables].every((assignedTable) =>
|
|
100
|
+
expectedTables.has(assignedTable),
|
|
101
|
+
);
|
|
102
|
+
if (slotExists && pubExists && tablesRemainTheSame) {
|
|
87
103
|
(logger ?? console).log(
|
|
88
104
|
`The replication slot "${replicationSlotName}" and publication "${publicationName}" already exist.`,
|
|
89
105
|
);
|