Skip to content

Commit 2469c6d

Browse files
committed
Merge pull request #199 from braydonf/start-error
Start/Stop Improvements
2 parents 16aa96a + 60af867 commit 2469c6d

8 files changed

Lines changed: 211 additions & 99 deletions

File tree

integration/regtest-node.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,12 @@ describe('Node Functionality', function() {
117117
});
118118
});
119119

120+
node.start(function(err) {
121+
if (err) {
122+
throw err;
123+
}
124+
});
125+
120126

121127
});
122128
});

lib/node.js

Lines changed: 55 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,13 @@ var _ = bitcore.deps._;
1010
var index = require('./');
1111
var log = index.log;
1212
var Bus = require('./bus');
13-
var BaseService = require('./service');
1413
var errors = require('./errors');
1514

1615
function Node(config) {
1716
if(!(this instanceof Node)) {
1817
return new Node(config);
1918
}
2019

21-
var self = this;
22-
2320
this.errors = errors; // So services can use errors without having to have bitcore-node as a dependency
2421
this.log = log;
2522
this.network = null;
@@ -38,13 +35,6 @@ function Node(config) {
3835

3936
this._setNetwork(config);
4037

41-
this.start(function(err) {
42-
if(err) {
43-
return self.emit('error', err);
44-
}
45-
self.emit('ready');
46-
});
47-
4838
}
4939

5040
util.inherits(Node, EventEmitter);
@@ -137,35 +127,53 @@ Node.prototype.getServiceOrder = function() {
137127
return stack;
138128
};
139129

140-
Node.prototype._instantiateService = function(service) {
130+
Node.prototype._startService = function(serviceInfo, callback) {
141131
var self = this;
142132

143-
$.checkState(_.isObject(service.config));
144-
$.checkState(!service.config.node);
133+
$.checkState(_.isObject(serviceInfo.config));
134+
$.checkState(!serviceInfo.config.node);
135+
$.checkState(!serviceInfo.config.name);
145136

146-
var config = service.config;
137+
log.info('Starting ' + serviceInfo.name);
138+
139+
var config = serviceInfo.config;
147140
config.node = this;
148-
config.name = service.name;
149-
var mod = new service.module(config);
150-
151-
// include in loaded services
152-
this.services[service.name] = mod;
153-
154-
// add API methods
155-
var methodData = mod.getAPIMethods();
156-
methodData.forEach(function(data) {
157-
var name = data[0];
158-
var instance = data[1];
159-
var method = data[2];
160-
161-
if (self[name]) {
162-
throw new Error('Existing API method exists: ' + name);
163-
} else {
164-
self[name] = function() {
165-
return method.apply(instance, arguments);
166-
};
141+
config.name = serviceInfo.name;
142+
var service = new serviceInfo.module(config);
143+
144+
service.start(function(err) {
145+
if (err) {
146+
return callback(err);
147+
}
148+
149+
// include in loaded services
150+
self.services[serviceInfo.name] = service;
151+
152+
// add API methods
153+
var methodData = service.getAPIMethods();
154+
var methodNameConflicts = [];
155+
methodData.forEach(function(data) {
156+
var name = data[0];
157+
var instance = data[1];
158+
var method = data[2];
159+
160+
if (self[name]) {
161+
methodNameConflicts.push(name);
162+
} else {
163+
self[name] = function() {
164+
return method.apply(instance, arguments);
165+
};
166+
}
167+
});
168+
169+
if (methodNameConflicts.length > 0) {
170+
return callback(new Error('Existing API method(s) exists: ' + methodNameConflicts.join(', ')));
167171
}
172+
173+
callback();
174+
168175
});
176+
169177
};
170178

171179
Node.prototype.start = function(callback) {
@@ -175,15 +183,15 @@ Node.prototype.start = function(callback) {
175183
async.eachSeries(
176184
servicesOrder,
177185
function(service, next) {
178-
log.info('Starting ' + service.name);
179-
try {
180-
self._instantiateService(service);
181-
} catch(err) {
186+
self._startService(service, next);
187+
},
188+
function(err) {
189+
if (err) {
182190
return callback(err);
183191
}
184-
self.services[service.name].start(next);
185-
},
186-
callback
192+
self.emit('ready');
193+
callback();
194+
}
187195
);
188196
};
189197

@@ -198,8 +206,13 @@ Node.prototype.stop = function(callback) {
198206
async.eachSeries(
199207
services,
200208
function(service, next) {
201-
log.info('Stopping ' + service.name);
202-
self.services[service.name].stop(next);
209+
if (self.services[service.name]) {
210+
log.info('Stopping ' + service.name);
211+
self.services[service.name].stop(next);
212+
} else {
213+
log.info('Stopping ' + service.name + ' (not started)');
214+
setImmediate(next);
215+
}
203216
},
204217
callback
205218
);

lib/scaffold/start.js

Lines changed: 47 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -83,34 +83,54 @@ function registerSyncHandlers(node, delay) {
8383

8484
function logSyncStatus() {
8585
log.info(
86-
'Sync Status: Tip:', node.services.db.tip.hash,
86+
'Database Sync Status: Tip:', node.services.db.tip.hash,
8787
'Height:', node.services.db.tip.__height,
8888
'Rate:', count/10, 'blocks per second'
8989
);
9090
}
9191

92-
node.on('synced', function() {
93-
clearInterval(interval);
94-
});
95-
9692
node.on('ready', function() {
97-
node.services.db.on('addblock', function(block) {
98-
count++;
99-
// Initialize logging if not already instantiated
100-
if (!interval) {
101-
interval = setInterval(function() {
102-
logSyncStatus();
103-
count = 0;
104-
}, delay);
105-
}
106-
});
93+
94+
if (node.services.db) {
95+
node.on('synced', function() {
96+
clearInterval(interval);
97+
logSyncStatus();
98+
});
99+
node.services.db.on('addblock', function(block) {
100+
count++;
101+
// Initialize logging if not already instantiated
102+
if (!interval) {
103+
interval = setInterval(function() {
104+
logSyncStatus();
105+
count = 0;
106+
}, delay);
107+
}
108+
});
109+
}
110+
107111
});
108112

109113
node.on('stopping', function() {
110114
clearInterval(interval);
111115
});
112116
}
113117

118+
/**
119+
* Will shutdown a node and then the process
120+
* @param {Object} _process - The Node.js process object
121+
* @param {Node} node - The Bitcore Node instance
122+
*/
123+
function cleanShutdown(_process, node) {
124+
node.stop(function(err) {
125+
if(err) {
126+
log.error('Failed to stop services: ' + err);
127+
return _process.exit(1);
128+
}
129+
log.info('Halted');
130+
_process.exit(0);
131+
});
132+
}
133+
114134
/**
115135
* Will register event handlers to stop the node for `process` events
116136
* `uncaughtException` and `SIGINT`.
@@ -133,15 +153,7 @@ function registerExitHandlers(_process, node) {
133153
});
134154
}
135155
if (options.sigint) {
136-
node.stop(function(err) {
137-
if(err) {
138-
log.error('Failed to stop services: ' + err);
139-
return _process.exit(1);
140-
}
141-
142-
log.info('Halted');
143-
_process.exit(0);
144-
});
156+
cleanShutdown(_process, node);
145157
}
146158
}
147159

@@ -190,6 +202,16 @@ function start(options) {
190202
log.error(err);
191203
});
192204

205+
node.start(function(err) {
206+
if(err) {
207+
log.error('Failed to start services');
208+
if (err.stack) {
209+
log.error(err.stack);
210+
}
211+
start.cleanShutdown(process, node);
212+
}
213+
});
214+
193215
return node;
194216

195217
}
@@ -235,3 +257,4 @@ module.exports.registerExitHandlers = registerExitHandlers;
235257
module.exports.registerSyncHandlers = registerSyncHandlers;
236258
module.exports.setupServices = setupServices;
237259
module.exports.spawnChildProcess = spawnChildProcess;
260+
module.exports.cleanShutdown = cleanShutdown;

lib/services/db.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ DB.prototype._setDataPath = function() {
7575
};
7676

7777
DB.prototype.start = function(callback) {
78+
7879
var self = this;
7980
if (!fs.existsSync(this.dataPath)) {
8081
mkdirp.sync(this.dataPath);

lib/services/web.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ WebService.prototype.stop = function(callback) {
4848
}
4949

5050
callback();
51-
})
51+
});
5252
};
5353

5454
WebService.prototype.setupAllRoutes = function() {
@@ -60,7 +60,7 @@ WebService.prototype.setupAllRoutes = function() {
6060
this.app.use('/' + this.node.services[key].getRoutePrefix(), subApp);
6161
this.node.services[key].setupRoutes(subApp, express);
6262
} else {
63-
log.info('Not setting up routes for ' + service.name);
63+
log.debug('No routes defined for: ' + key);
6464
}
6565
}
6666
};
@@ -186,4 +186,4 @@ WebService.prototype.socketMessageHandler = function(message, socketCallback) {
186186
}
187187
};
188188

189-
module.exports = WebService;
189+
module.exports = WebService;

test/node.unit.js

Lines changed: 25 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@ describe('Bitcore Node', function() {
6565
var TestNode = proxyquire('../lib/node', {});
6666
TestNode.prototype.start = sinon.spy();
6767
var node = new TestNode(config);
68-
TestNode.prototype.start.callCount.should.equal(1);
6968
node._unloadedServices.length.should.equal(1);
7069
node._unloadedServices[0].name.should.equal('test1');
7170
node._unloadedServices[0].module.should.equal(TestService);
@@ -105,29 +104,6 @@ describe('Bitcore Node', function() {
105104
should.exist(regtest);
106105
node.network.should.equal(regtest);
107106
});
108-
it('should emit error if an error occurred starting services', function(done) {
109-
var config = {
110-
datadir: 'testdir',
111-
services: [
112-
{
113-
name: 'test1',
114-
module: TestService
115-
}
116-
],
117-
};
118-
var TestNode = proxyquire('../lib/node', {});
119-
TestNode.prototype.start = function(callback) {
120-
setImmediate(function() {
121-
callback(new Error('error'));
122-
});
123-
};
124-
var node = new TestNode(config);
125-
node.once('error', function(err) {
126-
should.exist(err);
127-
err.message.should.equal('error');
128-
done();
129-
});
130-
});
131107
});
132108

133109
describe('#openBus', function() {
@@ -214,11 +190,12 @@ describe('Bitcore Node', function() {
214190
});
215191
});
216192

217-
describe('#_instantiateService', function() {
193+
describe('#_startService', function() {
218194
it('will instantiate an instance and load api methods', function() {
219195
var node = new Node(baseConfig);
220196
function TestService() {}
221197
util.inherits(TestService, BaseService);
198+
TestService.prototype.start = sinon.stub().callsArg(0);
222199
TestService.prototype.getData = function() {};
223200
TestService.prototype.getAPIMethods = function() {
224201
return [
@@ -230,9 +207,28 @@ describe('Bitcore Node', function() {
230207
module: TestService,
231208
config: {}
232209
};
233-
node._instantiateService(service);
234-
should.exist(node.services.testservice);
235-
should.exist(node.getData);
210+
node._startService(service, function(err) {
211+
if (err) {
212+
throw err;
213+
}
214+
TestService.prototype.start.callCount.should.equal(1);
215+
should.exist(node.services.testservice);
216+
should.exist(node.getData);
217+
});
218+
});
219+
it('will give an error from start', function() {
220+
var node = new Node(baseConfig);
221+
function TestService() {}
222+
util.inherits(TestService, BaseService);
223+
TestService.prototype.start = sinon.stub().callsArgWith(0, new Error('test'));
224+
var service = {
225+
name: 'testservice',
226+
module: TestService,
227+
config: {}
228+
};
229+
node._startService(service, function(err) {
230+
err.message.should.equal('test');
231+
});
236232
});
237233
});
238234

@@ -318,7 +314,7 @@ describe('Bitcore Node', function() {
318314

319315
node.start(function(err) {
320316
should.exist(err);
321-
err.message.should.match(/^Existing API method exists/);
317+
err.message.should.match(/^Existing API method\(s\) exists\:/);
322318
done();
323319
});
324320

0 commit comments

Comments
 (0)