diff --git a/README.md b/README.md
index df57e34..9211760 100644
--- a/README.md
+++ b/README.md
@@ -1,24 +1,13 @@
-# EventStore .NET Core Client
-
-Official direct port of the Event Store .NET client to .NET Core targetting .netstandard 2.0
-
-Works with EventStore server 4.0.0 and above.
+# Deprecated
-[](https://app.wercker.com/project/byKey/21204a69779b2b4543242fd543d0aaab)
-[](https://ci.appveyor.com/project/EventStore/clientapi-netcore/branch/master)
-
-## Installation
-
-You can install the Eventstore .NET Core client by copying and pasting the following command into your Package Manager Console within Visual Studio (Tools > NuGet Package Manager > Package Manager Console).
+# EventStore .NET Core Client
-```bash
-Install-Package EventStore.ClientAPI.NetCore
-```
+This repository contained an Event Store .NET client for .NET Core, targetting netstandard-2.0.
-Alternatively if you're using the .NET Core CLI then you can install the client via the command line interface with the following command:
+As of version 5.0.0, the official client maintained as part of [github.com/EventStore/EventStore](github.com/EventStore/EventStore) now targets all supported .NET platforms:
-```bash
-dotnet add package EventStore.ClientAPI.NetCore
-```
+- netstandard2.0
+- net452
+- net46
-For more information see the [EventStore.ClientAPI.NetCore NuGet page](https://www.nuget.org/packages/EventStore.ClientAPI.NetCore/).
+This code and repository is not maintained.
diff --git a/appveyor.yml b/appveyor.yml
index 14b2919..44eac44 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,4 +1,4 @@
-version: 4.0.2.{build}
+version: 4.1.0.{build}
image: Visual Studio 2017
@@ -22,9 +22,6 @@ build:
publish_nuget: true
verbosity: minimal
-test_script:
- - ps: .\scripts\appveyortest.ps1
-
deploy:
provider: NuGet
on:
diff --git a/scripts/installandruneventstore.ps1 b/scripts/installandruneventstore.ps1
index 7ede819..65f2a2d 100644
--- a/scripts/installandruneventstore.ps1
+++ b/scripts/installandruneventstore.ps1
@@ -1,3 +1,3 @@
-(New-Object Net.WebClient).DownloadFile('https://eventstore.org/downloads/EventStore-OSS-Win-v4.0.3.zip','c:\tools\eventstore.zip')
+(New-Object Net.WebClient).DownloadFile('https://eventstore.org/downloads/EventStore-OSS-Win-v4.1.0.zip','c:\tools\eventstore.zip')
7z e C:\tools\eventstore.zip -oC:\tools\EventStore
start-process -NoNewWindow "C:\tools\EventStore\EventStore.ClusterNode.exe" "--memdb --run-projections ALL"
\ No newline at end of file
diff --git a/src/EventStore.ClientAPI.NetCore/CatchUpSubscriptionSettings.cs b/src/EventStore.ClientAPI.NetCore/CatchUpSubscriptionSettings.cs
index 50ec220..d6cc329 100644
--- a/src/EventStore.ClientAPI.NetCore/CatchUpSubscriptionSettings.cs
+++ b/src/EventStore.ClientAPI.NetCore/CatchUpSubscriptionSettings.cs
@@ -3,37 +3,37 @@
namespace EventStore.ClientAPI
{
///
- /// Settings for
+ /// Settings for .
///
public class CatchUpSubscriptionSettings
{
///
- /// The maximum amount to cache when processing from live subscription. Going above will drop subscription.
+ /// The maximum amount of events to cache when processing from a live subscription. Going above this value will drop the subscription.
///
public readonly int MaxLiveQueueSize;
///
- /// The number of events to read per batch when reading history
+ /// The number of events to read per batch when reading the history.
///
public readonly int ReadBatchSize;
///
- /// Enables verbose logging on the subscription
+ /// Enables verbose logging on the subscription.
///
public readonly bool VerboseLogging;
///
- /// Whether or not to resolve link events
+ /// Whether to resolve link events.
///
public readonly bool ResolveLinkTos;
///
- /// The name of subscription.
+ /// The name of the subscription.
///
public readonly string SubscriptionName;
///
- /// Returns default settings
+ /// Returns default settings.
///
public static readonly CatchUpSubscriptionSettings Default = new CatchUpSubscriptionSettings(
Consts.CatchUpDefaultMaxPushQueueSize,
@@ -42,19 +42,18 @@ public class CatchUpSubscriptionSettings
resolveLinkTos: true,
subscriptionName: String.Empty
);
-
///
- /// Constructs a object
+ /// Constructs a object.
///
- /// The maximum amount to buffer when processing from live subscription. Going above will drop subscription.
- /// The number of events to read per batch when reading history
- /// Enables verbose logging on the subscription
- /// Whether or not to resolve link events
- /// The name of subscription.
+ /// The maximum amount of events to buffer when processing from a live subscription. Going above this amount will drop the subscription.
+ /// The number of events to read per batch when reading through history.
+ /// Enables verbose logging on the subscription.
+ /// Whether to resolve link events.
+ /// The name of the subscription.
public CatchUpSubscriptionSettings(int maxLiveQueueSize, int readBatchSize, bool verboseLogging, bool resolveLinkTos, string subscriptionName = "") {
Ensure.Positive(readBatchSize, "readBatchSize");
Ensure.Positive(maxLiveQueueSize, "maxLiveQueueSize");
- if (readBatchSize > Consts.MaxReadSize) throw new ArgumentException(string.Format("Read batch size should be less than {0}. For larger reads you should page.", Consts.MaxReadSize));
+ if (readBatchSize > ClientApiConstants.MaxReadSize) throw new ArgumentException(string.Format("Read batch size should be less than {0}. For larger reads you should page.", ClientApiConstants.MaxReadSize));
MaxLiveQueueSize = maxLiveQueueSize;
ReadBatchSize = readBatchSize;
VerboseLogging = verboseLogging;
diff --git a/src/EventStore.ClientAPI.NetCore/ClientApiConstants.cs b/src/EventStore.ClientAPI.NetCore/ClientApiConstants.cs
new file mode 100644
index 0000000..86e13a5
--- /dev/null
+++ b/src/EventStore.ClientAPI.NetCore/ClientApiConstants.cs
@@ -0,0 +1,13 @@
+namespace EventStore.ClientAPI
+{
+ ///
+ /// Various constant values that may be useful when working with the ClientAPI.
+ ///
+ public static class ClientApiConstants
+ {
+ ///
+ /// The maximum number of events that can be read in a single operation.
+ ///
+ public static readonly int MaxReadSize = Consts.MaxReadSize;
+ }
+}
\ No newline at end of file
diff --git a/src/EventStore.ClientAPI.NetCore/ClientOperations/ConnectToPersistentSubscriptionOperation.cs b/src/EventStore.ClientAPI.NetCore/ClientOperations/ConnectToPersistentSubscriptionOperation.cs
index 1a6008d..dd4ea08 100644
--- a/src/EventStore.ClientAPI.NetCore/ClientOperations/ConnectToPersistentSubscriptionOperation.cs
+++ b/src/EventStore.ClientAPI.NetCore/ClientOperations/ConnectToPersistentSubscriptionOperation.cs
@@ -9,13 +9,13 @@
namespace EventStore.ClientAPI.ClientOperations
{
- internal class ConnectToPersistentSubscriptionOperation : SubscriptionOperation, IConnectToPersistentSubscriptions
+ internal class ConnectToPersistentSubscriptionOperation : SubscriptionOperation, IConnectToPersistentSubscriptions
{
private readonly string _groupName;
private readonly int _bufferSize;
private string _subscriptionId;
- public ConnectToPersistentSubscriptionOperation(ILogger log, TaskCompletionSource source, string groupName, int bufferSize, string streamId, UserCredentials userCredentials, Func eventAppeared, Action subscriptionDropped, bool verboseLogging, Func getConnection)
+ public ConnectToPersistentSubscriptionOperation(ILogger log, TaskCompletionSource source, string groupName, int bufferSize, string streamId, UserCredentials userCredentials, Func eventAppeared, Action subscriptionDropped, bool verboseLogging, Func getConnection)
: base(log, source, streamId, false, userCredentials, eventAppeared, subscriptionDropped, verboseLogging, getConnection)
{
_groupName = groupName;
@@ -46,7 +46,7 @@ protected override bool InspectPackage(TcpPackage package, out InspectionResult
if (package.Command == TcpCommand.PersistentSubscriptionStreamEventAppeared)
{
var dto = package.Data.Deserialize();
- EventAppeared(new ResolvedEvent(dto.Event));
+ EventAppeared(new PersistentSubscriptionResolvedEvent(new ResolvedEvent(dto.Event), dto.RetryCount));
result = new InspectionResult(InspectionDecision.DoNothing, "StreamEventAppeared");
return true;
}
diff --git a/src/EventStore.ClientAPI.NetCore/ClientOperations/SubscriptionOperation.cs b/src/EventStore.ClientAPI.NetCore/ClientOperations/SubscriptionOperation.cs
index 38bc5cd..2e6ea5b 100644
--- a/src/EventStore.ClientAPI.NetCore/ClientOperations/SubscriptionOperation.cs
+++ b/src/EventStore.ClientAPI.NetCore/ClientOperations/SubscriptionOperation.cs
@@ -10,14 +10,21 @@
namespace EventStore.ClientAPI.ClientOperations
{
- internal abstract class SubscriptionOperation : ISubscriptionOperation where T : EventStoreSubscription
+ internal interface IResolvedEvent
+ {
+ string OriginalStreamId{ get; }
+ long OriginalEventNumber { get; }
+ RecordedEvent OriginalEvent { get; }
+ Position? OriginalPosition { get; }
+ }
+ internal abstract class SubscriptionOperation : ISubscriptionOperation where T : EventStoreSubscription where TE: IResolvedEvent
{
private readonly ILogger _log;
private readonly TaskCompletionSource _source;
protected readonly string _streamId;
protected readonly bool _resolveLinkTos;
protected readonly UserCredentials _userCredentials;
- protected readonly Func _eventAppeared;
+ protected readonly Func _eventAppeared;
private readonly Action _subscriptionDropped;
private readonly bool _verboseLogging;
protected readonly Func _getConnection;
@@ -33,7 +40,7 @@ protected SubscriptionOperation(ILogger log,
string streamId,
bool resolveLinkTos,
UserCredentials userCredentials,
- Func eventAppeared,
+ Func eventAppeared,
Action subscriptionDropped,
bool verboseLogging,
Func getConnection)
@@ -84,7 +91,6 @@ private TcpPackage CreateUnsubscriptionPackage()
}
protected abstract bool InspectPackage(TcpPackage package, out InspectionResult result);
-
public InspectionResult InspectPackage(TcpPackage package)
{
try
@@ -97,13 +103,6 @@ public InspectionResult InspectPackage(TcpPackage package)
switch (package.Command)
{
- case TcpCommand.StreamEventAppeared:
- {
- var dto = package.Data.Deserialize();
- EventAppeared(new ResolvedEvent(dto.Event));
- return new InspectionResult(InspectionDecision.DoNothing, "StreamEventAppeared");
- }
-
case TcpCommand.SubscriptionDropped:
{
var dto = package.Data.Deserialize();
@@ -240,7 +239,7 @@ protected void ConfirmSubscription(long lastCommitPosition, long? lastEventNumbe
protected abstract T CreateSubscriptionObject(long lastCommitPosition, long? lastEventNumber);
- protected void EventAppeared(ResolvedEvent e)
+ protected void EventAppeared(TE e)
{
if (_unsubscribed != 0)
return;
diff --git a/src/EventStore.ClientAPI.NetCore/ClientOperations/VolatileSubscriptionOperation.cs b/src/EventStore.ClientAPI.NetCore/ClientOperations/VolatileSubscriptionOperation.cs
index 179e620..361733d 100644
--- a/src/EventStore.ClientAPI.NetCore/ClientOperations/VolatileSubscriptionOperation.cs
+++ b/src/EventStore.ClientAPI.NetCore/ClientOperations/VolatileSubscriptionOperation.cs
@@ -7,7 +7,7 @@
namespace EventStore.ClientAPI.ClientOperations
{
- internal class VolatileSubscriptionOperation : SubscriptionOperation
+ internal class VolatileSubscriptionOperation : SubscriptionOperation
{
public VolatileSubscriptionOperation(ILogger log, TaskCompletionSource source, string streamId, bool resolveLinkTos, UserCredentials userCredentials, Func eventAppeared, Action subscriptionDropped, bool verboseLogging, Func getConnection)
: base(log, source, streamId, resolveLinkTos, userCredentials, eventAppeared, subscriptionDropped, verboseLogging, getConnection)
diff --git a/src/EventStore.ClientAPI.NetCore/ClusterSettings.cs b/src/EventStore.ClientAPI.NetCore/ClusterSettings.cs
index 9566aca..c503d3b 100644
--- a/src/EventStore.ClientAPI.NetCore/ClusterSettings.cs
+++ b/src/EventStore.ClientAPI.NetCore/ClusterSettings.cs
@@ -43,34 +43,34 @@ public static ClusterSettingsBuilder Create()
///
/// Prefer a randomly selected node.
///
- public bool PreferRandomNode;
+ public NodePreference NodePreference;
///
- /// Used if we're connecting with gossip seeds
+ /// Used if connecting with gossip seeds.
///
- /// Endpoints for seeding gossip
- /// Maximum number of attempts to discover the cluster
- /// Timeout for cluster gossip
- /// Whether to prefer a random node selection or master
- internal ClusterSettings(GossipSeed[] gossipSeeds, int maxDiscoverAttempts, TimeSpan gossipTimeout, bool preferRandomNode)
+ /// Endpoints for seeding gossip.
+ /// Maximum number of attempts to discover the cluster.
+ /// Timeout for cluster gossip.
+ /// Whether to prefer slave, random, or master node selection.
+ internal ClusterSettings(GossipSeed[] gossipSeeds, int maxDiscoverAttempts, TimeSpan gossipTimeout, NodePreference nodePreference)
{
ClusterDns = "";
MaxDiscoverAttempts = maxDiscoverAttempts;
ExternalGossipPort = 0;
GossipTimeout = gossipTimeout;
GossipSeeds = gossipSeeds;
- PreferRandomNode = preferRandomNode;
+ NodePreference = nodePreference;
}
///
- /// Used if we're discovering via DNS
+ /// Used if discovering via DNS.
///
- /// The DNS name to use for discovering endpoints
- /// The maximum number of attempts for discovering endpoints
- /// The well-known endpoint on which cluster managers are running
- /// Timeout for cluster gossip
- /// Whether to prefer a random node selection or master
- internal ClusterSettings(string clusterDns, int maxDiscoverAttempts, int externalGossipPort, TimeSpan gossipTimeout, bool preferRandomNode)
+ /// The DNS name to use for discovering endpoints.
+ /// The maximum number of attempts for discovering endpoints.
+ /// The well-known endpoint on which cluster managers are running.
+ /// Timeout for cluster gossip.
+ /// Whether to prefer slave, random, or master node selection.
+ internal ClusterSettings(string clusterDns, int maxDiscoverAttempts, int externalGossipPort, TimeSpan gossipTimeout, NodePreference nodePreference)
{
Ensure.NotNullOrEmpty(clusterDns, "clusterDns");
if (maxDiscoverAttempts < -1)
@@ -82,7 +82,7 @@ internal ClusterSettings(string clusterDns, int maxDiscoverAttempts, int externa
ExternalGossipPort = externalGossipPort;
GossipTimeout = gossipTimeout;
GossipSeeds = new GossipSeed[0];
- PreferRandomNode = preferRandomNode;
+ NodePreference = nodePreference;
}
}
}
diff --git a/src/EventStore.ClientAPI.NetCore/Common/Log/DebugLogger.cs b/src/EventStore.ClientAPI.NetCore/Common/Log/DebugLogger.cs
index 91c03dd..d3a803e 100644
--- a/src/EventStore.ClientAPI.NetCore/Common/Log/DebugLogger.cs
+++ b/src/EventStore.ClientAPI.NetCore/Common/Log/DebugLogger.cs
@@ -6,32 +6,32 @@ class DebugLogger : ILogger
{
public void Error(string format, params object[] args)
{
- System.Diagnostics.Debug.WriteLine(Log("ERROR", format, args));
+ System.Diagnostics.Trace.WriteLine(Log("ERROR", format, args));
}
public void Error(Exception ex, string format, params object[] args)
{
- System.Diagnostics.Debug.WriteLine(Log("ERROR", ex, format, args));
+ System.Diagnostics.Trace.WriteLine(Log("ERROR", ex, format, args));
}
public void Debug(string format, params object[] args)
{
- System.Diagnostics.Debug.WriteLine(Log("DEBUG", format, args));
+ System.Diagnostics.Trace.WriteLine(Log("DEBUG", format, args));
}
public void Debug(Exception ex, string format, params object[] args)
{
- System.Diagnostics.Debug.WriteLine(Log("DEBUG", ex, format, args));
+ System.Diagnostics.Trace.WriteLine(Log("DEBUG", ex, format, args));
}
public void Info(string format, params object[] args)
{
- System.Diagnostics.Debug.WriteLine(Log("INFO", format, args));
+ System.Diagnostics.Trace.WriteLine(Log("INFO", format, args));
}
public void Info(Exception ex, string format, params object[] args)
{
- System.Diagnostics.Debug.WriteLine(Log("INFO", ex, format, args));
+ System.Diagnostics.Trace.WriteLine(Log("INFO", ex, format, args));
}
diff --git a/src/EventStore.ClientAPI.NetCore/ConnectionSettings.cs b/src/EventStore.ClientAPI.NetCore/ConnectionSettings.cs
index 56c0c11..5397411 100644
--- a/src/EventStore.ClientAPI.NetCore/ConnectionSettings.cs
+++ b/src/EventStore.ClientAPI.NetCore/ConnectionSettings.cs
@@ -6,7 +6,7 @@ namespace EventStore.ClientAPI
{
///
/// A object is an immutable representation of the settings for an
- /// . A object can be built using
+ /// . You can build a object using
/// a , either via the method, or via
/// the constructor of .
///
@@ -15,53 +15,57 @@ public sealed class ConnectionSettings
private static readonly Lazy DefaultSettings = new Lazy(() => Create(), true);
///
- /// The default
+ /// The default .
///
public static ConnectionSettings Default { get { return DefaultSettings.Value; } }
///
- /// Creates a new set of
+ /// Creates a new set of .
///
- /// A that can be used to build up a
+ /// A you can use to build up a .
public static ConnectionSettingsBuilder Create()
{
return new ConnectionSettingsBuilder();
}
///
- /// The that this connection will use
+ /// The that this connection will use.
///
public readonly ILogger Log;
///
- /// Whether or not do excessive logging of internal logic.
+ /// Whether to use excessive logging of internal logic.
///
public readonly bool VerboseLogging;
///
- /// The maximum number of outstanding items allowed in the queue
+ /// The maximum number of outstanding items allowed in the queue.
///
public readonly int MaxQueueSize;
///
- /// The maximum number of allowed asynchronous operations to be in process
+ /// The maximum number of allowed asynchronous operations to be in process.
///
public readonly int MaxConcurrentItems;
///
- /// The maximum number of retry attempts
+ /// The maximum number of retry attempts.
///
public readonly int MaxRetries;
///
- /// The maximum number of times to allow for reconnection
+ /// The maximum number of times to allow for reconnection.
///
public readonly int MaxReconnections;
///
- /// Whether or not to require EventStore to refuse serving read or write request if it is not master
+ /// Whether to require EventStore to refuse serving read or write request if it is not master.
///
public readonly bool RequireMaster;
///
- /// The amount of time to delay before attempting to reconnect
+ /// The amount of time to delay before attempting to reconnect.
///
public readonly TimeSpan ReconnectionDelay;
///
- /// The amount of time before an operation is considered to have timed out
+ /// The amount of time a request for an operation is permitted to be queued awaiting transmission to the server.
+ ///
+ public readonly TimeSpan QueueTimeout;
+ ///
+ /// The amount of time before an operation is considered to have timed out.
///
public readonly TimeSpan OperationTimeout;
///
@@ -73,7 +77,7 @@ public static ConnectionSettingsBuilder Create()
///
public readonly UserCredentials DefaultUserCredentials;
///
- /// Whether or not the connection is encrypted using SSL.
+ /// Whether the connection is encrypted using SSL.
///
public readonly bool UseSslConnection;
///
@@ -81,12 +85,12 @@ public static ConnectionSettingsBuilder Create()
///
public readonly string TargetHost;
///
- /// Whether or not to validate the server SSL certificate.
+ /// Whether to validate the server SSL certificate.
///
public readonly bool ValidateServer;
///
- /// Whether or not to raise an error if no response is received from the server for an operation.
+ /// Whether to raise an error if no response is received from the server for an operation.
///
public readonly bool FailOnNoServerResponse;
///
@@ -124,7 +128,7 @@ public static ConnectionSettingsBuilder Create()
///
/// Whether to randomly choose a node that's alive from the known nodes.
///
- public readonly bool PreferRandomNode;
+ public readonly NodePreference NodePreference;
///
/// The interval after which a client will time out during connection.
@@ -139,6 +143,7 @@ internal ConnectionSettings(ILogger log,
int maxReconnections,
bool requireMaster,
TimeSpan reconnectionDelay,
+ TimeSpan queueTimeout,
TimeSpan operationTimeout,
TimeSpan operationTimeoutCheckPeriod,
UserCredentials defaultUserCredentials,
@@ -154,7 +159,7 @@ internal ConnectionSettings(ILogger log,
int maxDiscoverAttempts,
int externalGossipPort,
TimeSpan gossipTimeout,
- bool preferRandomNode)
+ NodePreference nodePreference)
{
Ensure.NotNull(log, "log");
Ensure.Positive(maxQueueSize, "maxQueueSize");
@@ -173,6 +178,7 @@ internal ConnectionSettings(ILogger log,
MaxReconnections = maxReconnections;
RequireMaster = requireMaster;
ReconnectionDelay = reconnectionDelay;
+ QueueTimeout = queueTimeout;
OperationTimeout = operationTimeout;
OperationTimeoutCheckPeriod = operationTimeoutCheckPeriod;
ClientConnectionTimeout = clientConnectionTimeout;
@@ -189,7 +195,7 @@ internal ConnectionSettings(ILogger log,
MaxDiscoverAttempts = maxDiscoverAttempts;
ExternalGossipPort = externalGossipPort;
GossipTimeout = gossipTimeout;
- PreferRandomNode = preferRandomNode;
+ NodePreference = nodePreference;
}
}
}
diff --git a/src/EventStore.ClientAPI.NetCore/ConnectionSettingsBuilder.cs b/src/EventStore.ClientAPI.NetCore/ConnectionSettingsBuilder.cs
index 490b598..ed8ef6f 100644
--- a/src/EventStore.ClientAPI.NetCore/ConnectionSettingsBuilder.cs
+++ b/src/EventStore.ClientAPI.NetCore/ConnectionSettingsBuilder.cs
@@ -23,6 +23,7 @@ public class ConnectionSettingsBuilder
private bool _requireMaster = Consts.DefaultRequireMaster;
private TimeSpan _reconnectionDelay = Consts.DefaultReconnectionDelay;
+ private TimeSpan _queueTimeout = Consts.DefaultQueueTimeout;
private TimeSpan _operationTimeout = Consts.DefaultOperationTimeout;
private TimeSpan _operationTimeoutCheckPeriod = Consts.DefaultOperationTimeoutCheckPeriod;
@@ -40,7 +41,7 @@ public class ConnectionSettingsBuilder
private int _gossipExternalHttpPort = Consts.DefaultClusterManagerExternalHttpPort;
private TimeSpan _gossipTimeout = TimeSpan.FromSeconds(1);
private GossipSeed[] _gossipSeeds;
- private bool _preferRandomNode = false;
+ private NodePreference _nodePreference = NodePreference.Master;
internal ConnectionSettingsBuilder()
@@ -48,7 +49,7 @@ internal ConnectionSettingsBuilder()
}
///
- /// Configures the connection to output log messages to the given .
+ /// Configures the connection to output log messages to the given . You should implement this interface using another library such as NLog or log4net.
///
/// The to use.
///
@@ -89,7 +90,7 @@ public ConnectionSettingsBuilder UseFileLogger(string filename)
}
///
- /// Turns on verbose internal logic logging.
+ /// Turns on verbose internal logic logging. By contains default information about connection, disconnection and errors, but you can customize output.
///
///
public ConnectionSettingsBuilder EnableVerboseLogging()
@@ -99,7 +100,7 @@ public ConnectionSettingsBuilder EnableVerboseLogging()
}
///
- /// Sets the limit for number of outstanding operations
+ /// Sets the limit for number of outstanding operations.
///
/// The new limit of outstanding operations
///
@@ -112,7 +113,7 @@ public ConnectionSettingsBuilder LimitOperationsQueueTo(int limit)
}
///
- /// Limits the number of concurrent operations that this connection can have
+ /// Limits the number of concurrent operations that this connection can have.
///
///
///
@@ -125,7 +126,7 @@ public ConnectionSettingsBuilder LimitConcurrentOperationsTo(int limit)
}
///
- /// Limits the number of operation attempts
+ /// Limits the number of operation attempts.
///
///
///
@@ -138,7 +139,7 @@ public ConnectionSettingsBuilder LimitAttemptsForOperationTo(int limit)
}
///
- /// Limits the number of operation retries
+ /// Limits the number of operation retries.
///
///
///
@@ -151,7 +152,7 @@ public ConnectionSettingsBuilder LimitRetriesForOperationTo(int limit)
}
///
- /// Allows infinite operation retry attempts
+ /// Allows infinite operation retry attempts.
///
///
public ConnectionSettingsBuilder KeepRetrying()
@@ -161,7 +162,7 @@ public ConnectionSettingsBuilder KeepRetrying()
}
///
- /// Limits the number of reconnections this connection can try to make
+ /// Limits the number of reconnections this connection can try to make.
///
///
///
@@ -174,7 +175,7 @@ public ConnectionSettingsBuilder LimitReconnectionsTo(int limit)
}
///
- /// Allows infinite reconnection attempts
+ /// Allows infinite reconnection attempts.
///
///
public ConnectionSettingsBuilder KeepReconnecting()
@@ -184,7 +185,7 @@ public ConnectionSettingsBuilder KeepReconnecting()
}
///
- /// Requires all write and read requests to be served only by master (cluster version only)
+ /// Requires all write and read requests to be served only by master (cluster version only).
///
///
public ConnectionSettingsBuilder PerformOnMasterOnly()
@@ -194,7 +195,7 @@ public ConnectionSettingsBuilder PerformOnMasterOnly()
}
///
- /// Allow for writes to be forwarded and read requests served locally if node is not master (cluster version only)
+ /// Allow for writes to be forwarded and read requests served locally if node is not master (cluster version only).
///
///
public ConnectionSettingsBuilder PerformOnAnyNode()
@@ -204,7 +205,7 @@ public ConnectionSettingsBuilder PerformOnAnyNode()
}
///
- /// Sets the delay between reconnection attempts
+ /// Sets the delay between reconnection attempts.
///
///
///
@@ -215,7 +216,18 @@ public ConnectionSettingsBuilder SetReconnectionDelayTo(TimeSpan reconnectionDel
}
///
- /// Sets the operation timeout duration
+ /// Sets the maximum permitted time a request may be queued awaiting transmission; if exceeded an is thrown.
+ ///
+ ///
+ ///
+ public ConnectionSettingsBuilder SetQueueTimeoutTo(TimeSpan queueTimeout)
+ {
+ _queueTimeout = queueTimeout;
+ return this;
+ }
+
+ ///
+ /// Sets the operation timeout duration.
///
///
///
@@ -237,7 +249,7 @@ public ConnectionSettingsBuilder SetTimeoutCheckPeriodTo(TimeSpan timeoutCheckPe
}
///
- /// Sets the default to be used for this connection.
+ /// Sets the default used for this connection.
/// If user credentials are not given for an operation, these credentials will be used.
///
///
@@ -264,7 +276,7 @@ public ConnectionSettingsBuilder UseSslConnection(string targetHost, bool valida
}
///
- /// Marks that no response from server should cause an error on the request
+ /// Marks that no response from server should cause an error on the request.
///
///
public ConnectionSettingsBuilder FailOnNoServerResponse()
@@ -274,7 +286,7 @@ public ConnectionSettingsBuilder FailOnNoServerResponse()
}
///
- /// Sets how often heartbeats should be expected on the connection (lower values detect broken sockets faster)
+ /// Sets how often heartbeats should be expected on the connection (lower values detect broken sockets faster).
///
///
///
@@ -285,7 +297,7 @@ public ConnectionSettingsBuilder SetHeartbeatInterval(TimeSpan interval)
}
///
- /// Sets how long to wait without heartbeats before determining a connection to be dead (must be longer than heartbeat interval)
+ /// Sets how long to wait without heartbeats before determining a connection to be dead (must be longer than heartbeat interval).
///
///
///
@@ -350,7 +362,17 @@ public ConnectionSettingsBuilder SetGossipTimeout(TimeSpan timeout)
/// A for further configuration.
public ConnectionSettingsBuilder PreferRandomNode()
{
- _preferRandomNode = true;
+ _nodePreference = NodePreference.Random;
+ return this;
+ }
+
+ ///
+ /// Whether to prioritize choosing a slave node that's alive from the known nodes.
+ ///
+ /// A for further configuration.
+ public ConnectionSettingsBuilder PreferSlaveNode()
+ {
+ _nodePreference = NodePreference.Slave;
return this;
}
@@ -378,10 +400,11 @@ public ConnectionSettingsBuilder SetClusterGossipPort(int clusterGossipPort)
///
/// Sets gossip seed endpoints for the client.
///
- /// Note that this should be the external HTTP endpoint of the server, as it is required
- /// for the client to exchange gossip with the server. The standard port which should be
- /// used here is 2113.
- ///
+ ///
+ /// This should be the external HTTP endpoint of the server, as it is required
+ /// for the client to exchange gossip with the server. The standard port is 2113.
+ ///
+ ///
/// If the server requires a specific Host header to be sent as part of the gossip
/// request, use the overload of this method taking instead.
///
@@ -438,6 +461,7 @@ public ConnectionSettings Build()
_maxReconnections,
_requireMaster,
_reconnectionDelay,
+ _queueTimeout,
_operationTimeout,
_operationTimeoutCheckPeriod,
_defaultUserCredentials,
@@ -453,7 +477,7 @@ public ConnectionSettings Build()
_maxDiscoverAttempts,
_gossipExternalHttpPort,
_gossipTimeout,
- _preferRandomNode);
+ _nodePreference);
}
}
}
\ No newline at end of file
diff --git a/src/EventStore.ClientAPI.NetCore/DnsClusterSettingsBuilder.cs b/src/EventStore.ClientAPI.NetCore/DnsClusterSettingsBuilder.cs
index 3786afc..9c0c2a1 100644
--- a/src/EventStore.ClientAPI.NetCore/DnsClusterSettingsBuilder.cs
+++ b/src/EventStore.ClientAPI.NetCore/DnsClusterSettingsBuilder.cs
@@ -14,7 +14,7 @@ public class DnsClusterSettingsBuilder
private int _maxDiscoverAttempts = Consts.DefaultMaxClusterDiscoverAttempts;
private int _managerExternalHttpPort = Consts.DefaultClusterManagerExternalHttpPort;
private TimeSpan _gossipTimeout = TimeSpan.FromSeconds(1);
- private bool _preferRandomNode = false;
+ private NodePreference _nodePreference = NodePreference.Master;
///
/// Sets the DNS name under which cluster nodes are listed.
@@ -54,13 +54,33 @@ public DnsClusterSettingsBuilder SetGossipTimeout(TimeSpan timeout)
return this;
}
+ ///
+ /// Allows infinite nodes discovery attempts.
+ ///
+ /// A for further configuration.
+ public DnsClusterSettingsBuilder KeepDiscovering()
+ {
+ _maxDiscoverAttempts = Int32.MaxValue;
+ return this;
+ }
+
///
/// Whether to randomly choose a node that's alive from the known nodes.
///
/// A for further configuration.
public DnsClusterSettingsBuilder PreferRandomNode()
{
- _preferRandomNode = true;
+ _nodePreference = NodePreference.Random;
+ return this;
+ }
+
+ ///
+ /// Whether to prioritize choosing a slave node that's alive from the known nodes.
+ ///
+ /// A for further configuration.
+ public DnsClusterSettingsBuilder PreferSlaveNode()
+ {
+ _nodePreference = NodePreference.Slave;
return this;
}
@@ -103,7 +123,7 @@ public ClusterSettings Build() {
this._maxDiscoverAttempts,
this._managerExternalHttpPort,
this._gossipTimeout,
- this._preferRandomNode);
+ this._nodePreference);
}
}
}
\ No newline at end of file
diff --git a/src/EventStore.ClientAPI.NetCore/EventReadResult.cs b/src/EventStore.ClientAPI.NetCore/EventReadResult.cs
index 7cca733..3d1c095 100644
--- a/src/EventStore.ClientAPI.NetCore/EventReadResult.cs
+++ b/src/EventStore.ClientAPI.NetCore/EventReadResult.cs
@@ -4,17 +4,17 @@
namespace EventStore.ClientAPI
{
///
- /// A Event Read Result is the result of a single event read operation to the event store.
+ /// A event read result is the result of a single event read operation to Event Store.
///
public class EventReadResult
{
///
- /// The representing the status of this read attempt
+ /// The representing the status of this read attempt.
///
public readonly EventReadStatus Status;
///
- /// The name of the stream read
+ /// The name of the stream read.
///
public readonly string Stream;
@@ -24,7 +24,7 @@ public class EventReadResult
public readonly long EventNumber;
///
- /// The event read represented as
+ /// The event read represented as .
///
public readonly ResolvedEvent? Event;
diff --git a/src/EventStore.ClientAPI.NetCore/EventStore.ClientAPI.NetCore.csproj b/src/EventStore.ClientAPI.NetCore/EventStore.ClientAPI.NetCore.csproj
old mode 100755
new mode 100644
index 51ca040..62cba69
--- a/src/EventStore.ClientAPI.NetCore/EventStore.ClientAPI.NetCore.csproj
+++ b/src/EventStore.ClientAPI.NetCore/EventStore.ClientAPI.NetCore.csproj
@@ -1,32 +1,30 @@
-
-
-
- netstandard2.0
-
-
- EventStore.ClientAPI
- EventStore NetStandard ClientApi
- Event Store LLP
- Copyright © Event Store LLP. All rights reserved.
- EventStore.ClientAPI
- 4.0.2-alpha
-
-
- The client API for the Event Store. Get the open source or commercial versions of the Event Store server from http://geteventstore.com
- Event Store LLP
- portable
- EventStore.ClientAPI.NetCore
- EventStore.ClientAPI.NetCore
- eventstore;client;netstandard2.0
- http://geteventstore.com/assets/ouro-200.png
- https://github.com/EventStore/netcore-client/blob/master/LICENSE.md
- git
- https://github.com/EventStore/ClientAPI.NetCore
-
-
-
-
-
-
-
+
+
+ netstandard2.0
+ EventStore.ClientAPI
+
+
+ EventStore.ClientAPI
+ EventStore NetStandard ClientApi
+ Event Store LLP
+ Copyright © Event Store LLP. All rights reserved.
+ EventStore.ClientAPI
+ 4.1.1-rc
+
+
+ The client API for the Event Store. Get the open source or commercial versions of the Event Store server from http://geteventstore.com
+ Event Store LLP
+ portable
+ EventStore.ClientAPI.NetCore
+ EventStore.ClientAPI.NetCore
+ eventstore;client;netstandard2.0
+ http://geteventstore.com/assets/ouro-200.png
+ https://github.com/EventStore/netcore-client/blob/master/LICENSE.md
+ git
+ https://github.com/EventStore/ClientAPI.NetCore
+
+
+
+
+
diff --git a/src/EventStore.ClientAPI.NetCore/EventStoreCatchUpSubscription.cs b/src/EventStore.ClientAPI.NetCore/EventStoreCatchUpSubscription.cs
index 351f655..98fac26 100644
--- a/src/EventStore.ClientAPI.NetCore/EventStoreCatchUpSubscription.cs
+++ b/src/EventStore.ClientAPI.NetCore/EventStoreCatchUpSubscription.cs
@@ -136,9 +136,9 @@ internal Task StartAsync()
}
///
- /// Attempts to stop the subscription.
+ /// Attempts to stop the subscription blocking for completion of stop.
///
- /// The amount of time within which the subscription should stop.
+ /// The maximum amount of time which the current thread will block waiting for the subscription to stop before throwing a TimeoutException.
/// Thrown if the subscription fails to stop within it's timeout period.
public void Stop(TimeSpan timeout)
{
@@ -430,7 +430,7 @@ private async Task ReadEventsInternalAsync(IEventStoreConnection connection, boo
private async Task ReadEventsCallbackAsync(AllEventsSlice slice, long? lastCommitPosition)
{
bool shouldStopOrDone = ShouldStop || await ProcessEventsAsync(lastCommitPosition, slice).ConfigureAwait(false);
- if (shouldStopOrDone && Verbose)
+ if(shouldStopOrDone && Verbose)
{
Log.Debug(
"Catch-up Subscription {0} to {1}: finished reading events, nextReadPosition = {2}.",
@@ -455,7 +455,7 @@ private async Task ProcessEventsAsync(long? lastCommitPosition, AllEventsS
: slice.NextPosition >= new Position(lastCommitPosition.Value, lastCommitPosition.Value);
if (!done && slice.IsEndOfStream)
- Thread.Sleep(1); // we are waiting for server to flush its data
+ await Task.Delay(1).ConfigureAwait(false); // we are waiting for server to flush its data
return done;
}
@@ -534,7 +534,7 @@ internal EventStoreStreamCatchUpSubscription(IEventStoreConnection connection,
protected override Task ReadEventsTillAsync(IEventStoreConnection connection, bool resolveLinkTos, UserCredentials userCredentials,
long? lastCommitPosition, long? lastEventNumber) =>
ReadEventsInternalAsync(connection, resolveLinkTos, userCredentials, lastEventNumber);
-
+
private async Task ReadEventsInternalAsync(IEventStoreConnection connection, bool resolveLinkTos,
UserCredentials userCredentials, long? lastEventNumber)
{
@@ -595,7 +595,7 @@ private async Task ProcessEventsAsync(long? lastEventNumber, StreamEventsS
}
if (!done && slice.IsEndOfStream)
- Thread.Sleep(1); // we are waiting for server to flush its data
+ await Task.Delay(1).ConfigureAwait(false); // we are waiting for server to flush its data
return done;
}
diff --git a/src/EventStore.ClientAPI.NetCore/EventStoreConnection.cs b/src/EventStore.ClientAPI.NetCore/EventStoreConnection.cs
index 58d7601..342e3d9 100644
--- a/src/EventStore.ClientAPI.NetCore/EventStoreConnection.cs
+++ b/src/EventStore.ClientAPI.NetCore/EventStoreConnection.cs
@@ -4,7 +4,6 @@
using EventStore.ClientAPI.Common.Utils;
using EventStore.ClientAPI.Internal;
using System;
-using EventStore.ClientApi.Core.Internal;
using EventStore.ClientAPI.SystemData;
namespace EventStore.ClientAPI
@@ -37,7 +36,7 @@ public static IEventStoreConnection Create(string connectionString, string conne
}
///
- /// Creates a new to single node using the overriding them with values provided via the connectionstring
+ /// Creates a new to single node using default provided via a connectionstring
///
/// Optional name of connection (will be generated automatically, if not provided)
/// Pre-populated settings builder, optional. If not specified, a new builder will be created.
@@ -93,7 +92,7 @@ public static IEventStoreConnection Create(ConnectionSettings connectionSettings
connectionSettings.MaxQueueSize, connectionSettings.MaxConcurrentItems,
connectionSettings.MaxRetries, connectionSettings.MaxReconnections,
connectionSettings.RequireMaster, connectionSettings.ReconnectionDelay,
- connectionSettings.OperationTimeout,
+ connectionSettings.QueueTimeout, connectionSettings.OperationTimeout,
connectionSettings.OperationTimeoutCheckPeriod, credential, connectionSettings.UseSslConnection,
connectionSettings.TargetHost,
connectionSettings.ValidateServer, connectionSettings.FailOnNoServerResponse,
@@ -101,12 +100,12 @@ public static IEventStoreConnection Create(ConnectionSettings connectionSettings
connectionSettings.ClientConnectionTimeout, connectionSettings.ClusterDns,
connectionSettings.GossipSeeds, connectionSettings.MaxDiscoverAttempts,
connectionSettings.ExternalGossipPort, connectionSettings.GossipTimeout,
- connectionSettings.PreferRandomNode);
+ connectionSettings.NodePreference);
}
if (scheme == "discover")
{
var clusterSettings = new ClusterSettings(uri.Host, connectionSettings.MaxDiscoverAttempts, uri.Port,
- connectionSettings.GossipTimeout, connectionSettings.PreferRandomNode);
+ connectionSettings.GossipTimeout, connectionSettings.NodePreference);
Ensure.NotNull(connectionSettings, "connectionSettings");
Ensure.NotNull(clusterSettings, "clusterSettings");
@@ -116,7 +115,7 @@ public static IEventStoreConnection Create(ConnectionSettings connectionSettings
clusterSettings.ExternalGossipPort,
clusterSettings.GossipSeeds,
clusterSettings.GossipTimeout,
- clusterSettings.PreferRandomNode);
+ clusterSettings.NodePreference);
return new EventStoreNodeConnection(connectionSettings, clusterSettings, endPointDiscoverer,
connectionName);
@@ -134,7 +133,7 @@ public static IEventStoreConnection Create(ConnectionSettings connectionSettings
var clusterSettings = new ClusterSettings(connectionSettings.GossipSeeds,
connectionSettings.MaxDiscoverAttempts,
connectionSettings.GossipTimeout,
- connectionSettings.PreferRandomNode);
+ connectionSettings.NodePreference);
Ensure.NotNull(connectionSettings, "connectionSettings");
Ensure.NotNull(clusterSettings, "clusterSettings");
@@ -144,7 +143,7 @@ public static IEventStoreConnection Create(ConnectionSettings connectionSettings
clusterSettings.ExternalGossipPort,
clusterSettings.GossipSeeds,
clusterSettings.GossipTimeout,
- clusterSettings.PreferRandomNode);
+ clusterSettings.NodePreference);
return new EventStoreNodeConnection(connectionSettings, clusterSettings, endPointDiscoverer,
connectionName);
@@ -210,9 +209,23 @@ public static IEventStoreConnection Create(ConnectionSettings connectionSettings
clusterSettings.ExternalGossipPort,
clusterSettings.GossipSeeds,
clusterSettings.GossipTimeout,
- clusterSettings.PreferRandomNode);
+ clusterSettings.NodePreference);
return new EventStoreNodeConnection(connectionSettings, clusterSettings, endPointDiscoverer, connectionName);
}
+
+ ///
+ /// Creates a new using specific and a custom-defined
+ ///
+ /// The to apply to the new connection
+ /// The custom-defined to use for node discovery
+ /// Optional name of connection (will be generated automatically, if not provided)
+ /// a new
+ public static IEventStoreConnection Create(ConnectionSettings connectionSettings, IEndPointDiscoverer endPointDiscoverer, string connectionName = null)
+ {
+ Ensure.NotNull(connectionSettings, "settings");
+ Ensure.NotNull(endPointDiscoverer, "endPointDiscoverer");
+ return new EventStoreNodeConnection(connectionSettings, null, endPointDiscoverer, connectionName);
+ }
}
}
diff --git a/src/EventStore.ClientAPI.NetCore/EventStorePersistentSubscription.cs b/src/EventStore.ClientAPI.NetCore/EventStorePersistentSubscription.cs
index 001e1e8..701c0e2 100644
--- a/src/EventStore.ClientAPI.NetCore/EventStorePersistentSubscription.cs
+++ b/src/EventStore.ClientAPI.NetCore/EventStorePersistentSubscription.cs
@@ -1,10 +1,48 @@
using System;
using System.Threading.Tasks;
+using EventStore.ClientAPI.ClientOperations;
using EventStore.ClientAPI.Internal;
using EventStore.ClientAPI.SystemData;
namespace EventStore.ClientAPI
{
+ internal struct PersistentSubscriptionResolvedEvent : IResolvedEvent
+ {
+ ///
+ ///
+ ///
+ public readonly int? RetryCount;
+ ///
+ ///
+ ///
+ public readonly ResolvedEvent Event;
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ internal PersistentSubscriptionResolvedEvent(ResolvedEvent @event, int? retryCount)
+ {
+ Event = @event;
+ RetryCount = retryCount;
+ }
+
+ string IResolvedEvent.OriginalStreamId => Event.OriginalStreamId;
+
+ long IResolvedEvent.OriginalEventNumber => Event.OriginalEventNumber;
+
+ RecordedEvent IResolvedEvent.OriginalEvent => Event.OriginalEvent;
+
+ Position? IResolvedEvent.OriginalPosition => Event.OriginalPosition;
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static implicit operator ResolvedEvent(PersistentSubscriptionResolvedEvent x) => x.Event;
+ }
///
/// Represents a persistent subscription connection.
///
@@ -14,7 +52,7 @@ public class EventStorePersistentSubscription : EventStorePersistentSubscription
internal EventStorePersistentSubscription(
string subscriptionId, string streamId,
- Func eventAppeared,
+ Func eventAppeared,
Action subscriptionDropped,
UserCredentials userCredentials, ILogger log, bool verboseLogging, ConnectionSettings settings,
EventStoreConnectionLogicHandler handler, int bufferSize = 10, bool autoAck = true)
@@ -27,7 +65,7 @@ internal EventStorePersistentSubscription(
internal override Task StartSubscription(
string subscriptionId, string streamId, int bufferSize, UserCredentials userCredentials,
- Func onEventAppeared,
+ Func onEventAppeared,
Action onSubscriptionDropped, ConnectionSettings settings)
{
var source = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
diff --git a/src/EventStore.ClientAPI.NetCore/EventStorePersistentSubscriptionBase.cs b/src/EventStore.ClientAPI.NetCore/EventStorePersistentSubscriptionBase.cs
index 9297793..0a37376 100644
--- a/src/EventStore.ClientAPI.NetCore/EventStorePersistentSubscriptionBase.cs
+++ b/src/EventStore.ClientAPI.NetCore/EventStorePersistentSubscriptionBase.cs
@@ -21,7 +21,7 @@ public abstract class EventStorePersistentSubscriptionBase
private readonly string _subscriptionId;
private readonly string _streamId;
- private readonly Func _eventAppeared;
+ private readonly Func _eventAppeared;
private readonly Action _subscriptionDropped;
private readonly UserCredentials _userCredentials;
private readonly ILogger _log;
@@ -30,7 +30,7 @@ public abstract class EventStorePersistentSubscriptionBase
private readonly bool _autoAck;
private PersistentEventStoreSubscription _subscription;
- private readonly ConcurrentQueue _queue = new ConcurrentQueue();
+ private readonly ConcurrentQueue _queue = new ConcurrentQueue();
private int _isProcessing;
private DropData _dropData;
@@ -40,7 +40,7 @@ public abstract class EventStorePersistentSubscriptionBase
internal EventStorePersistentSubscriptionBase(string subscriptionId,
string streamId,
- Func eventAppeared,
+ Func eventAppeared,
Action subscriptionDropped,
UserCredentials userCredentials,
ILogger log,
@@ -77,7 +77,7 @@ internal Task Start()
internal abstract Task StartSubscription(
string subscriptionId, string streamId, int bufferSize, UserCredentials userCredentials,
- Func onEventAppeared,
+ Func onEventAppeared,
Action onSubscriptionDropped,
ConnectionSettings settings);
@@ -168,7 +168,7 @@ private void EnqueueSubscriptionDropNotification(SubscriptionDropReason reason,
var dropData = new DropData(reason, error);
if (Interlocked.CompareExchange(ref _dropData, dropData, null) == null)
{
- Enqueue(DropSubscriptionEvent);
+ Enqueue(new PersistentSubscriptionResolvedEvent(DropSubscriptionEvent, null));
}
}
@@ -177,13 +177,13 @@ private void OnSubscriptionDropped(EventStoreSubscription subscription, Subscrip
EnqueueSubscriptionDropNotification(reason, exception);
}
- private Task OnEventAppeared(EventStoreSubscription subscription, ResolvedEvent resolvedEvent)
+ private Task OnEventAppeared(EventStoreSubscription subscription, PersistentSubscriptionResolvedEvent resolvedEvent)
{
Enqueue(resolvedEvent);
return Task.CompletedTask;
}
- private void Enqueue(ResolvedEvent resolvedEvent)
+ private void Enqueue(PersistentSubscriptionResolvedEvent resolvedEvent)
{
_queue.Enqueue(resolvedEvent);
if (Interlocked.CompareExchange(ref _isProcessing, 1, 0) == 0)
@@ -197,14 +197,14 @@ private async void ProcessQueue()
{
if (_subscription == null)
{
- Thread.Sleep(1);
+ await Task.Delay(1).ConfigureAwait(false);
}
else
- {
- ResolvedEvent e;
+ {
+ PersistentSubscriptionResolvedEvent e;
while (_queue.TryDequeue(out e))
{
- if (e.Equals(DropSubscriptionEvent)) // drop subscription artificial ResolvedEvent
+ if (e.Event.Equals(DropSubscriptionEvent)) // drop subscription artificial ResolvedEvent
{
if (_dropData == null) throw new Exception("Drop reason not specified.");
DropSubscription(_dropData.Reason, _dropData.Error);
@@ -217,13 +217,13 @@ private async void ProcessQueue()
}
try
{
- await _eventAppeared(this, e).ConfigureAwait(false);
+ await _eventAppeared(this, e, e.RetryCount).ConfigureAwait(false);
if (_autoAck)
- _subscription.NotifyEventsProcessed(new[] { e.OriginalEvent.EventId });
+ _subscription.NotifyEventsProcessed(new[] { e.Event.OriginalEvent.EventId });
if (_verbose)
_log.Debug("Persistent Subscription to {0}: processed event ({1}, {2}, {3} @ {4}).",
_streamId,
- e.OriginalEvent.EventStreamId, e.OriginalEvent.EventNumber, e.OriginalEvent.EventType, e.OriginalEventNumber);
+ e.Event.OriginalEvent.EventStreamId, e.Event.OriginalEvent.EventNumber, e.Event.OriginalEvent.EventType, e.Event.OriginalEventNumber);
}
catch (Exception exc)
{
diff --git a/src/EventStore.ClientAPI.NetCore/Exceptions/OperationExpiredException.cs b/src/EventStore.ClientAPI.NetCore/Exceptions/OperationExpiredException.cs
new file mode 100644
index 0000000..47abe9f
--- /dev/null
+++ b/src/EventStore.ClientAPI.NetCore/Exceptions/OperationExpiredException.cs
@@ -0,0 +1,35 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace EventStore.ClientAPI.Exceptions
+{
+ ///
+ /// Exception thrown if an operation expires before it can be scheduled.
+ ///
+ public class OperationExpiredException : EventStoreConnectionException
+ {
+ ///
+ /// Constructs a new .
+ ///
+ public OperationExpiredException()
+ {
+ }
+
+ ///
+ /// Constructs a new .
+ ///
+ ///
+ public OperationExpiredException(string message) : base(message)
+ {
+ }
+
+ ///
+ /// Constructs a new .
+ ///
+ public OperationExpiredException(string message, Exception innerException):base(message, innerException)
+ {
+
+ }
+ }
+}
diff --git a/src/EventStore.ClientAPI.NetCore/GossipSeedClusterSettingsBuilder.cs b/src/EventStore.ClientAPI.NetCore/GossipSeedClusterSettingsBuilder.cs
index 9d1b95d..32f96d2 100644
--- a/src/EventStore.ClientAPI.NetCore/GossipSeedClusterSettingsBuilder.cs
+++ b/src/EventStore.ClientAPI.NetCore/GossipSeedClusterSettingsBuilder.cs
@@ -13,14 +13,13 @@ public class GossipSeedClusterSettingsBuilder
private GossipSeed[] _gossipSeeds;
private TimeSpan _gossipTimeout = TimeSpan.FromSeconds(1);
private int _maxDiscoverAttempts = Consts.DefaultMaxClusterDiscoverAttempts;
- private bool _preferRandomNode = false;
+ private NodePreference _nodePreference = NodePreference.Master;
///
/// Sets gossip seed endpoints for the client.
- ///
- /// Note that this should be the external HTTP endpoint of the server, as it is required
- /// for the client to exchange gossip with the server. The standard port which should be
- /// used here is 2113.
+ /// TODO: This was a note.
+ /// This should be the external HTTP endpoint of the server, as it is required
+ /// for the client to exchange gossip with the server. The standard port is 2113.
///
/// If the server requires a specific Host header to be sent as part of the gossip
/// request, use the overload of this method taking instead.
@@ -51,6 +50,16 @@ public GossipSeedClusterSettingsBuilder SetGossipSeedEndPoints(params GossipSeed
_gossipSeeds = gossipSeeds;
return this;
}
+
+ ///
+ /// Allows infinite nodes discovery attempts.
+ ///
+ /// A for further configuration.
+ public GossipSeedClusterSettingsBuilder KeepDiscovering()
+ {
+ _maxDiscoverAttempts = Int32.MaxValue;
+ return this;
+ }
///
/// Sets the maximum number of attempts for discovery.
@@ -83,7 +92,17 @@ public GossipSeedClusterSettingsBuilder SetGossipTimeout(TimeSpan timeout)
/// A for further configuration.
public GossipSeedClusterSettingsBuilder PreferRandomNode()
{
- _preferRandomNode = true;
+ _nodePreference = NodePreference.Random;
+ return this;
+ }
+
+ ///
+ /// hether to prioritize choosing a slave node that's alive from the known nodes.
+ ///
+ /// A for further configuration.
+ public GossipSeedClusterSettingsBuilder PreferSlaveNode()
+ {
+ _nodePreference = NodePreference.Slave;
return this;
}
@@ -101,7 +120,7 @@ public static implicit operator ClusterSettings(GossipSeedClusterSettingsBuilder
/// Builds a object from a .
///
public ClusterSettings Build() {
- return new ClusterSettings(this._gossipSeeds, this._maxDiscoverAttempts, this._gossipTimeout, this._preferRandomNode);
+ return new ClusterSettings(this._gossipSeeds, this._maxDiscoverAttempts, this._gossipTimeout, this._nodePreference);
}
}
}
diff --git a/src/EventStore.ClientAPI.NetCore/IEndPointDiscoverer.cs b/src/EventStore.ClientAPI.NetCore/IEndPointDiscoverer.cs
new file mode 100644
index 0000000..e44ac5f
--- /dev/null
+++ b/src/EventStore.ClientAPI.NetCore/IEndPointDiscoverer.cs
@@ -0,0 +1,18 @@
+using System.Net;
+using System.Threading.Tasks;
+
+namespace EventStore.ClientAPI
+{
+ ///
+ /// Represents a way of discovering cluster endpoints. This could be through gossip, consul, text files, etc
+ ///
+ public interface IEndPointDiscoverer
+ {
+ ///
+ /// Called to discover a new
+ ///
+ /// The The recently failed endpoint
+ ///
+ Task DiscoverAsync(IPEndPoint failedTcpEndPoint);
+ }
+}
\ No newline at end of file
diff --git a/src/EventStore.ClientAPI.NetCore/IEventStoreConnection.cs b/src/EventStore.ClientAPI.NetCore/IEventStoreConnection.cs
index e191a90..f6cbd47 100644
--- a/src/EventStore.ClientAPI.NetCore/IEventStoreConnection.cs
+++ b/src/EventStore.ClientAPI.NetCore/IEventStoreConnection.cs
@@ -7,40 +7,40 @@
namespace EventStore.ClientAPI
{
///
- /// Maintains a full duplex connection to the EventStore
+ /// Maintains a full duplex connection to Event Store.
///
///
- /// An operates quite differently than say a SqlConnection. Normally
+ /// An operates differently than a SqlConnection. Normally
/// when using an you want to keep the connection open for a much longer of time than
/// when you use a SqlConnection. If you prefer the usage pattern of using(new Connection()) .. then you would likely
/// want to create a FlyWeight on top of the .
///
/// Another difference is that with the all operations are handled in a full async manner
/// (even if you call the synchronous behaviors). Many threads can use an at the same
- /// time or a single thread can make many asynchronous requests. To get the most performance out of the connection
+ /// time or a single thread can make many asynchronous requests. To get the best performance out of the connection
/// it is generally recommended to use it in this way.
///
public interface IEventStoreConnection : IDisposable
{
///
- /// Gets the name of this connection. A connection name can be used for disambiguation
+ /// Gets the name of this connection. A connection name is useful for disambiguation
/// in log files.
///
string ConnectionName { get; }
///
- /// Connects the asynchronously to a destination
+ /// Connects the asynchronously to a destination.
///
- /// A that can be waited upon.
+ /// A to wait upon.
Task ConnectAsync();
///
- /// Closes this
+ /// Closes this .
///
void Close();
///
- /// Deletes a stream from the Event Store asynchronously
+ /// Deletes a stream from Event Store asynchronously.
///
/// The name of the stream to delete.
/// The expected version that the streams should have when being deleted.
@@ -49,111 +49,110 @@ public interface IEventStoreConnection : IDisposable
Task DeleteStreamAsync(string stream, long expectedVersion, UserCredentials userCredentials = null);
///
- /// Deletes a stream from the Event Store asynchronously
+ /// Deletes a stream from Event Store asynchronously.
///
/// The name of the stream to delete.
/// The expected version that the streams should have when being deleted.
- /// Indicator for tombstoning vs soft-deleting the stream. Tombstoned streams can never be recreated. Soft-deleted streams
- /// can be written to again, but the EventNumber sequence will not start from 0.
+ /// Indicator for tombstoning vs soft-deleting the stream. Tombstoned streams can never be recreated. Soft-deleted streams can be written to again, but the EventNumber sequence will not start from 0.
/// The optional user credentials to perform operation with.
/// A containing the results of the delete stream operation.
Task DeleteStreamAsync(string stream, long expectedVersion, bool hardDelete, UserCredentials userCredentials = null);
///
- /// Appends Events asynchronously to a stream.
+ /// Appends events asynchronously to a stream.
///
///
/// When appending events to a stream the choice can
- /// make a very large difference in the observed behavior. For example, if no stream exists
+ /// make a large difference in the observed behavior. For example, if no stream exists
/// and ExpectedVersion.Any is used, a new stream will be implicitly created when appending.
- ///
+ /// TODO: Link above and below?
/// There are also differences in idempotency between different types of calls.
- /// If you specify an ExpectedVersion aside from ExpectedVersion.Any the Event Store
- /// will give you an idempotency guarantee. If using ExpectedVersion.Any the Event Store
- /// will do its best to provide idempotency but does not guarantee idempotency
+ /// If you specify an ExpectedVersion aside from ExpectedVersion.Any, Event Store
+ /// will give you an idempotency guarantee. If using ExpectedVersion.Any, Event Store
+ /// will do its best to provide idempotency but does not guarantee idempotency.
///
- /// The name of the stream to append events to
- /// The of the stream to append to
- /// The events to append to the stream
+ /// The name of the stream to append events to.
+ /// The of the stream to append to.
+ /// The events to append to the stream.
/// A containing the results of the write operation.
Task AppendToStreamAsync(string stream, long expectedVersion, params EventData[] events);
///
- /// Appends Events asynchronously to a stream.
+ /// Appends events asynchronously to a stream.
///
///
/// When appending events to a stream the choice can
- /// make a very large difference in the observed behavior. For example, if no stream exists
+ /// make a large difference in the observed behavior. For example, if no stream exists
/// and ExpectedVersion.Any is used, a new stream will be implicitly created when appending.
- ///
+ ///
/// There are also differences in idempotency between different types of calls.
- /// If you specify an ExpectedVersion aside from ExpectedVersion.Any the Event Store
- /// will give you an idempotency guarantee. If using ExpectedVersion.Any the Event Store
- /// will do its best to provide idempotency but does not guarantee idempotency
+ /// If you specify an ExpectedVersion aside from ExpectedVersion.Any, Event Store
+ /// will give you an idempotency guarantee. If using ExpectedVersion.Any, Event Store
+ /// will do its best to provide idempotency but does not guarantee idempotency.
///
- /// The name of the stream to append events to
- /// The of the stream to append to
+ /// The name of the stream to append events to.
+ /// The of the stream to append to.
/// The optional user credentials to perform operation with.
- /// The events to append to the stream
+ /// The events to append to the stream.
/// A containing the results of the write operation.
Task AppendToStreamAsync(string stream, long expectedVersion, UserCredentials userCredentials, params EventData[] events);
///
- /// Appends Events asynchronously to a stream.
+ /// Appends events asynchronously to a stream.
///
///
/// When appending events to a stream the choice can
- /// make a very large difference in the observed behavior. For example, if no stream exists
+ /// make a large difference in the observed behavior. For example, if no stream exists
/// and ExpectedVersion.Any is used, a new stream will be implicitly created when appending.
///
/// There are also differences in idempotency between different types of calls.
- /// If you specify an ExpectedVersion aside from ExpectedVersion.Any the Event Store
- /// will give you an idempotency guarantee. If using ExpectedVersion.Any the Event Store
- /// will do its best to provide idempotency but does not guarantee idempotency
+ /// If you specify an ExpectedVersion aside from ExpectedVersion.Any, Event Store
+ /// will give you an idempotency guarantee. If using ExpectedVersion.Any, Event Store
+ /// will do its best to provide idempotency but does not guarantee idempotency.
///
- /// The name of the stream to append events to
- /// The of the stream to append to
- /// The events to append to the stream
+ /// The name of the stream to append events to.
+ /// The of the stream to append to.
+ /// The events to append to the stream.
/// The optional user credentials to perform operation with.
///A containing the results of the write operation.
Task AppendToStreamAsync(string stream, long expectedVersion, IEnumerable events, UserCredentials userCredentials = null);
///
- /// Appends Events asynchronously to a stream if the stream version matches the .
+ /// Appends events asynchronously to a stream if the stream version matches the .
///
///
/// When appending events to a stream the choice can
- /// make a very large difference in the observed behavior. For example, if no stream exists
+ /// make a large difference in the observed behavior. For example, if no stream exists
/// and ExpectedVersion.Any is used, a new stream will be implicitly created when appending.
///
/// There are also differences in idempotency between different types of calls.
- /// If you specify an ExpectedVersion aside from ExpectedVersion.Any the Event Store
- /// will give you an idempotency guarantee. If using ExpectedVersion.Any the Event Store
- /// will do its best to provide idempotency but does not guarantee idempotency
+ /// If you specify an ExpectedVersion aside from ExpectedVersion.Any, Event Store
+ /// will give you an idempotency guarantee. If using ExpectedVersion.Any, Event Store
+ /// will do its best to provide idempotency but does not guarantee idempotency.
///
- /// The name of the stream to append events to
- /// The of the stream to append to
- /// The events to append to the stream
+ /// The name of the stream to append events to.
+ /// The of the stream to append to.
+ /// The events to append to the stream.
/// The optional user credentials to perform operation with.
/// A describing if the operation succeeded and, if not, the reason for failure (which can be either stream version mismatch or trying to write to a deleted stream).
Task ConditionalAppendToStreamAsync(string stream, long expectedVersion, IEnumerable events, UserCredentials userCredentials = null);
///
- /// Starts a transaction in the event store on a given stream asynchronously
+ /// Starts an asynchronous transaction in Event Store on a given stream.
///
///
/// A allows the calling of multiple writes with multiple
- /// round trips over long periods of time between the caller and the event store. This method
+ /// round trips over long periods of time between the caller and Event Store. This method
/// is only available through the TCP interface and no equivalent exists for the RESTful interface.
///
- /// The stream to start a transaction on
- /// The expected version of the stream at the time of starting the transaction
+ /// The stream to start a transaction on.
+ /// The expected version of the stream at the time of starting the transaction.
/// The optional user credentials to perform operation with.
/// A representing a multi-request transaction.
Task StartTransactionAsync(string stream, long expectedVersion, UserCredentials userCredentials = null);
///
- /// Continues transaction by provided transaction ID.
+ /// Continues specified transaction.
///
///
/// A allows the calling of multiple writes with multiple
@@ -176,43 +175,43 @@ public interface IEventStoreConnection : IDisposable
Task ReadEventAsync(string stream, long eventNumber, bool resolveLinkTos, UserCredentials userCredentials = null);
///
- /// Reads count Events from an Event Stream forwards (e.g. oldest to newest) starting from position start
+ /// Asynchronously reads count events from an event stream forwards (e.g. oldest to newest) starting from position start.
///
- /// The stream to read from
- /// The starting point to read from
- /// The count of items to read
- /// Whether to resolve LinkTo events automatically
+ /// The stream to read from.
+ /// The starting point to read from.
+ /// The count of items to read.
+ /// Whether to resolve LinkTo events automatically.
/// The optional user credentials to perform operation with.
/// A containing the results of the read operation.
Task ReadStreamEventsForwardAsync(string stream, long start, int count, bool resolveLinkTos, UserCredentials userCredentials = null);
///
- /// Reads count events from an Event Stream backwards (e.g. newest to oldest) from position asynchronously
+ /// Asynchronously reads count events from an event stream backwards (e.g. newest to oldest) from position.
///
- /// The Event Stream to read from
- /// The position to start reading from
- /// The count to read from the position
- /// Whether to resolve LinkTo events automatically
+ /// The stream to read from.
+ /// The position to start reading from.
+ /// The count to read from the position.
+ /// Whether to resolve LinkTo events automatically.
/// The optional user credentials to perform operation with.
/// A containing the results of the read operation.
Task ReadStreamEventsBackwardAsync(string stream, long start, int count, bool resolveLinkTos, UserCredentials userCredentials = null);
///
- /// Reads All Events in the node forward asynchronously (e.g. beginning to end)
+ /// Asynchronously reads all events in the node forward (e.g. beginning to end).
///
- /// The position to start reading from
- /// The maximum count to read
- /// Whether to resolve LinkTo events automatically
+ /// The position to start reading from.
+ /// The maximum count to read.
+ /// Whether to resolve LinkTo events automatically.
/// The optional user credentials to perform operation with.
/// A containing the records read.
Task ReadAllEventsForwardAsync(Position position, int maxCount, bool resolveLinkTos, UserCredentials userCredentials = null);
///
- /// Reads All Events in the node backwards (e.g. end to beginning)
+ /// Asynchronously reads all events in the node backwards (e.g. end to beginning).
///
- /// The position to start reading from
- /// The maximum count to read
- /// Whether to resolve Link events automatically
+ /// The position to start reading from.
+ /// The maximum count to read.
+ /// Whether to resolve Link events automatically.
/// The optional user credentials to perform operation with.
/// A containing the records read.
Task ReadAllEventsBackwardAsync(Position position, int maxCount, bool resolveLinkTos, UserCredentials userCredentials = null);
@@ -222,11 +221,11 @@ public interface IEventStoreConnection : IDisposable
/// written to the stream while the subscription is active will be
/// pushed to the client.
///
- /// The stream to subscribe to
- /// Whether to resolve Link events automatically
- /// A Task invoked and awaited when a new event is received over the subscription
- /// An action invoked if the subscription is dropped
- /// User credentials to use for the operation
+ /// The stream to subscribe to.
+ /// Whether to resolve Link events automatically.
+ /// A Task invoked and awaited when a new event is received over the subscription.
+ /// An action invoked if the subscription is dropped.
+ /// User credentials to use for the operation.
/// A representing the subscription.
Task SubscribeToStreamAsync(
string stream,
@@ -257,15 +256,15 @@ Task SubscribeToStreamAsync(
/// is desired, use the event number of the last event processed which
/// appeared on the subscription.
///
- /// NOTE: Using here will result in missing
+ /// Using here will result in missing
/// the first event in the stream.
- /// Whether to resolve Link events automatically
- /// A Task invoked and awaited when a new event is received over the subscription
- /// An action invoked when the subscription switches to newly-pushed events
- /// An action invoked if the subscription is dropped
- /// User credentials to use for the operation
- /// The batch size to use during the read phase
- /// The name of subscription
+ /// Whether to resolve Link events automatically.
+ /// A Task invoked and awaited when a new event is received over the subscription.
+ /// An action invoked when the subscription switches to newly-pushed events.
+ /// An action invoked if the subscription is dropped.
+ /// User credentials to use for the operation.
+ /// The batch size to use during the read phase.
+ /// The name of subscription.
/// An representing the subscription.
[Obsolete("This method will be obsoleted in the next major version please switch to the overload with a settings object")]
EventStoreStreamCatchUpSubscription SubscribeToStreamFrom(
@@ -293,7 +292,7 @@ EventStoreStreamCatchUpSubscription SubscribeToStreamFrom(
/// switches from the reading
/// phase to the live subscription phase.
///
- /// The stream to subscribe to
+ /// The stream to subscribe to.
/// The event number from which to start.
///
/// To receive all events in the stream, use .
@@ -301,13 +300,13 @@ EventStoreStreamCatchUpSubscription SubscribeToStreamFrom(
/// is desired, use the event number of the last event processed which
/// appeared on the subscription.
///
- /// NOTE: Using here will result in missing
+ /// Using here will result in missing
/// the first event in the stream.
- /// A Task invoked and awaited when a new event is received over the subscription
- /// An action invoked when the subscription switches to newly-pushed events
- /// An action invoked if the subscription is dropped
- /// User credentials to use for the operation
- /// The for the subscription
+ /// A Task invoked and awaited when a new event is received over the subscription.
+ /// An action invoked when the subscription switches to newly-pushed events.
+ /// An action invoked if the subscription is dropped.
+ /// User credentials to use for the operation.
+ /// The for the subscription.
/// An representing the subscription.
EventStoreStreamCatchUpSubscription SubscribeToStreamFrom(
string stream,
@@ -321,14 +320,14 @@ EventStoreStreamCatchUpSubscription SubscribeToStreamFrom(
///
- /// Asynchronously subscribes to all events in the Event Store. New
+ /// Asynchronously subscribes to all events in Event Store. New
/// events written to the stream while the subscription is active
/// will be pushed to the client.
///
- /// Whether to resolve Link events automatically
- /// A Task invoked and awaited when a new event is received over the subscription
- /// An action invoked if the subscription is dropped
- /// User credentials to use for the operation
+ /// Whether to resolve Link events automatically.
+ /// A Task invoked and awaited when a new event is received over the subscription.
+ /// An action invoked if the subscription is dropped.
+ /// User credentials to use for the operation.
/// A representing the subscription.
Task SubscribeToAllAsync(
bool resolveLinkTos,
@@ -337,18 +336,18 @@ Task SubscribeToAllAsync(
UserCredentials userCredentials = null);
///
- /// Subscribes to a persistent subscription(competing consumer) on event store
+ /// Subscribes to a persistent subscription (competing consumer) on an event store.
///
- /// The subscription group to connect to
- /// The stream to subscribe to
- /// A Task invoked and awaited when a new event is received over the subscription
- /// An action invoked if the subscription is dropped
- /// User credentials to use for the operation
- /// The buffer size to use for the persistent subscription
+ /// The subscription group to connect to.
+ /// The stream to subscribe to.
+ /// A Task invoked and awaited when a new event is received over the subscription.
+ /// An action invoked if the subscription is dropped.
+ /// User credentials to use for the operation.
+ /// The buffer size to use for the persistent subscription.
/// Whether the subscription should automatically acknowledge messages processed.
/// If not set the receiver is required to explicitly acknowledge messages through the subscription.
/// This will connect you to a persistent subscription group for a stream. The subscription group
- /// must first be created with CreatePersistentSubscriptionAsync many connections
+ /// must first be created with CreatePersistentSubscriptionAsync. Many connections
/// can connect to the same group and they will be treated as competing consumers within the group.
/// If one connection dies work will be balanced across the rest of the consumers in the group. If
/// you attempt to connect to a group that does not exist you will be given an exception.
@@ -357,25 +356,25 @@ Task SubscribeToAllAsync(
EventStorePersistentSubscriptionBase ConnectToPersistentSubscription(
string stream,
string groupName,
- Func eventAppeared,
+ Func eventAppeared,
Action subscriptionDropped = null,
UserCredentials userCredentials = null,
int bufferSize = 10,
bool autoAck = true);
///
- /// Asynchronously subscribes to a persistent subscription(competing consumer) on event store
+ /// Asynchronously subscribes to a persistent subscription (competing consumer) on an event store.
///
- /// The subscription group to connect to
- /// The stream to subscribe to
- /// A Task invoked and awaited when a new event is received over the subscription
- /// An action invoked if the subscription is dropped
- /// User credentials to use for the operation
- /// The buffer size to use for the persistent subscription
+ /// The subscription group to connect to.
+ /// The stream to subscribe to.
+ /// A Task invoked and awaited when a new event is received over the subscription.
+ /// An action invoked if the subscription is dropped.
+ /// User credentials to use for the operation.
+ /// The buffer size to use for the persistent subscription.
/// Whether the subscription should automatically acknowledge messages processed.
/// If not set the receiver is required to explicitly acknowledge messages through the subscription.
/// This will connect you to a persistent subscription group for a stream. The subscription group
- /// must first be created with CreatePersistentSubscriptionAsync many connections
+ /// must first be created with CreatePersistentSubscriptionAsync. Many connections
/// can connect to the same group and they will be treated as competing consumers within the group.
/// If one connection dies work will be balanced across the rest of the consumers in the group. If
/// you attempt to connect to a group that does not exist you will be given an exception.
@@ -384,7 +383,7 @@ EventStorePersistentSubscriptionBase ConnectToPersistentSubscription(
Task ConnectToPersistentSubscriptionAsync(
string stream,
string groupName,
- Func eventAppeared,
+ Func eventAppeared,
Action subscriptionDropped = null,
UserCredentials userCredentials = null,
int bufferSize = 10,
@@ -419,7 +418,7 @@ EventStorePersistentSubscription ConnectToPersistentSubscriptionForAll(
///
/// Subscribes to all events. Existing events from lastCheckpoint
- /// onwards are read from the Event Store and presented to the user of
+ /// onwards are read from Event Store and presented to the user of
/// as if they had been pushed.
///
/// Once the end of the stream is read the subscription is
@@ -437,15 +436,15 @@ EventStorePersistentSubscription ConnectToPersistentSubscriptionForAll(
/// is desired, use the position representing the last event processed which
/// appeared on the subscription.
///
- /// NOTE: Using here will result in missing
+ /// Using here will result in missing
/// the first event in the stream.
- /// Whether to resolve Link events automatically
- /// A Task invoked and awaited when a new event is received over the subscription
- /// An action invoked when the subscription switches to newly-pushed events
- /// An action invoked if the subscription is dropped
- /// User credentials to use for the operation
- /// The batch size to use during the read phase
- /// The name of subscription
+ /// Whether to resolve Link events automatically.
+ /// A Task invoked and awaited when a new event is received over the subscription.
+ /// An action invoked when the subscription switches to newly-pushed events.
+ /// An action invoked if the subscription is dropped.
+ /// User credentials to use for the operation.
+ /// The batch size to use during the read phase.
+ /// The name of subscription.
/// An representing the subscription.
[Obsolete("This overload will be removed in the next major release please use the overload with a settings object")]
EventStoreAllCatchUpSubscription SubscribeToAllFrom(
@@ -460,7 +459,7 @@ EventStoreAllCatchUpSubscription SubscribeToAllFrom(
///
/// Subscribes to a all events. Existing events from lastCheckpoint
- /// onwards are read from the Event Store and presented to the user of
+ /// onwards are read from Event Store and presented to the user of
/// as if they had been pushed.
///
/// Once the end of the stream is read the subscription is
@@ -478,13 +477,13 @@ EventStoreAllCatchUpSubscription SubscribeToAllFrom(
/// is desired, use the position representing the last event processed which
/// appeared on the subscription.
///
- /// NOTE: Using here will result in missing
+ /// Using here will result in missing
/// the first event in the stream.
- /// A Task invoked and awaited when a new event is received over the subscription
- /// An action invoked when the subscription switches to newly-pushed events
- /// An action invoked if the subscription is dropped
- /// User credentials to use for the operation
- /// The for the subscription
+ /// A Task invoked and awaited when a new event is received over the subscription.
+ /// An action invoked when the subscription switches to newly-pushed events.
+ /// An action invoked if the subscription is dropped.
+ /// User credentials to use for the operation.
+ /// The for the subscription.
/// An representing the subscription.
EventStoreAllCatchUpSubscription SubscribeToAllFrom(
Position? lastCheckpoint,
@@ -508,10 +507,10 @@ EventStoreAllCatchUpSubscription SubscribeToAllFrom(
*/
///
- /// Asynchronously update a persistent subscription group on a stream
+ /// Asynchronously update a persistent subscription group on a stream.
///
- /// The name of the stream to create the persistent subscription on
- /// The name of the group to create
+ /// The name of the stream to create the persistent subscription on.
+ /// The name of the group to create.
/// The for the subscription
/// The credentials to be used for this operation.
/// A that can be waited upon.
@@ -519,22 +518,22 @@ EventStoreAllCatchUpSubscription SubscribeToAllFrom(
///
- /// Asynchronously create a persistent subscription group on a stream
+ /// Asynchronously create a persistent subscription group on a stream.
///
- /// The name of the stream to create the persistent subscription on
- /// The name of the group to create
- /// The for the subscription
+ /// The name of the stream to create the persistent subscription on.
+ /// The name of the group to create.
+ /// The for the subscription.
/// The credentials to be used for this operation.
/// A that can be waited upon.
Task CreatePersistentSubscriptionAsync(string stream, string groupName, PersistentSubscriptionSettings settings, UserCredentials credentials);
///
- /// Asynchronously delete a persistent subscription group on a stream
+ /// Asynchronously delete a persistent subscription group on a stream.
///
- /// The name of the stream to delete the persistent subscription on
- /// The name of the group to delete
- /// User credentials to use for the operation
+ /// The name of the stream to delete the persistent subscription on.
+ /// The name of the group to delete.
+ /// User credentials to use for the operation.
/// A that can be waited upon.
Task DeletePersistentSubscriptionAsync(string stream, string groupName, UserCredentials userCredentials = null);
@@ -554,7 +553,7 @@ EventStoreAllCatchUpSubscription SubscribeToAllFrom(
/// The name of the stream for which to set metadata.
/// The expected version for the write to the metadata stream.
/// A representing the new metadata.
- /// User credentials to use for the operation
+ /// User credentials to use for the operation.
/// A containing the results of the write operation.
Task SetStreamMetadataAsync(string stream, long expectedMetastreamVersion, StreamMetadata metadata, UserCredentials userCredentials = null);
diff --git a/src/EventStore.ClientAPI.NetCore/IEventStoreConnectionExtensions.cs b/src/EventStore.ClientAPI.NetCore/IEventStoreConnectionExtensions.cs
index f9dce42..af5f306 100644
--- a/src/EventStore.ClientAPI.NetCore/IEventStoreConnectionExtensions.cs
+++ b/src/EventStore.ClientAPI.NetCore/IEventStoreConnectionExtensions.cs
@@ -16,7 +16,7 @@ private static Func ToTask(Action
eventAppeared(subscription, e);
return Task.CompletedTask;
};
-
+
///
/// Asynchronously subscribes to a single event stream. New events
/// written to the stream while the subscription is active will be
@@ -214,7 +214,44 @@ public static EventStorePersistentSubscriptionBase ConnectToPersistentSubscripti
);
///
- /// Asynchronously subscribes to a persistent subscription(competing consumer) on event store
+ /// Subscribes to a persistent subscription(competing consumer) on event store
+ ///
+ /// The connection to subscribe to
+ /// The subscription group to connect to
+ /// The stream to subscribe to
+ /// An action invoked when a new event is received over the subscription
+ /// An action invoked if the subscription is dropped
+ /// User credentials to use for the operation
+ /// The buffer size to use for the persistent subscription
+ /// Whether the subscription should automatically acknowledge messages processed.
+ /// If not set the receiver is required to explicitly acknowledge messages through the subscription.
+ /// This will connect you to a persistent subscription group for a stream. The subscription group
+ /// must first be created with CreatePersistentSubscriptionAsync many connections
+ /// can connect to the same group and they will be treated as competing consumers within the group.
+ /// If one connection dies work will be balanced across the rest of the consumers in the group. If
+ /// you attempt to connect to a group that does not exist you will be given an exception.
+ ///
+ /// An representing the subscription
+ public static EventStorePersistentSubscriptionBase ConnectToPersistentSubscription(
+ this IEventStoreConnection target,
+ string stream,
+ string groupName,
+ Func eventAppeared,
+ Action subscriptionDropped = null,
+ UserCredentials userCredentials = null,
+ int bufferSize = 10,
+ bool autoAck = true) =>
+ target.ConnectToPersistentSubscription(
+ stream,
+ groupName,
+ (s,e, i) => eventAppeared(s,e),
+ subscriptionDropped,
+ userCredentials,
+ bufferSize,
+ autoAck
+ );
+ ///
+ /// Subscribes to a persistent subscription(competing consumer) on event store
///
/// The connection to subscribe to
/// The subscription group to connect to
@@ -251,6 +288,44 @@ public static Task ConnectToPersistentSubs
autoAck
);
+ ///
+ /// Subscribes to a persistent subscription(competing consumer) on event store
+ ///
+ /// The connection to subscribe to
+ /// The subscription group to connect to
+ /// The stream to subscribe to
+ /// An action invoked when a new event is received over the subscription
+ /// An action invoked if the subscription is dropped
+ /// User credentials to use for the operation
+ /// The buffer size to use for the persistent subscription
+ /// Whether the subscription should automatically acknowledge messages processed.
+ /// If not set the receiver is required to explicitly acknowledge messages through the subscription.
+ /// This will connect you to a persistent subscription group for a stream. The subscription group
+ /// must first be created with CreatePersistentSubscriptionAsync many connections
+ /// can connect to the same group and they will be treated as competing consumers within the group.
+ /// If one connection dies work will be balanced across the rest of the consumers in the group. If
+ /// you attempt to connect to a group that does not exist you will be given an exception.
+ ///
+ /// An representing the subscription
+ public static Task ConnectToPersistentSubscriptionAsync(
+ this IEventStoreConnection target,
+ string stream,
+ string groupName,
+ Func eventAppeared,
+ Action subscriptionDropped = null,
+ UserCredentials userCredentials = null,
+ int bufferSize = 10,
+ bool autoAck = true) =>
+ target.ConnectToPersistentSubscriptionAsync(
+ stream,
+ groupName,
+ (s,e, i) => eventAppeared(s,e),
+ subscriptionDropped,
+ userCredentials,
+ bufferSize,
+ autoAck
+ );
+
///
/// Subscribes to all events. Existing events from lastCheckpoint
/// onwards are read from the Event Store and presented to the user of
diff --git a/src/EventStore.ClientAPI.NetCore/Internal/ClusterDnsEndPointDiscoverer.cs b/src/EventStore.ClientAPI.NetCore/Internal/ClusterDnsEndPointDiscoverer.cs
index 7df33d9..f227ffb 100644
--- a/src/EventStore.ClientAPI.NetCore/Internal/ClusterDnsEndPointDiscoverer.cs
+++ b/src/EventStore.ClientAPI.NetCore/Internal/ClusterDnsEndPointDiscoverer.cs
@@ -23,7 +23,7 @@ internal class ClusterDnsEndPointDiscoverer: IEndPointDiscoverer
private readonly HttpAsyncClient _client;
private ClusterMessages.MemberInfoDto[] _oldGossip;
private TimeSpan _gossipTimeout;
- private readonly bool _preferRandomNode;
+ private readonly NodePreference _nodePreference;
public ClusterDnsEndPointDiscoverer(ILogger log,
string clusterDns,
@@ -31,7 +31,7 @@ public ClusterDnsEndPointDiscoverer(ILogger log,
int managerExternalHttpPort,
GossipSeed[] gossipSeeds,
TimeSpan gossipTimeout,
- bool preferRandomNode)
+ NodePreference nodePreference)
{
Ensure.NotNull(log, "log");
@@ -41,12 +41,15 @@ public ClusterDnsEndPointDiscoverer(ILogger log,
_managerExternalHttpPort = managerExternalHttpPort;
_gossipSeeds = gossipSeeds;
_gossipTimeout = gossipTimeout;
- _preferRandomNode = preferRandomNode;
+ _nodePreference = nodePreference;
_client = new HttpAsyncClient(_gossipTimeout);
}
public async Task DiscoverAsync(IPEndPoint failedTcpEndPoint )
{
+ var maxDiscoverAttemptsStr = "";
+ if(_maxDiscoverAttempts != Int32.MaxValue)
+ maxDiscoverAttemptsStr = "/" + _maxDiscoverAttempts;
for (int attempt = 1; attempt <= _maxDiscoverAttempts; ++attempt)
{
//_log.Info("Discovering cluster. Attempt {0}/{1}...", attempt, _maxDiscoverAttempts);
@@ -55,18 +58,19 @@ public async Task DiscoverAsync(IPEndPoint failedTcpEndPoint )
var endPoints = await DiscoverEndPoint(failedTcpEndPoint).ConfigureAwait(false);
if (endPoints != null)
{
- _log.Info("Discovering attempt {0}/{1} successful: best candidate is {2}.", attempt, _maxDiscoverAttempts, endPoints);
+ _log.Info("Discovering attempt {0}{1} successful: best candidate is {2}.", attempt, maxDiscoverAttemptsStr, endPoints);
return endPoints.Value;
}
_log.Info("Discovering attempt {0}/{1} failed: no candidate found.", attempt, _maxDiscoverAttempts);
+
}
catch (Exception exc)
{
- _log.Info("Discovering attempt {0}/{1} failed with error: {2}.", attempt, _maxDiscoverAttempts, exc);
+ _log.Info("Discovering attempt {0}{1} failed with error: {2}.", attempt, maxDiscoverAttemptsStr, exc);
}
- Thread.Sleep(500);
+ await Task.Delay(500).ConfigureAwait(false);
}
throw new ClusterException(string.Format("Failed to discover candidate in {0} attempts.", _maxDiscoverAttempts));
}
@@ -83,7 +87,7 @@ public async Task DiscoverAsync(IPEndPoint failedTcpEndPoint )
if (gossip == null || gossip.Members == null || gossip.Members.Length == 0)
continue;
- var bestNode = TryDetermineBestNode(gossip.Members, _preferRandomNode);
+ var bestNode = TryDetermineBestNode(gossip.Members, _nodePreference);
if (bestNode != null)
{
_oldGossip = gossip.Members;
@@ -210,7 +214,7 @@ private ClusterMessages.ClusterInfoDto TryGetGossipFrom(GossipSeed endPoint)
return result;
}
- private NodeEndPoints? TryDetermineBestNode(IEnumerable members, bool preferRandomNode)
+ private NodeEndPoints? TryDetermineBestNode(IEnumerable members, NodePreference nodePreference)
{
var notAllowedStates = new[]
{
@@ -222,14 +226,21 @@ private ClusterMessages.ClusterInfoDto TryGetGossipFrom(GossipSeed endPoint)
.Where(x => !notAllowedStates.Contains(x.State))
.OrderByDescending(x => x.State)
.ToArray();
- if (preferRandomNode)
+
+ switch (nodePreference)
{
- RandomShuffle(nodes, 0, nodes.Length - 1);
+ case NodePreference.Random:
+ RandomShuffle(nodes, 0, nodes.Length - 1);
+ break;
+ case NodePreference.Slave:
+ nodes = nodes.OrderBy(nodeEntry => nodeEntry.State != ClusterMessages.VNodeState.Slave).ToArray(); // OrderBy is a stable sort and only affects order of matching entries
+ RandomShuffle(nodes, 0, nodes.Count(nodeEntry => nodeEntry.State == ClusterMessages.VNodeState.Slave) - 1);
+ break;
}
var node = nodes.FirstOrDefault();
- if (node == null)
+ if (node == default(ClusterMessages.MemberInfoDto))
{
//_log.Info("Unable to locate suitable node. Gossip info:\n{0}.", string.Join("\n", members.Select(x => x.ToString())));
return null;
@@ -243,4 +254,4 @@ private ClusterMessages.ClusterInfoDto TryGetGossipFrom(GossipSeed endPoint)
return new NodeEndPoints(normTcp, secTcp);
}
}
-}
+}
\ No newline at end of file
diff --git a/src/EventStore.ClientAPI.NetCore/Internal/Consts.cs b/src/EventStore.ClientAPI.NetCore/Internal/Consts.cs
index e2566e2..23bc70a 100644
--- a/src/EventStore.ClientAPI.NetCore/Internal/Consts.cs
+++ b/src/EventStore.ClientAPI.NetCore/Internal/Consts.cs
@@ -12,6 +12,7 @@ internal static class Consts
public const bool DefaultRequireMaster = true;
public static readonly TimeSpan DefaultReconnectionDelay = TimeSpan.FromMilliseconds(100);
+ public static readonly TimeSpan DefaultQueueTimeout = TimeSpan.Zero; //unlimited
public static readonly TimeSpan DefaultOperationTimeout = TimeSpan.FromSeconds(7);
public static readonly TimeSpan DefaultOperationTimeoutCheckPeriod = TimeSpan.FromSeconds(1);
diff --git a/src/EventStore.ClientAPI.NetCore/Internal/EventStoreNodeConnection.cs b/src/EventStore.ClientAPI.NetCore/Internal/EventStoreNodeConnection.cs
index 46870bf..a9058f6 100644
--- a/src/EventStore.ClientAPI.NetCore/Internal/EventStoreNodeConnection.cs
+++ b/src/EventStore.ClientAPI.NetCore/Internal/EventStoreNodeConnection.cs
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
-using System.Threading;
using System.Threading.Tasks;
using EventStore.ClientAPI.ClientOperations;
using EventStore.ClientAPI.Common;
@@ -80,14 +79,15 @@ public Task DeleteStreamAsync(string stream, long expectedVersion,
return DeleteStreamAsync(stream, expectedVersion, false, userCredentials);
}
- public Task DeleteStreamAsync(string stream, long expectedVersion, bool hardDelete, UserCredentials userCredentials = null)
+ public async Task DeleteStreamAsync(string stream, long expectedVersion, bool hardDelete, UserCredentials userCredentials = null)
{
Ensure.NotNullOrEmpty(stream, "stream");
var source = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
- EnqueueOperation(new DeleteStreamOperation(Settings.Log, source, Settings.RequireMaster,
- stream, expectedVersion, hardDelete, userCredentials));
- return source.Task;
+ var operation = new DeleteStreamOperation(Settings.Log, source, Settings.RequireMaster,
+ stream, expectedVersion, hardDelete, userCredentials);
+ await EnqueueOperation(operation).ConfigureAwait(false);
+ return await source.Task.ConfigureAwait(false);
}
public Task AppendToStreamAsync(string stream, long expectedVersion, params EventData[] events)
@@ -106,20 +106,21 @@ public Task AppendToStreamAsync(string stream, long expectedVersion
// ReSharper restore RedundantCast
}
- public Task AppendToStreamAsync(string stream, long expectedVersion, IEnumerable events, UserCredentials userCredentials = null)
+ public async Task AppendToStreamAsync(string stream, long expectedVersion, IEnumerable events, UserCredentials userCredentials = null)
{
// ReSharper disable PossibleMultipleEnumeration
Ensure.NotNullOrEmpty(stream, "stream");
Ensure.NotNull(events, "events");
var source = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
- EnqueueOperation(new AppendToStreamOperation(Settings.Log, source, Settings.RequireMaster,
- stream, expectedVersion, events, userCredentials));
- return source.Task;
+ var operation = new AppendToStreamOperation(Settings.Log, source, Settings.RequireMaster,
+ stream, expectedVersion, events, userCredentials);
+ await EnqueueOperation(operation).ConfigureAwait(false);
+ return await source.Task.ConfigureAwait(false);
// ReSharper restore PossibleMultipleEnumeration
}
- public Task ConditionalAppendToStreamAsync(string stream, long expectedVersion, IEnumerable events,
+ public async Task ConditionalAppendToStreamAsync(string stream, long expectedVersion, IEnumerable events,
UserCredentials userCredentials = null)
{
// ReSharper disable PossibleMultipleEnumeration
@@ -127,20 +128,22 @@ public Task ConditionalAppendToStreamAsync(string stream
Ensure.NotNull(events, "events");
var source = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
- EnqueueOperation(new ConditionalAppendToStreamOperation(Settings.Log, source, Settings.RequireMaster,
- stream, expectedVersion, events, userCredentials));
- return source.Task;
+ var operation = new ConditionalAppendToStreamOperation(Settings.Log, source, Settings.RequireMaster,
+ stream, expectedVersion, events, userCredentials);
+ await EnqueueOperation(operation).ConfigureAwait(false);
+ return await source.Task.ConfigureAwait(false);
// ReSharper restore PossibleMultipleEnumeration
}
- public Task StartTransactionAsync(string stream, long expectedVersion, UserCredentials userCredentials = null)
+ public async Task StartTransactionAsync(string stream, long expectedVersion, UserCredentials userCredentials = null)
{
Ensure.NotNullOrEmpty(stream, "stream");
var source = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
- EnqueueOperation(new StartTransactionOperation(Settings.Log, source, Settings.RequireMaster,
- stream, expectedVersion, this, userCredentials));
- return source.Task;
+ var operation = new StartTransactionOperation(Settings.Log, source, Settings.RequireMaster,
+ stream, expectedVersion, this, userCredentials);
+ await EnqueueOperation(operation).ConfigureAwait(false);
+ return await source.Task.ConfigureAwait(false);
}
public EventStoreTransaction ContinueTransaction(long transactionId, UserCredentials userCredentials = null)
@@ -149,93 +152,95 @@ public EventStoreTransaction ContinueTransaction(long transactionId, UserCredent
return new EventStoreTransaction(transactionId, userCredentials, this);
}
- Task IEventStoreTransactionConnection.TransactionalWriteAsync(EventStoreTransaction transaction, IEnumerable events, UserCredentials userCredentials)
+ async Task IEventStoreTransactionConnection.TransactionalWriteAsync(EventStoreTransaction transaction, IEnumerable events, UserCredentials userCredentials)
{
// ReSharper disable PossibleMultipleEnumeration
Ensure.NotNull(transaction, "transaction");
Ensure.NotNull(events, "events");
var source = new TaskCompletionSource