@proteinjs/db-driver-spanner 1.20.1 → 1.20.2
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
CHANGED
|
@@ -3,6 +3,17 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [1.20.2](https://github.com/proteinjs/db/compare/@proteinjs/db-driver-spanner@1.20.1...@proteinjs/db-driver-spanner@1.20.2) (2026-08-15)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* schema reconcile logs the concurrent-ALREADY_EXISTS tolerance loudly (WARN) ([8e95c92](https://github.com/proteinjs/db/commit/8e95c92c3757bd577048a269e28a072a2e045208))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
6
17
|
## [1.20.1](https://github.com/proteinjs/db/compare/@proteinjs/db-driver-spanner@1.20.0...@proteinjs/db-driver-spanner@1.20.1) (2026-08-15)
|
|
7
18
|
|
|
8
19
|
|
|
@@ -143,6 +143,15 @@ var internals = function (tableManager) { return tableManager; };
|
|
|
143
143
|
var classifier = function (tableManager) {
|
|
144
144
|
return tableManager.schemaOperations;
|
|
145
145
|
};
|
|
146
|
+
/**
|
|
147
|
+
* Spy on THIS TableManager instance's own logger.warn. TableManager (@proteinjs/db) and this test
|
|
148
|
+
* (@proteinjs/db-driver-spanner) resolve separate physical @proteinjs/logger copies, so a
|
|
149
|
+
* Logger.prototype spy here would never intercept the reconcile's warns — spying the instance's
|
|
150
|
+
* method does, regardless of module identity.
|
|
151
|
+
*/
|
|
152
|
+
var spyOnReconcileWarn = function (tableManager) {
|
|
153
|
+
return jest.spyOn(tableManager.logger, 'warn');
|
|
154
|
+
};
|
|
146
155
|
describe('Concurrent schema reconcile', function () {
|
|
147
156
|
var dropTable = (0, getDropTestTable_1.getDropTestTable)(spannerDriver);
|
|
148
157
|
var tableManager = spannerDriver.getTableManager();
|
|
@@ -187,7 +196,7 @@ describe('Concurrent schema reconcile', function () {
|
|
|
187
196
|
jest.restoreAllMocks();
|
|
188
197
|
});
|
|
189
198
|
test('two actors adding the SAME new column both succeed; the column lands once with the intended type', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
190
|
-
var loserTm, realGetColumnMetadata, columnMetadata, runUpdateSchemaSpy;
|
|
199
|
+
var loserTm, realGetColumnMetadata, loserWarnSpy, toleratedWarn, columnMetadata, cleanWarnSpy, runUpdateSchemaSpy;
|
|
191
200
|
return __generator(this, function (_a) {
|
|
192
201
|
switch (_a.label) {
|
|
193
202
|
case 0:
|
|
@@ -213,6 +222,7 @@ describe('Concurrent schema reconcile', function () {
|
|
|
213
222
|
}
|
|
214
223
|
});
|
|
215
224
|
}); });
|
|
225
|
+
loserWarnSpy = spyOnReconcileWarn(loserTm);
|
|
216
226
|
// OUTCOME: the loser's Db.init-equivalent does NOT reject — the duplicate DDL error is
|
|
217
227
|
// reconciled to success. (Pre-fix, this rejects with "Duplicate column name" — the red run.)
|
|
218
228
|
return [4 /*yield*/, expect(loserTm.loadTable(grownTable())).resolves.toBeUndefined()];
|
|
@@ -220,16 +230,27 @@ describe('Concurrent schema reconcile', function () {
|
|
|
220
230
|
// OUTCOME: the loser's Db.init-equivalent does NOT reject — the duplicate DDL error is
|
|
221
231
|
// reconciled to success. (Pre-fix, this rejects with "Duplicate column name" — the red run.)
|
|
222
232
|
_a.sent();
|
|
233
|
+
toleratedWarn = loserWarnSpy.mock.calls.find(function (_a) {
|
|
234
|
+
var entry = _a[0];
|
|
235
|
+
return /\[schema reconcile\] tolerated concurrent ALREADY_EXISTS/.test(String(entry.message));
|
|
236
|
+
});
|
|
237
|
+
expect(toleratedWarn).toBeDefined();
|
|
238
|
+
expect(String(toleratedWarn[0].message)).toContain('db_test_reconcile');
|
|
223
239
|
return [4 /*yield*/, tableManager.schemaMetadata.getColumnMetadata(grownTable())];
|
|
224
240
|
case 4:
|
|
225
241
|
columnMetadata = _a.sent();
|
|
226
242
|
expect(columnMetadata['nickname']).toBeDefined();
|
|
227
243
|
expect(columnMetadata['nickname'].type).toBe('STRING(255)');
|
|
244
|
+
cleanWarnSpy = spyOnReconcileWarn(tableManager);
|
|
228
245
|
runUpdateSchemaSpy = jest.spyOn(spannerDriver, 'runUpdateSchema');
|
|
229
246
|
return [4 /*yield*/, tableManager.loadTable(grownTable())];
|
|
230
247
|
case 5:
|
|
231
248
|
_a.sent();
|
|
232
249
|
expect(runUpdateSchemaSpy).not.toHaveBeenCalled();
|
|
250
|
+
expect(cleanWarnSpy.mock.calls.filter(function (_a) {
|
|
251
|
+
var entry = _a[0];
|
|
252
|
+
return /\[schema reconcile\]/.test(String(entry.message));
|
|
253
|
+
})).toHaveLength(0);
|
|
233
254
|
return [2 /*return*/];
|
|
234
255
|
}
|
|
235
256
|
});
|
|
@@ -264,7 +285,7 @@ describe('Concurrent schema reconcile', function () {
|
|
|
264
285
|
});
|
|
265
286
|
}); }, 60000);
|
|
266
287
|
test('GENUINE CONFLICT: an already-exists error whose live definition differs from intent STILL throws', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
267
|
-
var duplicateError, error_1;
|
|
288
|
+
var duplicateError, error_1, warnSpy, conflictWarn;
|
|
268
289
|
return __generator(this, function (_a) {
|
|
269
290
|
switch (_a.label) {
|
|
270
291
|
case 0:
|
|
@@ -287,15 +308,16 @@ describe('Concurrent schema reconcile', function () {
|
|
|
287
308
|
case 5:
|
|
288
309
|
expect(duplicateError).toBeDefined();
|
|
289
310
|
expect(classifier(tableManager).isAlreadyExistsError(duplicateError)).toBe(true);
|
|
290
|
-
|
|
291
|
-
// so reconcile must RETHROW the original error rather than mask a genuine conflict.
|
|
292
|
-
// (Bite: turn reconcile into a blanket swallow and this rejection disappears.)
|
|
311
|
+
warnSpy = spyOnReconcileWarn(tableManager);
|
|
293
312
|
return [4 /*yield*/, expect(internals(tableManager).reconcileConcurrentSchemaChange([conflictIntegerTable()], duplicateError)).rejects.toBe(duplicateError)];
|
|
294
313
|
case 6:
|
|
295
|
-
// ...but the live schema (qty STRING(MAX)) does NOT match the intended definition (qty INT64),
|
|
296
|
-
// so reconcile must RETHROW the original error rather than mask a genuine conflict.
|
|
297
|
-
// (Bite: turn reconcile into a blanket swallow and this rejection disappears.)
|
|
298
314
|
_a.sent();
|
|
315
|
+
conflictWarn = warnSpy.mock.calls.find(function (_a) {
|
|
316
|
+
var entry = _a[0];
|
|
317
|
+
return /\[schema reconcile\].*genuine conflict/.test(String(entry.message));
|
|
318
|
+
});
|
|
319
|
+
expect(conflictWarn).toBeDefined();
|
|
320
|
+
expect(String(conflictWarn[0].message)).toContain('db_test_reconcile_conflict');
|
|
299
321
|
return [2 /*return*/];
|
|
300
322
|
}
|
|
301
323
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConcurrentSchemaReconcile.test.js","sourceRoot":"","sources":["../../test/ConcurrentSchemaReconcile.test.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"ConcurrentSchemaReconcile.test.js","sourceRoot":"","sources":["../../test/ConcurrentSchemaReconcile.test.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,oCAA4G;AAC5G,kEAA6D;AAC7D,4DAA2D;AAC3D,gFAA+E;AAC/E,mCAAiC;AAEjC;;;;;;;;;;;;;GAaG;AAEH,IAAM,aAAa,GAAG;IACpB,SAAS,EAAE,gBAAgB;IAC3B,YAAY,EAAE,gBAAgB;IAC9B,YAAY,EAAE,MAAM;CACrB,CAAC;AAEF,IAAM,aAAa,GAAG,IAAI,iCAAa,CAAC,aAAa,CAAC,CAAC;AAUvD,uDAAuD;AACvD,IAAM,SAAS,GAAG;IAChB,OAAA,IAAI;QAAe,2BAAmB;QAAjC;YAAA,qEAKJ;YAJC,UAAI,GAAG,mBAAmB,CAAC;YAC3B,aAAO,GAAG,IAAA,sBAAiB,EAAe;gBACxC,IAAI,EAAE,IAAI,iBAAY,CAAC,MAAM,CAAC;aAC/B,CAAC,CAAC;;QACL,CAAC;QAAD,cAAC;IAAD,CAAC,AALI,CAAc,UAAK,GAKtB,EAAE;AALJ,CAKI,CAAC;AAEP,0FAA0F;AAC1F,IAAM,UAAU,GAAG;IACjB,OAAA,IAAI;QAAe,2BAAe;QAA7B;YAAA,qEAMJ;YALC,UAAI,GAAG,mBAAmB,CAAC;YAC3B,aAAO,GAAG,IAAA,sBAAiB,EAAW;gBACpC,IAAI,EAAE,IAAI,iBAAY,CAAC,MAAM,CAAC;gBAC9B,QAAQ,EAAE,IAAI,iBAAY,CAAC,UAAU,CAAC;aACvC,CAAC,CAAC;;QACL,CAAC;QAAD,cAAC;IAAD,CAAC,AANI,CAAc,UAAK,GAMtB,EAAE;AANJ,CAMI,CAAC;AAMP,sEAAsE;AACtE,IAAM,mBAAmB,GAAG;IAC1B,OAAA,IAAI;QAAe,2BAAkB;QAAhC;YAAA,qEAKJ;YAJC,UAAI,GAAG,4BAA4B,CAAC;YACpC,aAAO,GAAG,IAAA,sBAAiB,EAAc;gBACvC,GAAG,EAAE,IAAI,iBAAY,CAAC,KAAK,CAAC;aAC7B,CAAC,CAAC;;QACL,CAAC;QAAD,cAAC;IAAD,CAAC,AALI,CAAc,UAAK,GAKtB,EAAE;AALJ,CAKI,CAAC;AAEP,mFAAmF;AACnF,IAAM,oBAAoB,GAAG;IAC3B,OAAA,IAAI;QAAe,2BAAkB;QAAhC;YAAA,qEAKJ;YAJC,UAAI,GAAG,4BAA4B,CAAC;YACpC,aAAO,GAAG,IAAA,sBAAiB,EAAc;gBACvC,GAAG,EAAE,IAAI,kBAAa,CAAC,KAAK,CAAC;aAC9B,CAAC,CAAC;;QACL,CAAC;QAAD,cAAC;IAAD,CAAC,AALI,CAAc,UAAK,GAKtB,EAAE;AALJ,CAKI,CAAC;AASP,oGAAoG;AACpG,IAAM,SAAS,GAAG,UAAC,YAAiC,IAAK,OAAA,YAAgD,EAAhD,CAAgD,CAAC;AAE1G,4DAA4D;AAC5D,IAAM,UAAU,GAAG,UAAC,YAAiC;IACnD,OAAA,YAAY,CAAC,gBAAgF;AAA7F,CAA6F,CAAC;AAEhG;;;;;GAKG;AACH,IAAM,kBAAkB,GAAG,UAAC,YAAiC;IAC3D,OAAA,IAAI,CAAC,KAAK,CAAE,YAAsF,CAAC,MAAM,EAAE,MAAM,CAAC;AAAlH,CAAkH,CAAC;AAErH,QAAQ,CAAC,6BAA6B,EAAE;IACtC,IAAM,SAAS,GAAG,IAAA,mCAAgB,EAAC,aAAa,CAAC,CAAC;IAClD,IAAM,YAAY,GAAG,aAAa,CAAC,eAAe,EAAE,CAAC;IAErD,SAAS,CAAC;;;wBACR,qBAAM,uDAA0B,CAAC,iBAAiB,CAAC,aAAa,CAAC,EAAA;;oBAAjE,SAAiE,CAAC;;;;SACnE,EAAE,KAAK,CAAC,CAAC;IAEV,UAAU,CAAC;;;wBACT,qBAAM,SAAS,CAAC,SAAS,EAAE,CAAC,EAAA;;oBAA5B,SAA4B,CAAC;oBAC7B,qBAAM,SAAS,CAAC,mBAAmB,EAAE,CAAC,EAAA;;oBAAtC,SAAsC,CAAC;;;;SACxC,EAAE,KAAK,CAAC,CAAC;IAEV,QAAQ,CAAC;;;wBACP,qBAAM,SAAS,CAAC,SAAS,EAAE,CAAC,EAAA;;oBAA5B,SAA4B,CAAC;oBAC7B,qBAAM,SAAS,CAAC,mBAAmB,EAAE,CAAC,EAAA;;oBAAtC,SAAsC,CAAC;oBACvC,uDAA0B,CAAC,OAAO,EAAE,CAAC;;;;SACtC,EAAE,KAAK,CAAC,CAAC;IAEV,SAAS,CAAC;QACR,IAAI,CAAC,eAAe,EAAE,CAAC;IACzB,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,kGAAkG,EAAE;;;;;gBACvG,+DAA+D;gBAC/D,qBAAM,YAAY,CAAC,SAAS,CAAC,SAAS,EAAE,CAAC,EAAA;;oBADzC,+DAA+D;oBAC/D,SAAyC,CAAC;oBAC1C,qBAAM,YAAY,CAAC,SAAS,CAAC,UAAU,EAAE,CAAC,EAAA;;oBAA1C,SAA0C,CAAC;oBAMrC,OAAO,GAAG,aAAa,CAAC,eAAe,EAAE,CAAC;oBAC1C,qBAAqB,GAAG,OAAO,CAAC,cAAc,CAAC,iBAAiB,CAAC,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;oBACpG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,cAAc,EAAE,mBAAmB,CAAC,CAAC,sBAAsB,CAAC,UAAO,KAAK;;;;wCAClE,qBAAM,qBAAqB,CAAC,KAAK,CAAC,EAAA;;oCAAnD,cAAc,GAAG,SAAkC;oCACzD,OAAO,cAAc,CAAC,UAAU,CAAC,CAAC;oCAClC,sBAAO,cAAc,EAAC;;;yBACvB,CAAC,CAAC;oBACG,YAAY,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;oBAEjD,uFAAuF;oBACvF,6FAA6F;oBAC7F,qBAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,aAAa,EAAE,EAAA;;oBAFtE,uFAAuF;oBACvF,6FAA6F;oBAC7F,SAAsE,CAAC;oBAIjE,aAAa,GAAG,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,UAAC,EAAO;4BAAN,KAAK,QAAA;wBACxD,OAAA,0DAA0D,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;oBAAtF,CAAsF,CACvF,CAAC;oBACF,MAAM,CAAC,aAAa,CAAC,CAAC,WAAW,EAAE,CAAC;oBACpC,MAAM,CAAC,MAAM,CAAC,aAAc,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;oBAGlD,qBAAM,YAAY,CAAC,cAAc,CAAC,iBAAiB,CAAC,UAAU,EAAE,CAAC,EAAA;;oBAAlF,cAAc,GAAG,SAAiE;oBACxF,MAAM,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;oBACjD,MAAM,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;oBAItD,YAAY,GAAG,kBAAkB,CAAC,YAAY,CAAC,CAAC;oBAChD,kBAAkB,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,EAAE,iBAAiB,CAAC,CAAC;oBACxE,qBAAM,YAAY,CAAC,SAAS,CAAC,UAAU,EAAE,CAAC,EAAA;;oBAA1C,SAA0C,CAAC;oBAC3C,MAAM,CAAC,kBAAkB,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;oBAClD,MAAM,CACJ,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,UAAC,EAAO;4BAAN,KAAK,QAAA;wBAAM,OAAA,sBAAsB,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;oBAAlD,CAAkD,CAAC,CAChG,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;;;;SACnB,EAAE,KAAK,CAAC,CAAC;IAEV,IAAI,CAAC,8EAA8E,EAAE;;;;;gBACnF,qCAAqC;gBACrC,qBAAM,YAAY,CAAC,SAAS,CAAC,SAAS,EAAE,CAAC,EAAA;;oBADzC,qCAAqC;oBACrC,SAAyC,CAAC;oBAIpC,OAAO,GAAG,aAAa,CAAC,eAAe,EAAE,CAAC;oBAChD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,cAAc,EAAE,aAAa,CAAC,CAAC,sBAAsB,CAAC;wBAAY,sBAAA,KAAK,EAAA;6BAAA,CAAC,CAAC;oBAE5F,8FAA8F;oBAC9F,0BAA0B;oBAC1B,qBAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,aAAa,EAAE,EAAA;;oBAFrE,8FAA8F;oBAC9F,0BAA0B;oBAC1B,SAAqE,CAAC;oBAEtE,KAAA,MAAM,CAAA;oBAAC,qBAAM,YAAY,CAAC,WAAW,CAAC,SAAS,EAAE,CAAC,EAAA;;oBAAlD,kBAAO,SAA2C,EAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;;;;SAChE,EAAE,KAAK,CAAC,CAAC;IAEV,IAAI,CAAC,kGAAkG,EAAE;;;;;gBACvG,sCAAsC;gBACtC,qBAAM,YAAY,CAAC,SAAS,CAAC,mBAAmB,EAAE,CAAC,EAAA;;oBADnD,sCAAsC;oBACtC,SAAmD,CAAC;;;;oBAMlD,qBAAM,aAAa,CAAC,eAAe,CAAC,iEAAiE,CAAC,EAAA;;oBAAtG,SAAsG,CAAC;;;;oBAEvG,cAAc,GAAG,OAAK,CAAC;;;oBAEzB,MAAM,CAAC,cAAc,CAAC,CAAC,WAAW,EAAE,CAAC;oBACrC,MAAM,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC,oBAAoB,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;oBAK3E,OAAO,GAAG,kBAAkB,CAAC,YAAY,CAAC,CAAC;oBACjD,qBAAM,MAAM,CACV,SAAS,CAAC,YAAY,CAAC,CAAC,+BAA+B,CAAC,CAAC,oBAAoB,EAAE,CAAC,EAAE,cAAc,CAAC,CAClG,CAAC,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,EAAA;;oBAF9B,SAE8B,CAAC;oBAGzB,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,UAAC,EAAO;4BAAN,KAAK,QAAA;wBAClD,OAAA,wCAAwC,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;oBAApE,CAAoE,CACrE,CAAC;oBACF,MAAM,CAAC,YAAY,CAAC,CAAC,WAAW,EAAE,CAAC;oBACnC,MAAM,CAAC,MAAM,CAAC,YAAa,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,4BAA4B,CAAC,CAAC;;;;SAClF,EAAE,KAAK,CAAC,CAAC;IAEV,IAAI,CAAC,4EAA4E,EAAE;;;;;gBACjF,8FAA8F;gBAC9F,qBAAM,YAAY,CAAC,SAAS,CAAC,SAAS,EAAE,CAAC,EAAA;;oBADzC,8FAA8F;oBAC9F,SAAyC,CAAC;;;;oBAKxC,qBAAM,aAAa,CAAC,eAAe,CAAC,sEAAsE,CAAC,EAAA;;oBAA3G,SAA2G,CAAC;;;;oBAE5G,cAAc,GAAG,OAAK,CAAC;;;oBAEzB,MAAM,CAAC,cAAc,CAAC,CAAC,WAAW,EAAE,CAAC;oBACrC,MAAM,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC,oBAAoB,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;oBAElF,uFAAuF;oBACvF,+FAA+F;oBAC/F,+FAA+F;oBAC/F,qBAAM,MAAM,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,+BAA+B,CAAC,CAAC,SAAS,EAAE,CAAC,EAAE,cAAc,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAC/G,cAAc,CACf,EAAA;;oBALD,uFAAuF;oBACvF,+FAA+F;oBAC/F,+FAA+F;oBAC/F,SAEC,CAAC;;;;SACH,EAAE,KAAK,CAAC,CAAC;IAEV,IAAI,CAAC,qFAAqF,EAAE;QAC1F,IAAM,eAAe,GAAG,UAAC,IAAwB,EAAE,OAAe;YAChE,OAAA,UAAU,CAAC,YAAY,CAAC,CAAC,oBAAoB,CAAC,EAAE,IAAI,MAAA,EAAE,OAAO,SAAA,EAAE,CAAC;QAAhE,CAAgE,CAAC;QAEnE,+FAA+F;QAC/F,MAAM,CAAC,eAAe,CAAC,CAAC,EAAE,2DAA2D,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnG,MAAM,CAAC,eAAe,CAAC,CAAC,EAAE,2DAA2D,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnG,MAAM,CAAC,eAAe,CAAC,CAAC,EAAE,gDAAgD,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAExF,wFAAwF;QACxF,MAAM,CACJ,eAAe,CACb,CAAC,EACD,8GAA8G,CAC/G,CACF,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACd,MAAM,CAAC,eAAe,CAAC,CAAC,EAAE,oEAAoE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAE7G,kDAAkD;QAClD,MAAM,CAAC,eAAe,CAAC,CAAC,EAAE,wDAAwD,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACjG,MAAM,CAAC,eAAe,CAAC,CAAC,EAAE,uCAAuC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAEhF,wEAAwE;QACxE,MAAM,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC,oBAAoB,CAAC,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC,CAAC,CAAC,IAAI,CAC/G,KAAK,CACN,CAAC;QACF,MAAM,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC,oBAAoB,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC/E,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@proteinjs/db-driver-spanner",
|
|
3
|
-
"version": "1.20.
|
|
3
|
+
"version": "1.20.2",
|
|
4
4
|
"main": "./dist/generated/index.js",
|
|
5
5
|
"types": "./dist/generated/index.d.ts",
|
|
6
6
|
"exports": {
|
|
@@ -42,10 +42,10 @@
|
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"@google-cloud/spanner": "7.5.0",
|
|
45
|
-
"@proteinjs/db": "^1.32.
|
|
45
|
+
"@proteinjs/db": "^1.32.2",
|
|
46
46
|
"@proteinjs/db-query": "^1.7.0",
|
|
47
|
-
"@proteinjs/db-spanner-common": "^1.3.
|
|
48
|
-
"@proteinjs/db-transaction-context": "^0.5.
|
|
47
|
+
"@proteinjs/db-spanner-common": "^1.3.10",
|
|
48
|
+
"@proteinjs/db-transaction-context": "^0.5.10",
|
|
49
49
|
"@proteinjs/logger": "^1.0.21",
|
|
50
50
|
"@proteinjs/reflection": "^1.1.12",
|
|
51
51
|
"@proteinjs/util": "^1.6.1"
|
|
@@ -63,5 +63,5 @@
|
|
|
63
63
|
"ts-jest": "29.1.1",
|
|
64
64
|
"typescript": "5.2.2"
|
|
65
65
|
},
|
|
66
|
-
"gitHead": "
|
|
66
|
+
"gitHead": "744307f0dc93bb03bf9cecb76251a6848511b327"
|
|
67
67
|
}
|
|
@@ -90,6 +90,15 @@ const internals = (tableManager: SpannerTableManager) => tableManager as unknown
|
|
|
90
90
|
const classifier = (tableManager: SpannerTableManager) =>
|
|
91
91
|
tableManager.schemaOperations as unknown as { isAlreadyExistsError(error: unknown): boolean };
|
|
92
92
|
|
|
93
|
+
/**
|
|
94
|
+
* Spy on THIS TableManager instance's own logger.warn. TableManager (@proteinjs/db) and this test
|
|
95
|
+
* (@proteinjs/db-driver-spanner) resolve separate physical @proteinjs/logger copies, so a
|
|
96
|
+
* Logger.prototype spy here would never intercept the reconcile's warns — spying the instance's
|
|
97
|
+
* method does, regardless of module identity.
|
|
98
|
+
*/
|
|
99
|
+
const spyOnReconcileWarn = (tableManager: SpannerTableManager) =>
|
|
100
|
+
jest.spyOn((tableManager as unknown as { logger: { warn: (log: { message?: unknown }) => void } }).logger, 'warn');
|
|
101
|
+
|
|
93
102
|
describe('Concurrent schema reconcile', () => {
|
|
94
103
|
const dropTable = getDropTestTable(spannerDriver);
|
|
95
104
|
const tableManager = spannerDriver.getTableManager();
|
|
@@ -129,20 +138,34 @@ describe('Concurrent schema reconcile', () => {
|
|
|
129
138
|
delete columnMetadata['nickname'];
|
|
130
139
|
return columnMetadata;
|
|
131
140
|
});
|
|
141
|
+
const loserWarnSpy = spyOnReconcileWarn(loserTm);
|
|
132
142
|
|
|
133
143
|
// OUTCOME: the loser's Db.init-equivalent does NOT reject — the duplicate DDL error is
|
|
134
144
|
// reconciled to success. (Pre-fix, this rejects with "Duplicate column name" — the red run.)
|
|
135
145
|
await expect(loserTm.loadTable(grownTable())).resolves.toBeUndefined();
|
|
136
146
|
|
|
147
|
+
// OBSERVABILITY: the tolerance fired LOUDLY (WARN), naming the table so an unexpected
|
|
148
|
+
// activation is never silent in prod.
|
|
149
|
+
const toleratedWarn = loserWarnSpy.mock.calls.find(([entry]) =>
|
|
150
|
+
/\[schema reconcile\] tolerated concurrent ALREADY_EXISTS/.test(String(entry.message))
|
|
151
|
+
);
|
|
152
|
+
expect(toleratedWarn).toBeDefined();
|
|
153
|
+
expect(String(toleratedWarn![0].message)).toContain('db_test_reconcile');
|
|
154
|
+
|
|
137
155
|
// The column exists exactly once, with the intended type (StringColumn defaults to STRING(255)).
|
|
138
156
|
const columnMetadata = await tableManager.schemaMetadata.getColumnMetadata(grownTable());
|
|
139
157
|
expect(columnMetadata['nickname']).toBeDefined();
|
|
140
158
|
expect(columnMetadata['nickname'].type).toBe('STRING(255)');
|
|
141
159
|
|
|
142
|
-
//
|
|
160
|
+
// HAPPY PATH UNTOUCHED: a clean pass (DDL succeeds, nothing thrown) issues ZERO DDL and never
|
|
161
|
+
// reaches the reconcile path, so it emits no reconcile WARN.
|
|
162
|
+
const cleanWarnSpy = spyOnReconcileWarn(tableManager);
|
|
143
163
|
const runUpdateSchemaSpy = jest.spyOn(spannerDriver, 'runUpdateSchema');
|
|
144
164
|
await tableManager.loadTable(grownTable());
|
|
145
165
|
expect(runUpdateSchemaSpy).not.toHaveBeenCalled();
|
|
166
|
+
expect(
|
|
167
|
+
cleanWarnSpy.mock.calls.filter(([entry]) => /\[schema reconcile\]/.test(String(entry.message)))
|
|
168
|
+
).toHaveLength(0);
|
|
146
169
|
}, 60000);
|
|
147
170
|
|
|
148
171
|
test('two actors creating the SAME absent table both succeed; the table lands once', async () => {
|
|
@@ -179,9 +202,17 @@ describe('Concurrent schema reconcile', () => {
|
|
|
179
202
|
// ...but the live schema (qty STRING(MAX)) does NOT match the intended definition (qty INT64),
|
|
180
203
|
// so reconcile must RETHROW the original error rather than mask a genuine conflict.
|
|
181
204
|
// (Bite: turn reconcile into a blanket swallow and this rejection disappears.)
|
|
205
|
+
const warnSpy = spyOnReconcileWarn(tableManager);
|
|
182
206
|
await expect(
|
|
183
207
|
internals(tableManager).reconcileConcurrentSchemaChange([conflictIntegerTable()], duplicateError)
|
|
184
208
|
).rejects.toBe(duplicateError);
|
|
209
|
+
|
|
210
|
+
// OBSERVABILITY: a genuine conflict is ALSO loud (WARN) before it re-throws.
|
|
211
|
+
const conflictWarn = warnSpy.mock.calls.find(([entry]) =>
|
|
212
|
+
/\[schema reconcile\].*genuine conflict/.test(String(entry.message))
|
|
213
|
+
);
|
|
214
|
+
expect(conflictWarn).toBeDefined();
|
|
215
|
+
expect(String(conflictWarn![0].message)).toContain('db_test_reconcile_conflict');
|
|
185
216
|
}, 60000);
|
|
186
217
|
|
|
187
218
|
test('UNRELATED ERROR CLASS: a non-already-exists DDL error propagates unchanged', async () => {
|