db-migrate 1.0.0-beta.9 → 1.0.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/LICENSE +26 -0
- package/README.md +13 -5
- package/api.js +27 -2
- package/connect.js +29 -11
- package/lib/chain.js +57 -2
- package/lib/commands/check.js +3 -3
- package/lib/commands/create-migration.js +23 -3
- package/lib/commands/down.js +1 -1
- package/lib/commands/fix.js +53 -0
- package/lib/commands/generated.js +1 -0
- package/lib/commands/helper/register-events.js +3 -2
- package/lib/commands/run.js +23 -8
- package/lib/commands/seed.js +11 -45
- package/lib/commands/set-default-argv.js +17 -1
- package/lib/commands/sync.js +3 -3
- package/lib/commands/undo-seed.js +11 -51
- package/lib/commands/up.js +3 -3
- package/lib/driver/index.js +110 -41
- package/lib/executors/versioned/v2.js +190 -12
- package/lib/file.js +36 -1
- package/lib/format-error.js +119 -0
- package/lib/interface/seederInterface.js +1 -1
- package/lib/learn.js +54 -17
- package/lib/methods/v2/statedone.js +18 -0
- package/lib/methods/v2/statetravel.js +41 -35
- package/lib/methods/v2/translatestate.js +41 -9
- package/lib/state.js +459 -76
- package/lib/walker.js +195 -89
- package/package.json +18 -22
- package/CHANGELOG.md +0 -487
- package/get.js +0 -67
- package/pnpm-lock.yaml +0 -2574
- package/te.js +0 -70
package/lib/state.js
CHANGED
|
@@ -1,74 +1,462 @@
|
|
|
1
1
|
const MSTATE = '__dbmigrate_state__';
|
|
2
2
|
const SSTATE = '__dbmigrate_schema__';
|
|
3
|
+
|
|
3
4
|
const log = require('db-migrate-shared').log;
|
|
5
|
+
const fs = require('fs').promises;
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const crypto = require('crypto');
|
|
8
|
+
const { performance } = require('perf_hooks');
|
|
9
|
+
|
|
10
|
+
const ID = crypto.randomBytes(32).toString('base64');
|
|
11
|
+
|
|
12
|
+
const LOCK_TIMEOUT = 60000;
|
|
13
|
+
const LOCK_INTERVAL = 1000;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* active: a lock session is running, from acquire until release, even if
|
|
17
|
+
* the lock got lost in between
|
|
18
|
+
* owner: we currently hold the lock
|
|
19
|
+
* current: the last MSTATE row we wrote and verified, the expected
|
|
20
|
+
* value for our next compare and swap
|
|
21
|
+
* queue: serializes our own writes, so heartbeat and state updates
|
|
22
|
+
* never race each other on the same expected row
|
|
23
|
+
*/
|
|
24
|
+
let active = false;
|
|
25
|
+
let owner = false;
|
|
26
|
+
let current = null;
|
|
27
|
+
let heartbeat = null;
|
|
28
|
+
let queue = Promise.resolve();
|
|
29
|
+
|
|
30
|
+
const nonce = () => crypto.randomBytes(8).toString('hex');
|
|
31
|
+
|
|
32
|
+
const hashFile = async file => {
|
|
33
|
+
if (!file.path) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
return crypto
|
|
39
|
+
.createHash('sha256')
|
|
40
|
+
.update(await fs.readFile(file.path))
|
|
41
|
+
.digest('hex');
|
|
42
|
+
} catch (err) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
|
|
48
|
+
|
|
49
|
+
const lockOptions = internals => {
|
|
50
|
+
const argv = internals.argv || {};
|
|
51
|
+
return {
|
|
52
|
+
timeout: Number(argv['lock-timeout']) || LOCK_TIMEOUT,
|
|
53
|
+
interval: Number(argv['lock-interval']) || LOCK_INTERVAL
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const read = (driver, internals) =>
|
|
58
|
+
driver._getKV(internals.migrationState, MSTATE);
|
|
59
|
+
|
|
60
|
+
const holder = row => JSON.parse(row.value).s.ID;
|
|
61
|
+
|
|
62
|
+
const heldByOther = row => {
|
|
63
|
+
const id = holder(row);
|
|
64
|
+
return !!id && id !== ID;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Atomic update of the lock row. The where clause contains the value we
|
|
69
|
+
* have read before, which carries the date and a nonce renewed on every
|
|
70
|
+
* write. If anybody else wrote in between, the update matches no row.
|
|
71
|
+
* Whoever wins, sees his own value on re-read.
|
|
72
|
+
*/
|
|
73
|
+
const cas = (driver, internals, expected, value) =>
|
|
74
|
+
driver._updateKVC(
|
|
75
|
+
internals.migrationState,
|
|
76
|
+
MSTATE,
|
|
77
|
+
value,
|
|
78
|
+
'value',
|
|
79
|
+
expected.value
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Concurrent CREATE TABLE IF NOT EXISTS can still collide (e.g. on pg_type
|
|
84
|
+
* in postgres) when several processes start on an empty database. The
|
|
85
|
+
* second attempt finds the table and is a noop.
|
|
86
|
+
*/
|
|
87
|
+
const createConcurrently = async create => {
|
|
88
|
+
try {
|
|
89
|
+
await create();
|
|
90
|
+
} catch (err) {
|
|
91
|
+
log.verbose('[state] create failed, retrying once', err.message);
|
|
92
|
+
await create();
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* The stored schema starts as '{}', keep the structure the walker expects.
|
|
98
|
+
*/
|
|
99
|
+
const parseSchema = value =>
|
|
100
|
+
Object.assign({ i: {}, c: {}, f: {}, e: {} }, JSON.parse(value));
|
|
101
|
+
|
|
102
|
+
const stopHeartbeat = () => {
|
|
103
|
+
if (heartbeat) {
|
|
104
|
+
clearInterval(heartbeat);
|
|
105
|
+
heartbeat = null;
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const write = (driver, internals, mutate) => {
|
|
110
|
+
const run = queue.then(async () => {
|
|
111
|
+
if (!owner) {
|
|
112
|
+
throw new Error('[state] the migration lock is not held by this process');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const state = JSON.parse(current.value);
|
|
116
|
+
Object.assign(state.s, { ID, date: new Date(), n: nonce() });
|
|
117
|
+
mutate(state.s);
|
|
118
|
+
|
|
119
|
+
const value = JSON.stringify(state);
|
|
120
|
+
await cas(driver, internals, current, value);
|
|
121
|
+
const row = await read(driver, internals);
|
|
122
|
+
|
|
123
|
+
if (!row || row.value !== value) {
|
|
124
|
+
owner = false;
|
|
125
|
+
current = null;
|
|
126
|
+
stopHeartbeat();
|
|
127
|
+
throw new Error('[state] lost the migration lock to another process');
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
current = row;
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
queue = run.catch(() => {});
|
|
134
|
+
return run;
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
const claim = (row, driver, internals) => {
|
|
138
|
+
if (!row || holder(row) !== ID) {
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
active = true;
|
|
143
|
+
owner = true;
|
|
144
|
+
current = row;
|
|
145
|
+
|
|
146
|
+
const { timeout } = lockOptions(internals);
|
|
147
|
+
heartbeat = setInterval(() => {
|
|
148
|
+
write(driver, internals, () => {}).catch(err => log.error(err.message));
|
|
149
|
+
}, Math.max(Math.floor(timeout / 3), 1));
|
|
150
|
+
heartbeat.unref();
|
|
151
|
+
|
|
152
|
+
return true;
|
|
153
|
+
};
|
|
4
154
|
|
|
5
155
|
module.exports = {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
156
|
+
createConcurrently,
|
|
157
|
+
|
|
158
|
+
// for tests, simulates a process which died while holding the lock
|
|
159
|
+
__stopHeartbeat: stopHeartbeat,
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Drivers declare that their state methods were verified for locking,
|
|
163
|
+
* with an atomic _updateKVC and run_on set by the database clock.
|
|
164
|
+
*/
|
|
165
|
+
supportsLock: function (driver) {
|
|
166
|
+
return !!(
|
|
167
|
+
driver &&
|
|
168
|
+
driver._meta &&
|
|
169
|
+
driver._meta.supports &&
|
|
170
|
+
driver._meta.supports.locking === true
|
|
171
|
+
);
|
|
172
|
+
},
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Try to acquire the migration lock once. Resolves to true if this process
|
|
176
|
+
* is the owner afterwards. A lock held by another process is only taken
|
|
177
|
+
* over, if it is the exact row waitForRelease reported as stale.
|
|
178
|
+
*/
|
|
179
|
+
acquire: async function (driver, internals, stale = null, retries = 3) {
|
|
180
|
+
if (owner) {
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const row = await read(driver, internals);
|
|
185
|
+
|
|
186
|
+
if (!row) {
|
|
187
|
+
try {
|
|
188
|
+
await driver._insertKV(
|
|
189
|
+
internals.migrationState,
|
|
190
|
+
MSTATE,
|
|
191
|
+
JSON.stringify({
|
|
192
|
+
s: { step: 0, fin: 1, ID, date: new Date(), n: nonce() }
|
|
193
|
+
})
|
|
194
|
+
);
|
|
195
|
+
} catch (err) {
|
|
196
|
+
// somebody else inserted the row first, the re-read decides
|
|
197
|
+
log.verbose('[state] lock insert failed', err.message);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return claim(await read(driver, internals), driver, internals);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (heldByOther(row)) {
|
|
204
|
+
if (!stale || stale.value !== row.value) {
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const { s } = JSON.parse(row.value);
|
|
209
|
+
log.warn(
|
|
210
|
+
`[state] taking over a stale migration lock from ${s.date}` +
|
|
211
|
+
(s.fin === 0
|
|
212
|
+
? `, the previous run was interrupted during ${
|
|
213
|
+
s.f ? `migration "${s.f}"` : 'a migration'
|
|
214
|
+
} at step ${s.step}`
|
|
215
|
+
: '')
|
|
18
216
|
);
|
|
19
|
-
}
|
|
20
|
-
state = state.value;
|
|
21
|
-
state = JSON.parse(state);
|
|
22
|
-
state.s.date = new Date();
|
|
23
|
-
return driver._updateKV(
|
|
24
|
-
internals.migrationState,
|
|
25
|
-
MSTATE,
|
|
217
|
+
}
|
|
26
218
|
|
|
27
|
-
|
|
219
|
+
const state = JSON.parse(row.value);
|
|
220
|
+
Object.assign(state.s, { ID, date: new Date(), n: nonce() });
|
|
221
|
+
await cas(driver, internals, row, JSON.stringify(state));
|
|
222
|
+
|
|
223
|
+
const after = await read(driver, internals);
|
|
224
|
+
if (claim(after, driver, internals)) {
|
|
225
|
+
return true;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Nobody holds the lock, still our update did not apply. Either another
|
|
229
|
+
// process acquired and released in between, or the compare and swap does
|
|
230
|
+
// not work with this driver at all, which must not end in a busy loop.
|
|
231
|
+
if (after && !heldByOther(after)) {
|
|
232
|
+
if (retries > 0) {
|
|
233
|
+
return module.exports.acquire(driver, internals, null, retries - 1);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
throw new Error(
|
|
237
|
+
'[state] could not acquire the migration lock although it is free, ' +
|
|
238
|
+
'the driver does not seem to support compare and swap on the state'
|
|
28
239
|
);
|
|
29
240
|
}
|
|
241
|
+
|
|
242
|
+
return false;
|
|
30
243
|
},
|
|
31
244
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
245
|
+
/**
|
|
246
|
+
* Wait until the lock is released. A lock whose row did not change for
|
|
247
|
+
* lock-timeout ms is considered stale, its holder is gone. This is
|
|
248
|
+
* measured on our own monotonic clock, so no clocks have to be in sync.
|
|
249
|
+
*
|
|
250
|
+
* Resolves to the stale row, or null if the lock was released properly.
|
|
251
|
+
*/
|
|
252
|
+
waitForRelease: async function (driver, internals) {
|
|
253
|
+
const { timeout, interval } = lockOptions(internals);
|
|
254
|
+
let seen = null;
|
|
255
|
+
let since = 0;
|
|
256
|
+
|
|
257
|
+
for (;;) {
|
|
258
|
+
const row = await read(driver, internals);
|
|
259
|
+
|
|
260
|
+
if (!row || !heldByOther(row)) {
|
|
261
|
+
return null;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const now = performance.now();
|
|
265
|
+
if (
|
|
266
|
+
!seen ||
|
|
267
|
+
seen.value !== row.value ||
|
|
268
|
+
String(seen.run_on) !== String(row.run_on)
|
|
269
|
+
) {
|
|
270
|
+
seen = row;
|
|
271
|
+
since = now;
|
|
272
|
+
} else if (now - since >= timeout) {
|
|
273
|
+
return row;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
await delay(interval);
|
|
277
|
+
}
|
|
278
|
+
},
|
|
279
|
+
|
|
280
|
+
release: async function (driver, internals) {
|
|
281
|
+
stopHeartbeat();
|
|
282
|
+
|
|
283
|
+
if (!owner) {
|
|
284
|
+
active = false;
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
try {
|
|
289
|
+
await write(driver, internals, s => {
|
|
290
|
+
s.ID = 0;
|
|
291
|
+
});
|
|
292
|
+
} catch (err) {
|
|
293
|
+
log.warn(err.message);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
active = false;
|
|
297
|
+
owner = false;
|
|
298
|
+
current = null;
|
|
299
|
+
},
|
|
300
|
+
|
|
301
|
+
isOwner: function () {
|
|
302
|
+
return owner;
|
|
303
|
+
},
|
|
304
|
+
|
|
305
|
+
init: async function (driver, internals, { emptyState, backupState }) {
|
|
306
|
+
await createConcurrently(() => driver._createKV(internals.migrationState));
|
|
307
|
+
const _schema = await driver._getKV(internals.migrationState, SSTATE);
|
|
308
|
+
|
|
309
|
+
if (_schema && backupState) {
|
|
310
|
+
const newName = `${internals.migrationState}_b_${Math.floor((new Date() - 0) / 1000)}`;
|
|
311
|
+
log.info(`[state] Created a backup of ${internals.migrationState} by writing to file ${newName}.dbmigrate`);
|
|
312
|
+
|
|
313
|
+
await fs.writeFile(path.resolve(`${newName}.dbmigrate`), JSON.stringify(_schema), 'utf8');
|
|
314
|
+
|
|
315
|
+
if (emptyState) {
|
|
316
|
+
await driver.renameTable(internals.migrationState,
|
|
317
|
+
newName);
|
|
318
|
+
|
|
319
|
+
log.info(`[state] Created a backup of ${internals.migrationState} by renaming table to ${newName}`);
|
|
320
|
+
await driver._createKV(internals.migrationState);
|
|
321
|
+
await driver._insertKV(internals.migrationState, SSTATE, '{}');
|
|
322
|
+
}
|
|
39
323
|
}
|
|
40
324
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
325
|
+
const schema = emptyState !== true ? _schema : null;
|
|
326
|
+
if (schema) {
|
|
327
|
+
internals.schema = parseSchema(schema.value);
|
|
328
|
+
} else if (!emptyState) {
|
|
329
|
+
try {
|
|
330
|
+
await driver._insertKV(internals.migrationState, SSTATE, '{}');
|
|
331
|
+
} catch (err) {
|
|
332
|
+
// another process initialized the state at the same time
|
|
333
|
+
const row = await driver._getKV(internals.migrationState, SSTATE);
|
|
334
|
+
if (!row) {
|
|
335
|
+
throw err;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
internals.schema = parseSchema(row.value);
|
|
339
|
+
}
|
|
44
340
|
}
|
|
341
|
+
},
|
|
45
342
|
|
|
46
|
-
|
|
343
|
+
/**
|
|
344
|
+
* Another process may have migrated while we were waiting for the lock,
|
|
345
|
+
* so the schema loaded during init is outdated.
|
|
346
|
+
*/
|
|
347
|
+
reloadSchema: async function (driver, internals) {
|
|
348
|
+
const schema = await driver._getKV(internals.migrationState, SSTATE);
|
|
349
|
+
if (schema) {
|
|
350
|
+
internals.schema = parseSchema(schema.value);
|
|
351
|
+
}
|
|
47
352
|
},
|
|
48
353
|
|
|
49
|
-
|
|
50
|
-
|
|
354
|
+
/**
|
|
355
|
+
* Start a migration in the state. With recover, an interrupted previous
|
|
356
|
+
* run of the same migration is not reset but returned, so the caller can
|
|
357
|
+
* decide how to recover:
|
|
358
|
+
*
|
|
359
|
+
* step: the last step started
|
|
360
|
+
* learned: the last step whose reverse operation was recorded
|
|
361
|
+
* done: the last step executed on the database
|
|
362
|
+
* rollback: the run was interrupted while rolling back
|
|
363
|
+
* changed: the migration file changed since
|
|
364
|
+
*/
|
|
365
|
+
startMigration: async function (
|
|
366
|
+
driver,
|
|
367
|
+
file,
|
|
368
|
+
internals,
|
|
369
|
+
{ op = 'up', recover = false } = {}
|
|
370
|
+
) {
|
|
51
371
|
const mig = await driver._getKV(internals.migrationState, file.name);
|
|
52
372
|
|
|
53
373
|
if (mig && mig.value !== '{}') {
|
|
54
374
|
internals.modSchema = JSON.parse(mig.value);
|
|
375
|
+
// records of older runs, they belong to no step of this run
|
|
376
|
+
(internals.modSchema.s || []).forEach(entry => {
|
|
377
|
+
if (entry.n === undefined) entry.n = 0;
|
|
378
|
+
});
|
|
55
379
|
}
|
|
56
380
|
|
|
57
381
|
if (internals.dryRun) {
|
|
58
|
-
return
|
|
382
|
+
return null;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const h = await hashFile(file);
|
|
386
|
+
const row = await read(driver, internals);
|
|
387
|
+
const prev = row ? JSON.parse(row.value).s : null;
|
|
388
|
+
|
|
389
|
+
if (prev && prev.fin === 0 && prev.f) {
|
|
390
|
+
if (recover && prev.f === file.name && prev.o === 'up' && op === 'up') {
|
|
391
|
+
return {
|
|
392
|
+
step: prev.step || 0,
|
|
393
|
+
learned: prev.learned || 0,
|
|
394
|
+
done: prev.done || 0,
|
|
395
|
+
rollback: prev.rb === 1,
|
|
396
|
+
changed: !!prev.h && !!h && prev.h !== h
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
log.warn(
|
|
401
|
+
`[state] ignoring the interrupted ${prev.o} of migration ` +
|
|
402
|
+
`"${prev.f}" at step ${prev.step}`
|
|
403
|
+
);
|
|
59
404
|
}
|
|
60
405
|
|
|
61
|
-
await module.exports.
|
|
406
|
+
await module.exports.progress(driver, internals, s => {
|
|
407
|
+
Object.assign(s, {
|
|
408
|
+
step: 0,
|
|
409
|
+
fin: 0,
|
|
410
|
+
f: file.name,
|
|
411
|
+
o: op,
|
|
412
|
+
learned: 0,
|
|
413
|
+
done: 0,
|
|
414
|
+
rb: 0,
|
|
415
|
+
h
|
|
416
|
+
});
|
|
417
|
+
});
|
|
62
418
|
|
|
63
419
|
if (!mig) {
|
|
64
|
-
|
|
420
|
+
await driver._insertKV(
|
|
65
421
|
internals.migrationState,
|
|
66
422
|
file.name,
|
|
67
423
|
JSON.stringify({})
|
|
68
424
|
);
|
|
69
|
-
} else {
|
|
70
|
-
return Promise.resolve();
|
|
71
425
|
}
|
|
426
|
+
|
|
427
|
+
return null;
|
|
428
|
+
},
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Write migration progress into the lock row. While holding the lock this
|
|
432
|
+
* is a compare and swap, which fails if we lost the lock. Without a lock
|
|
433
|
+
* (driver does not support it) the row is written unconditionally.
|
|
434
|
+
*/
|
|
435
|
+
progress: async function (driver, internals, mutate) {
|
|
436
|
+
if (active) {
|
|
437
|
+
return write(driver, internals, mutate);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
const row = await read(driver, internals);
|
|
441
|
+
const state = row
|
|
442
|
+
? JSON.parse(row.value)
|
|
443
|
+
: { s: { step: 0, fin: 0, ID: 0 } };
|
|
444
|
+
state.s.date = new Date();
|
|
445
|
+
mutate(state.s);
|
|
446
|
+
|
|
447
|
+
if (!row) {
|
|
448
|
+
return driver._insertKV(
|
|
449
|
+
internals.migrationState,
|
|
450
|
+
MSTATE,
|
|
451
|
+
JSON.stringify(state)
|
|
452
|
+
);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
return driver._updateKV(
|
|
456
|
+
internals.migrationState,
|
|
457
|
+
MSTATE,
|
|
458
|
+
JSON.stringify(state)
|
|
459
|
+
);
|
|
72
460
|
},
|
|
73
461
|
|
|
74
462
|
update: async function (driver, file, state, internals) {
|
|
@@ -77,7 +465,7 @@ module.exports = {
|
|
|
77
465
|
return Promise.resolve();
|
|
78
466
|
}
|
|
79
467
|
|
|
80
|
-
await driver._updateKV(internals.migrationState, SSTATE, internals.schema);
|
|
468
|
+
await driver._updateKV(internals.migrationState, SSTATE, JSON.stringify(internals.schema));
|
|
81
469
|
|
|
82
470
|
return driver._updateKV(
|
|
83
471
|
internals.migrationState,
|
|
@@ -90,22 +478,34 @@ module.exports = {
|
|
|
90
478
|
return driver._getKV(internals.migrationState, file.name);
|
|
91
479
|
},
|
|
92
480
|
|
|
93
|
-
|
|
481
|
+
learned: async function (driver, step, internals) {
|
|
94
482
|
if (internals.dryRun) {
|
|
95
483
|
return Promise.resolve();
|
|
96
484
|
}
|
|
97
485
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
486
|
+
return module.exports.progress(driver, internals, s => {
|
|
487
|
+
s.learned = step;
|
|
488
|
+
});
|
|
489
|
+
},
|
|
490
|
+
|
|
491
|
+
done: async function (driver, step, internals) {
|
|
492
|
+
if (internals.dryRun) {
|
|
493
|
+
return Promise.resolve();
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
return module.exports.progress(driver, internals, s => {
|
|
497
|
+
s.done = step;
|
|
498
|
+
});
|
|
499
|
+
},
|
|
500
|
+
|
|
501
|
+
rollingBack: async function (driver, internals) {
|
|
502
|
+
if (internals.dryRun) {
|
|
503
|
+
return Promise.resolve();
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
return module.exports.progress(driver, internals, s => {
|
|
507
|
+
s.rb = 1;
|
|
508
|
+
});
|
|
109
509
|
},
|
|
110
510
|
|
|
111
511
|
step: async function (driver, step, internals) {
|
|
@@ -114,18 +514,9 @@ module.exports = {
|
|
|
114
514
|
return Promise.resolve();
|
|
115
515
|
}
|
|
116
516
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
);
|
|
121
|
-
state = JSON.parse(state);
|
|
122
|
-
state.s.date = new Date();
|
|
123
|
-
state.s.step = step;
|
|
124
|
-
return driver._updateKV(
|
|
125
|
-
internals.migrationState,
|
|
126
|
-
MSTATE,
|
|
127
|
-
JSON.stringify(state)
|
|
128
|
-
);
|
|
517
|
+
return module.exports.progress(driver, internals, s => {
|
|
518
|
+
s.step = step;
|
|
519
|
+
});
|
|
129
520
|
},
|
|
130
521
|
|
|
131
522
|
deleteState: function (driver, file, internals) {
|
|
@@ -137,18 +528,10 @@ module.exports = {
|
|
|
137
528
|
return Promise.resolve();
|
|
138
529
|
}
|
|
139
530
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
state.s.date = new Date();
|
|
146
|
-
state.s.fin = 1;
|
|
147
|
-
|
|
148
|
-
return driver._updateKV(
|
|
149
|
-
internals.migrationState,
|
|
150
|
-
MSTATE,
|
|
151
|
-
JSON.stringify(state)
|
|
152
|
-
);
|
|
531
|
+
// the lock itself stays until the walker releases it
|
|
532
|
+
return module.exports.progress(driver, internals, s => {
|
|
533
|
+
s.fin = 1;
|
|
534
|
+
s.rb = 0;
|
|
535
|
+
});
|
|
153
536
|
}
|
|
154
537
|
};
|