UNPKG

abraxas

Version:

A streaming gearman client / worker / server (as you choose)

306 lines (269 loc) 11.7 kB
"use strict"; var AbraxasSocket = require('./socket'); var util = require('util'); var cvar = require('./cvar'); var packet = require('gearman-packet'); var stream = require('readable-stream'); var streamToBuffer = require('./stream-to-buffer'); var AbraxasError = require('./errors'); var ClientConnection = module.exports = function (options,callback) { AbraxasSocket.call(this,options); this.feature = { exceptions: false, streaming: false }; this.adminTableQueue = []; var self = this; this.packets.acceptDefault('ERROR', function (data) { streamToBuffer(data.body,function(err, body) { if (err) { self.emitError(new AbraxasError.Receive()); } else { self.emitError(new AbraxasError.Server(data.args.errorcode,body)); } }); }); var init = cvar(callback); init.begin(); this.packets.acceptSerialWithError('OPTION_RES', function (err,data) { init.end(); if (err) return; if (data.args.option == 'exceptions') { self.feature.exceptions = true; } }); this.socket.write({kind:'request',type:packet.types['OPTION_REQ'],args:{option:'exceptions'}}); if (options.streaming) { init.begin(); var trace = AbraxasError.trace(ClientConnection); this.packets.acceptSerialWithError('OPTION_RES', function (err,data) { init.end(); if (err) { if (err.code == 'UNKNOWN_OPTION') { self.emitError(trace.withError(new AbraxasError.NoStreaming)); } else { self.emitError(trace.withError(err)); } return; } if (data.args.option == 'streaming') { self.feature.streaming = true; } }); this.socket.write({kind:'request',type:packet.types['OPTION_REQ'],args:{option:'streaming'}}); } } util.inherits( ClientConnection, AbraxasSocket ); ClientConnection.prototype.setClientId = function (id) { this.socket.write({kind:'request',type:packet.types['SET_CLIENT_ID'], args:{workerid:id}}); } ClientConnection.prototype.handleNoJob = function (handler) { this.packets.accept('NO_JOB', handler); } ClientConnection.prototype.handleNoOp = function (handler) { this.packets.accept('NOOP', handler); } ClientConnection.prototype.wakeup = function () { this.sleeping = false; } ClientConnection.prototype.sleep = function () { this.sleeping = true; this.socket.write({kind:'request',type:packet.types['PRE_SLEEP']}); } ClientConnection.prototype.sleeping = function () { return this.sleeping } ClientConnection.prototype.grabJob = function () { this.socket.write({kind:'request',type:packet.types['GRAB_JOB_UNIQ']}); } ClientConnection.prototype.handleJobAssign = function (handler) { this.connection.ref(); this.packets.accept('JOB_ASSIGN_UNIQ', this.onJobAssign = handler); } ClientConnection.prototype.unhandleJobAssign = function (handler) { this.connection.unref(); this.packets.removeHandler('JOB_ASSIGN_UNIQ', this.onJobAssign); } ClientConnection.prototype.canDo = function (func,timeout) { if (timeout) { this.socket.write({kind:'request',type:packet.types['CAN_DO_TIMEOUT'], args:{function: func, timeout: timeout}}); } else { this.socket.write({kind:'request',type:packet.types['CAN_DO'], args:{function: func}}); } } ClientConnection.prototype.resetAbilities = function () { this.socket.write({kind:'request',type:packet.types['RESET_ABILITIES']}); } ClientConnection.prototype.cantDo = function (func) { this.socket.write({kind:'request',type:packet.types['CANT_DO'],args:{functon: func}}); } ClientConnection.prototype.workData = function (jobid,data) { this.socket.write({kind:'request',type:packet.types['WORK_DATA'], args:{job:jobid}, body:data}); } ClientConnection.prototype.workComplete = function (jobid,data) { this.socket.write({kind:'request',type:packet.types['WORK_COMPLETE'], args:{job:jobid}, body:data}); } ClientConnection.prototype.workWarning = function (jobid,data) { this.socket.write({kind:'request',type:packet.types['WORK_WARNING'], args:{job:jobid}, body:data}); } ClientConnection.prototype.workException = function (jobid,data) { if (this.feature.exceptions) { this.socket.write({kind:'request',type:packet.types['WORK_EXCEPTION'], args:{job:jobid}, body:data}); this.socket.write({kind:'request',type:packet.types['WORK_FAIL'], args:{job:jobid}, body:data}); } else { this.workWarning(jobid,data); this.socket.write({kind:'request',type:packet.types['WORK_FAIL'], args:{job:jobid}, body:data}); } } ClientConnection.prototype.workStatus = function (jobid,percent) { this.socket.write({kind:'request',type:packet.types['WORK_STATUS'], args:{job:jobid, complete:percent*100, total: 100}}); } ClientConnection.prototype.echo = function (data, handler) { this.packets.acceptSerial('ECHO_RES', handler); this.socket.write({kind:'request',type:packet.types['ECHO_REQ'],body:data}); } ClientConnection.prototype.getStatus = function (jobid,handler) { this.packets.acceptByJobOnce('STATUS_RES', jobid, handler); this.socket.write({kind:'request',type:packet.types['GET_STATUS'],args:{job:jobid}}); } ClientConnection.prototype.submitJob = function(func,data,options,handler) { var type = 'SUBMIT_JOB'; if (options.priority=='high') type += '_HIGH'; else if (options.priority=='low') type += '_LOW'; this.packets.acceptSerialWithError('JOB_CREATED', handler); var args = {function: func, uniqueid:options.uniqueid==null?'':options.uniqueid}; this.socket.write({ kind:'request', type:packet.types[type], args:args, body:data }); } ClientConnection.prototype.submitJobBg = function(func,data,options,handler) { var type = 'SUBMIT_JOB'; if (options.priority=='high') type += '_HIGH'; else if (options.priority=='low') type += '_LOW'; type += '_BG'; this.packets.acceptSerialWithError('JOB_CREATED', handler); var args = {function: func, uniqueid:options.uniqueid==null?'':options.uniqueid}; this.socket.write({ kind:'request', type:packet.types[type], args:args, body:data }); } ClientConnection.prototype.submitJobEpoch = function(func,data,time,options,handler) { this.packets.acceptSerialWithError('JOB_CREATED', handler); var args = {function: func, uniqueid:options.uniqueid==null?'':options.uniqueid}; args.time = Math.round(Number(time instanceof Date ? time.getTime() / 1000 : time)); // this.socket.write({ kind:'request', type:packet.types['SUBMIT_JOB_EPOCH'], args:args, body:data }); } ClientConnection.prototype.submitJobSched = function(func,data,time,options,handler) { this.packets.acceptSerialWithError('JOB_CREATED', handler); var args = {function: func, uniqueid:options.uniqueid==null?'':options.uniqueid}; args.minute = time.minute==null?'':time.minute; args.hour = time.hour==null?'':time.hour; args.day = time.day==null?'':time.day; args.month = time.month==null?'':time.month; args.dow = time.dow==null?'':time.dow; this.socket.write({ kind:'request', type:packet.types['SUBMIT_JOB_SCHED'], args:args, body:data }); } ClientConnection.prototype.handleJobResult = function (task,func,trace) { task.emit('created',task); var out = new stream.PassThrough(); task.acceptResult(out); var self = this; var cancel = function () { self.packets.removeByJob('WORK_STATUS', task.jobid); self.packets.removeByJob('WORK_WARNING', task.jobid); self.packets.removeByJob('WORK_DATA', task.jobid); self.packets.removeByJob('WORK_FAIL', task.jobid); self.packets.removeByJob('WORK_EXCEPTION', task.jobid); self.packets.removeByJob('WORK_COMPLETE', task.jobid); }; this.packets.acceptByJob('WORK_STATUS', task.jobid, function (data) { var complete = Number(data.args.complete); var total = Number(data.args.total); var percent = complete = total ? complete / total : complete; task.emit('status',percent); }); var lastWarning; this.packets.acceptByJob('WORK_WARNING', task.jobid, function (data) { lastWarning = null; streamToBuffer(data.body,function(err, body) { if (err) { cancel(); task.acceptError(trace.withError(new AbraxasError.Receive(err.message))); task.end(); } else { lastWarning = body.toString(); task.emit('warn',lastWarning); } }); }); this.packets.acceptByJob('WORK_DATA', task.jobid, function (data) { data.body.pipe(out,{end: false }); }); this.packets.acceptByJob('WORK_FAIL', task.jobid, function (data) { cancel(); if (lastWarning == null) { task.acceptError(trace.withError(new AbraxasError.JobFail(func,task.jobid))); } else { task.acceptError(trace.withError(new AbraxasError.JobException(func,task.jobid,lastWarning))); } task.end(); }); this.packets.acceptByJob('WORK_EXCEPTION', task.jobid, function (data) { cancel(); streamToBuffer(data.body,function (err, body) { if (err) { task.acceptError(trace.withError(new AbraxasError.Receive(err.message))); } else { task.acceptError(trace.withError(new AbraxasError.JobException(func,task.jobid,body.toString()))); } task.end(); }); }); this.packets.acceptByJob('WORK_COMPLETE', task.jobid, function (data) { cancel(); data.body.once('error',function (err) { task.acceptError(trace.withError(new AbraxasError.Receive(err))); task.end(); }); data.body.pipe(out); }); } ClientConnection.prototype.adminSingleResult = function (command, handler) { this.socket.write({kind:'admin',type:packet.adminTypes['line'],args:{line:command}}); this.packets.acceptSerialAdminWithError('ADMIN:ok', handler); } ClientConnection.prototype._handleNextQueuedTable = function () { if (! this.adminTableQueue.length) return; var args = this.adminTableQueue[0]; var lineParser = args[0], onComplete = args[1]; var status = []; var self = this; this.packets.accept('ADMIN:line', function (result) { var parsed = lineParser(result); if (!parsed) return; status.push(parsed); }); this.packets.accept('ADMIN:block-complete', function (result) { self.packets.removeHandler('ADMIN:block-complete'); self.packets.removeHandler('ADMIN:line'); self.packets.removeHandler('ADMIN:error'); onComplete(null,status); self.adminTableQueue.shift(); self._handleNextQueuedTable(); }); this.packets.accept('ADMIN:error', function (result) { self.packets.removeHandler('ADMIN:error'); self.packets.removeListener('ADMIN:line'); self.packets.removeListener('ADMIN:block-complete'); onComplete(result); self.adminTableQueue.shift(); self._handleNextQueuedTable(); }); } ClientConnection.prototype.adminTable = function (command, onComplete, lineParser) { var self = this; this.adminTableQueue.push([lineParser, onComplete]); if (this.adminTableQueue.length==1) this._handleNextQueuedTable(); this.socket.write({kind:'admin',type:packet.adminTypes['line'],args:{line:command}}); }