From 1a0e2b681bcc443fc07aa7da1b8601b762cdf3fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Aubert?= Date: Thu, 26 May 2016 17:37:37 +0200 Subject: [PATCH 1/9] Fixes #309, added skipped status to fallback-jobs --- batch/job_status.js | 1 + batch/models/job_base.js | 1 + batch/models/job_fallback.js | 72 +++++- test/acceptance/job.fallback.test.js | 330 +++++++++++++++++++++++++-- 4 files changed, 388 insertions(+), 16 deletions(-) diff --git a/batch/job_status.js b/batch/job_status.js index d212679f..889f1b43 100644 --- a/batch/job_status.js +++ b/batch/job_status.js @@ -6,6 +6,7 @@ var JOB_STATUS_ENUM = { DONE: 'done', CANCELLED: 'cancelled', FAILED: 'failed', + SKIPPED: 'skipped', UNKNOWN: 'unknown' }; diff --git a/batch/models/job_base.js b/batch/models/job_base.js index 5169e560..3b9fc611 100644 --- a/batch/models/job_base.js +++ b/batch/models/job_base.js @@ -7,6 +7,7 @@ var validStatusTransitions = [ [jobStatus.PENDING, jobStatus.RUNNING], [jobStatus.PENDING, jobStatus.CANCELLED], [jobStatus.PENDING, jobStatus.UNKNOWN], + [jobStatus.PENDING, jobStatus.SKIPPED], [jobStatus.RUNNING, jobStatus.DONE], [jobStatus.RUNNING, jobStatus.FAILED], [jobStatus.RUNNING, jobStatus.CANCELLED], diff --git a/batch/models/job_fallback.js b/batch/models/job_fallback.js index 22d08547..ca582512 100644 --- a/batch/models/job_fallback.js +++ b/batch/models/job_fallback.js @@ -185,9 +185,79 @@ JobFallback.prototype.setStatus = function (status, errorMesssage) { throw new Error('Cannot set status from ' + this.data.status + ' to ' + status); } + if (!resultFromQuery.isChangeAppliedToQueryFallback || status === jobStatus.CANCELLED) { + this._setSkipped(status); + } + this.data.updated_at = now; }; +JobFallback.prototype._setSkipped = function (status) { + this._setSkippedQueryStatus(); + this._setSkippedJobStatus(); + + if (status === jobStatus.CANCELLED || status === jobStatus.FAILED) { + this._setRestPendingToSkipped(status); + } +}; + +JobFallback.prototype._setSkippedQueryStatus = function () { + // jshint maxcomplexity: 8 + for (var i = 0; i < this.data.query.query.length; i++) { + if (this.data.query.query[i].status === jobStatus.FAILED && this.data.query.query[i].onsuccess) { + if (this.isValidStatusTransition(this.data.query.query[i].fallback_status, jobStatus.SKIPPED)) { + this.data.query.query[i].fallback_status = jobStatus.SKIPPED; + } + } + + if (this.data.query.query[i].status === jobStatus.DONE && this.data.query.query[i].onerror) { + if (this.isValidStatusTransition(this.data.query.query[i].fallback_status, jobStatus.SKIPPED)) { + this.data.query.query[i].fallback_status = jobStatus.SKIPPED; + } + } + + if (this.data.query.query[i].status === jobStatus.CANCELLED && this.data.query.query[i].fallback_status) { + if (this.isValidStatusTransition(this.data.query.query[i].fallback_status, jobStatus.SKIPPED)) { + this.data.query.query[i].fallback_status = jobStatus.SKIPPED; + } + } + } +}; + +JobFallback.prototype._setSkippedJobStatus = function () { + // jshint maxcomplexity: 7 + + if (this.data.status === jobStatus.FAILED && this.data.query.onsuccess) { + if (this.isValidStatusTransition(this.data.fallback_status, jobStatus.SKIPPED)) { + this.data.fallback_status = jobStatus.SKIPPED; + } + } + + if (this.data.status === jobStatus.DONE && this.data.query.onerror) { + if (this.isValidStatusTransition(this.data.fallback_status, jobStatus.SKIPPED)) { + this.data.fallback_status = jobStatus.SKIPPED; + } + } + + if (this.data.status === jobStatus.CANCELLED && this.data.fallback_status) { + if (this.isValidStatusTransition(this.data.fallback_status, jobStatus.SKIPPED)) { + this.data.fallback_status = jobStatus.SKIPPED; + } + } +}; + +JobFallback.prototype._setRestPendingToSkipped = function (status) { + for (var i = 0; i < this.data.query.query.length; i++) { + if (this.data.query.query[i].status === jobStatus.PENDING) { + this.data.query.query[i].status = jobStatus.SKIPPED; + } + if (this.data.query.query[i].status !== status && + this.data.query.query[i].fallback_status === jobStatus.PENDING) { + this.data.query.query[i].fallback_status = jobStatus.SKIPPED; + } + } +}; + JobFallback.prototype._getLastStatusFromFinishedQuery = function () { var lastStatus = jobStatus.DONE; @@ -263,7 +333,7 @@ JobFallback.prototype._shouldTryToApplyStatusTransitionToQueryFallback = functio }; JobFallback.prototype._setQueryStatus = function (status, errorMesssage) { - // jshint maxcomplexity: 7 + // jshint maxcomplexity: 8 var isValid = false; var isChangeAppliedToQueryFallback = false; diff --git a/test/acceptance/job.fallback.test.js b/test/acceptance/job.fallback.test.js index 9a13491e..d549b279 100644 --- a/test/acceptance/job.fallback.test.js +++ b/test/acceptance/job.fallback.test.js @@ -133,7 +133,7 @@ describe('Batch API fallback job', function () { "query": "SELECT * FROM untitle_table_4", "onerror": "SELECT * FROM untitle_table_4 limit 1", "status": "done", - "fallback_status": "pending" + "fallback_status": "skipped" }] }; var interval = setInterval(function () { @@ -268,7 +268,7 @@ describe('Batch API fallback job', function () { query: 'SELECT * FROM nonexistent_table /* query should fail */', onsuccess: 'SELECT * FROM untitle_table_4 limit 1', status: 'failed', - fallback_status: 'pending', + fallback_status: 'skipped', failed_reason: 'relation "nonexistent_table" does not exist' }] }; @@ -424,7 +424,7 @@ describe('Batch API fallback job', function () { return done(err); } var job = JSON.parse(res.body); - if (job.status === jobStatus.FAILED && job.fallback_status === jobStatus.PENDING) { + if (job.status === jobStatus.FAILED && job.fallback_status === jobStatus.SKIPPED) { clearInterval(interval); assert.deepEqual(job.query, expectedQuery); done(); @@ -560,7 +560,7 @@ describe('Batch API fallback job', function () { return done(err); } var job = JSON.parse(res.body); - if (job.status === jobStatus.DONE && job.fallback_status === jobStatus.PENDING) { + if (job.status === jobStatus.DONE && job.fallback_status === jobStatus.SKIPPED) { clearInterval(interval); assert.deepEqual(job.query, expectedQuery); done(); @@ -759,13 +759,13 @@ describe('Batch API fallback job', function () { "query": "SELECT * FROM nonexistent_table /* should fail */", "onsuccess": "SELECT * FROM untitle_table_4 limit 1", "status": "failed", - "fallback_status": "pending", + "fallback_status": "skipped", "failed_reason": 'relation "nonexistent_table" does not exist' }, { "query": "SELECT * FROM untitle_table_4 limit 2", "onsuccess": "SELECT * FROM untitle_table_4 limit 3", - "status": "pending", - "fallback_status": "pending" + "status": "skipped", + "fallback_status": "skipped" }] }; @@ -842,7 +842,7 @@ describe('Batch API fallback job', function () { "query": "SELECT * FROM nonexistent_table /* should fail */", "onsuccess": "SELECT * FROM untitle_table_4 limit 3", "status": "failed", - "fallback_status": "pending", + "fallback_status": "skipped", "failed_reason": 'relation "nonexistent_table" does not exist' }] }; @@ -875,7 +875,7 @@ describe('Batch API fallback job', function () { }); }); - describe('"onerror" should not be triggered for any query', function () { + describe('"onerror" should not be triggered for any query and "skipped"', function () { var fallbackJob = {}; it('should create a job', function (done) { @@ -914,12 +914,12 @@ describe('Batch API fallback job', function () { query: 'SELECT * FROM untitle_table_4 limit 1', onerror: 'SELECT * FROM untitle_table_4 limit 2', status: 'done', - fallback_status: 'pending' + fallback_status: 'skipped' }, { query: 'SELECT * FROM untitle_table_4 limit 3', onerror: 'SELECT * FROM untitle_table_4 limit 4', status: 'done', - fallback_status: 'pending' + fallback_status: 'skipped' }] }; @@ -943,6 +943,144 @@ describe('Batch API fallback job', function () { assert.deepEqual(job.query, expectedQuery); done(); } else if (job.status === jobStatus.FAILED || job.status === jobStatus.CANCELLED) { + clearInterval(interval); + done(new Error('Job ' + job.job_id + ' is ' + job.status + ', expected to be done')); + } + }); + }, 50); + }); + }); + + describe('"onsuccess" should not be triggered for any query and "skipped"', function () { + var fallbackJob = {}; + + it('should create a job', function (done) { + assert.response(app, { + url: '/api/v2/sql/job?api_key=1234', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'host': 'vizzuality.cartodb.com' + }, + method: 'POST', + data: querystring.stringify({ + query: { + query: [{ + query: "SELECT * FROM untitle_table_4 limit 1, /* should fail */", + onsuccess: "SELECT * FROM untitle_table_4 limit 2" + }] + } + }) + }, { + status: 201 + }, function (res, err) { + if (err) { + return done(err); + } + fallbackJob = JSON.parse(res.body); + done(); + }); + }); + + it('job should be failed', function (done) { + var expectedQuery = { + query: [{ + query: 'SELECT * FROM untitle_table_4 limit 1, /* should fail */', + onsuccess: 'SELECT * FROM untitle_table_4 limit 2', + status: 'failed', + fallback_status: 'skipped', + failed_reason: 'syntax error at end of input' + }] + }; + + var interval = setInterval(function () { + assert.response(app, { + url: '/api/v2/sql/job/' + fallbackJob.job_id + '?api_key=1234&', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'host': 'vizzuality.cartodb.com' + }, + method: 'GET' + }, { + status: 200 + }, function (res, err) { + if (err) { + return done(err); + } + var job = JSON.parse(res.body); + if (job.status === jobStatus.FAILED) { + clearInterval(interval); + assert.deepEqual(job.query, expectedQuery); + done(); + } else if (job.status === jobStatus.DONE || job.status === jobStatus.CANCELLED) { + clearInterval(interval); + done(new Error('Job ' + job.job_id + ' is ' + job.status + ', expected to be failed')); + } + }); + }, 50); + }); + }); + + + describe('"onsuccess" should not be triggered and "skipped"', function () { + var fallbackJob = {}; + + it('should create a job', function (done) { + assert.response(app, { + url: '/api/v2/sql/job?api_key=1234', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'host': 'vizzuality.cartodb.com' + }, + method: 'POST', + data: querystring.stringify({ + query: { + query: [{ + query: "SELECT * FROM untitle_table_4 limit 1, /* should fail */", + }], + onsuccess: "SELECT * FROM untitle_table_4 limit 2" + } + }) + }, { + status: 201 + }, function (res, err) { + if (err) { + return done(err); + } + fallbackJob = JSON.parse(res.body); + done(); + }); + }); + + it('job should be failed', function (done) { + var expectedQuery = { + query: [{ + query: 'SELECT * FROM untitle_table_4 limit 1, /* should fail */', + status: 'failed', + failed_reason: 'syntax error at end of input' + }], + onsuccess: 'SELECT * FROM untitle_table_4 limit 2' + }; + + var interval = setInterval(function () { + assert.response(app, { + url: '/api/v2/sql/job/' + fallbackJob.job_id + '?api_key=1234&', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'host': 'vizzuality.cartodb.com' + }, + method: 'GET' + }, { + status: 200 + }, function (res, err) { + if (err) { + return done(err); + } + var job = JSON.parse(res.body); + if (job.status === jobStatus.FAILED && job.fallback_status === jobStatus.SKIPPED) { + clearInterval(interval); + assert.deepEqual(job.query, expectedQuery); + done(); + } else if (job.status === jobStatus.DONE || job.status === jobStatus.CANCELLED) { clearInterval(interval); done(new Error('Job ' + job.job_id + ' is ' + job.status + ', expected to be failed')); } @@ -1329,7 +1467,7 @@ describe('Batch API fallback job', function () { job.status === jobStatus.FAILED || job.status === jobStatus.CANCELLED) { clearInterval(interval); - done(new Error('Job ' + job.job_id + ' is ' + job.status + ', expected to be done')); + done(new Error('Job ' + job.job_id + ' is ' + job.status + ', expected to be running')); } }); }, 50); @@ -1341,7 +1479,7 @@ describe('Batch API fallback job', function () { "query": "SELECT pg_sleep(3)", "onsuccess": "SELECT pg_sleep(0)", "status": "cancelled", - "fallback_status": "pending" + "fallback_status": "skipped" }], "onsuccess": "SELECT pg_sleep(0)" }; @@ -1360,7 +1498,7 @@ describe('Batch API fallback job', function () { return done(err); } var job = JSON.parse(res.body); - if (job.status === jobStatus.CANCELLED && job.fallback_status === jobStatus.PENDING) { + if (job.status === jobStatus.CANCELLED && job.fallback_status === jobStatus.SKIPPED) { assert.deepEqual(job.query, expectedQuery); done(); } else if (job.status === jobStatus.DONE || job.status === jobStatus.FAILED) { @@ -1469,7 +1607,7 @@ describe('Batch API fallback job', function () { return done(err); } var job = JSON.parse(res.body); - if (job.status === jobStatus.CANCELLED && job.fallback_status === jobStatus.PENDING) { + if (job.status === jobStatus.CANCELLED && job.fallback_status === jobStatus.SKIPPED) { assert.deepEqual(job.query, expectedQuery); done(); } else if (job.status === jobStatus.DONE || job.status === jobStatus.FAILED) { @@ -1478,4 +1616,166 @@ describe('Batch API fallback job', function () { }); }); }); + + describe('should run first "onerror" and job "onerror" and skip the other ones', function () { + var fallbackJob = {}; + + it('should create a job', function (done) { + assert.response(app, { + url: '/api/v2/sql/job?api_key=1234', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'host': 'vizzuality.cartodb.com' + }, + method: 'POST', + data: querystring.stringify({ + "query": { + "query": [{ + "query": "SELECT * FROM untitle_table_4 limit 1, should fail", + "onerror": "SELECT * FROM untitle_table_4 limit 2" + }, { + "query": "SELECT * FROM untitle_table_4 limit 3", + "onerror": "SELECT * FROM untitle_table_4 limit 4" + }], + "onerror": "SELECT * FROM untitle_table_4 limit 5" + } + }) + }, { + status: 201 + }, function (res, err) { + if (err) { + return done(err); + } + fallbackJob = JSON.parse(res.body); + done(); + }); + }); + + it('job should fail', function (done) { + var expectedQuery = { + "query": [ + { + "query": "SELECT * FROM untitle_table_4 limit 1, should fail", + "onerror": "SELECT * FROM untitle_table_4 limit 2", + "status": "failed", + "fallback_status": "done", + "failed_reason": "LIMIT #,# syntax is not supported" + }, + { + "query": "SELECT * FROM untitle_table_4 limit 3", + "onerror": "SELECT * FROM untitle_table_4 limit 4", + "status": "skipped", + "fallback_status": "skipped" + } + ], + "onerror": "SELECT * FROM untitle_table_4 limit 5" + }; + + var interval = setInterval(function () { + assert.response(app, { + url: '/api/v2/sql/job/' + fallbackJob.job_id + '?api_key=1234&', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'host': 'vizzuality.cartodb.com' + }, + method: 'GET' + }, { + status: 200 + }, function (res, err) { + if (err) { + return done(err); + } + var job = JSON.parse(res.body); + if (job.status === jobStatus.FAILED && job.fallback_status === jobStatus.DONE) { + clearInterval(interval); + assert.deepEqual(job.query, expectedQuery); + done(); + } else if (job.status === jobStatus.DONE || job.status === jobStatus.CANCELLED) { + clearInterval(interval); + done(new Error('Job ' + job.job_id + ' is ' + job.status + ', expected to be done')); + } + }); + }, 50); + }); + }); + + + describe('should fail first "onerror" and job "onerror" and skip the other ones', function () { + var fallbackJob = {}; + + it('should create a job', function (done) { + assert.response(app, { + url: '/api/v2/sql/job?api_key=1234', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'host': 'vizzuality.cartodb.com' + }, + method: 'POST', + data: querystring.stringify({ + "query": { + "query": [{ + "query": "SELECT * FROM atm_madrid limit 1, should fail", + "onerror": "SELECT * FROM atm_madrid limit 2" + }, { + "query": "SELECT * FROM atm_madrid limit 3", + "onerror": "SELECT * FROM atm_madrid limit 4" + }], + "onerror": "SELECT * FROM atm_madrid limit 5" + } + }) + }, { + status: 201 + }, function (res, err) { + if (err) { + return done(err); + } + fallbackJob = JSON.parse(res.body); + done(); + }); + }); + + it('job should fail', function (done) { + var expectedQuery = { + query: [{ + query: 'SELECT * FROM atm_madrid limit 1, should fail', + onerror: 'SELECT * FROM atm_madrid limit 2', + status: 'failed', + fallback_status: 'failed', + failed_reason: 'relation "atm_madrid" does not exist' + }, { + query: 'SELECT * FROM atm_madrid limit 3', + onerror: 'SELECT * FROM atm_madrid limit 4', + status: 'skipped', + fallback_status: 'skipped' + }], + onerror: 'SELECT * FROM atm_madrid limit 5' + }; + + var interval = setInterval(function () { + assert.response(app, { + url: '/api/v2/sql/job/' + fallbackJob.job_id + '?api_key=1234&', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'host': 'vizzuality.cartodb.com' + }, + method: 'GET' + }, { + status: 200 + }, function (res, err) { + if (err) { + return done(err); + } + var job = JSON.parse(res.body); + if (job.status === jobStatus.FAILED && job.fallback_status === jobStatus.FAILED) { + clearInterval(interval); + assert.deepEqual(job.query, expectedQuery); + done(); + } else if (job.status === jobStatus.DONE || job.status === jobStatus.CANCELLED) { + clearInterval(interval); + done(new Error('Job ' + job.job_id + ' is ' + job.status + ', expected to be done')); + } + }); + }, 50); + }); + }); }); From 481a82500b6830d979b207eb13cc22d9327570dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Aubert?= Date: Wed, 1 Jun 2016 10:39:01 +0200 Subject: [PATCH 2/9] Refactored fallback jobs --- batch/models/job_base.js | 22 +- batch/models/job_fallback.js | 337 +++++++------------------ batch/models/job_status_transitions.js | 17 ++ batch/models/query/fallback.js | 65 +++++ batch/models/query/main_fallback.js | 74 ++++++ batch/models/query/query.js | 41 +++ batch/models/query/query_base.js | 51 ++++ batch/models/query/query_factory.js | 16 ++ batch/models/query/query_fallback.js | 71 ++++++ test/acceptance/job.fallback.test.js | 3 +- 10 files changed, 438 insertions(+), 259 deletions(-) create mode 100644 batch/models/job_status_transitions.js create mode 100644 batch/models/query/fallback.js create mode 100644 batch/models/query/main_fallback.js create mode 100644 batch/models/query/query.js create mode 100644 batch/models/query/query_base.js create mode 100644 batch/models/query/query_factory.js create mode 100644 batch/models/query/query_fallback.js diff --git a/batch/models/job_base.js b/batch/models/job_base.js index 3b9fc611..2e97186d 100644 --- a/batch/models/job_base.js +++ b/batch/models/job_base.js @@ -3,17 +3,7 @@ var assert = require('assert'); var uuid = require('node-uuid'); var jobStatus = require('../job_status'); -var validStatusTransitions = [ - [jobStatus.PENDING, jobStatus.RUNNING], - [jobStatus.PENDING, jobStatus.CANCELLED], - [jobStatus.PENDING, jobStatus.UNKNOWN], - [jobStatus.PENDING, jobStatus.SKIPPED], - [jobStatus.RUNNING, jobStatus.DONE], - [jobStatus.RUNNING, jobStatus.FAILED], - [jobStatus.RUNNING, jobStatus.CANCELLED], - [jobStatus.RUNNING, jobStatus.PENDING], - [jobStatus.RUNNING, jobStatus.UNKNOWN] -]; +var validStatusTransitions = require('./job_status_transitions'); var mandatoryProperties = [ 'job_id', 'status', @@ -22,6 +12,12 @@ var mandatoryProperties = [ 'updated_at', 'user' ]; +var finalStatus = [ + jobStatus.CANCELLED, + jobStatus.DONE, + jobStatus.FAILED, + jobStatus.UNKNOWN +]; function JobBase(data) { var now = new Date().toISOString(); @@ -58,6 +54,10 @@ JobBase.prototype.isValidStatusTransition = function (initialStatus, finalStatus return false; }; +JobBase.prototype.isFinalStatus = function (status) { + return finalStatus.indexOf(status) !== -1; +}; + // should be implemented by childs JobBase.prototype.getNextQuery = function () { throw new Error('Unimplemented method'); diff --git a/batch/models/job_fallback.js b/batch/models/job_fallback.js index ca582512..0a6e9a23 100644 --- a/batch/models/job_fallback.js +++ b/batch/models/job_fallback.js @@ -3,28 +3,23 @@ var util = require('util'); var JobBase = require('./job_base'); var jobStatus = require('../job_status'); -var breakStatus = [ - jobStatus.CANCELLED, - jobStatus.FAILED, - jobStatus.UNKNOWN -]; -function isBreakStatus(status) { - return breakStatus.indexOf(status) !== -1; -} -var finalStatus = [ - jobStatus.CANCELLED, - jobStatus.DONE, - jobStatus.FAILED, - jobStatus.UNKNOWN -]; -function isFinalStatus(status) { - return finalStatus.indexOf(status) !== -1; -} +var QueryFallback = require('./query/query_fallback'); +var MainFallback = require('./query/main_fallback'); +var QueryFactory = require('./query/query_factory'); function JobFallback(jobDefinition) { JobBase.call(this, jobDefinition); this.init(); + + this.queries = []; + for (var i = 0; i < this.data.query.query.length; i++) { + this.queries[i] = QueryFactory.create(this.data, i); + } + + if (MainFallback.is(this.data)) { + this.fallback = new MainFallback(); + } } util.inherits(JobFallback, JobBase); @@ -63,11 +58,7 @@ JobFallback.is = function (query) { } for (var i = 0; i < query.query.length; i++) { - if (!query.query[i].query) { - return false; - } - - if (typeof query.query[i].query !== 'string') { + if (!QueryFallback.is(query.query[i])) { return false; } } @@ -76,7 +67,7 @@ JobFallback.is = function (query) { }; JobFallback.prototype.init = function () { - // jshint maxcomplexity: 8 + // jshint maxcomplexity: 9 for (var i = 0; i < this.data.query.query.length; i++) { if ((this.data.query.query[i].onsuccess || this.data.query.query[i].onerror) && !this.data.query.query[i].status) { @@ -87,87 +78,42 @@ JobFallback.prototype.init = function () { } } - if ((this.data.query.onsuccess || this.data.query.onerror) && !this.data.status) { + if (!this.data.status) { this.data.status = jobStatus.PENDING; - this.data.fallback_status = jobStatus.PENDING; - + if (this.data.query.onsuccess || this.data.query.onerror) { + this.data.status = jobStatus.PENDING; + this.data.fallback_status = jobStatus.PENDING; + } } else if (!this.data.status) { this.data.status = jobStatus.PENDING; } }; +JobFallback.prototype.getNextQueryFromQueries = function () { + for (var i = 0; i < this.queries.length; i++) { + if (this.queries[i].hasNextQuery(this.data)) { + return this.queries[i].getNextQuery(this.data); + } + } +}; + +JobFallback.prototype.getNextQueryFromFallback = function () { + if (this.fallback && this.fallback.hasNextQuery(this.data)) { + + return this.fallback.getNextQuery(this.data); + } +}; + JobFallback.prototype.getNextQuery = function () { - var query = this._getNextQueryFromQuery(); + var query = this.getNextQueryFromQueries(); if (!query) { - query = this._getNextQueryFromJobFallback(); + query = this.getNextQueryFromFallback(); } return query; }; -JobFallback.prototype._hasNextQueryFromQuery = function () { - return !!this._getNextQueryFromQuery(); -}; - -JobFallback.prototype._getNextQueryFromQuery = function () { - // jshint maxcomplexity: 8 - for (var i = 0; i < this.data.query.query.length; i++) { - - if (this.data.query.query[i].fallback_status) { - if (this._isNextQuery(i)) { - return this.data.query.query[i].query; - } else if (this._isNextQueryOnSuccess(i)) { - return this.data.query.query[i].onsuccess; - } else if (this._isNextQueryOnError(i)) { - return this.data.query.query[i].onerror; - } else if (isBreakStatus(this.data.query.query[i].status)) { - return; - } - } else if (this.data.query.query[i].status === jobStatus.PENDING) { - return this.data.query.query[i].query; - } - } -}; - -JobFallback.prototype._getNextQueryFromJobFallback = function () { - if (this.data.fallback_status) { - if (this._isNextQueryOnSuccessJob()) { - return this.data.query.onsuccess; - } else if (this._isNextQueryOnErrorJob()) { - return this.data.query.onerror; - } - } -}; - -JobFallback.prototype._isNextQuery = function (index) { - return this.data.query.query[index].status === jobStatus.PENDING; -}; - -JobFallback.prototype._isNextQueryOnSuccess = function (index) { - return this.data.query.query[index].status === jobStatus.DONE && - this.data.query.query[index].onsuccess && - this.data.query.query[index].fallback_status === jobStatus.PENDING; -}; - -JobFallback.prototype._isNextQueryOnError = function (index) { - return this.data.query.query[index].status === jobStatus.FAILED && - this.data.query.query[index].onerror && - this.data.query.query[index].fallback_status === jobStatus.PENDING; -}; - -JobFallback.prototype._isNextQueryOnSuccessJob = function () { - return this.data.status === jobStatus.DONE && - this.data.query.onsuccess && - this.data.fallback_status === jobStatus.PENDING; -}; - -JobFallback.prototype._isNextQueryOnErrorJob = function () { - return this.data.status === jobStatus.FAILED && - this.data.query.onerror && - this.data.fallback_status === jobStatus.PENDING; -}; - JobFallback.prototype.setQuery = function (query) { if (!JobFallback.is(query)) { throw new Error('You must indicate a valid SQL'); @@ -177,99 +123,90 @@ JobFallback.prototype.setQuery = function (query) { }; JobFallback.prototype.setStatus = function (status, errorMesssage) { - var now = new Date().toISOString(); - var resultFromQuery = this._setQueryStatus(status, errorMesssage); - var resultFromJob = this._setJobStatus(status, resultFromQuery.isChangeAppliedToQueryFallback, errorMesssage); + // jshint maxcomplexity: 7 - if (!resultFromJob.isValid && !resultFromQuery.isValid) { - throw new Error('Cannot set status from ' + this.data.status + ' to ' + status); + var now = new Date().toISOString(); + var hasChanged = { + isValid: false, + appliedToFallback: false + }; + var result = {}; + + for (var i = 0; i < this.queries.length; i++) { + result = this.queries[i].setStatus(status, this.data, hasChanged, errorMesssage); + + if (result.isValid) { + hasChanged = result; + } } - if (!resultFromQuery.isChangeAppliedToQueryFallback || status === jobStatus.CANCELLED) { - this._setSkipped(status); + result = this.setJobStatus(status, this.data, hasChanged, errorMesssage); + + if (result.isValid) { + hasChanged = result; + } + + if (!this.getNextQueryFromQueries() && this.fallback) { + result = this.fallback.setStatus(status, this.data, hasChanged); + + if (result.isValid) { + hasChanged = result; + } + } + + if (!hasChanged.isValid) { + throw new Error('Cannot set status to ' + status); } this.data.updated_at = now; }; -JobFallback.prototype._setSkipped = function (status) { - this._setSkippedQueryStatus(); - this._setSkippedJobStatus(); +JobFallback.prototype.setJobStatus = function (status, job, hasChanged, errorMesssage) { + var isValid = false; - if (status === jobStatus.CANCELLED || status === jobStatus.FAILED) { - this._setRestPendingToSkipped(status); + status = this.shiftStatus(status, hasChanged); + + isValid = this.isValidStatusTransition(job.status, status); + + if (isValid) { + job.status = status; } + + if (status === jobStatus.FAILED && errorMesssage && !hasChanged.appliedToFallback) { + job.failed_reason = errorMesssage; + } + + return { isValid: isValid, appliedToFallback: false }; }; -JobFallback.prototype._setSkippedQueryStatus = function () { - // jshint maxcomplexity: 8 - for (var i = 0; i < this.data.query.query.length; i++) { - if (this.data.query.query[i].status === jobStatus.FAILED && this.data.query.query[i].onsuccess) { - if (this.isValidStatusTransition(this.data.query.query[i].fallback_status, jobStatus.SKIPPED)) { - this.data.query.query[i].fallback_status = jobStatus.SKIPPED; - } - } - - if (this.data.query.query[i].status === jobStatus.DONE && this.data.query.query[i].onerror) { - if (this.isValidStatusTransition(this.data.query.query[i].fallback_status, jobStatus.SKIPPED)) { - this.data.query.query[i].fallback_status = jobStatus.SKIPPED; - } - } - - if (this.data.query.query[i].status === jobStatus.CANCELLED && this.data.query.query[i].fallback_status) { - if (this.isValidStatusTransition(this.data.query.query[i].fallback_status, jobStatus.SKIPPED)) { - this.data.query.query[i].fallback_status = jobStatus.SKIPPED; - } - } - } -}; - -JobFallback.prototype._setSkippedJobStatus = function () { +JobFallback.prototype.shiftStatus = function (status, hasChanged) { // jshint maxcomplexity: 7 - - if (this.data.status === jobStatus.FAILED && this.data.query.onsuccess) { - if (this.isValidStatusTransition(this.data.fallback_status, jobStatus.SKIPPED)) { - this.data.fallback_status = jobStatus.SKIPPED; + if (hasChanged.appliedToFallback) { + if (!this.getNextQueryFromQueries() && (status === jobStatus.DONE || status === jobStatus.FAILED)) { + status = this._getLastStatusFromFinishedQuery(); + } else if (status === jobStatus.DONE || status === jobStatus.FAILED){ + status = jobStatus.PENDING; } + } else if (this.getNextQueryFromQueries() && status !== jobStatus.RUNNING) { + status = jobStatus.PENDING; } - if (this.data.status === jobStatus.DONE && this.data.query.onerror) { - if (this.isValidStatusTransition(this.data.fallback_status, jobStatus.SKIPPED)) { - this.data.fallback_status = jobStatus.SKIPPED; - } - } - - if (this.data.status === jobStatus.CANCELLED && this.data.fallback_status) { - if (this.isValidStatusTransition(this.data.fallback_status, jobStatus.SKIPPED)) { - this.data.fallback_status = jobStatus.SKIPPED; - } - } + return status; }; -JobFallback.prototype._setRestPendingToSkipped = function (status) { - for (var i = 0; i < this.data.query.query.length; i++) { - if (this.data.query.query[i].status === jobStatus.PENDING) { - this.data.query.query[i].status = jobStatus.SKIPPED; - } - if (this.data.query.query[i].status !== status && - this.data.query.query[i].fallback_status === jobStatus.PENDING) { - this.data.query.query[i].fallback_status = jobStatus.SKIPPED; - } - } -}; JobFallback.prototype._getLastStatusFromFinishedQuery = function () { var lastStatus = jobStatus.DONE; for (var i = 0; i < this.data.query.query.length; i++) { if (this.data.query.query[i].fallback_status) { - if (isFinalStatus(this.data.query.query[i].status)) { + if (this.isFinalStatus(this.data.query.query[i].status)) { lastStatus = this.data.query.query[i].status; } else { break; } } else { - if (isFinalStatus(this.data.query.query[i].status)) { + if (this.isFinalStatus(this.data.query.query[i].status)) { lastStatus = this.data.query.query[i].status; } else { break; @@ -279,95 +216,3 @@ JobFallback.prototype._getLastStatusFromFinishedQuery = function () { return lastStatus; }; - -JobFallback.prototype._setJobStatus = function (status, isChangeAppliedToQueryFallback, errorMesssage) { - var isValid = false; - - status = this._shiftJobStatus(status, isChangeAppliedToQueryFallback); - - isValid = this.isValidStatusTransition(this.data.status, status); - - if (isValid) { - this.data.status = status; - } else if (this.data.fallback_status) { - - isValid = this.isValidStatusTransition(this.data.fallback_status, status); - - if (isValid) { - this.data.fallback_status = status; - } - } - - if (status === jobStatus.FAILED && errorMesssage && !isChangeAppliedToQueryFallback) { - this.data.failed_reason = errorMesssage; - } - - return { - isValid: isValid - }; -}; - -JobFallback.prototype._shiftJobStatus = function (status, isChangeAppliedToQueryFallback) { - // jshint maxcomplexity: 7 - - // In some scenarios we have to change the normal flow in order to keep consistency - // between query's status and job's status. - - if (isChangeAppliedToQueryFallback) { - if (!this._hasNextQueryFromQuery() && (status === jobStatus.DONE || status === jobStatus.FAILED)) { - status = this._getLastStatusFromFinishedQuery(); - } else if (status === jobStatus.DONE || status === jobStatus.FAILED){ - status = jobStatus.PENDING; - } - } else if (this._hasNextQueryFromQuery() && status !== jobStatus.RUNNING) { - status = jobStatus.PENDING; - } - - return status; -}; - - -JobFallback.prototype._shouldTryToApplyStatusTransitionToQueryFallback = function (index) { - return (this.data.query.query[index].status === jobStatus.DONE && this.data.query.query[index].onsuccess) || - (this.data.query.query[index].status === jobStatus.FAILED && this.data.query.query[index].onerror); -}; - -JobFallback.prototype._setQueryStatus = function (status, errorMesssage) { - // jshint maxcomplexity: 8 - var isValid = false; - var isChangeAppliedToQueryFallback = false; - - for (var i = 0; i < this.data.query.query.length; i++) { - isValid = this.isValidStatusTransition(this.data.query.query[i].status, status); - - if (isValid) { - this.data.query.query[i].status = status; - - if (status === jobStatus.FAILED && errorMesssage) { - this.data.query.query[i].failed_reason = errorMesssage; - } - - break; - } - - if (this._shouldTryToApplyStatusTransitionToQueryFallback(i)) { - isValid = this.isValidStatusTransition(this.data.query.query[i].fallback_status, status); - - if (isValid) { - this.data.query.query[i].fallback_status = status; - - if (status === jobStatus.FAILED && errorMesssage) { - this.data.query.query[i].failed_reason = errorMesssage; - } - - isChangeAppliedToQueryFallback = true; - break; - } - } - } - - return { - isValid: isValid, - isChangeAppliedToQueryFallback: isChangeAppliedToQueryFallback - }; -}; diff --git a/batch/models/job_status_transitions.js b/batch/models/job_status_transitions.js new file mode 100644 index 00000000..5dd6f3f3 --- /dev/null +++ b/batch/models/job_status_transitions.js @@ -0,0 +1,17 @@ +'use strict'; + +var jobStatus = require('../job_status'); + +var validStatusTransitions = [ + [jobStatus.PENDING, jobStatus.RUNNING], + [jobStatus.PENDING, jobStatus.CANCELLED], + [jobStatus.PENDING, jobStatus.UNKNOWN], + [jobStatus.PENDING, jobStatus.SKIPPED], + [jobStatus.RUNNING, jobStatus.DONE], + [jobStatus.RUNNING, jobStatus.FAILED], + [jobStatus.RUNNING, jobStatus.CANCELLED], + [jobStatus.RUNNING, jobStatus.PENDING], + [jobStatus.RUNNING, jobStatus.UNKNOWN] +]; + +module.exports = validStatusTransitions; diff --git a/batch/models/query/fallback.js b/batch/models/query/fallback.js new file mode 100644 index 00000000..66c62ae7 --- /dev/null +++ b/batch/models/query/fallback.js @@ -0,0 +1,65 @@ +'use strict'; + +var util = require('util'); +var QueryBase = require('./query_base'); +var jobStatus = require('../../job_status'); + +function Fallback(index) { + QueryBase.call(this, index); +} +util.inherits(Fallback, QueryBase); + +module.exports = Fallback; + +Fallback.is = function (query) { + if (query.onsuccess || query.onerror) { + return true; + } + return false; +}; + +Fallback.prototype.getNextQuery = function (job) { + if (this.hasOnSuccess(job)) { + return this.getOnSuccess(job); + } + if (this.hasOnError(job)) { + return this.getOnError(job); + } +}; + +Fallback.prototype.getOnSuccess = function (job) { + if (job.query.query[this.index].status === jobStatus.DONE && + job.query.query[this.index].fallback_status === jobStatus.PENDING) { + return job.query.query[this.index].onsuccess; + } +}; + +Fallback.prototype.hasOnSuccess = function (job) { + return !!this.getOnSuccess(job); +}; + +Fallback.prototype.getOnError = function (job) { + if (job.query.query[this.index].status === jobStatus.FAILED && + job.query.query[this.index].fallback_status === jobStatus.PENDING) { + return job.query.query[this.index].onerror; + } +}; + +Fallback.prototype.hasOnError = function (job) { + return !!this.getOnError(job); +}; + +Fallback.prototype.setStatus = function (status, job, errorMesssage) { + var isValid = false; + + isValid = this.isValidTransition(job.query.query[this.index].fallback_status, status); + + if (isValid) { + job.query.query[this.index].fallback_status = status; + if (status === jobStatus.FAILED && errorMesssage) { + job.query.query[this.index].failed_reason = errorMesssage; + } + } + + return isValid; +}; diff --git a/batch/models/query/main_fallback.js b/batch/models/query/main_fallback.js new file mode 100644 index 00000000..7c52194b --- /dev/null +++ b/batch/models/query/main_fallback.js @@ -0,0 +1,74 @@ +'use strict'; + +var util = require('util'); +var QueryBase = require('./query_base'); +var jobStatus = require('../../job_status'); + +function MainFallback() { + QueryBase.call(this); +} +util.inherits(MainFallback, QueryBase); + +module.exports = MainFallback; + +MainFallback.is = function (job) { + if (job.query.onsuccess || job.query.onerror) { + return true; + } + return false; +}; + +MainFallback.prototype.getNextQuery = function (job) { + if (this.hasOnSuccess(job)) { + return this.getOnSuccess(job); + } + + if (this.hasOnError(job)) { + return this.getOnError(job); + } +}; + +MainFallback.prototype.getOnSuccess = function (job) { + if (job.status === jobStatus.DONE && job.fallback_status === jobStatus.PENDING) { + return job.query.onsuccess; + } +}; + +MainFallback.prototype.hasOnSuccess = function (job) { + return !!this.getOnSuccess(job); +}; + +MainFallback.prototype.getOnError = function (job) { + if (job.status === jobStatus.FAILED && job.fallback_status === jobStatus.PENDING) { + return job.query.onerror; + } +}; + +MainFallback.prototype.hasOnError = function (job) { + return !!this.getOnError(job); +}; + +MainFallback.prototype.setStatus = function (status, job, previous) { + var isValid = false; + var appliedToFallback = false; + + if (previous.isValid && !previous.appliedToFallback) { + if (this.isFinalStatus(status) && !this.hasNextQuery(job)) { + isValid = this.isValidTransition(job.fallback_status, jobStatus.SKIPPED); + + if (isValid) { + job.fallback_status = jobStatus.SKIPPED; + appliedToFallback = true; + } + } + } else if (!previous.isValid) { + isValid = this.isValidTransition(job.fallback_status, status); + + if (isValid) { + job.fallback_status = status; + appliedToFallback = true; + } + } + + return { isValid: isValid, appliedToFallback: appliedToFallback }; +}; diff --git a/batch/models/query/query.js b/batch/models/query/query.js new file mode 100644 index 00000000..19b0661a --- /dev/null +++ b/batch/models/query/query.js @@ -0,0 +1,41 @@ +'use strict'; + +var util = require('util'); +var QueryBase = require('./query_base'); +var jobStatus = require('../../job_status'); + +function Query(index) { + QueryBase.call(this, index); +} +util.inherits(Query, QueryBase); + +module.exports = Query; + +Query.is = function (query) { + if (query.query && typeof query.query === 'string') { + return true; + } + + return false; +}; + +Query.prototype.getNextQuery = function (job) { + if (job.query.query[this.index].status === jobStatus.PENDING) { + return job.query.query[this.index].query; + } +}; + +Query.prototype.setStatus = function (status, job, errorMesssage) { + var isValid = false; + + isValid = this.isValidTransition(job.query.query[this.index].status, status); + + if (isValid) { + job.query.query[this.index].status = status; + if (status === jobStatus.FAILED && errorMesssage) { + job.query.query[this.index].failed_reason = errorMesssage; + } + } + + return isValid; +}; diff --git a/batch/models/query/query_base.js b/batch/models/query/query_base.js new file mode 100644 index 00000000..157a77aa --- /dev/null +++ b/batch/models/query/query_base.js @@ -0,0 +1,51 @@ +'use strict'; + +var jobStatus = require('../../job_status'); +var assert = require('assert'); +var validStatusTransitions = require('../job_status_transitions'); +var finalStatus = [ + jobStatus.CANCELLED, + jobStatus.DONE, + jobStatus.FAILED, + jobStatus.UNKNOWN +]; + +function QueryBase(index) { + this.index = index; +} + +module.exports = QueryBase; + +QueryBase.prototype.isFinalStatus = function (status) { + return finalStatus.indexOf(status) !== -1; +}; + +// should be implemented +QueryBase.prototype.setStatus = function () { + throw new Error('Unimplemented method'); +}; + +// should be implemented +QueryBase.prototype.getNextQuery = function () { + throw new Error('Unimplemented method'); +}; + +QueryBase.prototype.hasNextQuery = function (job) { + return !!this.getNextQuery(job); +}; + + +QueryBase.prototype.isValidTransition = function (initialStatus, finalStatus) { + var transition = [ initialStatus, finalStatus ]; + + for (var i = 0; i < validStatusTransitions.length; i++) { + try { + assert.deepEqual(transition, validStatusTransitions[i]); + return true; + } catch (e) { + continue; + } + } + + return false; +}; diff --git a/batch/models/query/query_factory.js b/batch/models/query/query_factory.js new file mode 100644 index 00000000..c33534e0 --- /dev/null +++ b/batch/models/query/query_factory.js @@ -0,0 +1,16 @@ +'use strict'; + +var QueryFallback = require('./query_fallback'); + +function QueryFactory() { +} + +module.exports = QueryFactory; + +QueryFactory.create = function (job, index) { + if (QueryFallback.is(job.query.query[index])) { + return new QueryFallback(job, index); + } + + throw new Error('there is no query class for the provided query'); +}; diff --git a/batch/models/query/query_fallback.js b/batch/models/query/query_fallback.js new file mode 100644 index 00000000..5f368c77 --- /dev/null +++ b/batch/models/query/query_fallback.js @@ -0,0 +1,71 @@ +'use strict'; + +var util = require('util'); +var QueryBase = require('./query_base'); +var Query = require('./query'); +var Fallback = require('./fallback'); +var jobStatus = require('../../job_status'); + +function QueryFallback(job, index) { + QueryBase.call(this, index); + + this.init(job, index); +} + +util.inherits(QueryFallback, QueryBase); + +QueryFallback.is = function (query) { + if (Query.is(query)) { + return true; + } + return false; +}; + +QueryFallback.prototype.init = function (job, index) { + this.query = new Query(index); + + if (Fallback.is(job.query.query[index])) { + this.fallback = new Fallback(index); + } +}; + +QueryFallback.prototype.getNextQuery = function (job) { + if (this.query.hasNextQuery(job)) { + return this.query.getNextQuery(job); + } + + if (this.fallback && this.fallback.hasNextQuery(job)) { + return this.fallback.getNextQuery(job); + } +}; + +QueryFallback.prototype.setStatus = function (status, job, previous, errorMesssage) { + // jshint maxcomplexity: 9 + var isValid = false; + var appliedToFallback = false; + + if (previous.isValid && !previous.appliedToFallback) { + if (status === jobStatus.FAILED || status === jobStatus.CANCELLED) { + this.query.setStatus(jobStatus.SKIPPED, job, errorMesssage); + + if (this.fallback) { + this.fallback.setStatus(jobStatus.SKIPPED, job); + } + } + } else if (!previous.isValid) { + isValid = this.query.setStatus(status, job, errorMesssage); + + if (this.fallback) { + if (!isValid) { + isValid = this.fallback.setStatus(status, job, errorMesssage); + appliedToFallback = true; + } else if (isValid && this.isFinalStatus(status) && !this.fallback.hasNextQuery(job)) { + this.fallback.setStatus(jobStatus.SKIPPED, job); + } + } + } + + return { isValid: isValid, appliedToFallback: appliedToFallback }; +}; + +module.exports = QueryFallback; diff --git a/test/acceptance/job.fallback.test.js b/test/acceptance/job.fallback.test.js index d549b279..324a353b 100644 --- a/test/acceptance/job.fallback.test.js +++ b/test/acceptance/job.fallback.test.js @@ -31,7 +31,6 @@ describe('Batch API fallback job', function () { describe('"onsuccess" on first query should be triggered', function () { var fallbackJob = {}; - it('should create a job', function (done) { assert.response(app, { url: '/api/v2/sql/job?api_key=1234', @@ -951,7 +950,7 @@ describe('Batch API fallback job', function () { }); }); - describe('"onsuccess" should not be triggered for any query and "skipped"', function () { + describe('"onsuccess" should be "skipped"', function () { var fallbackJob = {}; it('should create a job', function (done) { From 25b9103af990f69d1b713c738932b625c475ed16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Aubert?= Date: Thu, 2 Jun 2016 12:26:20 +0200 Subject: [PATCH 3/9] Removed duplicated code, method isValidTransition is encapsulated into a new object and job and query inherits from it --- batch/models/job_base.js | 34 ++++--------------- batch/models/job_fallback.js | 2 +- batch/models/job_multiple.js | 2 +- batch/models/job_state_machine.js | 46 ++++++++++++++++++++++++++ batch/models/job_status_transitions.js | 17 ---------- batch/models/query/query_base.js | 34 +++---------------- 6 files changed, 59 insertions(+), 76 deletions(-) create mode 100644 batch/models/job_state_machine.js delete mode 100644 batch/models/job_status_transitions.js diff --git a/batch/models/job_base.js b/batch/models/job_base.js index 2e97186d..3bf745c2 100644 --- a/batch/models/job_base.js +++ b/batch/models/job_base.js @@ -1,9 +1,9 @@ 'use strict'; -var assert = require('assert'); +var util = require('util'); var uuid = require('node-uuid'); +var JobStateMachine = require('./job_state_machine'); var jobStatus = require('../job_status'); -var validStatusTransitions = require('./job_status_transitions'); var mandatoryProperties = [ 'job_id', 'status', @@ -12,14 +12,10 @@ var mandatoryProperties = [ 'updated_at', 'user' ]; -var finalStatus = [ - jobStatus.CANCELLED, - jobStatus.DONE, - jobStatus.FAILED, - jobStatus.UNKNOWN -]; function JobBase(data) { + JobStateMachine.call(this); + var now = new Date().toISOString(); this.data = data; @@ -36,28 +32,10 @@ function JobBase(data) { this.data.updated_at = now; } } +util.inherits(JobBase, JobStateMachine); module.exports = JobBase; -JobBase.prototype.isValidStatusTransition = function (initialStatus, finalStatus) { - var transition = [ initialStatus, finalStatus ]; - - for (var i = 0; i < validStatusTransitions.length; i++) { - try { - assert.deepEqual(transition, validStatusTransitions[i]); - return true; - } catch (e) { - continue; - } - } - - return false; -}; - -JobBase.prototype.isFinalStatus = function (status) { - return finalStatus.indexOf(status) !== -1; -}; - // should be implemented by childs JobBase.prototype.getNextQuery = function () { throw new Error('Unimplemented method'); @@ -105,7 +83,7 @@ JobBase.prototype.setQuery = function (query) { JobBase.prototype.setStatus = function (finalStatus, errorMesssage) { var now = new Date().toISOString(); var initialStatus = this.data.status; - var isValid = this.isValidStatusTransition(initialStatus, finalStatus); + var isValid = this.isValidTransition(initialStatus, finalStatus); if (!isValid) { throw new Error('Cannot set status from ' + initialStatus + ' to ' + finalStatus); diff --git a/batch/models/job_fallback.js b/batch/models/job_fallback.js index 0a6e9a23..347c884a 100644 --- a/batch/models/job_fallback.js +++ b/batch/models/job_fallback.js @@ -166,7 +166,7 @@ JobFallback.prototype.setJobStatus = function (status, job, hasChanged, errorMes status = this.shiftStatus(status, hasChanged); - isValid = this.isValidStatusTransition(job.status, status); + isValid = this.isValidTransition(job.status, status); if (isValid) { job.status = status; diff --git a/batch/models/job_multiple.js b/batch/models/job_multiple.js index a1fb8317..85cf1d87 100644 --- a/batch/models/job_multiple.js +++ b/batch/models/job_multiple.js @@ -76,7 +76,7 @@ JobMultiple.prototype.setStatus = function (finalStatus, errorMesssage) { } for (var i = 0; i < this.data.query.length; i++) { - var isValid = JobMultiple.super_.prototype.isValidStatusTransition(this.data.query[i].status, finalStatus); + var isValid = JobMultiple.super_.prototype.isValidTransition(this.data.query[i].status, finalStatus); if (isValid) { this.data.query[i].status = finalStatus; diff --git a/batch/models/job_state_machine.js b/batch/models/job_state_machine.js new file mode 100644 index 00000000..21ee60e5 --- /dev/null +++ b/batch/models/job_state_machine.js @@ -0,0 +1,46 @@ +'use strict'; + +var assert = require('assert'); +var jobStatus = require('../job_status'); +var finalStatus = [ + jobStatus.CANCELLED, + jobStatus.DONE, + jobStatus.FAILED, + jobStatus.UNKNOWN +]; + +var validStatusTransitions = [ + [jobStatus.PENDING, jobStatus.RUNNING], + [jobStatus.PENDING, jobStatus.CANCELLED], + [jobStatus.PENDING, jobStatus.UNKNOWN], + [jobStatus.PENDING, jobStatus.SKIPPED], + [jobStatus.RUNNING, jobStatus.DONE], + [jobStatus.RUNNING, jobStatus.FAILED], + [jobStatus.RUNNING, jobStatus.CANCELLED], + [jobStatus.RUNNING, jobStatus.PENDING], + [jobStatus.RUNNING, jobStatus.UNKNOWN] +]; + +function JobStateMachine () { +} + +module.exports = JobStateMachine; + +JobStateMachine.prototype.isValidTransition = function (initialStatus, finalStatus) { + var transition = [ initialStatus, finalStatus ]; + + for (var i = 0; i < validStatusTransitions.length; i++) { + try { + assert.deepEqual(transition, validStatusTransitions[i]); + return true; + } catch (e) { + continue; + } + } + + return false; +}; + +JobStateMachine.prototype.isFinalStatus = function (status) { + return finalStatus.indexOf(status) !== -1; +}; diff --git a/batch/models/job_status_transitions.js b/batch/models/job_status_transitions.js deleted file mode 100644 index 5dd6f3f3..00000000 --- a/batch/models/job_status_transitions.js +++ /dev/null @@ -1,17 +0,0 @@ -'use strict'; - -var jobStatus = require('../job_status'); - -var validStatusTransitions = [ - [jobStatus.PENDING, jobStatus.RUNNING], - [jobStatus.PENDING, jobStatus.CANCELLED], - [jobStatus.PENDING, jobStatus.UNKNOWN], - [jobStatus.PENDING, jobStatus.SKIPPED], - [jobStatus.RUNNING, jobStatus.DONE], - [jobStatus.RUNNING, jobStatus.FAILED], - [jobStatus.RUNNING, jobStatus.CANCELLED], - [jobStatus.RUNNING, jobStatus.PENDING], - [jobStatus.RUNNING, jobStatus.UNKNOWN] -]; - -module.exports = validStatusTransitions; diff --git a/batch/models/query/query_base.js b/batch/models/query/query_base.js index 157a77aa..5bc7f313 100644 --- a/batch/models/query/query_base.js +++ b/batch/models/query/query_base.js @@ -1,25 +1,17 @@ 'use strict'; -var jobStatus = require('../../job_status'); -var assert = require('assert'); -var validStatusTransitions = require('../job_status_transitions'); -var finalStatus = [ - jobStatus.CANCELLED, - jobStatus.DONE, - jobStatus.FAILED, - jobStatus.UNKNOWN -]; +var util = require('util'); +var JobStateMachine = require('../job_state_machine'); function QueryBase(index) { + JobStateMachine.call(this); + this.index = index; } +util.inherits(QueryBase, JobStateMachine); module.exports = QueryBase; -QueryBase.prototype.isFinalStatus = function (status) { - return finalStatus.indexOf(status) !== -1; -}; - // should be implemented QueryBase.prototype.setStatus = function () { throw new Error('Unimplemented method'); @@ -33,19 +25,3 @@ QueryBase.prototype.getNextQuery = function () { QueryBase.prototype.hasNextQuery = function (job) { return !!this.getNextQuery(job); }; - - -QueryBase.prototype.isValidTransition = function (initialStatus, finalStatus) { - var transition = [ initialStatus, finalStatus ]; - - for (var i = 0; i < validStatusTransitions.length; i++) { - try { - assert.deepEqual(transition, validStatusTransitions[i]); - return true; - } catch (e) { - continue; - } - } - - return false; -}; From cd439757b76016c220acb2a2842a210968639ca5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Aubert?= Date: Thu, 2 Jun 2016 13:47:45 +0200 Subject: [PATCH 4/9] Fix typo --- batch/models/job_fallback.js | 1 - batch/models/query/fallback.js | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/batch/models/job_fallback.js b/batch/models/job_fallback.js index 347c884a..22904946 100644 --- a/batch/models/job_fallback.js +++ b/batch/models/job_fallback.js @@ -124,7 +124,6 @@ JobFallback.prototype.setQuery = function (query) { JobFallback.prototype.setStatus = function (status, errorMesssage) { // jshint maxcomplexity: 7 - var now = new Date().toISOString(); var hasChanged = { isValid: false, diff --git a/batch/models/query/fallback.js b/batch/models/query/fallback.js index 66c62ae7..a5676319 100644 --- a/batch/models/query/fallback.js +++ b/batch/models/query/fallback.js @@ -49,15 +49,15 @@ Fallback.prototype.hasOnError = function (job) { return !!this.getOnError(job); }; -Fallback.prototype.setStatus = function (status, job, errorMesssage) { +Fallback.prototype.setStatus = function (status, job, errorMessage) { var isValid = false; isValid = this.isValidTransition(job.query.query[this.index].fallback_status, status); if (isValid) { job.query.query[this.index].fallback_status = status; - if (status === jobStatus.FAILED && errorMesssage) { - job.query.query[this.index].failed_reason = errorMesssage; + if (status === jobStatus.FAILED && errorMessage) { + job.query.query[this.index].failed_reason = errorMessage; } } From 86d02ab0b7265a36f634f32102dfa56b74c6467f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Aubert?= Date: Thu, 2 Jun 2016 14:28:35 +0200 Subject: [PATCH 5/9] Refactored job fallback initialization --- batch/models/job_fallback.js | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/batch/models/job_fallback.js b/batch/models/job_fallback.js index 22904946..23908076 100644 --- a/batch/models/job_fallback.js +++ b/batch/models/job_fallback.js @@ -67,28 +67,36 @@ JobFallback.is = function (query) { }; JobFallback.prototype.init = function () { - // jshint maxcomplexity: 9 for (var i = 0; i < this.data.query.query.length; i++) { - if ((this.data.query.query[i].onsuccess || this.data.query.query[i].onerror) && - !this.data.query.query[i].status) { + if (shouldInitStatus(this.data.query.query[i])){ this.data.query.query[i].status = jobStatus.PENDING; + } + if (shouldInitQueryFallbackStatus(this.data.query.query[i])) { this.data.query.query[i].fallback_status = jobStatus.PENDING; - } else if (!this.data.query.query[i].status){ - this.data.query.query[i].status = jobStatus.PENDING; } } - if (!this.data.status) { - this.data.status = jobStatus.PENDING; - if (this.data.query.onsuccess || this.data.query.onerror) { - this.data.status = jobStatus.PENDING; - this.data.fallback_status = jobStatus.PENDING; - } - } else if (!this.data.status) { + if (shouldInitStatus(this.data)) { this.data.status = jobStatus.PENDING; } + + if (shouldInitFallbackStatus(this.data)) { + this.data.fallback_status = jobStatus.PENDING; + } }; +function shouldInitStatus(jobOrQuery) { + return !jobOrQuery.status; +} + +function shouldInitQueryFallbackStatus(query) { + return (query.onsuccess || query.onerror) && !query.fallback_status; +} + +function shouldInitFallbackStatus(job) { + return (job.query.onsuccess || job.query.onerror) && !job.fallback_status; +} + JobFallback.prototype.getNextQueryFromQueries = function () { for (var i = 0; i < this.queries.length; i++) { if (this.queries[i].hasNextQuery(this.data)) { From e52b4a1120c77d5f7c3ebfa1b6435e6ba9237100 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Aubert?= Date: Thu, 2 Jun 2016 18:02:25 +0200 Subject: [PATCH 6/9] Improved falback job to get the status of last finished query --- batch/models/job_fallback.js | 29 ++++++++++------------------ batch/models/query/fallback.js | 4 ++++ batch/models/query/query.js | 4 ++++ batch/models/query/query_base.js | 4 ++++ batch/models/query/query_fallback.js | 4 ++++ test/acceptance/job.fallback.test.js | 4 ++-- 6 files changed, 28 insertions(+), 21 deletions(-) diff --git a/batch/models/job_fallback.js b/batch/models/job_fallback.js index 23908076..62e57d21 100644 --- a/batch/models/job_fallback.js +++ b/batch/models/job_fallback.js @@ -25,7 +25,7 @@ util.inherits(JobFallback, JobBase); module.exports = JobFallback; -// from user: { +// 1. from user: { // query: { // query: [{ // query: 'select ...', @@ -34,7 +34,8 @@ module.exports = JobFallback; // onerror: 'select ...' // } // } -// from redis: { +// +// 2. from redis: { // status: 'pending', // fallback_status: 'pending' // query: { @@ -190,7 +191,7 @@ JobFallback.prototype.shiftStatus = function (status, hasChanged) { // jshint maxcomplexity: 7 if (hasChanged.appliedToFallback) { if (!this.getNextQueryFromQueries() && (status === jobStatus.DONE || status === jobStatus.FAILED)) { - status = this._getLastStatusFromFinishedQuery(); + status = this.getLastFinishedStatus(); } else if (status === jobStatus.DONE || status === jobStatus.FAILED){ status = jobStatus.PENDING; } @@ -201,25 +202,15 @@ JobFallback.prototype.shiftStatus = function (status, hasChanged) { return status; }; - -JobFallback.prototype._getLastStatusFromFinishedQuery = function () { +JobFallback.prototype.getLastFinishedStatus = function () { var lastStatus = jobStatus.DONE; - for (var i = 0; i < this.data.query.query.length; i++) { - if (this.data.query.query[i].fallback_status) { - if (this.isFinalStatus(this.data.query.query[i].status)) { - lastStatus = this.data.query.query[i].status; - } else { - break; - } - } else { - if (this.isFinalStatus(this.data.query.query[i].status)) { - lastStatus = this.data.query.query[i].status; - } else { - break; - } + this.queries.forEach(function (query) { + var status = query.getStatus(this.data); + if (this.isFinalStatus(status)) { + lastStatus = status; } - } + }, this); return lastStatus; }; diff --git a/batch/models/query/fallback.js b/batch/models/query/fallback.js index a5676319..abfa89df 100644 --- a/batch/models/query/fallback.js +++ b/batch/models/query/fallback.js @@ -63,3 +63,7 @@ Fallback.prototype.setStatus = function (status, job, errorMessage) { return isValid; }; + +Fallback.prototype.getStatus = function (job) { + return job.query.query[this.index].fallback_status; +}; diff --git a/batch/models/query/query.js b/batch/models/query/query.js index 19b0661a..37fb9cef 100644 --- a/batch/models/query/query.js +++ b/batch/models/query/query.js @@ -39,3 +39,7 @@ Query.prototype.setStatus = function (status, job, errorMesssage) { return isValid; }; + +Query.prototype.getStatus = function (job) { + return job.query.query[this.index].status; +}; diff --git a/batch/models/query/query_base.js b/batch/models/query/query_base.js index 5bc7f313..737e1bf5 100644 --- a/batch/models/query/query_base.js +++ b/batch/models/query/query_base.js @@ -25,3 +25,7 @@ QueryBase.prototype.getNextQuery = function () { QueryBase.prototype.hasNextQuery = function (job) { return !!this.getNextQuery(job); }; + +QueryBase.prototype.getStatus = function () { + throw new Error('Unimplemented method'); +}; diff --git a/batch/models/query/query_fallback.js b/batch/models/query/query_fallback.js index 5f368c77..cb0579a3 100644 --- a/batch/models/query/query_fallback.js +++ b/batch/models/query/query_fallback.js @@ -68,4 +68,8 @@ QueryFallback.prototype.setStatus = function (status, job, previous, errorMesssa return { isValid: isValid, appliedToFallback: appliedToFallback }; }; +QueryFallback.prototype.getStatus = function (job) { + return this.query.getStatus(job); +}; + module.exports = QueryFallback; diff --git a/test/acceptance/job.fallback.test.js b/test/acceptance/job.fallback.test.js index 324a353b..39fd0128 100644 --- a/test/acceptance/job.fallback.test.js +++ b/test/acceptance/job.fallback.test.js @@ -1691,7 +1691,7 @@ describe('Batch API fallback job', function () { done(); } else if (job.status === jobStatus.DONE || job.status === jobStatus.CANCELLED) { clearInterval(interval); - done(new Error('Job ' + job.job_id + ' is ' + job.status + ', expected to be done')); + done(new Error('Job ' + job.job_id + ' is ' + job.status + ', expected to be failed')); } }); }, 50); @@ -1771,7 +1771,7 @@ describe('Batch API fallback job', function () { done(); } else if (job.status === jobStatus.DONE || job.status === jobStatus.CANCELLED) { clearInterval(interval); - done(new Error('Job ' + job.job_id + ' is ' + job.status + ', expected to be done')); + done(new Error('Job ' + job.job_id + ' is ' + job.status + ', expected to be failed')); } }); }, 50); From 2a2127f4e1a1bbb9a6db64a3252208af369f7950 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Aubert?= Date: Thu, 2 Jun 2016 18:02:25 +0200 Subject: [PATCH 7/9] Improved falback job to get the status of last finished query --- batch/models/job_fallback.js | 33 ++++++++-------------------- batch/models/query/fallback.js | 4 ++++ batch/models/query/query.js | 4 ++++ batch/models/query/query_base.js | 4 ++++ batch/models/query/query_fallback.js | 4 ++++ test/acceptance/job.fallback.test.js | 4 ++-- 6 files changed, 27 insertions(+), 26 deletions(-) diff --git a/batch/models/job_fallback.js b/batch/models/job_fallback.js index 23908076..8e357341 100644 --- a/batch/models/job_fallback.js +++ b/batch/models/job_fallback.js @@ -25,7 +25,7 @@ util.inherits(JobFallback, JobBase); module.exports = JobFallback; -// from user: { +// 1. from user: { // query: { // query: [{ // query: 'select ...', @@ -34,7 +34,8 @@ module.exports = JobFallback; // onerror: 'select ...' // } // } -// from redis: { +// +// 2. from redis: { // status: 'pending', // fallback_status: 'pending' // query: { @@ -190,7 +191,7 @@ JobFallback.prototype.shiftStatus = function (status, hasChanged) { // jshint maxcomplexity: 7 if (hasChanged.appliedToFallback) { if (!this.getNextQueryFromQueries() && (status === jobStatus.DONE || status === jobStatus.FAILED)) { - status = this._getLastStatusFromFinishedQuery(); + status = this.getLastFinishedStatus(); } else if (status === jobStatus.DONE || status === jobStatus.FAILED){ status = jobStatus.PENDING; } @@ -201,25 +202,9 @@ JobFallback.prototype.shiftStatus = function (status, hasChanged) { return status; }; - -JobFallback.prototype._getLastStatusFromFinishedQuery = function () { - var lastStatus = jobStatus.DONE; - - for (var i = 0; i < this.data.query.query.length; i++) { - if (this.data.query.query[i].fallback_status) { - if (this.isFinalStatus(this.data.query.query[i].status)) { - lastStatus = this.data.query.query[i].status; - } else { - break; - } - } else { - if (this.isFinalStatus(this.data.query.query[i].status)) { - lastStatus = this.data.query.query[i].status; - } else { - break; - } - } - } - - return lastStatus; +JobFallback.prototype.getLastFinishedStatus = function () { + return this.queries.reduce(function (lastFinished, query) { + var status = query.getStatus(this.data); + return this.isFinalStatus(status) ? status : lastFinished; + }.bind(this), jobStatus.DONE); }; diff --git a/batch/models/query/fallback.js b/batch/models/query/fallback.js index a5676319..abfa89df 100644 --- a/batch/models/query/fallback.js +++ b/batch/models/query/fallback.js @@ -63,3 +63,7 @@ Fallback.prototype.setStatus = function (status, job, errorMessage) { return isValid; }; + +Fallback.prototype.getStatus = function (job) { + return job.query.query[this.index].fallback_status; +}; diff --git a/batch/models/query/query.js b/batch/models/query/query.js index 19b0661a..37fb9cef 100644 --- a/batch/models/query/query.js +++ b/batch/models/query/query.js @@ -39,3 +39,7 @@ Query.prototype.setStatus = function (status, job, errorMesssage) { return isValid; }; + +Query.prototype.getStatus = function (job) { + return job.query.query[this.index].status; +}; diff --git a/batch/models/query/query_base.js b/batch/models/query/query_base.js index 5bc7f313..737e1bf5 100644 --- a/batch/models/query/query_base.js +++ b/batch/models/query/query_base.js @@ -25,3 +25,7 @@ QueryBase.prototype.getNextQuery = function () { QueryBase.prototype.hasNextQuery = function (job) { return !!this.getNextQuery(job); }; + +QueryBase.prototype.getStatus = function () { + throw new Error('Unimplemented method'); +}; diff --git a/batch/models/query/query_fallback.js b/batch/models/query/query_fallback.js index 5f368c77..cb0579a3 100644 --- a/batch/models/query/query_fallback.js +++ b/batch/models/query/query_fallback.js @@ -68,4 +68,8 @@ QueryFallback.prototype.setStatus = function (status, job, previous, errorMesssa return { isValid: isValid, appliedToFallback: appliedToFallback }; }; +QueryFallback.prototype.getStatus = function (job) { + return this.query.getStatus(job); +}; + module.exports = QueryFallback; diff --git a/test/acceptance/job.fallback.test.js b/test/acceptance/job.fallback.test.js index 324a353b..39fd0128 100644 --- a/test/acceptance/job.fallback.test.js +++ b/test/acceptance/job.fallback.test.js @@ -1691,7 +1691,7 @@ describe('Batch API fallback job', function () { done(); } else if (job.status === jobStatus.DONE || job.status === jobStatus.CANCELLED) { clearInterval(interval); - done(new Error('Job ' + job.job_id + ' is ' + job.status + ', expected to be done')); + done(new Error('Job ' + job.job_id + ' is ' + job.status + ', expected to be failed')); } }); }, 50); @@ -1771,7 +1771,7 @@ describe('Batch API fallback job', function () { done(); } else if (job.status === jobStatus.DONE || job.status === jobStatus.CANCELLED) { clearInterval(interval); - done(new Error('Job ' + job.job_id + ' is ' + job.status + ', expected to be done')); + done(new Error('Job ' + job.job_id + ' is ' + job.status + ', expected to be failed')); } }); }, 50); From 62cb63f13276b13a706ab098cdf9b566e7fd0498 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Aubert?= Date: Thu, 2 Jun 2016 19:39:48 +0200 Subject: [PATCH 8/9] Refactored job fallback method --- batch/models/job_fallback.js | 66 +++++++++++++++++------------------- 1 file changed, 32 insertions(+), 34 deletions(-) diff --git a/batch/models/job_fallback.js b/batch/models/job_fallback.js index 8e357341..0c14aaec 100644 --- a/batch/models/job_fallback.js +++ b/batch/models/job_fallback.js @@ -106,6 +106,10 @@ JobFallback.prototype.getNextQueryFromQueries = function () { } }; +JobFallback.prototype.hasNextQueryFromQueries = function () { + return !!this.getNextQueryFromQueries(); +}; + JobFallback.prototype.getNextQueryFromFallback = function () { if (this.fallback && this.fallback.hasNextQuery(this.data)) { @@ -132,35 +136,11 @@ JobFallback.prototype.setQuery = function (query) { }; JobFallback.prototype.setStatus = function (status, errorMesssage) { - // jshint maxcomplexity: 7 var now = new Date().toISOString(); - var hasChanged = { - isValid: false, - appliedToFallback: false - }; - var result = {}; - for (var i = 0; i < this.queries.length; i++) { - result = this.queries[i].setStatus(status, this.data, hasChanged, errorMesssage); - - if (result.isValid) { - hasChanged = result; - } - } - - result = this.setJobStatus(status, this.data, hasChanged, errorMesssage); - - if (result.isValid) { - hasChanged = result; - } - - if (!this.getNextQueryFromQueries() && this.fallback) { - result = this.fallback.setStatus(status, this.data, hasChanged); - - if (result.isValid) { - hasChanged = result; - } - } + var hasChanged = this.setQueryStatus(status, this.data, errorMesssage); + hasChanged = this.setJobStatus(status, this.data, hasChanged, errorMesssage); + hasChanged = this.setFallbackStatus(status, this.data, hasChanged); if (!hasChanged.isValid) { throw new Error('Cannot set status to ' + status); @@ -169,22 +149,40 @@ JobFallback.prototype.setStatus = function (status, errorMesssage) { this.data.updated_at = now; }; +JobFallback.prototype.setQueryStatus = function (status, job, errorMesssage) { + return this.queries.reduce(function (hasChanged, query) { + var result = query.setStatus(status, this.data, hasChanged, errorMesssage); + return result.isValid ? result : hasChanged; + }.bind(this), { isValid: false, appliedToFallback: false }); +}; + JobFallback.prototype.setJobStatus = function (status, job, hasChanged, errorMesssage) { - var isValid = false; + var result = { + isValid: false, + appliedToFallback: false + }; status = this.shiftStatus(status, hasChanged); - isValid = this.isValidTransition(job.status, status); - - if (isValid) { + result.isValid = this.isValidTransition(job.status, status); + if (result.isValid) { job.status = status; + if (status === jobStatus.FAILED && errorMesssage && !hasChanged.appliedToFallback) { + job.failed_reason = errorMesssage; + } } - if (status === jobStatus.FAILED && errorMesssage && !hasChanged.appliedToFallback) { - job.failed_reason = errorMesssage; + return result.isValid ? result : hasChanged; +}; + +JobFallback.prototype.setFallbackStatus = function (status, job, hasChanged) { + var result = hasChanged; + + if (this.fallback && !this.hasNextQueryFromQueries()) { + result = this.fallback.setStatus(status, job, hasChanged); } - return { isValid: isValid, appliedToFallback: false }; + return result.isValid ? result : hasChanged; }; JobFallback.prototype.shiftStatus = function (status, hasChanged) { From 7bd4f469355f31d9c562ee475a8171d64d3a9aa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa=20Aubert?= Date: Thu, 2 Jun 2016 19:55:04 +0200 Subject: [PATCH 9/9] Used right method to check if there is more queries from query in fallback job --- batch/models/job_fallback.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/batch/models/job_fallback.js b/batch/models/job_fallback.js index 0c14aaec..6117cb72 100644 --- a/batch/models/job_fallback.js +++ b/batch/models/job_fallback.js @@ -188,12 +188,12 @@ JobFallback.prototype.setFallbackStatus = function (status, job, hasChanged) { JobFallback.prototype.shiftStatus = function (status, hasChanged) { // jshint maxcomplexity: 7 if (hasChanged.appliedToFallback) { - if (!this.getNextQueryFromQueries() && (status === jobStatus.DONE || status === jobStatus.FAILED)) { + if (!this.hasNextQueryFromQueries() && (status === jobStatus.DONE || status === jobStatus.FAILED)) { status = this.getLastFinishedStatus(); } else if (status === jobStatus.DONE || status === jobStatus.FAILED){ status = jobStatus.PENDING; } - } else if (this.getNextQueryFromQueries() && status !== jobStatus.RUNNING) { + } else if (this.hasNextQueryFromQueries() && status !== jobStatus.RUNNING) { status = jobStatus.PENDING; }