Merge pull request #1 from w33ble/develop
Fix issue with slow responses from ES
This commit is contained in:
@@ -1 +1 @@
|
|||||||
4.x
|
6.x
|
||||||
|
|||||||
@@ -2,6 +2,13 @@
|
|||||||
|
|
||||||
Notable changes to the esqueue project. Pay attention to `[BREAKING]` changes when upgrading in pre-1.0 versions. As of 1.0, breaking changes will only happen in major versions.
|
Notable changes to the esqueue project. Pay attention to `[BREAKING]` changes when upgrading in pre-1.0 versions. As of 1.0, breaking changes will only happen in major versions.
|
||||||
|
|
||||||
|
## v3.0.0
|
||||||
|
|
||||||
|
- support for node v4 or earlier is no longer tested
|
||||||
|
- update several dependencies
|
||||||
|
- fix issue where job poller would not wait for ES response
|
||||||
|
- when job polling search fails, wait for a 20x interval before searching again
|
||||||
|
|
||||||
## v2.0.2
|
## v2.0.2
|
||||||
|
|
||||||
- Fix issue where creating a worker would not use the queue's doctype by default
|
- Fix issue where creating a worker would not use the queue's doctype by default
|
||||||
|
|||||||
13
package.json
13
package.json
@@ -29,20 +29,21 @@
|
|||||||
"@elastic/eslint-config-kibana": "^0.3.0",
|
"@elastic/eslint-config-kibana": "^0.3.0",
|
||||||
"babel-cli": "^6.23.0",
|
"babel-cli": "^6.23.0",
|
||||||
"babel-core": "^6.23.1",
|
"babel-core": "^6.23.1",
|
||||||
"babel-eslint": "^7.1.1",
|
"babel-eslint": "6.1.2",
|
||||||
"babel-plugin-add-module-exports": "^0.2.1",
|
"babel-plugin-add-module-exports": "^0.2.1",
|
||||||
"babel-preset-es2015": "^6.22.0",
|
"babel-preset-es2015": "^6.22.0",
|
||||||
"elasticsearch": "^12.0.0",
|
"elasticsearch": "^13.0.1",
|
||||||
"eslint": "^3.16.1",
|
"eslint": "3.11.1",
|
||||||
"eslint-plugin-mocha": "^4.8.0",
|
"eslint-plugin-babel": "4.0.0",
|
||||||
"eslint-plugin-react": "^6.10.0",
|
"eslint-plugin-mocha": "4.7.0",
|
||||||
|
"eslint-plugin-react": "^7.0.1",
|
||||||
"expect.js": "~0.3.1",
|
"expect.js": "~0.3.1",
|
||||||
"lodash": "^4.17.4",
|
"lodash": "^4.17.4",
|
||||||
"mocha": "^3.2.0",
|
"mocha": "^3.2.0",
|
||||||
"nyc": "^10.1.2",
|
"nyc": "^10.1.2",
|
||||||
"proxyquire": "^1.7.4",
|
"proxyquire": "^1.7.4",
|
||||||
"retire": "^1.2.12",
|
"retire": "^1.2.12",
|
||||||
"sinon": "^1.17.3"
|
"sinon": "^2.3.1"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"elasticsearch": ">=11.0.1"
|
"elasticsearch": ">=11.0.1"
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ export default {
|
|||||||
EVENT_JOB_CREATE_ERROR: 'job:creation error',
|
EVENT_JOB_CREATE_ERROR: 'job:creation error',
|
||||||
EVENT_WORKER_COMPLETE: 'worker:job complete',
|
EVENT_WORKER_COMPLETE: 'worker:job complete',
|
||||||
EVENT_WORKER_JOB_CLAIM_ERROR: 'worker:claim job error',
|
EVENT_WORKER_JOB_CLAIM_ERROR: 'worker:claim job error',
|
||||||
|
EVENT_WORKER_JOB_POLLING_READY: 'worker:job poller ready',
|
||||||
|
EVENT_WORKER_JOB_SEARCH_COMPLETE: 'worker:pending jobs returned',
|
||||||
EVENT_WORKER_JOB_SEARCH_ERROR: 'worker:pending jobs error',
|
EVENT_WORKER_JOB_SEARCH_ERROR: 'worker:pending jobs error',
|
||||||
EVENT_WORKER_JOB_UPDATE_ERROR: 'worker:update job error',
|
EVENT_WORKER_JOB_UPDATE_ERROR: 'worker:update job error',
|
||||||
EVENT_WORKER_JOB_FAIL: 'worker:job failed',
|
EVENT_WORKER_JOB_FAIL: 'worker:job failed',
|
||||||
|
|||||||
@@ -34,14 +34,18 @@ export default class Worker extends events.EventEmitter {
|
|||||||
|
|
||||||
this.debug = (...msg) => debug(...msg, `id: ${this.id}`);
|
this.debug = (...msg) => debug(...msg, `id: ${this.id}`);
|
||||||
|
|
||||||
this._checker = false;
|
this._poller = {
|
||||||
this._running = true;
|
timer: false,
|
||||||
|
enabled: true,
|
||||||
|
running: false,
|
||||||
|
};
|
||||||
|
|
||||||
this.debug(`Created worker for job type ${this.jobtype}`);
|
this.debug(`Created worker for job type ${this.jobtype}`);
|
||||||
this._startJobPolling();
|
this._startJobPolling();
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
this._running = false;
|
this._poller.enabled = false;
|
||||||
this._stopJobPolling();
|
this._stopJobPolling();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,24 +251,45 @@ export default class Worker extends events.EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_startJobPolling() {
|
_startJobPolling() {
|
||||||
if (!this._running) {
|
if (!this._poller.enabled || this._poller.running) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._checker = setInterval(() => {
|
this._poller.timer = setTimeout(() => {
|
||||||
this._getPendingJobs()
|
this._getPendingJobs()
|
||||||
.then((jobs) => this._claimPendingJobs(jobs));
|
.then((jobs) => {
|
||||||
|
if (!this._poller.running) return;
|
||||||
|
|
||||||
|
const foundJobs = (!jobs || jobs.length === 0);
|
||||||
|
const task = foundJobs ? Promise.resolve() : this._claimPendingJobs(jobs);
|
||||||
|
|
||||||
|
task.then(() => {
|
||||||
|
this._poller.running = false;
|
||||||
|
this._startJobPolling();
|
||||||
|
});
|
||||||
|
}, () => {
|
||||||
|
// if the search failed for some reason, back off the polling
|
||||||
|
// we assume errors came from a busy cluster
|
||||||
|
// TODO: check what error actually happened
|
||||||
|
const multiplier = 20;
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this._poller.running = false;
|
||||||
|
this._startJobPolling();
|
||||||
|
}, this.checkInterval * multiplier);
|
||||||
|
});
|
||||||
} , this.checkInterval);
|
} , this.checkInterval);
|
||||||
|
|
||||||
|
this._poller.running = true;
|
||||||
|
this.emit(constants.EVENT_WORKER_JOB_POLLING_READY);
|
||||||
}
|
}
|
||||||
|
|
||||||
_stopJobPolling() {
|
_stopJobPolling() {
|
||||||
clearInterval(this._checker);
|
this._poller.running = false;
|
||||||
|
clearTimeout(this._poller.timer);
|
||||||
}
|
}
|
||||||
|
|
||||||
_claimPendingJobs(jobs) {
|
_claimPendingJobs(jobs = []) {
|
||||||
if (!jobs || jobs.length === 0) return;
|
|
||||||
|
|
||||||
this._stopJobPolling();
|
|
||||||
let claimed = false;
|
let claimed = false;
|
||||||
|
|
||||||
// claim a single job, stopping after first successful claim
|
// claim a single job, stopping after first successful claim
|
||||||
@@ -290,10 +315,8 @@ export default class Worker extends events.EventEmitter {
|
|||||||
this.debug(`Claimed job ${claimedJob._id}`);
|
this.debug(`Claimed job ${claimedJob._id}`);
|
||||||
return this._performJob(claimedJob);
|
return this._performJob(claimedJob);
|
||||||
})
|
})
|
||||||
.then(() => this._startJobPolling())
|
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
this.debug('Error claiming jobs', err);
|
this.debug('Error claiming jobs', err);
|
||||||
this._startJobPolling();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -338,7 +361,10 @@ export default class Worker extends events.EventEmitter {
|
|||||||
})
|
})
|
||||||
.then((results) => {
|
.then((results) => {
|
||||||
const jobs = results.hits.hits;
|
const jobs = results.hits.hits;
|
||||||
|
|
||||||
this.debug(`${jobs.length} outstanding jobs returned`);
|
this.debug(`${jobs.length} outstanding jobs returned`);
|
||||||
|
this.emit(constants.EVENT_WORKER_JOB_SEARCH_COMPLETE, jobs);
|
||||||
|
|
||||||
return jobs;
|
return jobs;
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
|
|||||||
@@ -187,24 +187,63 @@ describe('Worker class', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should not poll once destroyed', function () {
|
it('should not poll once destroyed', function () {
|
||||||
|
// remove the search spy
|
||||||
|
mockQueue.client.search.restore();
|
||||||
|
|
||||||
|
// mock the search, return 0 new jobs
|
||||||
|
const zeroHits = { hits: { hits: [] } };
|
||||||
|
const searchStub = sinon.stub(mockQueue.client, 'search', () => Promise.resolve(zeroHits));
|
||||||
|
|
||||||
const worker = new Worker(mockQueue, 'test', noop);
|
const worker = new Worker(mockQueue, 'test', noop);
|
||||||
|
|
||||||
|
function waitForSearch() {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
worker.once(constants.EVENT_WORKER_JOB_SEARCH_COMPLETE, () => {
|
||||||
|
resolve()
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function waitForPoller() {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
worker.once(constants.EVENT_WORKER_JOB_POLLING_READY, () => {
|
||||||
|
resolve()
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// move the clock a couple times, test for searches each time
|
// move the clock a couple times, test for searches each time
|
||||||
sinon.assert.notCalled(searchSpy);
|
sinon.assert.notCalled(searchStub);
|
||||||
clock.tick(defaults.interval);
|
|
||||||
sinon.assert.calledOnce(searchSpy);
|
|
||||||
clock.tick(defaults.interval);
|
|
||||||
sinon.assert.calledTwice(searchSpy);
|
|
||||||
|
|
||||||
// destroy the worker, move the clock, make sure another search doesn't happen
|
const firstWait = waitForSearch();
|
||||||
worker.destroy();
|
|
||||||
clock.tick(defaults.interval);
|
clock.tick(defaults.interval);
|
||||||
sinon.assert.calledTwice(searchSpy);
|
|
||||||
|
|
||||||
// manually call job poller, move the clock, make sure another search doesn't happen
|
return firstWait
|
||||||
worker._startJobPolling();
|
.then(() => {
|
||||||
clock.tick(defaults.interval);
|
sinon.assert.calledOnce(searchStub);
|
||||||
sinon.assert.calledTwice(searchSpy);
|
return waitForPoller();
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
const secondWait = waitForSearch();
|
||||||
|
clock.tick(defaults.interval);
|
||||||
|
return secondWait;
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
sinon.assert.calledTwice(searchStub);
|
||||||
|
return waitForPoller();
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
// destroy the worker, move the clock, make sure another search doesn't happen
|
||||||
|
worker.destroy();
|
||||||
|
|
||||||
|
clock.tick(defaults.interval);
|
||||||
|
sinon.assert.calledTwice(searchStub);
|
||||||
|
|
||||||
|
// manually call job poller, move the clock, make sure another search doesn't happen
|
||||||
|
worker._startJobPolling();
|
||||||
|
clock.tick(defaults.interval);
|
||||||
|
sinon.assert.calledTwice(searchStub);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user