Crash on reindex

- Introduced the concept of a Cancellation error so that services can choose to watch for a cancellation flag. 
- Services can then send this error back and it will be forwarded to the node.
- The node will then know to call shutdown appropriately.
This commit is contained in:
Chris Kleeschulte 2015-09-15 16:38:41 -04:00
parent dc6d0e681c
commit 4ee11ed73b
5 changed files with 31 additions and 13 deletions

View File

@ -15,11 +15,14 @@ Consensus.BlockExists = createError('BlockExists', Consensus);
var Transaction = createError('Transaction', BitcoreNodeError);
Transaction.NotFound = createError('NotFound', Transaction);
var Cancellation = createError('Cancellation', BitcoreNodeError);
module.exports = {
Error: BitcoreNodeError,
NoOutputs: NoOutputs,
NoOutput: NoOutput,
Wallet: Wallet,
Consensus: Consensus,
Transaction: Transaction
Transaction: Transaction,
Cancellation: Cancellation
};

View File

@ -22,7 +22,7 @@ function Node(config) {
this.network = null;
this.services = {};
this._unloadedServices = [];
this._loadingServices = {};
this.started = false;
// TODO type check the arguments of config.services
if (config.services) {
@ -141,16 +141,15 @@ Node.prototype._startService = function(serviceInfo, callback) {
config.node = this;
config.name = serviceInfo.name;
var service = new serviceInfo.module(config);
self._loadingServices[service.name] = service;
// include in loaded services
self.services[serviceInfo.name] = service;
service.start(function(err) {
if (err) {
return callback(err);
}
// include in loaded services
self.services[serviceInfo.name] = service;
// add API methods
var methodData = service.getAPIMethods();
var methodNameConflicts = [];
@ -188,16 +187,24 @@ Node.prototype.start = function(callback) {
self._startService(service, next);
},
function(err) {
if (err) {
if (err instanceof errors.Cancellation) {
self.cancellation = true;
return callback(err);
} else if (err) {
return callback(err);
}
self.emit('ready');
self.started = true;
callback();
}
);
};
Node.prototype.stop = function(callback) {
if (!this.started && !this.cancellation) {
this.pendingStop = true;
return;
}
log.info('Beginning shutdown');
var self = this;
var services = this.getServiceOrder().reverse();
@ -208,9 +215,9 @@ Node.prototype.stop = function(callback) {
async.eachSeries(
services,
function(service, next) {
if (self._loadingServices[service.name]) {
if (self.services[service.name]) {
log.info('Stopping ' + service.name);
self._loadingServices[service.name].stop(next);
self.services[service.name].stop(next);
} else {
log.info('Stopping ' + service.name + ' (not started)');
setImmediate(next);

View File

@ -7,6 +7,7 @@ var bitcore = require('bitcore');
var _ = bitcore.deps._;
var $ = bitcore.util.preconditions;
var log = index.log;
var errors = index.errors;
var child_process = require('child_process');
var fs = require('fs');
var shuttingDown = false;
@ -215,7 +216,10 @@ function start(options) {
});
node.start(function(err) {
if(err) {
if (err instanceof errors.Cancellation) {
log.warn('Cancelled start up of all services');
start.cleanShutdown(process, node);
} else if(err) {
log.error('Failed to start services');
if (err.stack) {
log.error(err.stack);

View File

@ -9,6 +9,7 @@ var $ = bitcore.util.preconditions;
var _ = bitcore.deps._;
var index = require('../');
var log = index.log;
var errors = index.errors;
var Service = require('../service');
/**
@ -158,6 +159,10 @@ Bitcoin.prototype.start = function(callback) {
}
if (self._reindex) {
var interval = setInterval(function() {
if (self.node.pendingStop) {
clearInterval(interval);
return callback(new errors.Cancellation(), false);
}
var percentSynced = bindings.syncPercentage();
log.info("Bitcoin Core Daemon Reindex Percentage: " + percentSynced);
if (percentSynced >= 100) {

View File

@ -324,6 +324,7 @@ describe('Bitcore Node', function() {
describe('#stop', function() {
it('will call stop for each service', function(done) {
var node = new Node(baseConfig);
node.started = true;
function TestService() {}
util.inherits(TestService, BaseService);
TestService.prototype.stop = sinon.stub().callsArg(0);
@ -333,10 +334,8 @@ describe('Bitcore Node', function() {
['getData', this, this.getData, 1]
];
};
var testService = new TestService({node: node});
node._loadingServices = {'test1': testService};
node.services = {
'test1': testService
'test1': new TestService({node: node})
};
node.test2 = {};
node.test2.stop = sinon.stub().callsArg(0);