first commit
This commit is contained in:
122
batch/models/job_base.js
Normal file
122
batch/models/job_base.js
Normal file
@@ -0,0 +1,122 @@
|
||||
'use strict';
|
||||
|
||||
var util = require('util');
|
||||
var uuid = require('node-uuid');
|
||||
var JobStateMachine = require('./job_state_machine');
|
||||
var jobStatus = require('../job_status');
|
||||
var mandatoryProperties = [
|
||||
'job_id',
|
||||
'status',
|
||||
'query',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
'host',
|
||||
'user'
|
||||
];
|
||||
|
||||
function JobBase(data) {
|
||||
JobStateMachine.call(this);
|
||||
|
||||
var now = new Date().toISOString();
|
||||
|
||||
this.data = data;
|
||||
|
||||
if (!this.data.job_id) {
|
||||
this.data.job_id = uuid.v4();
|
||||
}
|
||||
|
||||
if (!this.data.created_at) {
|
||||
this.data.created_at = now;
|
||||
}
|
||||
|
||||
if (!this.data.updated_at) {
|
||||
this.data.updated_at = now;
|
||||
}
|
||||
}
|
||||
util.inherits(JobBase, JobStateMachine);
|
||||
|
||||
module.exports = JobBase;
|
||||
|
||||
// should be implemented by childs
|
||||
JobBase.prototype.getNextQuery = function () {
|
||||
throw new Error('Unimplemented method');
|
||||
};
|
||||
|
||||
JobBase.prototype.hasNextQuery = function () {
|
||||
return !!this.getNextQuery();
|
||||
};
|
||||
|
||||
JobBase.prototype.isPending = function () {
|
||||
return this.data.status === jobStatus.PENDING;
|
||||
};
|
||||
|
||||
JobBase.prototype.isRunning = function () {
|
||||
return this.data.status === jobStatus.RUNNING;
|
||||
};
|
||||
|
||||
JobBase.prototype.isDone = function () {
|
||||
return this.data.status === jobStatus.DONE;
|
||||
};
|
||||
|
||||
JobBase.prototype.isCancelled = function () {
|
||||
return this.data.status === jobStatus.CANCELLED;
|
||||
};
|
||||
|
||||
JobBase.prototype.isFailed = function () {
|
||||
return this.data.status === jobStatus.FAILED;
|
||||
};
|
||||
|
||||
JobBase.prototype.isUnknown = function () {
|
||||
return this.data.status === jobStatus.UNKNOWN;
|
||||
};
|
||||
|
||||
JobBase.prototype.setQuery = function (query) {
|
||||
var now = new Date().toISOString();
|
||||
|
||||
if (!this.isPending()) {
|
||||
throw new Error('Job is not pending, it cannot be updated');
|
||||
}
|
||||
|
||||
this.data.updated_at = now;
|
||||
this.data.query = query;
|
||||
};
|
||||
|
||||
JobBase.prototype.setStatus = function (finalStatus, errorMesssage) {
|
||||
var now = new Date().toISOString();
|
||||
var initialStatus = this.data.status;
|
||||
var isValid = this.isValidTransition(initialStatus, finalStatus);
|
||||
|
||||
if (!isValid) {
|
||||
throw new Error('Cannot set status from ' + initialStatus + ' to ' + finalStatus);
|
||||
}
|
||||
|
||||
this.data.updated_at = now;
|
||||
this.data.status = finalStatus;
|
||||
if (finalStatus === jobStatus.FAILED && errorMesssage) {
|
||||
this.data.failed_reason = errorMesssage;
|
||||
}
|
||||
};
|
||||
|
||||
JobBase.prototype.validate = function () {
|
||||
for (var i = 0; i < mandatoryProperties.length; i++) {
|
||||
if (!this.data[mandatoryProperties[i]]) {
|
||||
throw new Error('property "' + mandatoryProperties[i] + '" is mandatory');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
JobBase.prototype.serialize = function () {
|
||||
var data = JSON.parse(JSON.stringify(this.data));
|
||||
|
||||
delete data.host;
|
||||
delete data.dbuser;
|
||||
delete data.port;
|
||||
delete data.dbname;
|
||||
delete data.pass;
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
JobBase.prototype.log = function(/*logger*/) {
|
||||
return false;
|
||||
};
|
||||
26
batch/models/job_factory.js
Normal file
26
batch/models/job_factory.js
Normal file
@@ -0,0 +1,26 @@
|
||||
'use strict';
|
||||
|
||||
var JobSimple = require('./job_simple');
|
||||
var JobMultiple = require('./job_multiple');
|
||||
var JobFallback = require('./job_fallback');
|
||||
|
||||
var Models = [ JobSimple, JobMultiple, JobFallback ];
|
||||
|
||||
function JobFactory() {
|
||||
}
|
||||
|
||||
module.exports = JobFactory;
|
||||
|
||||
JobFactory.create = function (data) {
|
||||
if (!data.query) {
|
||||
throw new Error('You must indicate a valid SQL');
|
||||
}
|
||||
|
||||
for (var i = 0; i < Models.length; i++) {
|
||||
if (Models[i].is(data.query)) {
|
||||
return new Models[i](data);
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error('there is no job class for the provided query');
|
||||
};
|
||||
279
batch/models/job_fallback.js
Normal file
279
batch/models/job_fallback.js
Normal file
@@ -0,0 +1,279 @@
|
||||
'use strict';
|
||||
|
||||
var util = require('util');
|
||||
var JobBase = require('./job_base');
|
||||
var JobStatus = require('../job_status');
|
||||
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);
|
||||
|
||||
module.exports = JobFallback;
|
||||
|
||||
// 1. from user: {
|
||||
// query: {
|
||||
// query: [{
|
||||
// query: 'select ...',
|
||||
// onsuccess: 'select ..'
|
||||
// }],
|
||||
// onerror: 'select ...'
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// 2. from redis: {
|
||||
// status: 'pending',
|
||||
// fallback_status: 'pending'
|
||||
// query: {
|
||||
// query: [{
|
||||
// query: 'select ...',
|
||||
// onsuccess: 'select ..'
|
||||
// status: 'pending',
|
||||
// fallback_status: 'pending',
|
||||
// }],
|
||||
// onerror: 'select ...'
|
||||
// }
|
||||
// }
|
||||
|
||||
JobFallback.is = function (query) {
|
||||
if (!query.query) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Array.isArray(query.query)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var i = 0; i < query.query.length; i++) {
|
||||
if (!QueryFallback.is(query.query[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
if (shouldInitQueryFallbackStatus(this.data.query.query[i])) {
|
||||
this.data.query.query[i].fallback_status = JobStatus.PENDING;
|
||||
}
|
||||
}
|
||||
|
||||
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)) {
|
||||
return this.queries[i].getNextQuery(this.data);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
JobFallback.prototype.hasNextQueryFromQueries = function () {
|
||||
return !!this.getNextQueryFromQueries();
|
||||
};
|
||||
|
||||
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.getNextQueryFromQueries();
|
||||
|
||||
if (!query) {
|
||||
query = this.getNextQueryFromFallback();
|
||||
}
|
||||
|
||||
return query;
|
||||
};
|
||||
|
||||
JobFallback.prototype.setQuery = function (query) {
|
||||
if (!JobFallback.is(query)) {
|
||||
throw new Error('You must indicate a valid SQL');
|
||||
}
|
||||
|
||||
JobFallback.super_.prototype.setQuery.call(this, query);
|
||||
};
|
||||
|
||||
JobFallback.prototype.setStatus = function (status, errorMesssage) {
|
||||
var now = new Date().toISOString();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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 result = {
|
||||
isValid: false,
|
||||
appliedToFallback: false
|
||||
};
|
||||
|
||||
status = this.shiftStatus(status, hasChanged);
|
||||
|
||||
result.isValid = this.isValidTransition(job.status, status);
|
||||
if (result.isValid) {
|
||||
job.status = status;
|
||||
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 result.isValid ? result : hasChanged;
|
||||
};
|
||||
|
||||
JobFallback.prototype.shiftStatus = function (status, hasChanged) {
|
||||
// jshint maxcomplexity: 7
|
||||
if (hasChanged.appliedToFallback) {
|
||||
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.hasNextQueryFromQueries() && status !== JobStatus.RUNNING) {
|
||||
status = JobStatus.PENDING;
|
||||
}
|
||||
|
||||
return status;
|
||||
};
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
JobFallback.prototype.log = function(logger) {
|
||||
if (!isFinished(this)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var queries = this.data.query.query;
|
||||
|
||||
for (var i = 0; i < queries.length; i++) {
|
||||
var query = queries[i];
|
||||
|
||||
var logEntry = {
|
||||
created: this.data.created_at,
|
||||
waiting: elapsedTime(this.data.created_at, query.started_at),
|
||||
time: query.started_at,
|
||||
endtime: query.ended_at,
|
||||
username: this.data.user,
|
||||
dbhost: this.data.host,
|
||||
job: this.data.job_id,
|
||||
status: query.status,
|
||||
elapsed: elapsedTime(query.started_at, query.ended_at)
|
||||
};
|
||||
|
||||
var queryId = query.id;
|
||||
|
||||
var tag = 'query';
|
||||
if (queryId) {
|
||||
logEntry.query_id = queryId;
|
||||
|
||||
var node = parseQueryId(queryId);
|
||||
if (node) {
|
||||
logEntry.analysis = node.analysisId;
|
||||
logEntry.node = node.nodeId;
|
||||
logEntry.type = node.nodeType;
|
||||
tag = 'analysis';
|
||||
}
|
||||
}
|
||||
|
||||
logger.info(logEntry, tag);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
function isFinished (job) {
|
||||
return JobStatus.isFinal(job.data.status) &&
|
||||
(!job.data.fallback_status || JobStatus.isFinal(job.data.fallback_status));
|
||||
}
|
||||
|
||||
function parseQueryId (queryId) {
|
||||
var data = queryId.split(':');
|
||||
|
||||
if (data.length === 3) {
|
||||
return {
|
||||
analysisId: data[0],
|
||||
nodeId: data[1],
|
||||
nodeType: data[2]
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function elapsedTime (started_at, ended_at) {
|
||||
if (!started_at || !ended_at) {
|
||||
return;
|
||||
}
|
||||
|
||||
var start = new Date(started_at);
|
||||
var end = new Date(ended_at);
|
||||
return end.getTime() - start.getTime();
|
||||
}
|
||||
91
batch/models/job_multiple.js
Normal file
91
batch/models/job_multiple.js
Normal file
@@ -0,0 +1,91 @@
|
||||
'use strict';
|
||||
|
||||
var util = require('util');
|
||||
var JobBase = require('./job_base');
|
||||
var jobStatus = require('../job_status');
|
||||
|
||||
function JobMultiple(jobDefinition) {
|
||||
JobBase.call(this, jobDefinition);
|
||||
|
||||
this.init();
|
||||
}
|
||||
util.inherits(JobMultiple, JobBase);
|
||||
|
||||
module.exports = JobMultiple;
|
||||
|
||||
JobMultiple.is = function (query) {
|
||||
if (!Array.isArray(query)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 1. From user: ['select * from ...', 'select * from ...']
|
||||
// 2. From redis: [ { query: 'select * from ...', status: 'pending' },
|
||||
// { query: 'select * from ...', status: 'pending' } ]
|
||||
for (var i = 0; i < query.length; i++) {
|
||||
if (typeof query[i] !== 'string') {
|
||||
if (typeof query[i].query !== 'string') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
JobMultiple.prototype.init = function () {
|
||||
|
||||
if (!this.data.status) {
|
||||
this.data.status = jobStatus.PENDING;
|
||||
}
|
||||
|
||||
for (var i = 0; i < this.data.query.length; i++) {
|
||||
if (!this.data.query[i].query && !this.data.query[i].status) {
|
||||
this.data.query[i] = {
|
||||
query: this.data.query[i],
|
||||
status: jobStatus.PENDING
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
JobMultiple.prototype.getNextQuery = function () {
|
||||
for (var i = 0; i < this.data.query.length; i++) {
|
||||
if (this.data.query[i].status === jobStatus.PENDING) {
|
||||
return this.data.query[i].query;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
JobMultiple.prototype.setQuery = function (query) {
|
||||
if (!JobMultiple.is(query)) {
|
||||
throw new Error('You must indicate a valid SQL');
|
||||
}
|
||||
|
||||
JobMultiple.super_.prototype.setQuery.call(this, query);
|
||||
};
|
||||
|
||||
JobMultiple.prototype.setStatus = function (finalStatus, errorMesssage) {
|
||||
var initialStatus = this.data.status;
|
||||
// if transition is to "done" and there are more queries to run
|
||||
// then job status must be "pending" instead of "done"
|
||||
// else job status transition to done (if "running")
|
||||
if (finalStatus === jobStatus.DONE && this.hasNextQuery()) {
|
||||
JobMultiple.super_.prototype.setStatus.call(this, jobStatus.PENDING);
|
||||
} else {
|
||||
JobMultiple.super_.prototype.setStatus.call(this, finalStatus, errorMesssage);
|
||||
}
|
||||
|
||||
for (var i = 0; i < this.data.query.length; i++) {
|
||||
var isValid = JobMultiple.super_.prototype.isValidTransition(this.data.query[i].status, finalStatus);
|
||||
|
||||
if (isValid) {
|
||||
this.data.query[i].status = finalStatus;
|
||||
if (finalStatus === jobStatus.FAILED && errorMesssage) {
|
||||
this.data.query[i].failed_reason = errorMesssage;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error('Cannot set status from ' + initialStatus + ' to ' + finalStatus);
|
||||
};
|
||||
34
batch/models/job_simple.js
Normal file
34
batch/models/job_simple.js
Normal file
@@ -0,0 +1,34 @@
|
||||
'use strict';
|
||||
|
||||
var util = require('util');
|
||||
var JobBase = require('./job_base');
|
||||
var jobStatus = require('../job_status');
|
||||
|
||||
function JobSimple(jobDefinition) {
|
||||
JobBase.call(this, jobDefinition);
|
||||
|
||||
if (!this.data.status) {
|
||||
this.data.status = jobStatus.PENDING;
|
||||
}
|
||||
}
|
||||
util.inherits(JobSimple, JobBase);
|
||||
|
||||
module.exports = JobSimple;
|
||||
|
||||
JobSimple.is = function (query) {
|
||||
return typeof query === 'string';
|
||||
};
|
||||
|
||||
JobSimple.prototype.getNextQuery = function () {
|
||||
if (this.isPending()) {
|
||||
return this.data.query;
|
||||
}
|
||||
};
|
||||
|
||||
JobSimple.prototype.setQuery = function (query) {
|
||||
if (!JobSimple.is(query)) {
|
||||
throw new Error('You must indicate a valid SQL');
|
||||
}
|
||||
|
||||
JobSimple.super_.prototype.setQuery.call(this, query);
|
||||
};
|
||||
39
batch/models/job_state_machine.js
Normal file
39
batch/models/job_state_machine.js
Normal file
@@ -0,0 +1,39 @@
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert');
|
||||
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]
|
||||
];
|
||||
|
||||
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 JobStatus.isFinal(status);
|
||||
};
|
||||
78
batch/models/query/fallback.js
Normal file
78
batch/models/query/fallback.js
Normal file
@@ -0,0 +1,78 @@
|
||||
'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) {
|
||||
var onsuccessQuery = job.query.query[this.index].onsuccess;
|
||||
if (onsuccessQuery) {
|
||||
onsuccessQuery = onsuccessQuery.replace(/<%=\s*job_id\s*%>/g, job.job_id);
|
||||
}
|
||||
return onsuccessQuery;
|
||||
}
|
||||
};
|
||||
|
||||
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) {
|
||||
var onerrorQuery = job.query.query[this.index].onerror;
|
||||
if (onerrorQuery) {
|
||||
onerrorQuery = onerrorQuery.replace(/<%=\s*job_id\s*%>/g, job.job_id);
|
||||
onerrorQuery = onerrorQuery.replace(/<%=\s*error_message\s*%>/g, job.query.query[this.index].failed_reason);
|
||||
}
|
||||
return onerrorQuery;
|
||||
}
|
||||
};
|
||||
|
||||
Fallback.prototype.hasOnError = function (job) {
|
||||
return !!this.getOnError(job);
|
||||
};
|
||||
|
||||
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 && errorMessage) {
|
||||
job.query.query[this.index].failed_reason = errorMessage;
|
||||
}
|
||||
}
|
||||
|
||||
return isValid;
|
||||
};
|
||||
|
||||
Fallback.prototype.getStatus = function (job) {
|
||||
return job.query.query[this.index].fallback_status;
|
||||
};
|
||||
74
batch/models/query/main_fallback.js
Normal file
74
batch/models/query/main_fallback.js
Normal file
@@ -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 };
|
||||
};
|
||||
57
batch/models/query/query.js
Normal file
57
batch/models/query/query.js
Normal file
@@ -0,0 +1,57 @@
|
||||
'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) {
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
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.RUNNING) {
|
||||
job.query.query[this.index].started_at = new Date().toISOString();
|
||||
}
|
||||
if (this.isFinalStatus(status)) {
|
||||
job.query.query[this.index].ended_at = new Date().toISOString();
|
||||
}
|
||||
if (status === jobStatus.FAILED && errorMesssage) {
|
||||
job.query.query[this.index].failed_reason = errorMesssage;
|
||||
}
|
||||
}
|
||||
|
||||
return isValid;
|
||||
};
|
||||
|
||||
Query.prototype.getStatus = function (job) {
|
||||
return job.query.query[this.index].status;
|
||||
};
|
||||
31
batch/models/query/query_base.js
Normal file
31
batch/models/query/query_base.js
Normal file
@@ -0,0 +1,31 @@
|
||||
'use strict';
|
||||
|
||||
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;
|
||||
|
||||
// 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.getStatus = function () {
|
||||
throw new Error('Unimplemented method');
|
||||
};
|
||||
16
batch/models/query/query_factory.js
Normal file
16
batch/models/query/query_factory.js
Normal file
@@ -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');
|
||||
};
|
||||
75
batch/models/query/query_fallback.js
Normal file
75
batch/models/query/query_fallback.js
Normal file
@@ -0,0 +1,75 @@
|
||||
'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 };
|
||||
};
|
||||
|
||||
QueryFallback.prototype.getStatus = function (job) {
|
||||
return this.query.getStatus(job);
|
||||
};
|
||||
|
||||
module.exports = QueryFallback;
|
||||
Reference in New Issue
Block a user