Skip to content

Commit 7db8607

Browse files
committed
fix(v2): handle reversing of history sensitive operations better
fixes db-migrate#666 Signed-off-by: Tobias Gurtzick <[email protected]>
1 parent 9855bf0 commit 7db8607

File tree

5 files changed

+111
-22
lines changed

5 files changed

+111
-22
lines changed

lib/chain.js

Lines changed: 65 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,65 @@ const DEFAULT = [
1616
'removeForeignKey'
1717
];
1818

19+
let rollbackCounter = 0;
20+
let rollbackSignal = null;
21+
let signal = null;
22+
let counter = 0;
23+
24+
const dbmControl = {
25+
inc: () => {
26+
++counter;
27+
},
28+
29+
signal: () => {
30+
signal = true;
31+
},
32+
33+
hasSignaled: () => {
34+
return signal;
35+
},
36+
37+
previousSignal: () => {
38+
const r = rollbackSignal;
39+
// we reset on retrieval and mark as retrieved
40+
rollbackSignal = rollbackSignal === null ? null : false;
41+
return r;
42+
},
43+
44+
get: () => {
45+
return counter;
46+
},
47+
48+
getPrevious: () => {
49+
const r = rollbackCounter;
50+
51+
// we reset on retrieval and mark as retrieved
52+
rollbackCounter = rollbackCounter > 0 ? false : 0;
53+
54+
return r;
55+
}
56+
};
57+
1958
class Chain {
2059
constructor (context, file, driver, internals) {
2160
this.file = file;
2261
this.context = context;
2362
this.driver = context;
2463
this.udriver = driver;
64+
if (this.udriver._dbmControl !== true) {
65+
this.udriver._counter = dbmControl;
66+
this.udriver._dbmControl = true;
67+
const runSql = this.udriver.runSql;
68+
this.udriver._dbmControlRSQL = runSql;
69+
70+
// couple runSql
71+
this.udriver.runSql = function (...args) {
72+
return runSql.apply(this, args).then(x => {
73+
++counter;
74+
return x;
75+
});
76+
};
77+
}
2578
this.internals = internals;
2679
this.chains = [];
2780
this.interfaces = [];
@@ -32,20 +85,23 @@ class Chain {
3285
DEFAULT.concat(Object.keys(this.context.learnable || [])).forEach(
3386
method => {
3487
this[method] = function (...args) {
35-
let cb = args.pop();
36-
if (typeof cb !== 'function') {
37-
args.push(cb);
38-
cb = undefined;
39-
}
40-
41-
return Promise.resolve(
42-
this.exec.apply(this, [method].concat(args))
43-
).asCallback(cb);
88+
// we don't handle any callbacks here, v2 ultimately deprecates them
89+
90+
this.transferInt();
91+
92+
return this.exec.apply(this, [method].concat(args));
4493
};
4594
}
4695
);
4796
}
4897

98+
transferInt () {
99+
rollbackCounter = counter;
100+
counter = 0;
101+
rollbackSignal = signal;
102+
signal = null;
103+
}
104+
49105
addChain (chain) {
50106
this.chains.push(chain);
51107
}

lib/commands/helper/register-events.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@ var log = require('db-migrate-shared').log;
44

55
function registerEvents () {
66
process.on('uncaughtException', function (err) {
7+
log.error('uncaughtException');
78
log.error(err.stack || err);
89
process.exit(1);
910
});
1011

1112
process.on('unhandledRejection', function (reason) {
13+
log.error('unhandledRejection');
1214
log.error(reason.stack || reason);
1315
process.exit(1);
1416
});

lib/executors/versioned/v2.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@ const execUnit = {
4848
dbm: context.internals.safeOptions.dbmigrate
4949
});
5050
} catch (err) {
51+
context.internals.rollback = true;
52+
53+
// transfer last state
54+
chain.transferInt();
55+
5156
log.error(
5257
'An error occured. No alternative failure strategy defined. Rolling back!',
5358
err
@@ -61,8 +66,6 @@ const execUnit = {
6166
},
6267

6368
down: async function (context, driver, file) {
64-
context.internals.backwards = true;
65-
6669
// start migration, see up comments
6770
await State.startMigration(context._driver, file, context.internals);
6871
await TranslateState(context._driver, file, driver, context.internals);

lib/methods/v2/translatestate.js

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -52,17 +52,30 @@ async function processEntry (
5252
internals,
5353
{ t: type, a: action, c: args }
5454
) {
55-
const f = Object.assign(methods, context.reverse);
56-
switch (type) {
57-
case 0:
58-
await driver[action].apply(driver, args);
59-
break;
60-
case 1:
61-
await f[action](driver, args, internals);
62-
break;
55+
let skip = false;
6356

64-
default:
65-
throw new Error(`Invalid state record, of type ${type}`);
57+
if (
58+
internals.rollback === true &&
59+
internals.rollbackContinue !== true &&
60+
driver.udriver._meta.signalColumns.indexOf(action) !== -1
61+
) {
62+
internals.rollbackContinue = true;
63+
skip = driver.udriver._counter.previousSignal() === null;
64+
}
65+
66+
if (!skip) {
67+
const f = Object.assign(methods, context.reverse);
68+
switch (type) {
69+
case 0:
70+
await driver[action].apply(driver, args);
71+
break;
72+
case 1:
73+
await f[action](driver, args, internals);
74+
break;
75+
76+
default:
77+
throw new Error(`Invalid state record, of type ${type}`);
78+
}
6679
}
6780

6881
internals.modSchema.s.shift();
@@ -73,6 +86,7 @@ module.exports = async (context, file, driver, internals) => {
7386
const mod = internals.modSchema;
7487

7588
const chain = new Chain(context, file, driver, internals);
89+
// chain.addChain(Learn);
7690
chain.addChain(Migrate);
7791
await Promise.resolve(mod.s.reverse()).each(args =>
7892
processEntry(context, file, chain, internals, args)

lib/walker.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,18 @@ const Walker = function (driver, directory, mode, intern, prefix) {
3030
Promise.promisifyAll(this._driver);
3131
this.directory = directory;
3232
this.internals = intern;
33+
/**
34+
* i: index
35+
* c: schema
36+
* f: foreignKey
37+
*/
3338
this.internals.schema = { i: {}, c: {}, f: {} };
39+
/**
40+
* s: commands
41+
* i: index
42+
* f: foreignKey
43+
* c: schema
44+
*/
3445
this.internals.modSchema = { i: {}, c: {}, f: {}, s: [] };
3546
this.mode = mode;
3647

@@ -241,7 +252,10 @@ Walker.prototype = {
241252
// Requires pr to export filterCompleted from db-migrate-shared
242253
const toRun = dbmUtil.filterCompleted(allFiles, completedFiles);
243254

244-
log.info('Files to run:', toRun.map(migration => migration.name));
255+
log.info(
256+
'Files to run:',
257+
toRun.map(migration => migration.name)
258+
);
245259
return toRun;
246260
})
247261
.nodeify(callback);

0 commit comments

Comments
 (0)