Improve error handling in replication
Deprecate `Change.handleError`, it was used inconsistenly for a subset of possible errors only. Rework all `Change` methods to always report all errors to the caller via the callback. Rework `PersistedModel` to report change-tracking errors via the existing method `PersistedModel.handleChangeError`. This method can be customized on a per-model basis to provide different error handling. The default implementation emits `error` event on the model class, users can attach an event listener that can provide a custom error handler. NOTE: Unhandled `error` events crash the application by default.
This commit is contained in:
parent
6640f8a082
commit
63e2f4b134
|
@ -9,6 +9,7 @@ var CJSON = {stringify: require('canonical-json')};
|
|||
var async = require('async');
|
||||
var assert = require('assert');
|
||||
var debug = require('debug')('loopback:change');
|
||||
var deprecate = require('depd')('loopback');
|
||||
|
||||
/**
|
||||
* Change list entry.
|
||||
|
@ -73,18 +74,43 @@ module.exports = function(Change) {
|
|||
*/
|
||||
|
||||
Change.rectifyModelChanges = function(modelName, modelIds, callback) {
|
||||
var tasks = [];
|
||||
var Change = this;
|
||||
var errors = [];
|
||||
|
||||
modelIds.forEach(function(id) {
|
||||
tasks.push(function(cb) {
|
||||
var tasks = modelIds.map(function(id) {
|
||||
return function(cb) {
|
||||
Change.findOrCreateChange(modelName, id, function(err, change) {
|
||||
if (err) return Change.handleError(err, cb);
|
||||
change.rectify(cb);
|
||||
if (err) return next(err);
|
||||
change.rectify(next);
|
||||
});
|
||||
|
||||
function next(err) {
|
||||
if (err) {
|
||||
err.modelName = modelName;
|
||||
err.modelId = id;
|
||||
errors.push(err);
|
||||
}
|
||||
cb();
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
async.parallel(tasks, function(err) {
|
||||
if (err) return callback(err);
|
||||
if (errors.length) {
|
||||
var desc = errors
|
||||
.map(function(e) {
|
||||
return '#' + e.modelId + ' - ' + e.toString();
|
||||
})
|
||||
.join('\n');
|
||||
|
||||
var msg = 'Cannot rectify ' + modelName + ' changes:\n' + desc;
|
||||
err = new Error(msg);
|
||||
err.details = { errors: errors };
|
||||
return callback(err);
|
||||
}
|
||||
callback();
|
||||
});
|
||||
async.parallel(tasks, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -217,7 +243,7 @@ module.exports = function(Change) {
|
|||
var model = this.getModelCtor();
|
||||
var id = this.getModelId();
|
||||
model.findById(id, function(err, inst) {
|
||||
if (err) return Change.handleError(err, cb);
|
||||
if (err) return cb(err);
|
||||
if (inst) {
|
||||
cb(null, Change.revisionForInst(inst));
|
||||
} else {
|
||||
|
@ -459,6 +485,9 @@ module.exports = function(Change) {
|
|||
};
|
||||
|
||||
Change.handleError = function(err) {
|
||||
deprecate('Change.handleError is deprecated, ' +
|
||||
'you should pass errors to your callback instead.');
|
||||
|
||||
if (!this.settings.ignoreErrors) {
|
||||
throw err;
|
||||
}
|
||||
|
|
|
@ -1038,9 +1038,8 @@ PersistedModel.createUpdates = function(deltas, cb) {
|
|||
Model.findById(change.modelId, function(err, inst) {
|
||||
if (err) return cb(err);
|
||||
if (!inst) {
|
||||
console.error('missing data for change:', change);
|
||||
return cb &&
|
||||
cb(new Error('missing data for change: ' + change.modelId));
|
||||
cb(new Error('Missing data for change: ' + change.modelId));
|
||||
}
|
||||
if (inst.toObject) {
|
||||
update.data = inst.toObject();
|
||||
|
@ -1343,8 +1342,7 @@ PersistedModel.enableChangeTracking = function() {
|
|||
function cleanup() {
|
||||
Model.rectifyAllChanges(function(err) {
|
||||
if (err) {
|
||||
console.error(Model.modelName + ' Change Cleanup Error:');
|
||||
console.error(err);
|
||||
Model.handleChangeError(err, 'cleanup');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -1359,8 +1357,7 @@ function rectifyOnSave(ctx, next) {
|
|||
|
||||
function reportErrorAndNext(err) {
|
||||
if (err) {
|
||||
console.error(
|
||||
ctx.Model.modelName + '.rectifyChange(s) after save failed:' + err);
|
||||
ctx.Model.handleChangeError(err, 'after save');
|
||||
}
|
||||
next();
|
||||
}
|
||||
|
@ -1378,8 +1375,7 @@ function rectifyOnDelete(ctx, next) {
|
|||
|
||||
function reportErrorAndNext(err) {
|
||||
if (err) {
|
||||
console.error(
|
||||
ctx.Model.modelName + '.rectifyChange(s) after delete failed:' + err);
|
||||
ctx.Model.handleChangeError(err, 'after delete');
|
||||
}
|
||||
next();
|
||||
}
|
||||
|
@ -1426,11 +1422,9 @@ PersistedModel.rectifyAllChanges = function(callback) {
|
|||
* @param {Error} err Error object; see [Error object](http://docs.strongloop.com/display/LB/Error+object).
|
||||
*/
|
||||
|
||||
PersistedModel.handleChangeError = function(err) {
|
||||
if (err) {
|
||||
console.error(Model.modelName + ' Change Tracking Error:');
|
||||
console.error(err);
|
||||
}
|
||||
PersistedModel.handleChangeError = function(err, operationName) {
|
||||
if (!err) return;
|
||||
this.emit('error', err, operationName);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue