Add a fail fast mechanism instead of rejecting the whole process on error.

This prevent clobbering registry and cache, since when the promise is rejected usually process.exit(1) is called.
This commit is contained in:
André Cruz
2013-06-02 11:47:59 +01:00
parent c9ef3fa1d7
commit 12078f07c6

View File

@@ -64,6 +64,7 @@ Manager.prototype.resolve = function () {
this._fetching = {};
this._nrFetching = 0;
this._failed = {};
this._failFast = false;
this._deferred = Q.defer();
// If there's nothing to resolve, simply dissect
@@ -195,6 +196,11 @@ Manager.prototype._fetch = function (decEndpoint) {
var deferred = this._deferred;
var that = this;
// Check if the whole process is marked to fail fast
if (this._failFast) {
return;
}
// Mark as being fetched
this._fetching[name] = this._fetching[name] || [];
this._fetching[name].push(decEndpoint);
@@ -204,15 +210,13 @@ Manager.prototype._fetch = function (decEndpoint) {
// Note that the promise is stored in the decomposed endpoint
// because it might be reused if a similar endpoint needs to be resolved
decEndpoint.promise = this._repository.fetch(decEndpoint)
// When done, call onFetch
.spread(this._onFetch.bind(this, deferred, decEndpoint))
// If it fails, we make the whole process to error out
.fail(function (err) {
that._extendNotification(err, decEndpoint);
deferred.reject(err);
})
// When done, call onFetchSuccess
.spread(this._onFetchSuccess.bind(this, deferred, decEndpoint))
// If it fails, call onFetchFailure
.fail(this._onFetchError.bind(this, deferred, decEndpoint))
// Listen to progress to proxy them to the resolve deferred
// Note that we also mark where the notification is coming from
// Note that we also add additional information to it, such as
// where the notification is coming from
.progress(function (notification) {
that._extendNotification(notification, decEndpoint);
deferred.notify(notification);
@@ -221,18 +225,38 @@ Manager.prototype._fetch = function (decEndpoint) {
return decEndpoint.promise;
};
Manager.prototype._onFetch = function (deferred, decEndpoint, canonicalPkg, pkgMeta) {
Manager.prototype._onFetchError = function (deferred, decEndpoint, err) {
var name = decEndpoint.name;
this._extendNotification(err, decEndpoint);
// Remove from being fetched list
mout.array.remove(this._fetching[name], decEndpoint);
this._nrFetching--;
// Add to the failed list
this._failed[name] = this._failed[name] || [];
this._failed[name].push(err);
delete decEndpoint.promise;
// Make the whole process to fail fast
this._failFast = true;
// If the resolve process ended, parse the resolved packages
// to find the most suitable version for each package
if (this._nrFetching <= 0) {
process.nextTick(this._dissect.bind(this));
}
};
Manager.prototype._onFetchSuccess = function (deferred, decEndpoint, canonicalPkg, pkgMeta) {
var name;
var resolved;
var index;
var initialName = decEndpoint.name;
// If the deferred associated with the process is already rejected,
// do not proceed.
if (deferred.promise.isRejected()) {
return;
}
// Remove from being fetched list
mout.array.remove(this._fetching[initialName], decEndpoint);
this._nrFetching--;
@@ -320,8 +344,18 @@ Manager.prototype._parseDependencies = function (decEndpoint, pkgMeta) {
};
Manager.prototype._dissect = function () {
var suitables = {};
var err;
var suitables;
// If something failed, reject the whole resolve promise
// with the first error
if (this._failFast) {
err = mout.object.values(this._failed)[0][0];
this._deferred.reject(err);
return;
}
suitables = {};
mout.object.forOwn(this._resolved, function (decEndpoints, name) {
var nonSemver;
var validSemver;