Adding persistent subscription support
This commit is contained in:
parent
29f6bf210b
commit
f1c3c42d46
|
@ -15,7 +15,6 @@ Incomplete/missing features:
|
||||||
|
|
||||||
- Typed errors: currently most errors are direct instance of Error, which is not practical for error handling
|
- Typed errors: currently most errors are direct instance of Error, which is not practical for error handling
|
||||||
- Ssl connection: Ssl connetion is not implemented yet
|
- Ssl connection: Ssl connetion is not implemented yet
|
||||||
- Persistent subscription: create/update/delete/connec to a persistent subscription are not implemented yet
|
|
||||||
- Set system settings: not implemented yet
|
- Set system settings: not implemented yet
|
||||||
- Performance: there's still some while loop in the code that could be problematic with node.js
|
- Performance: there's still some while loop in the code that could be problematic with node.js
|
||||||
- Tests: tests are only covering happy path scenarios for now
|
- Tests: tests are only covering happy path scenarios for now
|
||||||
|
|
1040
lib/dist.js
1040
lib/dist.js
File diff suppressed because it is too large
Load Diff
|
@ -41,6 +41,8 @@ function eventDataFactory(eventId, type, isJson, data, metadata) {
|
||||||
module.exports.EventStoreConnection = require('./eventStoreConnection');
|
module.exports.EventStoreConnection = require('./eventStoreConnection');
|
||||||
module.exports.UserCredentials = require('./systemData/userCredentials');
|
module.exports.UserCredentials = require('./systemData/userCredentials');
|
||||||
module.exports.EventData = EventData;
|
module.exports.EventData = EventData;
|
||||||
|
module.exports.PersistentSubscriptionSettings = require('./persistentSubscriptionSettings');
|
||||||
|
module.exports.SystemConsumerStrategies = require('./systemConsumerStrategies');
|
||||||
module.exports.expectedVersion = expectedVersion;
|
module.exports.expectedVersion = expectedVersion;
|
||||||
module.exports.positions = positions;
|
module.exports.positions = positions;
|
||||||
|
|
||||||
|
|
122
src/clientOperations/connectToPersistentSubscriptionOperation.js
Normal file
122
src/clientOperations/connectToPersistentSubscriptionOperation.js
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
var util = require('util');
|
||||||
|
var uuid = require('uuid');
|
||||||
|
|
||||||
|
var SubscriptionOperation = require('./subscriptionOperation');
|
||||||
|
var ClientMessage = require('../messages/clientMessage');
|
||||||
|
var TcpCommand = require('../systemData/tcpCommand');
|
||||||
|
var TcpFlags = require('../systemData/tcpFlags');
|
||||||
|
var TcpPackage = require('../systemData/tcpPackage');
|
||||||
|
var createBufferSegment = require('../common/bufferSegment');
|
||||||
|
var InspectionResult = require('./../systemData/inspectionResult');
|
||||||
|
var InspectionDecision = require('../systemData/inspectionDecision');
|
||||||
|
var results = require('../results');
|
||||||
|
var SubscriptionDropReason = require('../subscriptionDropReason');
|
||||||
|
var PersistentEventStoreSubscription = require('../persistentEventStoreSubscription');
|
||||||
|
var ensure = require('../common/utils/ensure');
|
||||||
|
|
||||||
|
function ConnectToPersistentSubscriptionOperation(
|
||||||
|
log, cb, groupName, bufferSize, streamId, userCredentials, eventAppeared, subscriptionDropped,
|
||||||
|
verboseLogging, getConnection
|
||||||
|
) {
|
||||||
|
SubscriptionOperation.call(this, log, cb, streamId, false, userCredentials, eventAppeared, subscriptionDropped, verboseLogging, getConnection);
|
||||||
|
|
||||||
|
this._groupName = groupName;
|
||||||
|
this._bufferSize = bufferSize;
|
||||||
|
this._subscriptionId = null;
|
||||||
|
}
|
||||||
|
util.inherits(ConnectToPersistentSubscriptionOperation, SubscriptionOperation);
|
||||||
|
|
||||||
|
ConnectToPersistentSubscriptionOperation.prototype._createSubscriptionPackage = function() {
|
||||||
|
var dto = new ClientMessage.ConnectToPersistentSubscription(this._groupName, this._streamId, this._bufferSize);
|
||||||
|
return new TcpPackage(TcpCommand.ConnectToPersistentSubscription,
|
||||||
|
this._userCredentials !== null ? TcpFlags.Authenticated : TcpFlags.None,
|
||||||
|
this._correlationId,
|
||||||
|
this._userCredentials !== null ? this._userCredentials.username : null,
|
||||||
|
this._userCredentials !== null ? this._userCredentials.password : null,
|
||||||
|
createBufferSegment(dto.toBuffer()));
|
||||||
|
};
|
||||||
|
|
||||||
|
ConnectToPersistentSubscriptionOperation.prototype._inspectPackage = function(pkg) {
|
||||||
|
if (pkg.command == TcpCommand.PersistentSubscriptionConfirmation)
|
||||||
|
{
|
||||||
|
var dto = ClientMessage.PersistentSubscriptionConfirmation.decode(pkg.data.toBuffer());
|
||||||
|
this._confirmSubscription(dto.last_commit_position, dto.last_event_number);
|
||||||
|
this._subscriptionId = dto.subscription_id;
|
||||||
|
return new InspectionResult(InspectionDecision.Subscribed, "SubscriptionConfirmation");
|
||||||
|
}
|
||||||
|
if (pkg.command == TcpCommand.PersistentSubscriptionStreamEventAppeared)
|
||||||
|
{
|
||||||
|
var dto = ClientMessage.PersistentSubscriptionStreamEventAppeared.decode(pkg.data.toBuffer());
|
||||||
|
this._onEventAppeared(new results.ResolvedEvent(dto.event));
|
||||||
|
return new InspectionResult(InspectionDecision.DoNothing, "StreamEventAppeared");
|
||||||
|
}
|
||||||
|
if (pkg.command == TcpCommand.SubscriptionDropped)
|
||||||
|
{
|
||||||
|
var dto = ClientMessage.SubscriptionDropped.decode(pkg.data.toBuffer());
|
||||||
|
if (dto.reason == ClientMessage.SubscriptionDropped.SubscriptionDropReason.AccessDenied)
|
||||||
|
{
|
||||||
|
this.dropSubscription(SubscriptionDropReason.AccessDenied, new Error("You do not have access to the stream."));
|
||||||
|
return new InspectionResult(InspectionDecision.EndOperation, "SubscriptionDropped");
|
||||||
|
}
|
||||||
|
if (dto.reason == ClientMessage.SubscriptionDropped.SubscriptionDropReason.NotFound)
|
||||||
|
{
|
||||||
|
this.dropSubscription(SubscriptionDropReason.NotFound, new Error("Subscription not found"));
|
||||||
|
return new InspectionResult(InspectionDecision.EndOperation, "SubscriptionDropped");
|
||||||
|
}
|
||||||
|
if (dto.reason == ClientMessage.SubscriptionDropped.SubscriptionDropReason.PersistentSubscriptionDeleted)
|
||||||
|
{
|
||||||
|
this.dropSubscription(SubscriptionDropReason.PersistentSubscriptionDeleted, new Error("Persistent subscription deleted."));
|
||||||
|
return new InspectionResult(InspectionDecision.EndOperation, "SubscriptionDropped");
|
||||||
|
}
|
||||||
|
if (dto.reason == ClientMessage.SubscriptionDropped.SubscriptionDropReason.SubscriberMaxCountReached)
|
||||||
|
{
|
||||||
|
this.dropSubscription(SubscriptionDropReason.MaxSubscribersReached, new Error("Maximum subscribers reached."));
|
||||||
|
return new InspectionResult(InspectionDecision.EndOperation, "SubscriptionDropped");
|
||||||
|
}
|
||||||
|
this.dropSubscription(SubscriptionDropReason.UserInitiated, null, this._getConnection());
|
||||||
|
return new InspectionResult(InspectionDecision.EndOperation, "SubscriptionDropped");
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
ConnectToPersistentSubscriptionOperation.prototype._createSubscriptionObject = function(lastCommitPosition, lastEventNumber) {
|
||||||
|
return new PersistentEventStoreSubscription(this, this._streamId, lastCommitPosition, lastEventNumber);
|
||||||
|
};
|
||||||
|
|
||||||
|
ConnectToPersistentSubscriptionOperation.prototype.notifyEventsProcessed = function(processedEvents) {
|
||||||
|
ensure.notNull(processedEvents, "processedEvents");
|
||||||
|
var dto = new ClientMessage.PersistentSubscriptionAckEvents({
|
||||||
|
subscription_id: this._subscriptionId,
|
||||||
|
processed_event_ids: processedEvents.map(function (x) {
|
||||||
|
return new Buffer(uuid.parse(x));
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
var pkg = new TcpPackage(TcpCommand.PersistentSubscriptionAckEvents,
|
||||||
|
this._userCredentials !== null ? TcpFlags.Authenticated : TcpFlags.None,
|
||||||
|
this._correlationId,
|
||||||
|
this._userCredentials !== null ? this._userCredentials.username : null,
|
||||||
|
this._userCredentials !== null ? this._userCredentials.password : null,
|
||||||
|
createBufferSegment(dto.encode().toBuffer()));
|
||||||
|
this._enqueueSend(pkg);
|
||||||
|
};
|
||||||
|
|
||||||
|
ConnectToPersistentSubscriptionOperation.prototype.notifyEventsFailed = function(processedEvents, action, reason) {
|
||||||
|
ensure.notNull(processedEvents, "processedEvents");
|
||||||
|
ensure.notNull(reason, "reason");
|
||||||
|
var dto = new ClientMessage.PersistentSubscriptionNakEvents(
|
||||||
|
this._subscriptionId,
|
||||||
|
processedEvents.map(function(x) { return new Buffer(uuid.parse(x)); }),
|
||||||
|
reason,
|
||||||
|
action);
|
||||||
|
|
||||||
|
var pkg = new TcpPackage(TcpCommand.PersistentSubscriptionNakEvents,
|
||||||
|
this._userCredentials != null ? TcpFlags.Authenticated : TcpFlags.None,
|
||||||
|
this._correlationId,
|
||||||
|
this._userCredentials != null ? this._userCredentials.username : null,
|
||||||
|
this._userCredentials != null ? this._userCredentials.password : null,
|
||||||
|
createBufferSegment(dto.toBuffer()));
|
||||||
|
this._enqueueSend(pkg);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = ConnectToPersistentSubscriptionOperation;
|
|
@ -0,0 +1,74 @@
|
||||||
|
var util = require('util');
|
||||||
|
var uuid = require('uuid');
|
||||||
|
|
||||||
|
var ensure = require('../common/utils/ensure');
|
||||||
|
var OperationBase = require('../clientOperations/operationBase');
|
||||||
|
var TcpCommand = require('../systemData/tcpCommand');
|
||||||
|
var ClientMessage = require('../messages/clientMessage');
|
||||||
|
var SystemConsumerStrategies = require('../systemConsumerStrategies');
|
||||||
|
var InspectionDecision = require('../systemData/inspectionDecision');
|
||||||
|
var InspectionResult = require('./../systemData/inspectionResult');
|
||||||
|
var results = require('../results');
|
||||||
|
|
||||||
|
|
||||||
|
function CreatePersistentSubscriptionOperation(log, cb, stream, groupName, settings, userCredentials) {
|
||||||
|
OperationBase.call(this, log, cb, TcpCommand.CreatePersistentSubscription, TcpCommand.CreatePersistentSubscriptionCompleted, userCredentials);
|
||||||
|
|
||||||
|
ensure.notNull(settings, "settings");
|
||||||
|
this._resolveLinkTos = settings.resolveLinkTos;
|
||||||
|
this._stream = stream;
|
||||||
|
this._groupName = groupName;
|
||||||
|
this._startFromBeginning = settings.startFrom;
|
||||||
|
this._maxRetryCount = settings.maxRetryCount;
|
||||||
|
this._liveBufferSize = settings.liveBufferSize;
|
||||||
|
this._readBatchSize = settings.readBatchSize;
|
||||||
|
this._bufferSize = settings.historyBufferSize;
|
||||||
|
this._recordStatistics = settings.extraStatistics;
|
||||||
|
this._messageTimeoutMilliseconds = settings.messageTimeout;
|
||||||
|
this._checkPointAfter = settings.checkPointAfter;
|
||||||
|
this._minCheckPointCount = settings.minCheckPointCount;
|
||||||
|
this._maxCheckPointCount = settings.maxCheckPointCount;
|
||||||
|
this._maxSubscriberCount = settings.maxSubscriberCount;
|
||||||
|
this._namedConsumerStrategy = settings.namedConsumerStrategy;
|
||||||
|
|
||||||
|
this._responseType = ClientMessage.CreatePersistentSubscriptionCompleted;
|
||||||
|
}
|
||||||
|
util.inherits(CreatePersistentSubscriptionOperation, OperationBase);
|
||||||
|
|
||||||
|
CreatePersistentSubscriptionOperation.prototype._createRequestDto = function() {
|
||||||
|
return new ClientMessage.CreatePersistentSubscription(this._groupName, this._stream, this._resolveLinkTos,
|
||||||
|
this._startFromBeginning, this._messageTimeoutMilliseconds, this._recordStatistics, this._liveBufferSize,
|
||||||
|
this._readBatchSize, this._bufferSize, this._maxRetryCount,
|
||||||
|
this._namedConsumerStrategy == SystemConsumerStrategies.RoundRobin, this._checkPointAfter,
|
||||||
|
this._maxCheckPointCount, this._minCheckPointCount, this._maxSubscriberCount, this._namedConsumerStrategy);
|
||||||
|
};
|
||||||
|
|
||||||
|
CreatePersistentSubscriptionOperation.prototype._inspectResponse = function(response) {
|
||||||
|
switch (response.result)
|
||||||
|
{
|
||||||
|
case ClientMessage.CreatePersistentSubscriptionCompleted.CreatePersistentSubscriptionResult.Success:
|
||||||
|
this._succeed();
|
||||||
|
return new InspectionResult(InspectionDecision.EndOperation, "Success");
|
||||||
|
case ClientMessage.CreatePersistentSubscriptionCompleted.CreatePersistentSubscriptionResult.Fail:
|
||||||
|
this.fail(new Error(util.format("Subscription group %s on stream %s failed '%s'", this._groupName, this._stream, response.reason)));
|
||||||
|
return new InspectionResult(InspectionDecision.EndOperation, "Fail");
|
||||||
|
case ClientMessage.CreatePersistentSubscriptionCompleted.CreatePersistentSubscriptionResult.AccessDenied:
|
||||||
|
this.fail(new Error(util.format("Write access denied for stream '%s'.", this._stream)));
|
||||||
|
return new InspectionResult(InspectionDecision.EndOperation, "AccessDenied");
|
||||||
|
case ClientMessage.CreatePersistentSubscriptionCompleted.CreatePersistentSubscriptionResult.AlreadyExists:
|
||||||
|
this.fail(new Error(util.format("Subscription group %s on stream %s already exists", this._groupName, this._stream)));
|
||||||
|
return new InspectionResult(InspectionDecision.EndOperation, "AlreadyExists");
|
||||||
|
default:
|
||||||
|
throw new Error(util.format("Unexpected OperationResult: %s.", response.result));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
CreatePersistentSubscriptionOperation.prototype._transformResponse = function(response) {
|
||||||
|
return new results.PersistentSubscriptionCreateResult(results.PersistentSubscriptionCreateStatus.Success);
|
||||||
|
};
|
||||||
|
|
||||||
|
CreatePersistentSubscriptionOperation.prototype.toString = function() {
|
||||||
|
return util.format("Stream: %s, Group Name: %s", this._stream, this._groupName);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = CreatePersistentSubscriptionOperation;
|
|
@ -0,0 +1,55 @@
|
||||||
|
var util = require('util');
|
||||||
|
var uuid = require('uuid');
|
||||||
|
|
||||||
|
var ensure = require('../common/utils/ensure');
|
||||||
|
var OperationBase = require('../clientOperations/operationBase');
|
||||||
|
var TcpCommand = require('../systemData/tcpCommand');
|
||||||
|
var ClientMessage = require('../messages/clientMessage');
|
||||||
|
var InspectionDecision = require('../systemData/inspectionDecision');
|
||||||
|
var InspectionResult = require('./../systemData/inspectionResult');
|
||||||
|
var results = require('../results');
|
||||||
|
|
||||||
|
|
||||||
|
function DeletePersistentSubscriptionOperation(log, cb, stream, groupName, userCredentials) {
|
||||||
|
OperationBase.call(this, log, cb, TcpCommand.DeletePersistentSubscription, TcpCommand.DeletePersistentSubscriptionCompleted, userCredentials);
|
||||||
|
|
||||||
|
this._stream = stream;
|
||||||
|
this._groupName = groupName;
|
||||||
|
|
||||||
|
this._responseType = ClientMessage.DeletePersistentSubscriptionCompleted;
|
||||||
|
}
|
||||||
|
util.inherits(DeletePersistentSubscriptionOperation, OperationBase);
|
||||||
|
|
||||||
|
DeletePersistentSubscriptionOperation.prototype._createRequestDto = function() {
|
||||||
|
return new ClientMessage.DeletePersistentSubscription(this._groupName, this._stream);
|
||||||
|
};
|
||||||
|
|
||||||
|
DeletePersistentSubscriptionOperation.prototype._inspectResponse = function(response) {
|
||||||
|
switch (response.result)
|
||||||
|
{
|
||||||
|
case ClientMessage.DeletePersistentSubscriptionCompleted.DeletePersistentSubscriptionResult.Success:
|
||||||
|
this._succeed();
|
||||||
|
return new InspectionResult(InspectionDecision.EndOperation, "Success");
|
||||||
|
case ClientMessage.DeletePersistentSubscriptionCompleted.DeletePersistentSubscriptionResult.Fail:
|
||||||
|
this.fail(new Error(util.format("Subscription group %s on stream %s failed '%s'", this._groupName, this._stream, response.reason)));
|
||||||
|
return new InspectionResult(InspectionDecision.EndOperation, "Fail");
|
||||||
|
case ClientMessage.DeletePersistentSubscriptionCompleted.DeletePersistentSubscriptionResult.AccessDenied:
|
||||||
|
this.fail(new Error(util.format("Write access denied for stream '%s'.", this._stream)));
|
||||||
|
return new InspectionResult(InspectionDecision.EndOperation, "AccessDenied");
|
||||||
|
case ClientMessage.DeletePersistentSubscriptionCompleted.DeletePersistentSubscriptionResult.DoesNotExist:
|
||||||
|
this.fail(new Error(util.format("Subscription group %s on stream %s does not exist", this._groupName, this._stream)));
|
||||||
|
return new InspectionResult(InspectionDecision.EndOperation, "DoesNotExist");
|
||||||
|
default:
|
||||||
|
throw new Error(util.format("Unexpected OperationResult: %s.", response.result));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
DeletePersistentSubscriptionOperation.prototype._transformResponse = function(response) {
|
||||||
|
return new results.PersistentSubscriptionDeleteResult(results.PersistentSubscriptionDeleteStatus.Success);
|
||||||
|
};
|
||||||
|
|
||||||
|
DeletePersistentSubscriptionOperation.prototype.toString = function() {
|
||||||
|
return util.format("Stream: %s, Group Name: %s", this._stream, this._groupName);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = DeletePersistentSubscriptionOperation;
|
|
@ -51,7 +51,7 @@ OperationBase.prototype._succeed = function() {
|
||||||
|
|
||||||
OperationBase.prototype.createNetworkPackage = function(correlationId) {
|
OperationBase.prototype.createNetworkPackage = function(correlationId) {
|
||||||
var dto = this._createRequestDto();
|
var dto = this._createRequestDto();
|
||||||
var buf = dto.encode().toBuffer();
|
var buf = dto.toBuffer();
|
||||||
return new TcpPackage(
|
return new TcpPackage(
|
||||||
this._requestCommand,
|
this._requestCommand,
|
||||||
this.userCredentials ? TcpFlags.Authenticated : TcpFlags.None,
|
this.userCredentials ? TcpFlags.Authenticated : TcpFlags.None,
|
||||||
|
|
|
@ -39,6 +39,10 @@ function SubscriptionOperation(
|
||||||
this._actionQueue = [];
|
this._actionQueue = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SubscriptionOperation.prototype._enqueueSend = function(pkg) {
|
||||||
|
this._getConnection().enqueueSend(pkg);
|
||||||
|
};
|
||||||
|
|
||||||
SubscriptionOperation.prototype.subscribe = function(correlationId, connection) {
|
SubscriptionOperation.prototype.subscribe = function(correlationId, connection) {
|
||||||
if (connection === null) throw new TypeError("connection is null.");
|
if (connection === null) throw new TypeError("connection is null.");
|
||||||
|
|
||||||
|
@ -60,7 +64,7 @@ SubscriptionOperation.prototype.unsubscribe = function() {
|
||||||
|
|
||||||
SubscriptionOperation.prototype._createUnsubscriptionPackage = function() {
|
SubscriptionOperation.prototype._createUnsubscriptionPackage = function() {
|
||||||
var msg = new ClientMessage.UnsubscribeFromStream();
|
var msg = new ClientMessage.UnsubscribeFromStream();
|
||||||
var data = new BufferSegment(msg.encode().toBuffer());
|
var data = new BufferSegment(msg.toBuffer());
|
||||||
return new TcpPackage(TcpCommand.UnsubscribeFromStream, TcpFlags.None, this._correlationId, null, null, data);
|
return new TcpPackage(TcpCommand.UnsubscribeFromStream, TcpFlags.None, this._correlationId, null, null, data);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -182,11 +186,12 @@ SubscriptionOperation.prototype.dropSubscription = function(reason, err, connect
|
||||||
this._log.debug("Subscription %s to %s: closing subscription, reason: %s, exception: %s...",
|
this._log.debug("Subscription %s to %s: closing subscription, reason: %s, exception: %s...",
|
||||||
this._correlationId, this._streamId || "<all>", reason, err);
|
this._correlationId, this._streamId || "<all>", reason, err);
|
||||||
|
|
||||||
if (reason !== SubscriptionDropReason.UserInitiated)
|
if (reason !== SubscriptionDropReason.UserInitiated && this._subscription === null)
|
||||||
{
|
{
|
||||||
if (err === null) throw new Error(util.format("No exception provided for subscription drop reason '%s", reason));
|
if (err === null) throw new Error(util.format("No exception provided for subscription drop reason '%s", reason));
|
||||||
//TODO: this should be last thing to execute
|
//TODO: this should be last thing to execute
|
||||||
this._cb(err);
|
this._cb(err);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reason === SubscriptionDropReason.UserInitiated && this._subscription !== null && connection !== null)
|
if (reason === SubscriptionDropReason.UserInitiated && this._subscription !== null && connection !== null)
|
||||||
|
@ -223,7 +228,7 @@ SubscriptionOperation.prototype._onEventAppeared = function(e) {
|
||||||
if (this._subscription === null) throw new Error("Subscription not confirmed, but event appeared!");
|
if (this._subscription === null) throw new Error("Subscription not confirmed, but event appeared!");
|
||||||
|
|
||||||
if (this._verboseLogging)
|
if (this._verboseLogging)
|
||||||
this._log.debug("Subscription %s to %s: event appeared (%s, %d, %s @ %j).",
|
this._log.debug("Subscription %s to %s: event appeared (%s, %d, %s @ %s).",
|
||||||
this._correlationId, this._streamId || "<all>",
|
this._correlationId, this._streamId || "<all>",
|
||||||
e.originalStreamId, e.originalEventNumber, e.originalEvent.eventType, e.originalPosition);
|
e.originalStreamId, e.originalEventNumber, e.originalEvent.eventType, e.originalPosition);
|
||||||
|
|
||||||
|
@ -250,7 +255,7 @@ SubscriptionOperation.prototype._executeActions = function() {
|
||||||
}
|
}
|
||||||
catch (err)
|
catch (err)
|
||||||
{
|
{
|
||||||
this._log.error(err, "Exception during executing user callback: %s.", err.Message);
|
this._log.error(err, "Exception during executing user callback: %s.", err.message);
|
||||||
}
|
}
|
||||||
action = this._actionQueue.shift();
|
action = this._actionQueue.shift();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
var util = require('util');
|
||||||
|
var uuid = require('uuid');
|
||||||
|
|
||||||
|
var ensure = require('../common/utils/ensure');
|
||||||
|
var OperationBase = require('../clientOperations/operationBase');
|
||||||
|
var TcpCommand = require('../systemData/tcpCommand');
|
||||||
|
var ClientMessage = require('../messages/clientMessage');
|
||||||
|
var SystemConsumerStrategies = require('../systemConsumerStrategies');
|
||||||
|
var InspectionDecision = require('../systemData/inspectionDecision');
|
||||||
|
var InspectionResult = require('./../systemData/inspectionResult');
|
||||||
|
var results = require('../results');
|
||||||
|
|
||||||
|
|
||||||
|
function UpdatePersistentSubscriptionOperation(log, cb, stream, groupName, settings, userCredentials) {
|
||||||
|
OperationBase.call(this, log, cb, TcpCommand.UpdatePersistentSubscription, TcpCommand.UpdatePersistentSubscriptionCompleted, userCredentials);
|
||||||
|
|
||||||
|
ensure.notNull(settings, "settings");
|
||||||
|
this._resolveLinkTos = settings.resolveLinkTos;
|
||||||
|
this._stream = stream;
|
||||||
|
this._groupName = groupName;
|
||||||
|
this._startFromBeginning = settings.startFrom;
|
||||||
|
this._maxRetryCount = settings.maxRetryCount;
|
||||||
|
this._liveBufferSize = settings.liveBufferSize;
|
||||||
|
this._readBatchSize = settings.readBatchSize;
|
||||||
|
this._bufferSize = settings.historyBufferSize;
|
||||||
|
this._recordStatistics = settings.extraStatistics;
|
||||||
|
this._messageTimeoutMilliseconds = settings.messageTimeout;
|
||||||
|
this._checkPointAfter = settings.checkPointAfter;
|
||||||
|
this._minCheckPointCount = settings.minCheckPointCount;
|
||||||
|
this._maxCheckPointCount = settings.maxCheckPointCount;
|
||||||
|
this._maxSubscriberCount = settings.maxSubscriberCount;
|
||||||
|
this._namedConsumerStrategy = settings.namedConsumerStrategy;
|
||||||
|
|
||||||
|
this._responseType = ClientMessage.UpdatePersistentSubscriptionCompleted;
|
||||||
|
}
|
||||||
|
util.inherits(UpdatePersistentSubscriptionOperation, OperationBase);
|
||||||
|
|
||||||
|
UpdatePersistentSubscriptionOperation.prototype._createRequestDto = function() {
|
||||||
|
return new ClientMessage.UpdatePersistentSubscription(this._groupName, this._stream, this._resolveLinkTos,
|
||||||
|
this._startFromBeginning, this._messageTimeoutMilliseconds, this._recordStatistics, this._liveBufferSize,
|
||||||
|
this._readBatchSize, this._bufferSize, this._maxRetryCount,
|
||||||
|
this._namedConsumerStrategy == SystemConsumerStrategies.RoundRobin, this._checkPointAfter,
|
||||||
|
this._maxCheckPointCount, this._minCheckPointCount, this._maxSubscriberCount, this._namedConsumerStrategy);
|
||||||
|
};
|
||||||
|
|
||||||
|
UpdatePersistentSubscriptionOperation.prototype._inspectResponse = function(response) {
|
||||||
|
switch (response.result)
|
||||||
|
{
|
||||||
|
case ClientMessage.UpdatePersistentSubscriptionCompleted.UpdatePersistentSubscriptionResult.Success:
|
||||||
|
this._succeed();
|
||||||
|
return new InspectionResult(InspectionDecision.EndOperation, "Success");
|
||||||
|
case ClientMessage.UpdatePersistentSubscriptionCompleted.UpdatePersistentSubscriptionResult.Fail:
|
||||||
|
this.fail(new Error(util.format("Subscription group %s on stream %s failed '%s'", this._groupName, this._stream, response.reason)));
|
||||||
|
return new InspectionResult(InspectionDecision.EndOperation, "Fail");
|
||||||
|
case ClientMessage.UpdatePersistentSubscriptionCompleted.UpdatePersistentSubscriptionResult.AccessDenied:
|
||||||
|
this.fail(new Error(util.format("Write access denied for stream '%s'.", this._stream)));
|
||||||
|
return new InspectionResult(InspectionDecision.EndOperation, "AccessDenied");
|
||||||
|
case ClientMessage.UpdatePersistentSubscriptionCompleted.UpdatePersistentSubscriptionResult.DoesNotExist:
|
||||||
|
this.fail(new Error(util.format("Subscription group %s on stream %s does not exist", this._groupName, this._stream)));
|
||||||
|
return new InspectionResult(InspectionDecision.EndOperation, "DoesNotExist");
|
||||||
|
default:
|
||||||
|
throw new Error(util.format("Unexpected OperationResult: %s.", response.result));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
UpdatePersistentSubscriptionOperation.prototype._transformResponse = function(response) {
|
||||||
|
return new results.PersistentSubscriptionUpdateResult(results.PersistentSubscriptionUpdateStatus.Success);
|
||||||
|
};
|
||||||
|
|
||||||
|
UpdatePersistentSubscriptionOperation.prototype.toString = function() {
|
||||||
|
return util.format("Stream: %s, Group Name: %s", this._stream, this._groupName);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = UpdatePersistentSubscriptionOperation;
|
|
@ -26,7 +26,7 @@ VolatileSubscriptionOperation.prototype._createSubscriptionPackage = function()
|
||||||
this._correlationId,
|
this._correlationId,
|
||||||
this._userCredentials != null ? this._userCredentials.username : null,
|
this._userCredentials != null ? this._userCredentials.username : null,
|
||||||
this._userCredentials != null ? this._userCredentials.password : null,
|
this._userCredentials != null ? this._userCredentials.password : null,
|
||||||
new BufferSegment(dto.encode().toBuffer()));
|
new BufferSegment(dto.toBuffer()));
|
||||||
};
|
};
|
||||||
|
|
||||||
VolatileSubscriptionOperation.prototype._inspectPackage = function(pkg) {
|
VolatileSubscriptionOperation.prototype._inspectPackage = function(pkg) {
|
||||||
|
|
|
@ -4,3 +4,8 @@ module.exports.notNullOrEmpty = function(value, name) {
|
||||||
if (value === '')
|
if (value === '')
|
||||||
throw new Error(name + " is empty.");
|
throw new Error(name + " is empty.");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
module.exports.notNull = function(value, name) {
|
||||||
|
if (value === null)
|
||||||
|
throw new Error(name + " is null.");
|
||||||
|
};
|
|
@ -7,6 +7,7 @@ var TcpPackageConnection = require('../transport/tcp/tcpPackageConnection');
|
||||||
var OperationsManager = require('./operationsManager');
|
var OperationsManager = require('./operationsManager');
|
||||||
var SubscriptionsManager = require('./subscriptionsManager');
|
var SubscriptionsManager = require('./subscriptionsManager');
|
||||||
var VolatileSubscriptionOperation = require('../clientOperations/volatileSubscriptionOperation');
|
var VolatileSubscriptionOperation = require('../clientOperations/volatileSubscriptionOperation');
|
||||||
|
var ConnectToPersistentSubscriptionOperation = require('../clientOperations/connectToPersistentSubscriptionOperation');
|
||||||
var messages = require('./messages');
|
var messages = require('./messages');
|
||||||
|
|
||||||
var TcpPackage = require('../systemData/tcpPackage');
|
var TcpPackage = require('../systemData/tcpPackage');
|
||||||
|
@ -70,6 +71,9 @@ function EventStoreConnectionLogicHandler(esConnection, settings) {
|
||||||
this._queue.registerHandler(messages.StartSubscriptionMessage, function(msg) {
|
this._queue.registerHandler(messages.StartSubscriptionMessage, function(msg) {
|
||||||
self._startSubscription(msg);
|
self._startSubscription(msg);
|
||||||
});
|
});
|
||||||
|
this._queue.registerHandler(messages.StartPersistentSubscriptionMessage, function(msg) {
|
||||||
|
self._startPersistentSubscription(msg);
|
||||||
|
});
|
||||||
|
|
||||||
this._queue.registerHandler(messages.EstablishTcpConnectionMessage, function(msg) {
|
this._queue.registerHandler(messages.EstablishTcpConnectionMessage, function(msg) {
|
||||||
self._establishTcpConnection(msg.endPoints);
|
self._establishTcpConnection(msg.endPoints);
|
||||||
|
@ -249,7 +253,7 @@ function createSubscriptionItem(operation, maxRetries, timeout) {
|
||||||
isSubscribed: false
|
isSubscribed: false
|
||||||
};
|
};
|
||||||
subscriptionItem.toString = (function(){
|
subscriptionItem.toString = (function(){
|
||||||
return util.format("Subscription %s (%s): %s, is subscribed: %s, retry count: %d, created: %d, last updated: %d",
|
return util.format("Subscription %s (%s): %s, is subscribed: %s, retry count: %d, created: %s, last updated: %s",
|
||||||
this.operation.constructor.name, this.correlationId, this.operation, this.isSubscribed, this.retryCount,
|
this.operation.constructor.name, this.correlationId, this.operation, this.isSubscribed, this.retryCount,
|
||||||
new Date(this.createdTime).toISOString().substr(11,12),
|
new Date(this.createdTime).toISOString().substr(11,12),
|
||||||
new Date(this.lastUpdated).toISOString().substr(11,12));
|
new Date(this.lastUpdated).toISOString().substr(11,12));
|
||||||
|
@ -269,7 +273,9 @@ EventStoreConnectionLogicHandler.prototype._startSubscription = function(msg) {
|
||||||
var operation = new VolatileSubscriptionOperation(this._settings.log, msg.cb, msg.streamId, msg.resolveLinkTos,
|
var operation = new VolatileSubscriptionOperation(this._settings.log, msg.cb, msg.streamId, msg.resolveLinkTos,
|
||||||
msg.userCredentials, msg.eventAppeared, msg.subscriptionDropped,
|
msg.userCredentials, msg.eventAppeared, msg.subscriptionDropped,
|
||||||
this._settings.verboseLogging, function() { return self._connection });
|
this._settings.verboseLogging, function() { return self._connection });
|
||||||
this._logDebug("StartSubscription %s %s, %s, %d, %d.", operation.constructor.name, operation, msg.maxRetries, msg.timeout, this._state === ConnectionState.Connected ? "fire" : "enqueue");
|
this._logDebug("StartSubscription %s %s, %s, %d, %d.",
|
||||||
|
this._state === ConnectionState.Connected ? "fire" : "enqueue",
|
||||||
|
operation.constructor.name, operation, msg.maxRetries, msg.timeout);
|
||||||
var subscription = createSubscriptionItem(operation, msg.maxRetries, msg.timeout);
|
var subscription = createSubscriptionItem(operation, msg.maxRetries, msg.timeout);
|
||||||
if (this._state === ConnectionState.Connecting)
|
if (this._state === ConnectionState.Connecting)
|
||||||
this._subscriptions.enqueueSubscription(subscription);
|
this._subscriptions.enqueueSubscription(subscription);
|
||||||
|
@ -284,6 +290,34 @@ EventStoreConnectionLogicHandler.prototype._startSubscription = function(msg) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
EventStoreConnectionLogicHandler.prototype._startPersistentSubscription = function(msg) {
|
||||||
|
var self = this;
|
||||||
|
switch (this._state)
|
||||||
|
{
|
||||||
|
case ConnectionState.Init:
|
||||||
|
msg.cb(new Error(util.format("EventStoreConnection '%s' is not active.", this._esConnection.connectionName)));
|
||||||
|
break;
|
||||||
|
case ConnectionState.Connecting:
|
||||||
|
case ConnectionState.Connected:
|
||||||
|
var operation = new ConnectToPersistentSubscriptionOperation(this._settings.log, msg.cb, msg.subscriptionId,
|
||||||
|
msg.bufferSize, msg.streamId, msg.userCredentials, msg.eventAppeared, msg.subscriptionDropped,
|
||||||
|
this._settings.verboseLogging, function() { return self._connection });
|
||||||
|
this._logDebug("StartSubscription %s %s, %s, %d, %d.",
|
||||||
|
this._state === ConnectionState.Connected ? "fire" : "enqueue",
|
||||||
|
operation.constructor.name, operation, msg.maxRetries, msg.timeout);
|
||||||
|
var subscription = createSubscriptionItem(operation, msg.maxRetries, msg.timeout);
|
||||||
|
if (this._state === ConnectionState.Connecting)
|
||||||
|
this._subscriptions.enqueueSubscription(subscription);
|
||||||
|
else
|
||||||
|
this._subscriptions.startSubscription(subscription, this._connection);
|
||||||
|
break;
|
||||||
|
case ConnectionState.Closed:
|
||||||
|
msg.cb(new Error("Connection closed. " + this._esConnection.connectionName));
|
||||||
|
break;
|
||||||
|
default: throw new Error(util.format("Unknown state: %s.", this._state));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
EventStoreConnectionLogicHandler.prototype._establishTcpConnection = function(endPoints) {
|
EventStoreConnectionLogicHandler.prototype._establishTcpConnection = function(endPoints) {
|
||||||
var endPoint = this._settings.useSslConnection ? endPoints.secureTcpEndPoint : endPoints.tcpEndPoint;
|
var endPoint = this._settings.useSslConnection ? endPoints.secureTcpEndPoint : endPoints.tcpEndPoint;
|
||||||
if (endPoint == null)
|
if (endPoint == null)
|
||||||
|
@ -397,7 +431,7 @@ EventStoreConnectionLogicHandler.prototype._tcpConnectionClosed = function(conne
|
||||||
this._state = ConnectionState.Connecting;
|
this._state = ConnectionState.Connecting;
|
||||||
this._connectingPhase = ConnectingPhase.Reconnecting;
|
this._connectingPhase = ConnectingPhase.Reconnecting;
|
||||||
|
|
||||||
this._logDebug("TCP connection to [%j, L%j, %s] closed.", connection.remoteEndPoint, connection.localEndPoint, connection.connectionId);
|
this._logDebug("TCP connection to [%j, L%j, %s] closed. %s", connection.remoteEndPoint, connection.localEndPoint, connection.connectionId, error);
|
||||||
|
|
||||||
this._subscriptions.purgeSubscribedAndDroppedSubscriptions(this._connection.connectionId);
|
this._subscriptions.purgeSubscribedAndDroppedSubscriptions(this._connection.connectionId);
|
||||||
this._reconnInfo = {
|
this._reconnInfo = {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
|
var ensure = require('../common/utils/ensure');
|
||||||
|
|
||||||
function Message() {
|
function Message() {
|
||||||
}
|
}
|
||||||
|
@ -76,6 +77,22 @@ util.inherits(TcpConnectionClosedMessage, Message);
|
||||||
function TimerTickMessage() {}
|
function TimerTickMessage() {}
|
||||||
util.inherits(TimerTickMessage, Message);
|
util.inherits(TimerTickMessage, Message);
|
||||||
|
|
||||||
|
function StartPersistentSubscriptionMessage(
|
||||||
|
cb, subscriptionId, streamId, bufferSize, userCredentials, eventAppeared, subscriptionDropped,
|
||||||
|
maxRetries, operationTimeout
|
||||||
|
) {
|
||||||
|
this.cb = cb;
|
||||||
|
this.subscriptionId = subscriptionId;
|
||||||
|
this.streamId = streamId;
|
||||||
|
this.bufferSize = bufferSize;
|
||||||
|
this.userCredentials = userCredentials;
|
||||||
|
this.eventAppeared = eventAppeared;
|
||||||
|
this.subscriptionDropped = subscriptionDropped;
|
||||||
|
this.maxRetries = maxRetries;
|
||||||
|
this.timeout = operationTimeout;
|
||||||
|
}
|
||||||
|
util.inherits(StartPersistentSubscriptionMessage, Message);
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
StartConnectionMessage: StartConnectionMessage,
|
StartConnectionMessage: StartConnectionMessage,
|
||||||
CloseConnectionMessage: CloseConnectionMessage,
|
CloseConnectionMessage: CloseConnectionMessage,
|
||||||
|
@ -86,5 +103,6 @@ module.exports = {
|
||||||
TcpConnectionErrorMessage: TcpConnectionErrorMessage,
|
TcpConnectionErrorMessage: TcpConnectionErrorMessage,
|
||||||
TcpConnectionEstablishedMessage: TcpConnectionEstablishedMessage,
|
TcpConnectionEstablishedMessage: TcpConnectionEstablishedMessage,
|
||||||
TcpConnectionClosedMessage: TcpConnectionClosedMessage,
|
TcpConnectionClosedMessage: TcpConnectionClosedMessage,
|
||||||
TimerTickMessage: TimerTickMessage
|
TimerTickMessage: TimerTickMessage,
|
||||||
|
StartPersistentSubscriptionMessage: StartPersistentSubscriptionMessage
|
||||||
};
|
};
|
||||||
|
|
|
@ -16,10 +16,14 @@ var ReadStreamEventsForwardOperation = require('./clientOperations/readStreamEve
|
||||||
var ReadStreamEventsBackwardOperation = require('./clientOperations/readStreamEventsBackwardOperation');
|
var ReadStreamEventsBackwardOperation = require('./clientOperations/readStreamEventsBackwardOperation');
|
||||||
var ReadAllEventsForwardOperation = require('./clientOperations/readAllEventsForwardOperation');
|
var ReadAllEventsForwardOperation = require('./clientOperations/readAllEventsForwardOperation');
|
||||||
var ReadAllEventsBackwardOperation = require('./clientOperations/readAllEventsBackwardOperation');
|
var ReadAllEventsBackwardOperation = require('./clientOperations/readAllEventsBackwardOperation');
|
||||||
|
var CreatePersistentSubscriptionOperation = require('./clientOperations/createPersistentSubscriptionOperation');
|
||||||
|
var UpdatePersistentSubscriptionOperation = require('./clientOperations/updatePersistentSubscriptionOperation');
|
||||||
|
var DeletePersistentSubscriptionOperation = require('./clientOperations/deletePersistentSubscriptionOperation');
|
||||||
|
|
||||||
var EventStoreTransaction = require('./eventStoreTransaction');
|
var EventStoreTransaction = require('./eventStoreTransaction');
|
||||||
var EventStoreStreamCatchUpSubscription = require('./eventStoreStreamCatchUpSubscription');
|
var EventStoreStreamCatchUpSubscription = require('./eventStoreStreamCatchUpSubscription');
|
||||||
var EventStoreAllCatchUpSubscription = require('./eventStoreAllCatchUpSubscription');
|
var EventStoreAllCatchUpSubscription = require('./eventStoreAllCatchUpSubscription');
|
||||||
|
var EventStorePersistentSubscription = require('./eventStorePersistentSubscription');
|
||||||
|
|
||||||
var results = require('./results');
|
var results = require('./results');
|
||||||
var systemStreams = require('./common/systemStreams');
|
var systemStreams = require('./common/systemStreams');
|
||||||
|
@ -83,7 +87,6 @@ EventStoreNodeConnection.prototype.close = function() {
|
||||||
this._handler.enqueueMessage(new messages.CloseConnectionMessage("Connection close requested by client.", null));
|
this._handler.enqueueMessage(new messages.CloseConnectionMessage("Connection close requested by client.", null));
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- Writing ---
|
|
||||||
/**
|
/**
|
||||||
* Delete a stream (async)
|
* Delete a stream (async)
|
||||||
* @param {string} stream
|
* @param {string} stream
|
||||||
|
@ -193,7 +196,6 @@ EventStoreNodeConnection.prototype.commitTransaction = function(transaction, use
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- Reading ---
|
|
||||||
/**
|
/**
|
||||||
* Read a single event (async)
|
* Read a single event (async)
|
||||||
* @param {string} stream
|
* @param {string} stream
|
||||||
|
@ -328,7 +330,6 @@ EventStoreNodeConnection.prototype.readAllEventsBackward = function(
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- Subscriptions ---
|
|
||||||
/**
|
/**
|
||||||
* Subscribe to a stream (async)
|
* Subscribe to a stream (async)
|
||||||
* @param {!string} stream
|
* @param {!string} stream
|
||||||
|
@ -435,24 +436,99 @@ EventStoreNodeConnection.prototype.subscribeToAllFrom = function(
|
||||||
return catchUpSubscription;
|
return catchUpSubscription;
|
||||||
};
|
};
|
||||||
|
|
||||||
EventStoreNodeConnection.prototype.connectToPersistentSubscription = function() {
|
/**
|
||||||
//TODO: connect to persistent subscription
|
* Subscribe to a persistent subscription
|
||||||
throw new Error("Not implemented.");
|
* @param {string} stream
|
||||||
|
* @param {string} groupName
|
||||||
|
* @param {function} eventAppeared
|
||||||
|
* @param {function} [subscriptionDropped]
|
||||||
|
* @param {UserCredentials} [userCredentials]
|
||||||
|
* @param {number} [bufferSize]
|
||||||
|
* @param {boolean} [autoAck]
|
||||||
|
*/
|
||||||
|
EventStoreNodeConnection.prototype.connectToPersistentSubscription = function(
|
||||||
|
stream, groupName, eventAppeared, subscriptionDropped, userCredentials, bufferSize, autoAck
|
||||||
|
) {
|
||||||
|
ensure.notNullOrEmpty(groupName, "groupName");
|
||||||
|
ensure.notNullOrEmpty(stream, "stream");
|
||||||
|
ensure.notNull(eventAppeared, "eventAppeared");
|
||||||
|
|
||||||
|
subscriptionDropped = subscriptionDropped || null;
|
||||||
|
userCredentials = userCredentials || null;
|
||||||
|
bufferSize = bufferSize === undefined ? 10 : bufferSize;
|
||||||
|
autoAck = autoAck === undefined ? true : !!autoAck;
|
||||||
|
|
||||||
|
var subscription = new EventStorePersistentSubscription(
|
||||||
|
groupName, stream, eventAppeared, subscriptionDropped, userCredentials, this._settings.log,
|
||||||
|
this._settings.verboseLogging, this._settings, this._handler, bufferSize, autoAck);
|
||||||
|
subscription.start();
|
||||||
|
|
||||||
|
return subscription;
|
||||||
};
|
};
|
||||||
|
|
||||||
EventStoreNodeConnection.prototype.createPersistentSubscription = function() {
|
/**
|
||||||
//TODO: create persistent subscription
|
* @param {string} stream
|
||||||
throw new Error("Not implemented.");
|
* @param {string} groupName
|
||||||
|
* @param {PersistentSubscriptionSettings} settings
|
||||||
|
* @param {UserCredentials} [userCredentials]
|
||||||
|
* @returns {Promise.<PersistentSubscriptionCreateResult>}
|
||||||
|
*/
|
||||||
|
EventStoreNodeConnection.prototype.createPersistentSubscription = function(stream, groupName, settings, userCredentials) {
|
||||||
|
ensure.notNullOrEmpty(stream, "stream");
|
||||||
|
ensure.notNullOrEmpty(groupName, "groupName");
|
||||||
|
ensure.notNull(settings, "settings");
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
return new Promise(function(resolve, reject){
|
||||||
|
function cb(err, result) {
|
||||||
|
if (err) return reject(err);
|
||||||
|
resolve(result);
|
||||||
|
}
|
||||||
|
self._enqueueOperation(
|
||||||
|
new CreatePersistentSubscriptionOperation(self._settings.log, cb, stream, groupName, settings, userCredentials || null));
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
EventStoreNodeConnection.prototype.updatePersistentSubscription = function() {
|
/**
|
||||||
//TODO: update persistent subscription
|
* @param {string} stream
|
||||||
throw new Error("Not implemented.");
|
* @param {string} groupName
|
||||||
|
* @param {string} settings
|
||||||
|
* @param {UserCredentials} [userCredentials]
|
||||||
|
* @returns {Promise.<PersistentSubscriptionUpdateResult>}
|
||||||
|
*/
|
||||||
|
EventStoreNodeConnection.prototype.updatePersistentSubscription = function(stream, groupName, settings, userCredentials) {
|
||||||
|
ensure.notNullOrEmpty(stream, "stream");
|
||||||
|
ensure.notNullOrEmpty(groupName, "groupName");
|
||||||
|
ensure.notNull(settings, "settings");
|
||||||
|
var self = this;
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
function cb(err, result) {
|
||||||
|
if (err) return reject(err);
|
||||||
|
resolve(result);
|
||||||
|
}
|
||||||
|
self._enqueueOperation(
|
||||||
|
new UpdatePersistentSubscriptionOperation(self._settings.log, cb, stream, groupName, settings, userCredentials || null));
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
EventStoreNodeConnection.prototype.deletePersistentSubscription = function() {
|
/**
|
||||||
//TODO: delete persistent subscription
|
* @param {string} stream
|
||||||
throw new Error("Not implemented.");
|
* @param {string} groupName
|
||||||
|
* @param {UserCredentials} [userCredentials]
|
||||||
|
* @returns {Promise.<PersistentSubscriptionDeleteResult>}
|
||||||
|
*/
|
||||||
|
EventStoreNodeConnection.prototype.deletePersistentSubscription = function(stream, groupName, userCredentials) {
|
||||||
|
ensure.notNullOrEmpty(stream, "stream");
|
||||||
|
ensure.notNullOrEmpty(groupName, "groupName");
|
||||||
|
var self = this;
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
function cb(err, result) {
|
||||||
|
if (err) return reject(err);
|
||||||
|
resolve(result);
|
||||||
|
}
|
||||||
|
self._enqueueOperation(
|
||||||
|
new DeletePersistentSubscriptionOperation(self._settings.log, cb, stream, groupName, userCredentials || null));
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
EventStoreNodeConnection.prototype.setStreamMetadata = function() {
|
EventStoreNodeConnection.prototype.setStreamMetadata = function() {
|
||||||
|
|
35
src/eventStorePersistentSubscription.js
Normal file
35
src/eventStorePersistentSubscription.js
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
var util = require('util');
|
||||||
|
|
||||||
|
var EventStorePersistentSubscriptionBase = require('./eventStorePersistentSubscriptionBase');
|
||||||
|
var messages = require('./core/messages');
|
||||||
|
|
||||||
|
function EventStorePersistentSubscription(
|
||||||
|
subscriptionId, streamId, eventAppeared, subscriptionDropped, userCredentials, log, verboseLogging, settings,
|
||||||
|
handler, bufferSize, autoAck
|
||||||
|
) {
|
||||||
|
bufferSize = bufferSize === undefined ? 10 : bufferSize;
|
||||||
|
autoAck = autoAck === undefined ? true : !!autoAck;
|
||||||
|
|
||||||
|
EventStorePersistentSubscriptionBase.call(this, subscriptionId, streamId, eventAppeared, subscriptionDropped,
|
||||||
|
userCredentials, log, verboseLogging, settings, bufferSize, autoAck);
|
||||||
|
|
||||||
|
this._handler = handler;
|
||||||
|
}
|
||||||
|
util.inherits(EventStorePersistentSubscription, EventStorePersistentSubscriptionBase);
|
||||||
|
|
||||||
|
EventStorePersistentSubscription.prototype._startSubscription = function(
|
||||||
|
subscriptionId, streamId, bufferSize, userCredentials, onEventAppeared, onSubscriptionDropped, settings
|
||||||
|
) {
|
||||||
|
var self = this;
|
||||||
|
return new Promise(function(resolve, reject){
|
||||||
|
function cb(err, result) {
|
||||||
|
if (err) return reject(err);
|
||||||
|
resolve(result);
|
||||||
|
}
|
||||||
|
self._handler.enqueueMessage(new messages.StartPersistentSubscriptionMessage(cb, subscriptionId, streamId,
|
||||||
|
bufferSize, userCredentials, onEventAppeared, onSubscriptionDropped, settings.maxRetries,
|
||||||
|
settings.operationTimeout));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = EventStorePersistentSubscription;
|
167
src/eventStorePersistentSubscriptionBase.js
Normal file
167
src/eventStorePersistentSubscriptionBase.js
Normal file
|
@ -0,0 +1,167 @@
|
||||||
|
var util = require('util');
|
||||||
|
var ensure = require('./common/utils/ensure');
|
||||||
|
var PersistentSubscriptionNakEventAction = require('./persistentSubscriptionNakEventAction');
|
||||||
|
var SubscriptionDropReason = require('./subscriptionDropReason');
|
||||||
|
|
||||||
|
function DropSubscriptionEvent() {}
|
||||||
|
|
||||||
|
function EventStorePersistentSubscriptionBase(
|
||||||
|
subscriptionId, streamId,
|
||||||
|
eventAppeared, subscriptionDropped,
|
||||||
|
userCredentials, log, verboseLogging, settings, bufferSize, autoAck
|
||||||
|
) {
|
||||||
|
bufferSize = bufferSize === undefined ? 10 : bufferSize;
|
||||||
|
autoAck = autoAck === undefined ? true : autoAck;
|
||||||
|
|
||||||
|
this._subscriptionId = subscriptionId;
|
||||||
|
this._streamId = streamId;
|
||||||
|
this._eventAppeared = eventAppeared;
|
||||||
|
this._subscriptionDropped = subscriptionDropped;
|
||||||
|
this._userCredentials = userCredentials;
|
||||||
|
this._log = log;
|
||||||
|
this._verbose = verboseLogging;
|
||||||
|
this._settings = settings;
|
||||||
|
this._bufferSize = bufferSize;
|
||||||
|
this._autoAck = autoAck;
|
||||||
|
|
||||||
|
this._subscription = null;
|
||||||
|
this._dropData = null;
|
||||||
|
this._queue = [];
|
||||||
|
this._isProcessing = false;
|
||||||
|
this._isDropped = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
EventStorePersistentSubscriptionBase.prototype.start = function() {
|
||||||
|
this._stopped = false;
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
this._startSubscription(this._subscriptionId, this._streamId, this._bufferSize, this._userCredentials,
|
||||||
|
this._onEventAppeared.bind(this), this._onSubscriptionDropped.bind(this), this._settings)
|
||||||
|
.then(function(subscription) {
|
||||||
|
self._subscription = subscription;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
EventStorePersistentSubscriptionBase.prototype._startSubscription = function() {
|
||||||
|
throw new Error("EventStorePersistentSubscriptionBase._startSubscription abstract method called." +
|
||||||
|
this.constructor.name);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ResolvedEvent[]|ResolvedEvent} events
|
||||||
|
*/
|
||||||
|
EventStorePersistentSubscriptionBase.prototype.acknowledge = function(events) {
|
||||||
|
ensure.notNull(events, "events");
|
||||||
|
|
||||||
|
if (this._subscription === null) throw new Error("Invalid operation. Subscription is not active yet.");
|
||||||
|
if (!Array.isArray(events))
|
||||||
|
events = [events];
|
||||||
|
var ids = events.map(function(x) { return x.originalEvent.eventId; });
|
||||||
|
this._subscription.notifyEventsProcessed(ids);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ResolvedEvent[]|ResolvedEvent} events
|
||||||
|
* @param {number} action One of PersistentSubscriptionNakEventAction
|
||||||
|
* @param {string} reason
|
||||||
|
*/
|
||||||
|
EventStorePersistentSubscriptionBase.prototype.fail = function(events, action, reason) {
|
||||||
|
ensure.notNull(events, "events");
|
||||||
|
PersistentSubscriptionNakEventAction.isValid(action);
|
||||||
|
ensure.notNull(reason, "reason");
|
||||||
|
|
||||||
|
if (this._subscription === null) throw new Error("Invalid operation. Subscription is not active yet.");
|
||||||
|
if (!Array.isArray(events))
|
||||||
|
events = [events];
|
||||||
|
var ids = events.map(function(x) { return x.originalEvent.eventId; });
|
||||||
|
this._subscription.notifyEventsFailed(ids, action, reason);
|
||||||
|
};
|
||||||
|
|
||||||
|
EventStorePersistentSubscriptionBase.prototype.stop = function() {
|
||||||
|
if (this._verbose) this._log.debug("Persistent Subscription to %s: requesting stop...", this._streamId);
|
||||||
|
this._enqueueSubscriptionDropNotification(SubscriptionDropReason.UserInitiated, null);
|
||||||
|
//TODO figure out timeout with Promise still running
|
||||||
|
//if (!_stopped.Wait(timeout))
|
||||||
|
//throw new TimeoutException(string.Format("Could not stop {0} in time.", GetType().Name));
|
||||||
|
};
|
||||||
|
|
||||||
|
EventStorePersistentSubscriptionBase.prototype._enqueueSubscriptionDropNotification = function(reason, error) {
|
||||||
|
// if drop data was already set -- no need to enqueue drop again, somebody did that already
|
||||||
|
if (!this._dropData) {
|
||||||
|
this._dropData = {reason: reason, error: error};
|
||||||
|
this._enqueue(new DropSubscriptionEvent());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
EventStorePersistentSubscriptionBase.prototype._onSubscriptionDropped = function(subscription, reason, exception) {
|
||||||
|
this._enqueueSubscriptionDropNotification(reason, exception);
|
||||||
|
};
|
||||||
|
|
||||||
|
EventStorePersistentSubscriptionBase.prototype._onEventAppeared = function(subscription, resolvedEvent) {
|
||||||
|
this._enqueue(resolvedEvent);
|
||||||
|
};
|
||||||
|
|
||||||
|
EventStorePersistentSubscriptionBase.prototype._enqueue = function(resolvedEvent) {
|
||||||
|
this._queue.push(resolvedEvent);
|
||||||
|
if (!this._isProcessing) {
|
||||||
|
this._isProcessing = true;
|
||||||
|
setImmediate(this._processQueue.bind(this));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
EventStorePersistentSubscriptionBase.prototype._processQueue = function() {
|
||||||
|
//do
|
||||||
|
//{
|
||||||
|
var e = this._queue.shift();
|
||||||
|
while (e)
|
||||||
|
{
|
||||||
|
if (e instanceof DropSubscriptionEvent) // drop subscription artificial ResolvedEvent
|
||||||
|
{
|
||||||
|
if (this._dropData === null) throw new Error("Drop reason not specified.");
|
||||||
|
this._dropSubscription(this._dropData.reason, this._dropData.error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this._dropData !== null)
|
||||||
|
{
|
||||||
|
this._dropSubscription(this._dropData.reason, this._dropData.error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
this._eventAppeared(this, e);
|
||||||
|
if(this._autoAck)
|
||||||
|
this._subscription.notifyEventsProcessed([e.originalEvent.eventId]);
|
||||||
|
if (this._verbose)
|
||||||
|
this._log.debug("Persistent Subscription to %s: processed event (%s, %d, %s @ %d).",
|
||||||
|
this._streamId, e.originalEvent.eventStreamId, e.originalEvent.eventNumber, e.originalEvent.eventType,
|
||||||
|
e.originalEventNumber);
|
||||||
|
}
|
||||||
|
catch (err)
|
||||||
|
{
|
||||||
|
//TODO GFY should we autonak here?
|
||||||
|
this._dropSubscription(SubscriptionDropReason.EventHandlerException, err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
e = this._queue.shift();
|
||||||
|
}
|
||||||
|
this._isProcessing = false;
|
||||||
|
//} while (_queue.Count > 0 && Interlocked.CompareExchange(ref _isProcessing, 1, 0) == 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
EventStorePersistentSubscriptionBase.prototype._dropSubscription = function(reason, error) {
|
||||||
|
if (!this._isDropped)
|
||||||
|
{
|
||||||
|
this._isDropped = true;
|
||||||
|
if (this._verbose)
|
||||||
|
this._log.debug("Persistent Subscription to %s: dropping subscription, reason: %s %s.",
|
||||||
|
this._streamId, reason, error);
|
||||||
|
|
||||||
|
if (this._subscription !== null)
|
||||||
|
this._subscription.unsubscribe();
|
||||||
|
if (this._subscriptionDropped !== null)
|
||||||
|
this._subscriptionDropped(this, reason, error);
|
||||||
|
this._stopped = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = EventStorePersistentSubscriptionBase;
|
|
@ -35,12 +35,12 @@ message EventRecord {
|
||||||
}
|
}
|
||||||
|
|
||||||
message ResolvedIndexedEvent {
|
message ResolvedIndexedEvent {
|
||||||
optional EventRecord event = 1;
|
required EventRecord event = 1;
|
||||||
optional EventRecord link = 2;
|
optional EventRecord link = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message ResolvedEvent {
|
message ResolvedEvent {
|
||||||
optional EventRecord event = 1;
|
required EventRecord event = 1;
|
||||||
optional EventRecord link = 2;
|
optional EventRecord link = 2;
|
||||||
required int64 commit_position = 3;
|
required int64 commit_position = 3;
|
||||||
required int64 prepare_position = 4;
|
required int64 prepare_position = 4;
|
||||||
|
@ -195,6 +195,119 @@ message ReadAllEventsCompleted {
|
||||||
optional string error = 7;
|
optional string error = 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message CreatePersistentSubscription {
|
||||||
|
required string subscription_group_name = 1;
|
||||||
|
required string event_stream_id = 2;
|
||||||
|
required bool resolve_link_tos = 3;
|
||||||
|
required int32 start_from = 4;
|
||||||
|
required int32 message_timeout_milliseconds = 5;
|
||||||
|
required bool record_statistics = 6;
|
||||||
|
required int32 live_buffer_size = 7;
|
||||||
|
required int32 read_batch_size = 8;
|
||||||
|
required int32 buffer_size = 9;
|
||||||
|
required int32 max_retry_count = 10;
|
||||||
|
required bool prefer_round_robin = 11;
|
||||||
|
required int32 checkpoint_after_time = 12;
|
||||||
|
required int32 checkpoint_max_count = 13;
|
||||||
|
required int32 checkpoint_min_count = 14;
|
||||||
|
required int32 subscriber_max_count = 15;
|
||||||
|
optional string named_consumer_strategy = 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
message DeletePersistentSubscription {
|
||||||
|
required string subscription_group_name = 1;
|
||||||
|
required string event_stream_id = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message UpdatePersistentSubscription {
|
||||||
|
required string subscription_group_name = 1;
|
||||||
|
required string event_stream_id = 2;
|
||||||
|
required bool resolve_link_tos = 3;
|
||||||
|
required int32 start_from = 4;
|
||||||
|
required int32 message_timeout_milliseconds = 5;
|
||||||
|
required bool record_statistics = 6;
|
||||||
|
required int32 live_buffer_size = 7;
|
||||||
|
required int32 read_batch_size = 8;
|
||||||
|
required int32 buffer_size = 9;
|
||||||
|
required int32 max_retry_count = 10;
|
||||||
|
required bool prefer_round_robin = 11;
|
||||||
|
required int32 checkpoint_after_time = 12;
|
||||||
|
required int32 checkpoint_max_count = 13;
|
||||||
|
required int32 checkpoint_min_count = 14;
|
||||||
|
required int32 subscriber_max_count = 15;
|
||||||
|
optional string named_consumer_strategy = 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
message UpdatePersistentSubscriptionCompleted {
|
||||||
|
enum UpdatePersistentSubscriptionResult {
|
||||||
|
Success = 0;
|
||||||
|
DoesNotExist = 1;
|
||||||
|
Fail = 2;
|
||||||
|
AccessDenied=3;
|
||||||
|
}
|
||||||
|
required UpdatePersistentSubscriptionResult result = 1 [default = Success];
|
||||||
|
optional string reason = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message CreatePersistentSubscriptionCompleted {
|
||||||
|
enum CreatePersistentSubscriptionResult {
|
||||||
|
Success = 0;
|
||||||
|
AlreadyExists = 1;
|
||||||
|
Fail = 2;
|
||||||
|
AccessDenied=3;
|
||||||
|
}
|
||||||
|
required CreatePersistentSubscriptionResult result = 1 [default = Success];
|
||||||
|
optional string reason = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message DeletePersistentSubscriptionCompleted {
|
||||||
|
enum DeletePersistentSubscriptionResult {
|
||||||
|
Success = 0;
|
||||||
|
DoesNotExist = 1;
|
||||||
|
Fail = 2;
|
||||||
|
AccessDenied = 3;
|
||||||
|
}
|
||||||
|
required DeletePersistentSubscriptionResult result = 1 [default = Success];
|
||||||
|
optional string reason = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ConnectToPersistentSubscription {
|
||||||
|
required string subscription_id = 1;
|
||||||
|
required string event_stream_id = 2;
|
||||||
|
required int32 allowed_in_flight_messages = 3;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
message PersistentSubscriptionAckEvents {
|
||||||
|
required string subscription_id = 1;
|
||||||
|
repeated bytes processed_event_ids = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PersistentSubscriptionNakEvents {
|
||||||
|
enum NakAction {
|
||||||
|
Unknown = 0;
|
||||||
|
Park = 1;
|
||||||
|
Retry = 2;
|
||||||
|
Skip = 3;
|
||||||
|
Stop = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
required string subscription_id = 1;
|
||||||
|
repeated bytes processed_event_ids = 2;
|
||||||
|
optional string message = 3;
|
||||||
|
required NakAction action = 4 [default = Unknown];
|
||||||
|
}
|
||||||
|
|
||||||
|
message PersistentSubscriptionConfirmation {
|
||||||
|
required int64 last_commit_position = 1;
|
||||||
|
required string subscription_id = 2;
|
||||||
|
optional int32 last_event_number = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PersistentSubscriptionStreamEventAppeared {
|
||||||
|
required ResolvedIndexedEvent event = 1;
|
||||||
|
}
|
||||||
|
|
||||||
message SubscribeToStream {
|
message SubscribeToStream {
|
||||||
required string event_stream_id = 1;
|
required string event_stream_id = 1;
|
||||||
required bool resolve_link_tos = 2;
|
required bool resolve_link_tos = 2;
|
||||||
|
@ -217,6 +330,9 @@ message SubscriptionDropped {
|
||||||
enum SubscriptionDropReason {
|
enum SubscriptionDropReason {
|
||||||
Unsubscribed = 0;
|
Unsubscribed = 0;
|
||||||
AccessDenied = 1;
|
AccessDenied = 1;
|
||||||
|
NotFound=2;
|
||||||
|
PersistentSubscriptionDeleted=3;
|
||||||
|
SubscriberMaxCountReached=4;
|
||||||
}
|
}
|
||||||
|
|
||||||
optional SubscriptionDropReason reason = 1 [default = Unsubscribed];
|
optional SubscriptionDropReason reason = 1 [default = Unsubscribed];
|
||||||
|
|
25
src/persistentEventStoreSubscription.js
Normal file
25
src/persistentEventStoreSubscription.js
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
var util = require('util');
|
||||||
|
|
||||||
|
var EventStoreSubscription = require('./eventStoreSubscription');
|
||||||
|
|
||||||
|
|
||||||
|
function PersistentEventStoreSubscription(subscriptionOperation, streamId, lastCommitPosition, lastEventNumber) {
|
||||||
|
EventStoreSubscription.call(this, streamId, lastCommitPosition, lastEventNumber);
|
||||||
|
|
||||||
|
this._subscriptionOperation = subscriptionOperation;
|
||||||
|
}
|
||||||
|
util.inherits(PersistentEventStoreSubscription, EventStoreSubscription);
|
||||||
|
|
||||||
|
PersistentEventStoreSubscription.prototype.unsubscribe = function() {
|
||||||
|
this._subscriptionOperation.unsubscribe();
|
||||||
|
};
|
||||||
|
|
||||||
|
PersistentEventStoreSubscription.prototype.notifyEventsProcessed = function(processedEvents) {
|
||||||
|
this._subscriptionOperation.notifyEventsProcessed(processedEvents);
|
||||||
|
};
|
||||||
|
|
||||||
|
PersistentEventStoreSubscription.prototype.notifyEventsFailed = function(processedEvents, action, reason) {
|
||||||
|
this._subscriptionOperation.notifyEventsFailed(processedEvents, action, reason);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = PersistentEventStoreSubscription;
|
14
src/persistentSubscriptionNakEventAction.js
Normal file
14
src/persistentSubscriptionNakEventAction.js
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
const PersistentSubscriptionNakEventAction = {
|
||||||
|
Unknown: 0,
|
||||||
|
Park: 1,
|
||||||
|
Retry: 2,
|
||||||
|
Skip: 3,
|
||||||
|
Stop: 4
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = PersistentSubscriptionNakEventAction;
|
||||||
|
module.exports.isValid = function(value) {
|
||||||
|
for(var k in PersistentSubscriptionNakEventAction)
|
||||||
|
if (PersistentSubscriptionNakEventAction[k] === value) return true;
|
||||||
|
return false;
|
||||||
|
};
|
26
src/persistentSubscriptionSettings.js
Normal file
26
src/persistentSubscriptionSettings.js
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
var SystemConsumerStrategies = require('./systemConsumerStrategies');
|
||||||
|
|
||||||
|
function PersistentSubscriptionSettings(
|
||||||
|
resolveLinkTos, startFrom, extraStatistics, messageTimeout,
|
||||||
|
maxRetryCount, liveBufferSize, readBatchSize, historyBufferSize,
|
||||||
|
checkPointAfter, minCheckPointCount, maxCheckPointCount,
|
||||||
|
maxSubscriberCount, namedConsumerStrategy
|
||||||
|
) {
|
||||||
|
this.resolveLinkTos = resolveLinkTos;
|
||||||
|
this.startFrom = startFrom;
|
||||||
|
this.extraStatistics = extraStatistics;
|
||||||
|
this.messageTimeout = messageTimeout;
|
||||||
|
this.maxRetryCount = maxRetryCount;
|
||||||
|
this.liveBufferSize = liveBufferSize;
|
||||||
|
this.readBatchSize = readBatchSize;
|
||||||
|
this.historyBufferSize = historyBufferSize;
|
||||||
|
this.checkPointAfter = checkPointAfter;
|
||||||
|
this.minCheckPointCount = minCheckPointCount;
|
||||||
|
this.maxCheckPointCount = maxCheckPointCount;
|
||||||
|
this.maxSubscriberCount = maxSubscriberCount;
|
||||||
|
this.namedConsumerStrategy = namedConsumerStrategy;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.create = function() {
|
||||||
|
return new PersistentSubscriptionSettings(false, -1, false, 30000, 500, 500, 10, 20, 2000, 10, 1000, 0, SystemConsumerStrategies.RoundRobin);
|
||||||
|
};
|
|
@ -285,6 +285,57 @@ function RawStreamMetadataResult(stream, isStreamDeleted, metastreamVersion, str
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const PersistentSubscriptionCreateStatus = {
|
||||||
|
Success: 'success',
|
||||||
|
NotFound: 'notFound',
|
||||||
|
Failure: 'failure'
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} status
|
||||||
|
* @constructor
|
||||||
|
* @property {string} status
|
||||||
|
*/
|
||||||
|
function PersistentSubscriptionCreateResult(status) {
|
||||||
|
Object.defineProperties(this, {
|
||||||
|
status: {enumerable: true, value: status}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const PersistentSubscriptionUpdateStatus = {
|
||||||
|
Success: 'success',
|
||||||
|
NotFound: 'notFound',
|
||||||
|
Failure: 'failure',
|
||||||
|
AccessDenied: 'accessDenied'
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} status
|
||||||
|
* @constructor
|
||||||
|
* @property {string} status
|
||||||
|
*/
|
||||||
|
function PersistentSubscriptionUpdateResult(status) {
|
||||||
|
Object.defineProperties(this, {
|
||||||
|
status: {enumerable: true, value: status}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const PersistentSubscriptionDeleteStatus = {
|
||||||
|
Success: 'success',
|
||||||
|
Failure: 'failure'
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} status
|
||||||
|
* @constructor
|
||||||
|
* @property {string} status
|
||||||
|
*/
|
||||||
|
function PersistentSubscriptionDeleteResult(status) {
|
||||||
|
Object.defineProperties(this, {
|
||||||
|
status: {enumerable: true, value: status}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Exports Constructors
|
// Exports Constructors
|
||||||
module.exports.Position = Position;
|
module.exports.Position = Position;
|
||||||
module.exports.toNumber = toNumber;
|
module.exports.toNumber = toNumber;
|
||||||
|
@ -296,3 +347,9 @@ module.exports.StreamEventsSlice = StreamEventsSlice;
|
||||||
module.exports.AllEventsSlice = AllEventsSlice;
|
module.exports.AllEventsSlice = AllEventsSlice;
|
||||||
module.exports.DeleteResult = DeleteResult;
|
module.exports.DeleteResult = DeleteResult;
|
||||||
module.exports.RawStreamMetadataResult = RawStreamMetadataResult;
|
module.exports.RawStreamMetadataResult = RawStreamMetadataResult;
|
||||||
|
module.exports.PersistentSubscriptionCreateResult = PersistentSubscriptionCreateResult;
|
||||||
|
module.exports.PersistentSubscriptionCreateStatus = PersistentSubscriptionCreateStatus;
|
||||||
|
module.exports.PersistentSubscriptionUpdateResult = PersistentSubscriptionUpdateResult;
|
||||||
|
module.exports.PersistentSubscriptionUpdateStatus = PersistentSubscriptionUpdateStatus;
|
||||||
|
module.exports.PersistentSubscriptionDeleteResult = PersistentSubscriptionDeleteResult;
|
||||||
|
module.exports.PersistentSubscriptionDeleteStatus = PersistentSubscriptionDeleteStatus;
|
||||||
|
|
|
@ -3,6 +3,9 @@ const SubscriptionDropReason = {
|
||||||
CatchUpError: 'catchUpError',
|
CatchUpError: 'catchUpError',
|
||||||
ConnectionClosed: 'connectionClosed',
|
ConnectionClosed: 'connectionClosed',
|
||||||
EventHandlerException: 'eventHandlerException',
|
EventHandlerException: 'eventHandlerException',
|
||||||
|
MaxSubscribersReached: 'maxSubscribersReached',
|
||||||
|
NotFound: 'notFound',
|
||||||
|
PersistentSubscriptionDeleted: 'persistentSubscriptionDeleted',
|
||||||
ProcessingQueueOverflow: 'processingQueueOverflow',
|
ProcessingQueueOverflow: 'processingQueueOverflow',
|
||||||
ServerError: 'serverError',
|
ServerError: 'serverError',
|
||||||
SubscribingError: 'subscribingError',
|
SubscribingError: 'subscribingError',
|
||||||
|
|
7
src/systemConsumerStrategies.js
Normal file
7
src/systemConsumerStrategies.js
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
const SystemConsumerStrategies = {
|
||||||
|
DispatchToSingle: 'DispatchToSingle',
|
||||||
|
RoundRobin: 'RoundRobin',
|
||||||
|
Pinned: 'Pinned'
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = SystemConsumerStrategies;
|
|
@ -52,6 +52,17 @@ const TcpCommand = {
|
||||||
StreamEventAppeared: 0xC2,
|
StreamEventAppeared: 0xC2,
|
||||||
UnsubscribeFromStream: 0xC3,
|
UnsubscribeFromStream: 0xC3,
|
||||||
SubscriptionDropped: 0xC4,
|
SubscriptionDropped: 0xC4,
|
||||||
|
ConnectToPersistentSubscription: 0xC5,
|
||||||
|
PersistentSubscriptionConfirmation: 0xC6,
|
||||||
|
PersistentSubscriptionStreamEventAppeared: 0xC7,
|
||||||
|
CreatePersistentSubscription: 0xC8,
|
||||||
|
CreatePersistentSubscriptionCompleted: 0xC9,
|
||||||
|
DeletePersistentSubscription: 0xCA,
|
||||||
|
DeletePersistentSubscriptionCompleted: 0xCB,
|
||||||
|
PersistentSubscriptionAckEvents: 0xCC,
|
||||||
|
PersistentSubscriptionNakEvents: 0xCD,
|
||||||
|
UpdatePersistentSubscription: 0xCE,
|
||||||
|
UpdatePersistentSubscriptionCompleted: 0xCF,
|
||||||
|
|
||||||
ScavengeDatabase: 0xD0,
|
ScavengeDatabase: 0xD0,
|
||||||
ScavengeDatabaseCompleted: 0xD1,
|
ScavengeDatabaseCompleted: 0xD1,
|
||||||
|
|
|
@ -281,7 +281,36 @@ module.exports = {
|
||||||
test.done(err);
|
test.done(err);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
//TODO: Persistent Subscription
|
'Test Create Persistent Subscription': function(test) {
|
||||||
|
var settings = client.PersistentSubscriptionSettings.create();
|
||||||
|
this.conn.createPersistentSubscription(testStreamName, 'consumer-1', settings, userCredentialsForAll)
|
||||||
|
.then(function(result) {
|
||||||
|
test.done();
|
||||||
|
})
|
||||||
|
.catch(function(err) {
|
||||||
|
test.done(err);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
//TODO: Update Persistent Subscription
|
||||||
|
'Test ConnectTo Persistent Subscription': function(test) {
|
||||||
|
function eventAppeared(s, e) {
|
||||||
|
s.stop();
|
||||||
|
}
|
||||||
|
function subscriptionDropped(connection, reason, error) {
|
||||||
|
test.done(error);
|
||||||
|
}
|
||||||
|
var subscription = this.conn.connectToPersistentSubscription(testStreamName, 'consumer-1', eventAppeared, subscriptionDropped);
|
||||||
|
this.conn.appendToStream(testStreamName, client.expectedVersion.any, [createRandomEvent()]);
|
||||||
|
},
|
||||||
|
'Test Delete Persistent Subscription': function(test) {
|
||||||
|
this.conn.deletePersistentSubscription(testStreamName, 'consumer-1', userCredentialsForAll)
|
||||||
|
.then(function(result) {
|
||||||
|
test.done();
|
||||||
|
})
|
||||||
|
.catch(function(err) {
|
||||||
|
test.done(err);
|
||||||
|
});
|
||||||
|
},
|
||||||
'Test Delete Stream': function(test) {
|
'Test Delete Stream': function(test) {
|
||||||
this.conn.deleteStream(testStreamName, client.expectedVersion.any)
|
this.conn.deleteStream(testStreamName, client.expectedVersion.any)
|
||||||
.then(function(result) {
|
.then(function(result) {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user