db-migrate 1.5.0 → 1.6.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/lib/chain.js CHANGED
@@ -190,7 +190,7 @@ class Chain {
190
190
  throw unknownTable(t);
191
191
  }
192
192
 
193
- const entry = Release.entry(internals, t, options);
193
+ const entry = Release.entry(internals, t, options, this.file);
194
194
  return this.run('setDeprecated', ['tables', t, null, entry], 'learn');
195
195
  };
196
196
 
@@ -205,7 +205,7 @@ class Chain {
205
205
  throw unknownColumn(t, c);
206
206
  }
207
207
 
208
- const entry = Release.entry(internals, c, options);
208
+ const entry = Release.entry(internals, c, options, this.file);
209
209
  if (columns[c].notNull) {
210
210
  // the application stops writing it. The whole spec, MySQL changes
211
211
  // a column by its full definition
@@ -97,6 +97,49 @@ function run (internals, config) {
97
97
  report(load('db')(internals, config));
98
98
  }
99
99
  break;
100
+ case 'work': {
101
+ // runs the jobs of background migrations, until none is left or, with
102
+ // --watch, until stopped
103
+ const argv = internals.argv;
104
+ const options = {};
105
+ [
106
+ ['parallel', 'parallel'],
107
+ ['pause', 'pause'],
108
+ ['batch', 'batch'],
109
+ ['interval', 'interval'],
110
+ ['job-timeout', 'timeout']
111
+ ].forEach(([arg, option]) => {
112
+ if (argv[arg] !== undefined) {
113
+ options[option] = Number(argv[arg]);
114
+ }
115
+ });
116
+ options.watch = argv.watch === true;
117
+
118
+ const worker = load('work')(internals, config, options);
119
+ const stop = () => {
120
+ log.info('[jobs] stopping after the current batches');
121
+ worker.stop();
122
+ };
123
+ process.once('SIGINT', stop);
124
+ process.once('SIGTERM', stop);
125
+
126
+ report(
127
+ worker.done.then(result => {
128
+ log.info(`[jobs] ${result.done.length} job(s) done`);
129
+
130
+ if (result.failed.length) {
131
+ throw new Error(
132
+ `${result.failed.length} background job(s) failed: ` +
133
+ result.failed.map(f => f.name).join(', ')
134
+ );
135
+ }
136
+
137
+ process.exit(0);
138
+ })
139
+ );
140
+ break;
141
+ }
142
+
100
143
  case 'seed': {
101
144
  // seed [name], seed down [name], seed reset
102
145
  const args = internals.argv._.map(String);
@@ -123,7 +166,7 @@ function run (internals, config) {
123
166
  );
124
167
  } else {
125
168
  log.error(
126
- 'Invalid Action: Must be [up|down|check|create|reset|sync|db|seed].'
169
+ 'Invalid Action: Must be [up|down|check|create|reset|sync|db|seed|work].'
127
170
  );
128
171
  yargs.showHelp();
129
172
  process.exit(1);
@@ -27,7 +27,7 @@ module.exports = function (internals, isModule) {
27
27
  internals.argv = yargs
28
28
  .default(defaultConfig)
29
29
  .usage(
30
- 'Usage: db-migrate [up|down|check|reset|sync|create|db|seed] ' +
30
+ 'Usage: db-migrate [up|down|check|reset|sync|create|db|seed|work] ' +
31
31
  '[[dbname/]migrationName|all] [options]'
32
32
  )
33
33
  .describe(
@@ -128,6 +128,22 @@ module.exports = function (internals, isModule) {
128
128
  ' held by another process is considered stale and taken over.'
129
129
  )
130
130
  .number('lock-timeout')
131
+ .describe('parallel', 'work: jobs of background migrations run at once.')
132
+ .number('parallel')
133
+ .describe('pause', 'work: milliseconds to wait between two batches of a job.')
134
+ .number('pause')
135
+ .describe('batch', 'work: rows per batch of a job.')
136
+ .number('batch')
137
+ .describe('watch', 'work: keep looking for new jobs, until stopped.')
138
+ .boolean('watch')
139
+ .describe('interval', 'work: milliseconds between looking for new jobs.')
140
+ .number('interval')
141
+ .describe(
142
+ 'job-timeout',
143
+ 'work: milliseconds after which the job of a worker which stopped ' +
144
+ 'responding is taken over.'
145
+ )
146
+ .number('job-timeout')
131
147
  .describe(
132
148
  'lock-interval',
133
149
  'Milliseconds between checks while waiting for the migration lock.'
package/lib/release.js CHANGED
@@ -67,10 +67,11 @@ const releases = files => {
67
67
  };
68
68
 
69
69
  /**
70
- * The name a deprecated table or column is renamed to.
70
+ * The name a deprecated table or column is renamed to, with the time of the
71
+ * migration deprecating it, so fix learns the same name again.
71
72
  */
72
- const hiddenName = name => {
73
- const time = Math.floor(Date.now() / 1000);
73
+ const hiddenName = (name, file) => {
74
+ const time = String(file.name).split('-')[0];
74
75
  const plain = `__dbm_deprecated_${name}_${time}`;
75
76
 
76
77
  if (plain.length <= 60) {
@@ -116,8 +117,8 @@ const settings = (internals, entry) => {
116
117
  return Object.assign({}, DEFAULTS, project, entry.o || {});
117
118
  };
118
119
 
119
- const entry = (internals, name, options) => {
120
- const e = { r: internals.releaseLabel || null, to: hiddenName(name) };
120
+ const entry = (internals, name, options, file) => {
121
+ const e = { r: internals.releaseLabel || null, to: hiddenName(name, file) };
121
122
  const o = checkOptions(options);
122
123
 
123
124
  if (Object.keys(o).length) {
@@ -310,24 +311,28 @@ const unrecorded = walker =>
310
311
  });
311
312
 
312
313
  /**
313
- * Runs the steps due before the first migration of the release label.
314
+ * Runs the steps due before the first migration of the release label. fix
315
+ * only learns them again, like the migrations it learns.
314
316
  */
315
- const start = async (walker, label, index, releaseIndex) => {
317
+ const start = async (walker, label, index, releaseIndex, mode = 'up') => {
316
318
  const internals = walker.internals;
317
319
  const { renames, drops } = due(internals, index, releaseIndex);
318
- const purged = (await duePurges(walker._pdriver, internals, index, releaseIndex)).filter(
319
- e => e.drop === 'auto'
320
- );
321
320
 
322
- if (purged.length) {
323
- await purge(walker, label, purged);
321
+ if (mode === 'up') {
322
+ const purged = (
323
+ await duePurges(walker._pdriver, internals, index, releaseIndex)
324
+ ).filter(e => e.drop === 'auto');
325
+
326
+ if (purged.length) {
327
+ await purge(walker, label, purged);
328
+ }
324
329
  }
325
330
 
326
331
  if (!renames.length && !drops.length) {
327
332
  return;
328
333
  }
329
334
 
330
- log.info(`[release] starting release ${label}`);
335
+ log.info(`[release] ${mode === 'fix' ? 'learning' : 'starting'} release ${label}`);
331
336
  renames.forEach(item =>
332
337
  log.info(`[release] renaming the deprecated ${describe(item)} to ${item.entry.to}`)
333
338
  );
@@ -352,7 +357,11 @@ const start = async (walker, label, index, releaseIndex) => {
352
357
  return;
353
358
  }
354
359
 
355
- await require('./executors/versioned/v2').up(unrecorded(walker), walker.driver, file);
360
+ await require('./executors/versioned/v2')[mode](
361
+ unrecorded(walker),
362
+ walker.driver,
363
+ file
364
+ );
356
365
  };
357
366
 
358
367
  /**
package/lib/state.js CHANGED
@@ -328,6 +328,9 @@ module.exports = {
328
328
  }
329
329
  }
330
330
 
331
+ // fix learns the schema from scratch, it must not be loaded again
332
+ internals.learnFromScratch = emptyState === true;
333
+
331
334
  const schema = emptyState !== true ? _schema : null;
332
335
  if (schema) {
333
336
  internals.schema = parseSchema(schema.value);
@@ -351,6 +354,10 @@ module.exports = {
351
354
  * so the schema loaded during init is outdated.
352
355
  */
353
356
  reloadSchema: async function (driver, internals) {
357
+ if (internals.learnFromScratch) {
358
+ return;
359
+ }
360
+
354
361
  const schema = await driver._getKV(internals.migrationState, SSTATE);
355
362
  if (schema) {
356
363
  internals.schema = parseSchema(schema.value);
@@ -376,7 +383,8 @@ module.exports = {
376
383
  ) {
377
384
  const mig = await driver._getKV(internals.migrationState, file.name);
378
385
 
379
- if (mig && mig.value !== '{}') {
386
+ // fix learns the records again from scratch
387
+ if (mig && mig.value !== '{}' && op !== 'fix') {
380
388
  internals.modSchema = JSON.parse(mig.value);
381
389
  // records of older runs, they belong to no step of this run
382
390
  (internals.modSchema.s || []).forEach(entry => {
package/lib/walker.js CHANGED
@@ -363,13 +363,13 @@ Walker.prototype = {
363
363
  /**
364
364
  * Runs the steps due, before the first migration of a new release.
365
365
  */
366
- _enterRelease: async function (file) {
366
+ _enterRelease: async function (file, mode = 'up') {
367
367
  const release = this._release || { map: {}, index: {} };
368
368
  const r = release.map[file.name] || { label: null, index: 0 };
369
369
 
370
370
  if (r.index > this._applied) {
371
371
  this.internals.releaseIndex = release.index;
372
- await Release.start(this, r.label, r.index, release.index);
372
+ await Release.start(this, r.label, r.index, release.index, mode);
373
373
  this._applied = r.index;
374
374
  }
375
375
 
@@ -513,9 +513,23 @@ Walker.prototype = {
513
513
  this.prefix,
514
514
  this._driver,
515
515
  this.internals
516
- ).then(completedFiles =>
517
- dbmUtil.filterDown(completedFiles, partialName, count).sort(sortFn)
518
- );
516
+ ).then(async completedFiles => {
517
+ // the releases, learned again from the start
518
+ const allFiles = await File.loadFromFileystem(
519
+ this.directory,
520
+ this.prefix,
521
+ this.internals
522
+ ).catch(err => {
523
+ if (err.code === 'ENOENT') {
524
+ return [];
525
+ }
526
+
527
+ throw err;
528
+ });
529
+ this._releases(allFiles, []);
530
+
531
+ return dbmUtil.filterDown(completedFiles, partialName, count).sort(sortFn);
532
+ });
519
533
 
520
534
  if (this.internals.check) {
521
535
  return determine()
@@ -529,8 +543,9 @@ Walker.prototype = {
529
543
  log.info(this.title + 'Nothing to run');
530
544
  }
531
545
 
532
- return Promise.each(toRun, file => {
546
+ return Promise.each(toRun, async file => {
533
547
  log.verbose(this.title + 'preparing to run fix:', file.name);
548
+ await this._enterRelease(file, 'fix').catch(failedIn(file));
534
549
  const _meta = file.get()._meta || {};
535
550
  const version = _meta.version || 1;
536
551
  if (version < 2) {
package/package.json CHANGED
@@ -31,7 +31,7 @@
31
31
  "programmable",
32
32
  "api"
33
33
  ],
34
- "version": "1.5.0",
34
+ "version": "1.6.0",
35
35
  "bugs": {
36
36
  "url": "https://github.com/db-migrate/node-db-migrate/issues"
37
37
  },