176 lines
5.9 KiB
JavaScript
176 lines
5.9 KiB
JavaScript
var util = require('util');
|
|
var uuid = require('uuid');
|
|
|
|
var Hash = require('../common/hash');
|
|
var TcpCommand = require('../systemData/tcpCommand');
|
|
|
|
/**
|
|
* @private
|
|
* @param {string} connectionName
|
|
* @param {object} settings
|
|
* @constructor
|
|
* @property {number} totalOperationCount
|
|
*/
|
|
function OperationsManager(connectionName, settings) {
|
|
this._connectionName = connectionName;
|
|
this._settings = settings;
|
|
|
|
this._totalOperationCount = 0;
|
|
this._activeOperations = new Hash();
|
|
this._waitingOperations = [];
|
|
this._retryPendingOperations = [];
|
|
}
|
|
Object.defineProperty(OperationsManager.prototype, 'totalOperationCount', {
|
|
get: function() {
|
|
return this._totalOperationCount;
|
|
}
|
|
});
|
|
|
|
OperationsManager.prototype.getActiveOperation = function(correlationId) {
|
|
return this._activeOperations.get(correlationId);
|
|
};
|
|
|
|
function cleanUpError(connName, state, operation) {
|
|
return new Error(util.format("Connection '%s' was closed. %s %s.", connName, state, operation.toString()));
|
|
}
|
|
|
|
OperationsManager.prototype.cleanUp = function() {
|
|
var self = this;
|
|
this._activeOperations.forEach(function(correlationId, operation){
|
|
operation.operation.fail(cleanUpError(self._connectionName, 'Active', operation));
|
|
});
|
|
this._waitingOperations.forEach(function(operation) {
|
|
operation.operation.fail(cleanUpError(self._connectionName, 'Waiting', operation));
|
|
});
|
|
this._retryPendingOperations.forEach(function(operation) {
|
|
operation.operation.fail(cleanUpError(self._connectionName, 'Pending', operation));
|
|
});
|
|
|
|
this._activeOperations.clear();
|
|
this._waitingOperations = [];
|
|
this._retryPendingOperations = [];
|
|
this._totalOperationCount = 0;
|
|
};
|
|
|
|
OperationsManager.prototype.checkTimeoutsAndRetry = function(connection) {
|
|
if (!connection) throw new TypeError("Connection is null.");
|
|
|
|
var retryOperations = [];
|
|
var removeOperations = [];
|
|
var self = this;
|
|
this._activeOperations.forEach(function(correlationId, operation) {
|
|
if (operation.connectionId !== connection.connectionId)
|
|
{
|
|
retryOperations.push(operation);
|
|
}
|
|
else if (operation.timeout > 0 && Date.now() - operation.lastUpdated > self._settings.operationTimeout)
|
|
{
|
|
var err = util.format("EventStoreConnection '%s': operation never got response from server.\n"
|
|
+ "UTC now: %s, operation: %s.",
|
|
self._connectionName, new Date(), operation);
|
|
self._settings.log.error(err);
|
|
|
|
if (self._settings.failOnNoServerResponse)
|
|
{
|
|
operation.operation.fail(new Error(err));
|
|
removeOperations.push(operation);
|
|
}
|
|
else
|
|
{
|
|
retryOperations.push(operation);
|
|
}
|
|
}
|
|
});
|
|
|
|
retryOperations.forEach(function(operation) {
|
|
self.scheduleOperationRetry(operation);
|
|
});
|
|
removeOperations.forEach(function(operation) {
|
|
self.removeOperation(operation);
|
|
});
|
|
|
|
if (this._retryPendingOperations.length > 0)
|
|
{
|
|
this._retryPendingOperations.sort(function(x,y) {
|
|
if (x.seqNo < y.seqNo) return -1;
|
|
if (x.seqNo > y.seqNo) return 1;
|
|
return 0;
|
|
});
|
|
this._retryPendingOperations.forEach(function(operation) {
|
|
var oldCorrId = operation.correlationId;
|
|
operation.correlationId = uuid.v4();
|
|
operation.retryCount += 1;
|
|
self._logDebug("retrying, old corrId %s, operation %s.", oldCorrId, operation);
|
|
self.scheduleOperation(operation, connection);
|
|
});
|
|
this._retryPendingOperations = [];
|
|
}
|
|
|
|
this.scheduleWaitingOperations(connection);
|
|
};
|
|
|
|
OperationsManager.prototype.scheduleOperationRetry = function(operation) {
|
|
if (!this.removeOperation(operation))
|
|
return;
|
|
|
|
this._logDebug("ScheduleOperationRetry for %s.", operation);
|
|
if (operation.maxRetries >= 0 && operation.retryCount >= operation.maxRetries)
|
|
{
|
|
var err = util.format("Retry limit reached. Operation: %s, RetryCount: %d", operation, operation.retryCount);
|
|
operation.operation.fail(new Error(err));
|
|
return;
|
|
}
|
|
this._retryPendingOperations.push(operation);
|
|
};
|
|
|
|
OperationsManager.prototype.removeOperation = function(operation) {
|
|
this._activeOperations.remove(operation.correlationId);
|
|
this._logDebug("RemoveOperation SUCCEEDED for %s.", operation);
|
|
this._totalOperationCount = this._activeOperations.length + this._waitingOperations.length;
|
|
return true;
|
|
};
|
|
|
|
OperationsManager.prototype.scheduleWaitingOperations = function(connection) {
|
|
if (!connection) throw new TypeError("connection is null.");
|
|
while (this._waitingOperations.length > 0 && this._activeOperations.length < this._settings.maxConcurrentItems)
|
|
{
|
|
this.scheduleOperation(this._waitingOperations.shift(), connection);
|
|
}
|
|
this._totalOperationCount = this._activeOperations.length + this._waitingOperations.length;
|
|
};
|
|
|
|
OperationsManager.prototype.enqueueOperation = function(operation) {
|
|
this._logDebug("EnqueueOperation WAITING for %s.", operation);
|
|
this._waitingOperations.push(operation);
|
|
};
|
|
|
|
OperationsManager.prototype.scheduleOperation = function(operation, connection) {
|
|
if (this._activeOperations.length >= this._settings.maxConcurrentItems)
|
|
{
|
|
this._logDebug("ScheduleOperation WAITING for %s.", operation);
|
|
this._waitingOperations.push(operation);
|
|
}
|
|
else
|
|
{
|
|
operation.connectionId = connection.connectionId;
|
|
operation.lastUpdated = Date.now();
|
|
this._activeOperations.add(operation.correlationId, operation);
|
|
|
|
var pkg = operation.operation.createNetworkPackage(operation.correlationId);
|
|
this._logDebug("ScheduleOperation package %s, %s, %s.", TcpCommand.getName(pkg.command), pkg.correlationId, operation);
|
|
connection.enqueueSend(pkg);
|
|
}
|
|
this._totalOperationCount = this._activeOperations.length + this._waitingOperations.length;
|
|
};
|
|
|
|
OperationsManager.prototype._logDebug = function(message) {
|
|
if (!this._settings.verboseLogging) return;
|
|
|
|
if (arguments.length > 1)
|
|
message = util.format.apply(util, Array.prototype.slice.call(arguments));
|
|
|
|
this._settings.log.debug("EventStoreConnection '%s': %s.", this._connectionName, message);
|
|
};
|
|
|
|
module.exports = OperationsManager;
|