From 595d4f091855a557ff698d3d49f331ad2dc661bb Mon Sep 17 00:00:00 2001 From: Raul Ochoa Date: Mon, 10 Oct 2016 11:10:28 +0200 Subject: [PATCH 1/7] Fix sample config for batch timeout --- config/environments/development.js.example | 2 +- config/environments/production.js.example | 2 +- config/environments/staging.js.example | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config/environments/development.js.example b/config/environments/development.js.example index 9298dd0f1..b32dd945a 100644 --- a/config/environments/development.js.example +++ b/config/environments/development.js.example @@ -30,7 +30,7 @@ module.exports.db_host = 'localhost'; module.exports.db_port = '5432'; module.exports.db_batch_port = '5432'; module.exports.finished_jobs_ttl_in_seconds = 2 * 3600; // 2 hours -module.exports.batch_query_timeout || 12 * 3600 * 1000; // 12 hours in milliseconds +module.exports.batch_query_timeout = 12 * 3600 * 1000; // 12 hours in milliseconds module.exports.batch_log_filename = 'logs/batch-queries.log'; // Max database connections in the pool // Subsequent connections will wait for a free slot. diff --git a/config/environments/production.js.example b/config/environments/production.js.example index ed7a85100..296f2a63c 100644 --- a/config/environments/production.js.example +++ b/config/environments/production.js.example @@ -31,7 +31,7 @@ module.exports.db_host = 'localhost'; module.exports.db_port = '6432'; module.exports.db_batch_port = '5432'; module.exports.finished_jobs_ttl_in_seconds = 2 * 3600; // 2 hours -module.exports.batch_query_timeout || 12 * 3600 * 1000; // 12 hours in milliseconds +module.exports.batch_query_timeout = 12 * 3600 * 1000; // 12 hours in milliseconds module.exports.batch_log_filename = 'logs/batch-queries.log'; // Max database connections in the pool // Subsequent connections will wait for a free slot.i diff --git a/config/environments/staging.js.example b/config/environments/staging.js.example index f9c00dc36..245d5ea0f 100644 --- a/config/environments/staging.js.example +++ b/config/environments/staging.js.example @@ -31,7 +31,7 @@ module.exports.db_host = 'localhost'; module.exports.db_port = '6432'; module.exports.db_batch_port = '5432'; module.exports.finished_jobs_ttl_in_seconds = 2 * 3600; // 2 hours -module.exports.batch_query_timeout || 12 * 3600 * 1000; // 12 hours in milliseconds +module.exports.batch_query_timeout = 12 * 3600 * 1000; // 12 hours in milliseconds module.exports.batch_log_filename = 'logs/batch-queries.log'; // Max database connections in the pool // Subsequent connections will wait for a free slot. From e23edadcf18f03de4975ba80dd318fc42f73a689 Mon Sep 17 00:00:00 2001 From: Raul Ochoa Date: Mon, 10 Oct 2016 11:10:49 +0200 Subject: [PATCH 2/7] Fix and use a batch timeout good for tests --- config/environments/test.js.example | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/environments/test.js.example b/config/environments/test.js.example index cf7e84bc5..8e704aa6f 100644 --- a/config/environments/test.js.example +++ b/config/environments/test.js.example @@ -28,7 +28,7 @@ module.exports.db_host = 'localhost'; module.exports.db_port = '5432'; module.exports.db_batch_port = '5432'; module.exports.finished_jobs_ttl_in_seconds = 2 * 3600; // 2 hours -module.exports.batch_query_timeout || 12 * 3600 * 1000; // 12 hours in milliseconds +module.exports.batch_query_timeout = 5 * 1000; // 5 seconds in milliseconds module.exports.batch_log_filename = 'logs/batch-queries.log'; // Max database connections in the pool // Subsequent connections will wait for a free slot. From 51d4ff0698b3398897fa944ea6defab361bed5f2 Mon Sep 17 00:00:00 2001 From: Raul Ochoa Date: Mon, 10 Oct 2016 11:58:44 +0200 Subject: [PATCH 3/7] Differentiate between statement timeout and user cancelled query --- batch/job_runner.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/batch/job_runner.js b/batch/job_runner.js index cf82ddec1..a19157a7c 100644 --- a/batch/job_runner.js +++ b/batch/job_runner.js @@ -52,7 +52,7 @@ JobRunner.prototype._run = function (job, query, profiler, callback) { } // if query has been cancelled then it's going to get the current // job status saved by query_canceller - if (errorCodes[err.code.toString()] === 'query_canceled') { + if (cancelledByUser(err)) { return self.jobService.get(job.data.job_id, callback); } } @@ -93,4 +93,8 @@ JobRunner.prototype._run = function (job, query, profiler, callback) { }); }; +function cancelledByUser(err) { + return errorCodes[err.code.toString()] === 'query_canceled' && err.message.match(/user.*request/); +} + module.exports = JobRunner; From 5401a7edff34785ef4b3e7b7b0ffe9d7d20ba4e5 Mon Sep 17 00:00:00 2001 From: Raul Ochoa Date: Mon, 10 Oct 2016 12:00:54 +0200 Subject: [PATCH 4/7] Timeout is passed into query runner --- batch/job_runner.js | 10 +++++++--- batch/query_runner.js | 7 +++---- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/batch/job_runner.js b/batch/job_runner.js index a19157a7c..c5cfd09e5 100644 --- a/batch/job_runner.js +++ b/batch/job_runner.js @@ -23,6 +23,10 @@ JobRunner.prototype.run = function (job_id, callback) { } var query = job.getNextQuery(); + var timeout = 12 * 3600 * 1000; + if (Number.isFinite(global.settings.batch_query_timeout)) { + timeout = global.settings.batch_query_timeout; + } try { job.setStatus(jobStatus.RUNNING); @@ -37,15 +41,15 @@ JobRunner.prototype.run = function (job_id, callback) { profiler.done('running'); - self._run(job, query, profiler, callback); + self._run(job, query, timeout, profiler, callback); }); }); }; -JobRunner.prototype._run = function (job, query, profiler, callback) { +JobRunner.prototype._run = function (job, query, timeout, profiler, callback) { var self = this; - self.queryRunner.run(job.data.job_id, query, job.data.user, function (err /*, result */) { + self.queryRunner.run(job.data.job_id, query, job.data.user, timeout, function (err /*, result */) { if (err) { if (!err.code) { return callback(err); diff --git a/batch/query_runner.js b/batch/query_runner.js index 3336f29a3..e91469290 100644 --- a/batch/query_runner.js +++ b/batch/query_runner.js @@ -1,7 +1,6 @@ 'use strict'; var PSQL = require('cartodb-psql'); -var BATCH_QUERY_TIMEOUT = global.settings.batch_query_timeout || 12 * 3600 * 1000; // 12 hours in millisecond var debug = require('./util/debug')('query-runner'); function QueryRunner(userDatabaseMetadataService) { @@ -10,7 +9,7 @@ function QueryRunner(userDatabaseMetadataService) { module.exports = QueryRunner; -QueryRunner.prototype.run = function (job_id, sql, user, callback) { +QueryRunner.prototype.run = function (job_id, sql, user, timeout, callback) { this.userDatabaseMetadataService.getUserMetadata(user, function (err, userDatabaseMetadata) { if (err) { return callback(err); @@ -18,7 +17,7 @@ QueryRunner.prototype.run = function (job_id, sql, user, callback) { var pg = new PSQL(userDatabaseMetadata, {}, { destroyOnError: true }); - pg.query('SET statement_timeout=' + BATCH_QUERY_TIMEOUT, function (err) { + pg.query('SET statement_timeout=' + timeout, function (err) { if(err) { return callback(err); } @@ -26,7 +25,7 @@ QueryRunner.prototype.run = function (job_id, sql, user, callback) { // mark query to allow to users cancel their queries sql = '/* ' + job_id + ' */ ' + sql; - debug('Running query %s', sql); + debug('Running query [timeout=%d] %s', timeout, sql); pg.eventedQuery(sql, function (err, query) { if (err) { return callback(err); From 8a4f54bb8783f2ef9976dd10e3f37baa96fb73eb Mon Sep 17 00:00:00 2001 From: Raul Ochoa Date: Mon, 10 Oct 2016 12:01:36 +0200 Subject: [PATCH 5/7] Allow users to set max statement_timeout for their queries --- batch/job_runner.js | 7 + batch/job_status.js | 10 ++ batch/models/query/query.js | 8 +- test/acceptance/batch/batch-test-client.js | 165 ++++++++++++++++++ .../batch/job.query.timeout.test.js | 97 ++++++++++ 5 files changed, 286 insertions(+), 1 deletion(-) create mode 100644 test/acceptance/batch/batch-test-client.js create mode 100644 test/acceptance/batch/job.query.timeout.test.js diff --git a/batch/job_runner.js b/batch/job_runner.js index c5cfd09e5..0888c6a83 100644 --- a/batch/job_runner.js +++ b/batch/job_runner.js @@ -3,6 +3,7 @@ var errorCodes = require('../app/postgresql/error_codes').codeToCondition; var jobStatus = require('./job_status'); var Profiler = require('step-profiler'); +var _ = require('underscore'); function JobRunner(jobService, jobQueue, queryRunner, statsdClient) { this.jobService = jobService; @@ -27,6 +28,12 @@ JobRunner.prototype.run = function (job_id, callback) { if (Number.isFinite(global.settings.batch_query_timeout)) { timeout = global.settings.batch_query_timeout; } + if (_.isObject(query)) { + if (Number.isFinite(query.timeout) && query.timeout > 0) { + timeout = Math.min(timeout, query.timeout); + } + query = query.query; + } try { job.setStatus(jobStatus.RUNNING); diff --git a/batch/job_status.js b/batch/job_status.js index 889f1b43f..ef5ad1710 100644 --- a/batch/job_status.js +++ b/batch/job_status.js @@ -11,3 +11,13 @@ var JOB_STATUS_ENUM = { }; module.exports = JOB_STATUS_ENUM; + +var finalStatus = [ + JOB_STATUS_ENUM.CANCELLED, + JOB_STATUS_ENUM.DONE, + JOB_STATUS_ENUM.FAILED, + JOB_STATUS_ENUM.UNKNOWN +]; +module.exports.isFinal = function(status) { + return finalStatus.indexOf(status) !== -1; +}; diff --git a/batch/models/query/query.js b/batch/models/query/query.js index 2c3baa218..6970ae97e 100644 --- a/batch/models/query/query.js +++ b/batch/models/query/query.js @@ -21,7 +21,13 @@ Query.is = function (query) { Query.prototype.getNextQuery = function (job) { if (job.query.query[this.index].status === jobStatus.PENDING) { - return job.query.query[this.index].query; + var query = { + query: job.query.query[this.index].query + }; + if (Number.isFinite(job.query.query[this.index].timeout)) { + query.timeout = job.query.query[this.index].timeout; + } + return query; } }; diff --git a/test/acceptance/batch/batch-test-client.js b/test/acceptance/batch/batch-test-client.js new file mode 100644 index 000000000..0494038bd --- /dev/null +++ b/test/acceptance/batch/batch-test-client.js @@ -0,0 +1,165 @@ +'use strict'; + +require('../../helper'); +var assert = require('../../support/assert'); +var appServer = require('../../../app/server'); +var redisUtils = require('../../support/redis_utils'); +var debug = require('debug')('batch-test-client'); + +var JobStatus = require('../../../batch/job_status'); +var metadataBackend = require('cartodb-redis')(redisUtils.getConfig()); +var batchFactory = require('../../../batch/index'); + +function response(code) { + return { + status: code + }; +} + +var RESPONSE = { + OK: response(200), + CREATED: response(201) +}; + + +function BatchTestClient(config) { + this.config = config || {}; + this.server = appServer(); + + this.batch = batchFactory(metadataBackend, redisUtils.getConfig()); + this.batch.start(); + + this.pendingJobs = []; + this.ready = false; + this.batch.on('ready', function() { + this.ready = true; + this.pendingJobs.forEach(function(pendingJob) { + this.createJob(pendingJob.job, pendingJob.callback); + }.bind(this)); + }.bind(this)); +} + +module.exports = BatchTestClient; + +BatchTestClient.prototype.isReady = function() { + return this.ready; +}; + +BatchTestClient.prototype.createJob = function(job, callback) { + if (!this.isReady()) { + this.pendingJobs.push({ + job: job, + callback: callback + }); + return debug('Waiting for Batch service to be ready'); + } + assert.response( + this.server, + { + url: this.getUrl(), + headers: { + host: this.getHost(), + 'Content-Type': 'application/json' + }, + method: 'POST', + data: JSON.stringify(job) + }, + RESPONSE.CREATED, + function (err, res) { + if (err) { + return callback(err); + } + return callback(null, new JobResult(JSON.parse(res.body), this)); + }.bind(this) + ); +}; + +BatchTestClient.prototype.getJobStatus = function(jobId, callback) { + assert.response( + this.server, + { + url: this.getUrl(jobId), + headers: { + host: this.getHost() + }, + method: 'GET' + }, + RESPONSE.OK, + function (err, res) { + if (err) { + return callback(err); + } + return callback(null, JSON.parse(res.body)); + } + ); +}; + +BatchTestClient.prototype.cancelJob = function(jobId, callback) { + assert.response( + this.server, + { + url: this.getUrl(jobId), + headers: { + host: this.getHost() + }, + method: 'DELETE' + }, + RESPONSE.OK, + function (err, res) { + if (err) { + return callback(err); + } + return callback(null, JSON.parse(res.body)); + } + ); +}; + +BatchTestClient.prototype.drain = function(callback) { + this.batch.removeAllListeners(); + this.batch.stop(); + return redisUtils.clean('batch:*', callback); +}; + +BatchTestClient.prototype.getHost = function() { + return this.config.host || 'vizzuality.cartodb.com'; +}; + +BatchTestClient.prototype.getUrl = function(jobId) { + var urlParts = ['/api/v2/sql/job']; + if (jobId) { + urlParts.push(jobId); + } + return urlParts.join('/') + '?api_key=' + (this.config.apiKey || '1234'); +}; + + +/****************** JobResult ******************/ + + +function JobResult(job, batchTestClient) { + this.job = job; + this.batchTestClient = batchTestClient; +} + +JobResult.prototype.getStatus = function(callback) { + var self = this; + var interval = setInterval(function () { + self.batchTestClient.getJobStatus(self.job.job_id, function (err, job) { + if (err) { + clearInterval(interval); + return callback(err); + } + + if (JobStatus.isFinal(job.status)) { + clearInterval(interval); + return callback(null, job); + } else { + debug('Job %s [status=%s] waiting to be done', self.job.job_id, job.status); + } + }); + }, 50); +}; + +JobResult.prototype.cancel = function(callback) { + this.batchTestClient.cancelJob(this.job.job_id, callback); +}; diff --git a/test/acceptance/batch/job.query.timeout.test.js b/test/acceptance/batch/job.query.timeout.test.js new file mode 100644 index 000000000..853adf4d6 --- /dev/null +++ b/test/acceptance/batch/job.query.timeout.test.js @@ -0,0 +1,97 @@ +require('../../helper'); +var assert = require('../../support/assert'); + +var BatchTestClient = require('./batch-test-client'); +var JobStatus = require('../../../batch/job_status'); + +describe('job query timeout', function() { + + before(function() { + this.batchQueryTimeout = global.settings.batch_query_timeout; + this.batchTestClient = new BatchTestClient(); + }); + + after(function (done) { + global.settings.batch_query_timeout = this.batchQueryTimeout; + return this.batchTestClient.drain(done); + }); + + function createTimeoutQuery(query, timeout) { + return { + query: { + query: [ + { + timeout: timeout, + query: query + } + ] + } + }; + } + + it('should run query with higher user timeout', function (done) { + var jobRequest = createTimeoutQuery("select pg_sleep(0.1)", 200); + this.batchTestClient.createJob(jobRequest, function(err, jobResult) { + if (err) { + return done(err); + } + jobResult.getStatus(function(err, job) { + if (err) { + return done(err); + } + assert.equal(job.status, JobStatus.DONE); + done(); + }); + }); + }); + + it('should fail to run query with lower user timeout', function (done) { + var jobRequest = createTimeoutQuery("select pg_sleep(0.1)", 50); + this.batchTestClient.createJob(jobRequest, function(err, jobResult) { + if (err) { + return done(err); + } + jobResult.getStatus(function(err, job) { + if (err) { + return done(err); + } + assert.equal(job.status, JobStatus.FAILED); + done(); + }); + }); + }); + + it('should fail to run query with user timeout if it is higher than config', function (done) { + global.settings.batch_query_timeout = 100; + var jobRequest = createTimeoutQuery("select pg_sleep(1)", 2000); + this.batchTestClient.createJob(jobRequest, function(err, jobResult) { + if (err) { + return done(err); + } + jobResult.getStatus(function(err, job) { + if (err) { + return done(err); + } + assert.equal(job.status, JobStatus.FAILED); + done(); + }); + }); + }); + + it('should fail to run query with user timeout if set to 0 (ignored timeout)', function (done) { + global.settings.batch_query_timeout = 100; + var jobRequest = createTimeoutQuery("select pg_sleep(1)", 0); + this.batchTestClient.createJob(jobRequest, function(err, jobResult) { + if (err) { + return done(err); + } + jobResult.getStatus(function(err, job) { + if (err) { + return done(err); + } + assert.equal(job.status, JobStatus.FAILED); + done(); + }); + }); + }); +}); From deb1ccf876380df8aab8f21f9ee7b7467b641115 Mon Sep 17 00:00:00 2001 From: Raul Ochoa Date: Mon, 10 Oct 2016 12:09:13 +0200 Subject: [PATCH 6/7] DRY job final statuses --- batch/job_backend.js | 14 ++------------ batch/models/job_fallback.js | 31 ++++++++++++++----------------- batch/models/job_state_machine.js | 29 +++++++++++------------------ 3 files changed, 27 insertions(+), 47 deletions(-) diff --git a/batch/job_backend.js b/batch/job_backend.js index aeb22dcbe..3fcbee3d6 100644 --- a/batch/job_backend.js +++ b/batch/job_backend.js @@ -3,13 +3,7 @@ var REDIS_PREFIX = 'batch:jobs:'; var REDIS_DB = 5; var FINISHED_JOBS_TTL_IN_SECONDS = global.settings.finished_jobs_ttl_in_seconds || 2 * 3600; // 2 hours -var jobStatus = require('./job_status'); -var finalStatus = [ - jobStatus.CANCELLED, - jobStatus.DONE, - jobStatus.FAILED, - jobStatus.UNKNOWN -]; +var JobStatus = require('./job_status'); function JobBackend(metadataBackend, jobQueueProducer) { this.metadataBackend = metadataBackend; @@ -156,15 +150,11 @@ JobBackend.prototype.save = function (job, callback) { }); }; -function isFinalStatus(status) { - return finalStatus.indexOf(status) !== -1; -} - JobBackend.prototype.setTTL = function (job, callback) { var self = this; var redisKey = REDIS_PREFIX + job.job_id; - if (!isFinalStatus(job.status)) { + if (!JobStatus.isFinal(job.status)) { return callback(); } diff --git a/batch/models/job_fallback.js b/batch/models/job_fallback.js index 0d3b3beae..f7ee40c72 100644 --- a/batch/models/job_fallback.js +++ b/batch/models/job_fallback.js @@ -2,14 +2,11 @@ var util = require('util'); var JobBase = require('./job_base'); -var jobStatus = require('../job_status'); +var JobStatus = require('../job_status'); var QueryFallback = require('./query/query_fallback'); var MainFallback = require('./query/main_fallback'); var QueryFactory = require('./query/query_factory'); -var JobUtils = require('./job_state_machine'); -var jobUtils = new JobUtils(); - function JobFallback(jobDefinition) { JobBase.call(this, jobDefinition); @@ -73,19 +70,19 @@ JobFallback.is = function (query) { JobFallback.prototype.init = function () { for (var i = 0; i < this.data.query.query.length; i++) { if (shouldInitStatus(this.data.query.query[i])){ - this.data.query.query[i].status = jobStatus.PENDING; + this.data.query.query[i].status = JobStatus.PENDING; } if (shouldInitQueryFallbackStatus(this.data.query.query[i])) { - this.data.query.query[i].fallback_status = jobStatus.PENDING; + this.data.query.query[i].fallback_status = JobStatus.PENDING; } } if (shouldInitStatus(this.data)) { - this.data.status = jobStatus.PENDING; + this.data.status = JobStatus.PENDING; } if (shouldInitFallbackStatus(this.data)) { - this.data.fallback_status = jobStatus.PENDING; + this.data.fallback_status = JobStatus.PENDING; } }; @@ -170,7 +167,7 @@ JobFallback.prototype.setJobStatus = function (status, job, hasChanged, errorMes result.isValid = this.isValidTransition(job.status, status); if (result.isValid) { job.status = status; - if (status === jobStatus.FAILED && errorMesssage && !hasChanged.appliedToFallback) { + if (status === JobStatus.FAILED && errorMesssage && !hasChanged.appliedToFallback) { job.failed_reason = errorMesssage; } } @@ -191,13 +188,13 @@ JobFallback.prototype.setFallbackStatus = function (status, job, hasChanged) { JobFallback.prototype.shiftStatus = function (status, hasChanged) { // jshint maxcomplexity: 7 if (hasChanged.appliedToFallback) { - if (!this.hasNextQueryFromQueries() && (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 (status === JobStatus.DONE || status === JobStatus.FAILED){ + status = JobStatus.PENDING; } - } else if (this.hasNextQueryFromQueries() && status !== jobStatus.RUNNING) { - status = jobStatus.PENDING; + } else if (this.hasNextQueryFromQueries() && status !== JobStatus.RUNNING) { + status = JobStatus.PENDING; } return status; @@ -207,7 +204,7 @@ 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); + }.bind(this), JobStatus.DONE); }; JobFallback.prototype.log = function(logger) { @@ -251,8 +248,8 @@ JobFallback.prototype.log = function(logger) { }; function isFinished (job) { - return jobUtils.isFinalStatus(job.data.status) && - (!job.data.fallback_status || jobUtils.isFinalStatus(job.data.fallback_status)); + return JobStatus.isFinal(job.data.status) && + (!job.data.fallback_status || JobStatus.isFinal(job.data.fallback_status)); } function parseQueryId (queryId) { diff --git a/batch/models/job_state_machine.js b/batch/models/job_state_machine.js index 21ee60e52..f476b8a8f 100644 --- a/batch/models/job_state_machine.js +++ b/batch/models/job_state_machine.js @@ -1,24 +1,17 @@ 'use strict'; var assert = require('assert'); -var jobStatus = require('../job_status'); -var finalStatus = [ - jobStatus.CANCELLED, - jobStatus.DONE, - jobStatus.FAILED, - jobStatus.UNKNOWN -]; - +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] + [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 () { @@ -42,5 +35,5 @@ JobStateMachine.prototype.isValidTransition = function (initialStatus, finalStat }; JobStateMachine.prototype.isFinalStatus = function (status) { - return finalStatus.indexOf(status) !== -1; + return JobStatus.isFinal(status); }; From d8c3181054d1514c660b75e88c4cf9fe95f912f5 Mon Sep 17 00:00:00 2001 From: Raul Ochoa Date: Mon, 10 Oct 2016 12:11:21 +0200 Subject: [PATCH 7/7] Update news --- NEWS.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index db51fd4fe..09fa03333 100644 --- a/NEWS.md +++ b/NEWS.md @@ -2,7 +2,8 @@ ------------------- Announcements: - * limited batch queries to 12 hours + * Allow to set statement timeout per query in multi query batch queries. + * Batch queries default statement timeout set to 12 hours. * Multiple queries jobs pushed as first job between queries.