diff --git a/src/EventStore.ClusterNode/Components/Pages/Cluster.razor b/src/EventStore.ClusterNode/Components/Pages/Cluster.razor index 47d3c0279e..e72be103d8 100644 --- a/src/EventStore.ClusterNode/Components/Pages/Cluster.razor +++ b/src/EventStore.ClusterNode/Components/Pages/Cluster.razor @@ -43,8 +43,7 @@ Status Timestamp (UTC) Checkpoints - TCP - HTTP + HTTP / gRPC Actions @@ -52,7 +51,7 @@ @if (!ClusterMembers.Any()) { - @ClusterEmptyMessage + @ClusterEmptyMessage } else @@ -77,10 +76,6 @@

@EpochLabel(member)

} - -

Internal: @InternalTcpEndpoint(member)

-

External: @ExternalTcpEndpoint(member)

- @HttpEndpoint(member)
@@ -307,7 +302,7 @@
- +
@@ -370,9 +365,7 @@ .Append("Snapshot taken at ") .Append(TimestampLabel(ClusterReadAt ?? DateTime.UtcNow)) .AppendLine() - .Append(PadRight("Internal Tcp", 31)).Append(' ') - .Append(PadRight("External Tcp", 31)).Append(' ') - .Append(PadRight("Http", 23)).Append(' ') + .Append(PadRight("HTTP / gRPC", 23)).Append(' ') .Append(PadRight("Status", 11)).Append(' ') .Append(PadRight("State", 18)).Append(' ') .Append(PadRight("Timestamp (UTC)", 19)).Append(" Checkpoints"); @@ -380,8 +373,6 @@ foreach (var member in ClusterMembers) { builder.AppendLine() - .Append(PadRight(InternalTcpEndpoint(member), 31)).Append(' ') - .Append(PadRight(ExternalTcpEndpoint(member), 31)).Append(' ') .Append(PadRight(HttpEndpoint(member), 23)).Append(' ') .Append(PadRight(MemberStatus(member), 11)).Append(' ') .Append(PadRight(member.State.ToString(), 18)).Append(' ') @@ -500,16 +491,6 @@ private static string MemberStatus(ClientClusterInfo.ClientMemberInfo member) => member.IsAlive ? "Alive" : "Unreachable"; - private static string InternalTcpEndpoint(ClientClusterInfo.ClientMemberInfo member) => - Endpoint( - member.InternalTcpIp, - member.InternalSecureTcpPort != 0 ? member.InternalSecureTcpPort : member.InternalTcpPort); - - private static string ExternalTcpEndpoint(ClientClusterInfo.ClientMemberInfo member) => - Endpoint( - member.ExternalTcpIp, - member.ExternalSecureTcpPort != 0 ? member.ExternalSecureTcpPort : member.ExternalTcpPort); - private static string HttpEndpoint(ClientClusterInfo.ClientMemberInfo member) => Endpoint(member.HttpEndPointIp, member.HttpEndPointPort); diff --git a/src/EventStore.ClusterNode/Components/Services/ClusterStatusService.cs b/src/EventStore.ClusterNode/Components/Services/ClusterStatusService.cs index 5c753987ec..78ca5f48ac 100644 --- a/src/EventStore.ClusterNode/Components/Services/ClusterStatusService.cs +++ b/src/EventStore.ClusterNode/Components/Services/ClusterStatusService.cs @@ -171,7 +171,7 @@ private ClusterReplicaRow ParseReplicaRow( : Guid.Empty; var totalBytesSent = row.TotalBytesSent; var previousRow = _previousReplicas.GetValueOrDefault(connectionId); - var replicaNode = FindMemberByInternalEndpoint(members, row.SubscriptionEndpoint); + var replicaNode = FindMemberByEndpoint(members, row.SubscriptionEndpoint); var isCatchingUp = replicaNode?.State == VNodeState.CatchingUp; var catchupStartTime = now; var catchupStartBytesSent = totalBytesSent; @@ -206,12 +206,12 @@ private ClusterReplicaRow ParseReplicaRow( private ClaimsPrincipal CurrentUser => httpContextAccessor.HttpContext?.User ?? new ClaimsPrincipal(new ClaimsIdentity()); - private static ClientClusterInfo.ClientMemberInfo FindMemberByInternalEndpoint( + private static ClientClusterInfo.ClientMemberInfo FindMemberByEndpoint( IReadOnlyList members, string endpoint) { var cleaned = endpoint.Replace("Unspecified/", "", StringComparison.OrdinalIgnoreCase); - return members.FirstOrDefault(x => string.Equals(InternalTcpEndpoint(x), cleaned, StringComparison.OrdinalIgnoreCase)); + return members.FirstOrDefault(x => string.Equals(HttpEndpoint(x), cleaned, StringComparison.OrdinalIgnoreCase)); } private static Uri BuildLeaderAddress( @@ -219,11 +219,6 @@ private static Uri BuildLeaderAddress( ClientClusterInfo.ClientMemberInfo leader) => new UriBuilder(request.Scheme, leader.HttpEndPointIp, leader.HttpEndPointPort).Uri; - private static string InternalTcpEndpoint(ClientClusterInfo.ClientMemberInfo member) => - Endpoint( - member.InternalTcpIp, - member.InternalSecureTcpPort != 0 ? member.InternalSecureTcpPort : member.InternalTcpPort); - private static string HttpEndpoint(ClientClusterInfo.ClientMemberInfo member) => Endpoint(member.HttpEndPointIp, member.HttpEndPointPort); diff --git a/src/EventStore.ClusterNode/Components/Services/NodeConnectionTracker.cs b/src/EventStore.ClusterNode/Components/Services/NodeConnectionTracker.cs new file mode 100644 index 0000000000..8b98cc7262 --- /dev/null +++ b/src/EventStore.ClusterNode/Components/Services/NodeConnectionTracker.cs @@ -0,0 +1,278 @@ +using System; +using System.Buffers; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO.Pipelines; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Connections; + +namespace EventStore.ClusterNode.Components.Services; + +public sealed class NodeConnectionTracker +{ + private readonly ConcurrentDictionary _connections = new(); + + public IReadOnlyList Snapshot() => + _connections.Values.Select(x => x.Snapshot()) + .OrderBy(x => x.RemoteEndPoint, StringComparer.OrdinalIgnoreCase) + .ThenBy(x => x.ConnectionId, StringComparer.Ordinal) + .ToArray(); + + public async Task Track(ConnectionContext context, ConnectionDelegate next, bool isTls) + { + var state = new NodeConnectionState( + context.ConnectionId, + context.RemoteEndPoint?.ToString() ?? "", + context.LocalEndPoint?.ToString() ?? "", + isTls, + DateTimeOffset.UtcNow); + _connections[context.ConnectionId] = state; + context.Transport = new CountingDuplexPipe(context.Transport, state); + + try + { + await next(context); + } + finally + { + _connections.TryRemove(context.ConnectionId, out _); + } + } + + public void ObserveRequest( + string connectionId, + string protocol, + bool isGrpc, + string connectionName, + string userAgent) + { + if (_connections.TryGetValue(connectionId, out var connection)) + connection.ObserveRequest(protocol, isGrpc, connectionName, userAgent); + } +} + +public sealed record NodeConnectionSnapshot( + string ConnectionId, + string RemoteEndPoint, + string LocalEndPoint, + string ClientName, + string Application, + string Protocol, + bool IsTls, + DateTimeOffset ConnectedAt, + long TotalBytesSent, + long TotalBytesReceived, + long PendingSendBytes, + long PendingReceivedBytes); + +internal sealed class NodeConnectionState +{ + private readonly object _metadataLock = new(); + private string _clientName = ""; + private bool _hasExplicitConnectionName; + private bool _hasGrpcRequests; + private bool _hasHttpRequests; + private long _pendingReceivedBytes; + private long _pendingSendBytes; + private string _protocol = ""; + private long _totalBytesReceived; + private long _totalBytesSent; + + public NodeConnectionState( + string connectionId, + string remoteEndPoint, + string localEndPoint, + bool isTls, + DateTimeOffset connectedAt) + { + ConnectionId = connectionId; + RemoteEndPoint = remoteEndPoint; + LocalEndPoint = localEndPoint; + IsTls = isTls; + ConnectedAt = connectedAt; + } + + private string ConnectionId { get; } + private string RemoteEndPoint { get; } + private string LocalEndPoint { get; } + private bool IsTls { get; } + private DateTimeOffset ConnectedAt { get; } + + public void Received(long bytes, long pendingBytes) + { + Interlocked.Add(ref _totalBytesReceived, bytes); + Interlocked.Exchange(ref _pendingReceivedBytes, pendingBytes); + } + + public void Reading(long pendingBytes) => + Interlocked.Exchange(ref _pendingReceivedBytes, pendingBytes); + + public void Sending(int bytes) + { + Interlocked.Add(ref _totalBytesSent, bytes); + Interlocked.Add(ref _pendingSendBytes, bytes); + } + + public void Sent() => Interlocked.Exchange(ref _pendingSendBytes, 0); + + public void ObserveRequest( + string protocol, + bool isGrpc, + string connectionName, + string userAgent) + { + lock (_metadataLock) + { + _protocol = Merge(_protocol, protocol); + _hasGrpcRequests |= isGrpc; + _hasHttpRequests |= !isGrpc; + + if (!string.IsNullOrWhiteSpace(connectionName)) + { + _clientName = connectionName; + _hasExplicitConnectionName = true; + } + else if (!_hasExplicitConnectionName && !string.IsNullOrWhiteSpace(userAgent)) + { + _clientName = userAgent; + } + } + } + + public NodeConnectionSnapshot Snapshot() + { + lock (_metadataLock) + { + return new( + ConnectionId, + RemoteEndPoint, + LocalEndPoint, + _clientName, + ApplicationLabel(), + _protocol, + IsTls, + ConnectedAt, + Interlocked.Read(ref _totalBytesSent), + Interlocked.Read(ref _totalBytesReceived), + Interlocked.Read(ref _pendingSendBytes), + Interlocked.Read(ref _pendingReceivedBytes)); + } + } + + private string ApplicationLabel() => (_hasHttpRequests, _hasGrpcRequests) switch + { + (true, true) => "HTTP and gRPC", + (false, true) => "gRPC", + (true, false) => "HTTP", + _ => "Awaiting request" + }; + + private static string Merge(string current, string observed) + { + if (string.IsNullOrWhiteSpace(observed) || current == observed) + return current; + return string.IsNullOrWhiteSpace(current) ? observed : "Mixed"; + } +} + +internal sealed class CountingDuplexPipe : IDuplexPipe +{ + public CountingDuplexPipe(IDuplexPipe inner, NodeConnectionState state) + { + Input = new CountingPipeReader(inner.Input, state); + Output = new CountingPipeWriter(inner.Output, state); + } + + public PipeReader Input { get; } + public PipeWriter Output { get; } +} + +internal sealed class CountingPipeReader : PipeReader +{ + private readonly PipeReader _inner; + private readonly NodeConnectionState _state; + private ReadOnlySequence _currentBuffer; + + public CountingPipeReader(PipeReader inner, NodeConnectionState state) + { + _inner = inner; + _state = state; + } + + public override void AdvanceTo(SequencePosition consumed) => AdvanceTo(consumed, consumed); + + public override void AdvanceTo(SequencePosition consumed, SequencePosition examined) + { + var consumedBytes = _currentBuffer.IsEmpty ? 0 : _currentBuffer.Slice(0, consumed).Length; + var pendingBytes = _currentBuffer.IsEmpty ? 0 : _currentBuffer.Slice(consumed).Length; + _state.Received(consumedBytes, pendingBytes); + _currentBuffer = default; + _inner.AdvanceTo(consumed, examined); + } + + public override void CancelPendingRead() => _inner.CancelPendingRead(); + + public override void Complete(Exception exception = null) => _inner.Complete(exception); + + public override ValueTask CompleteAsync(Exception exception = null) => _inner.CompleteAsync(exception); + + public override async ValueTask ReadAsync(CancellationToken cancellationToken = default) + { + var result = await _inner.ReadAsync(cancellationToken); + Observe(result); + return result; + } + + public override bool TryRead(out ReadResult result) + { + if (!_inner.TryRead(out result)) + return false; + + Observe(result); + return true; + } + + private void Observe(ReadResult result) + { + _currentBuffer = result.Buffer; + _state.Reading(result.Buffer.Length); + } +} + +internal sealed class CountingPipeWriter : PipeWriter +{ + private readonly PipeWriter _inner; + private readonly NodeConnectionState _state; + + public CountingPipeWriter(PipeWriter inner, NodeConnectionState state) + { + _inner = inner; + _state = state; + } + + public override void Advance(int bytes) + { + _state.Sending(bytes); + _inner.Advance(bytes); + } + + public override void CancelPendingFlush() => _inner.CancelPendingFlush(); + + public override void Complete(Exception exception = null) => _inner.Complete(exception); + + public override ValueTask CompleteAsync(Exception exception = null) => _inner.CompleteAsync(exception); + + public override async ValueTask FlushAsync(CancellationToken cancellationToken = default) + { + var result = await _inner.FlushAsync(cancellationToken); + if (!result.IsCanceled) + _state.Sent(); + return result; + } + + public override Memory GetMemory(int sizeHint = 0) => _inner.GetMemory(sizeHint); + + public override Span GetSpan(int sizeHint = 0) => _inner.GetSpan(sizeHint); +} diff --git a/src/EventStore.ClusterNode/Components/Services/ReplicationEndpointPolicy.cs b/src/EventStore.ClusterNode/Components/Services/ReplicationEndpointPolicy.cs new file mode 100644 index 0000000000..1c8c2f7fd3 --- /dev/null +++ b/src/EventStore.ClusterNode/Components/Services/ReplicationEndpointPolicy.cs @@ -0,0 +1,32 @@ +using System; +using System.Net; +using Microsoft.AspNetCore.Http; + +namespace EventStore.ClusterNode.Components.Services; + +public sealed class ReplicationEndpointPolicy(IPEndPoint listenEndPoint) +{ + private const string ReplicationServicePath = "/event_store.replication.Replication"; + + public bool Allows(HttpContext context) + { + var isReplicationConnection = Matches(context.Connection.LocalIpAddress, context.Connection.LocalPort); + var isReplicationRequest = context.Request.Path.StartsWithSegments( + ReplicationServicePath, + StringComparison.Ordinal); + + return isReplicationConnection == isReplicationRequest; + } + + private bool Matches(IPAddress localAddress, int localPort) + { + if (localPort != listenEndPoint.Port) + { + return false; + } + + return listenEndPoint.Address.Equals(IPAddress.Any) || + listenEndPoint.Address.Equals(IPAddress.IPv6Any) || + listenEndPoint.Address.Equals(localAddress); + } +} diff --git a/src/EventStore.ClusterNode/Program.cs b/src/EventStore.ClusterNode/Program.cs index 270308e3c5..6dbe3b2dbb 100644 --- a/src/EventStore.ClusterNode/Program.cs +++ b/src/EventStore.ClusterNode/Program.cs @@ -25,6 +25,7 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -273,6 +274,10 @@ async Task Run(ClusterVNodeHostedService hostedService, ManualResetEventSlim sig x.SuppressStatusMessages = true; }); + var nodeConnectionTracker = new NodeConnectionTracker(); + var replicationEndpointPolicy = new ReplicationEndpointPolicy( + new System.Net.IPEndPoint(options.Interface.ReplicationIp, options.Interface.ReplicationPort)); + builder.Services.AddSingleton(nodeConnectionTracker); builder.WebHost.ConfigureKestrel(server => { server.Limits.Http2.KeepAlivePingDelay = @@ -281,12 +286,15 @@ async Task Run(ClusterVNodeHostedService hostedService, ManualResetEventSlim sig TimeSpan.FromMilliseconds(options.Grpc.KeepAliveTimeout); server.Listen(options.Interface.NodeIp, options.Interface.NodePort, listenOptions => - ConfigureHttpOptions(listenOptions, hostedService, + ConfigureHttpOptions(listenOptions, hostedService, nodeConnectionTracker, useHttps: !hostedService.Node.DisableHttps)); + server.Listen(options.Interface.ReplicationIp, options.Interface.ReplicationPort, listenOptions => + ConfigureHttpOptions(listenOptions, hostedService, nodeConnectionTracker, + useHttps: !hostedService.Node.DisableHttps, http2Only: true)); if (hostedService.Node.EnableUnixSocket) { - TryListenOnUnixSocket(hostedService, server); + TryListenOnUnixSocket(hostedService, server, nodeConnectionTracker); } }); @@ -325,6 +333,29 @@ async Task Run(ClusterVNodeHostedService hostedService, ManualResetEventSlim sig builder.Services.AddSingleton(hostedService); var app = builder.Build(); + app.Use((context, next) => + { + var isGrpc = context.Request.ContentType?.StartsWith( + "application/grpc", + StringComparison.OrdinalIgnoreCase) == true; + nodeConnectionTracker.ObserveRequest( + context.Connection.Id, + context.Request.Protocol, + isGrpc, + context.Request.Headers["connection-name"].FirstOrDefault(), + context.Request.Headers.UserAgent.ToString()); + return next(context); + }); + app.Use(async (context, next) => + { + if (!replicationEndpointPolicy.Allows(context)) + { + context.Response.StatusCode = StatusCodes.Status404NotFound; + return; + } + + await next(context); + }); app.UseMiddleware(); hostedService.Node.Startup.Configure(app); if (oauthEnabled) @@ -376,21 +407,34 @@ async Task Run(ClusterVNodeHostedService hostedService, ManualResetEventSlim sig } } - private static void ConfigureHttpOptions(ListenOptions listenOptions, ClusterVNodeHostedService hostedService, - bool useHttps) + private static void ConfigureHttpOptions( + ListenOptions listenOptions, + ClusterVNodeHostedService hostedService, + NodeConnectionTracker connectionTracker, + bool useHttps, + bool http2Only = false) { + listenOptions.Use(next => context => connectionTracker.Track(context, next, useHttps)); + if (http2Only) + { + listenOptions.Protocols = HttpProtocols.Http2; + } + if (useHttps) { listenOptions.UseHttps(CreateServerOptionsSelectionCallback(hostedService), null); } - else + else if (!http2Only) { listenOptions.Use(next => new ClearTextHttpMultiplexingMiddleware(next).OnConnectAsync); } } - private static void TryListenOnUnixSocket(ClusterVNodeHostedService hostedService, KestrelServerOptions server) + private static void TryListenOnUnixSocket( + ClusterVNodeHostedService hostedService, + KestrelServerOptions server, + NodeConnectionTracker connectionTracker) { if (!RuntimeInformation.IsLinux && !OperatingSystem.IsWindowsVersionAtLeast(10, 0, 17063)) { @@ -421,7 +465,7 @@ private static void TryListenOnUnixSocket(ClusterVNodeHostedService hostedServic server.ListenUnixSocket(unixSocket, listenOptions => { listenOptions.Use(next => new UnixSocketConnectionMiddleware(next).OnConnectAsync); - ConfigureHttpOptions(listenOptions, hostedService, useHttps: false); + ConfigureHttpOptions(listenOptions, hostedService, connectionTracker, useHttps: false); }); Log.Information("Listening on UNIX domain socket: {unixSocket}", unixSocket); } diff --git a/src/EventStore.Core.Tests/ClientAPI/Helpers/EventDataComparer.cs b/src/EventStore.Core.Tests/ClientAPI/Helpers/EventDataComparer.cs deleted file mode 100644 index 15a424be6c..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/Helpers/EventDataComparer.cs +++ /dev/null @@ -1,46 +0,0 @@ -using EventStore.ClientAPI; -using EventStore.Common.Utils; - -namespace EventStore.Core.Tests.ClientAPI.Helpers; - -internal static class EventDataComparer -{ - public static bool Equal(EventData expected, RecordedEvent actual) - { - if (expected.EventId != actual.EventId) - { - return false; - } - - if (expected.Type != actual.EventType) - { - return false; - } - - var expectedDataString = Helper.UTF8NoBom.GetString(expected.Data ?? new byte[0]); - var expectedMetadataString = Helper.UTF8NoBom.GetString(expected.Metadata ?? new byte[0]); - - var actualDataString = Helper.UTF8NoBom.GetString(actual.Data ?? new byte[0]); - var actualMetadataDataString = Helper.UTF8NoBom.GetString(actual.Metadata ?? new byte[0]); - - return expectedDataString == actualDataString && expectedMetadataString == actualMetadataDataString; - } - - public static bool Equal(EventData[] expected, RecordedEvent[] actual) - { - if (expected.Length != actual.Length) - { - return false; - } - - for (var i = 0; i < expected.Length; i++) - { - if (!Equal(expected[i], actual[i])) - { - return false; - } - } - - return true; - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/Helpers/EventsStream.cs b/src/EventStore.Core.Tests/ClientAPI/Helpers/EventsStream.cs deleted file mode 100644 index da4bb62ce2..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/Helpers/EventsStream.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Threading.Tasks; -using EventStore.ClientAPI; - -namespace EventStore.Core.Tests.ClientAPI.Helpers; - -internal class EventsStream -{ - private const int SliceSize = 10; - - public static async Task Count(IEventStoreConnection store, string stream) - { - var result = 0; - while (true) - { - var slice = await store.ReadStreamEventsForwardAsync(stream, result, SliceSize, false); - result += slice.Events.Length; - if (slice.IsEndOfStream) - { - break; - } - } - - return result; - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/Helpers/TcpType.cs b/src/EventStore.Core.Tests/ClientAPI/Helpers/TcpType.cs deleted file mode 100644 index b860eb63ac..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/Helpers/TcpType.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace EventStore.Core.Tests.ClientAPI.Helpers; - -public enum TcpType -{ - Normal, - Ssl -} diff --git a/src/EventStore.Core.Tests/ClientAPI/Helpers/TestConnection.cs b/src/EventStore.Core.Tests/ClientAPI/Helpers/TestConnection.cs deleted file mode 100644 index 674773889c..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/Helpers/TestConnection.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System; -using System.Net; -using System.Threading; -using EventStore.ClientAPI; -using EventStore.ClientAPI.Internal; -using EventStore.ClientAPI.SystemData; -using EventStore.Core.Tests.Helpers; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI.Helpers; - -public static class TestConnection -{ - private static int _nextConnId = -1; - - public static IEventStoreConnection Create(IPEndPoint endPoint, TcpType tcpType = TcpType.Ssl, - UserCredentials userCredentials = null) - { - return EventStoreConnection.Create(Settings(tcpType, userCredentials), - endPoint.ToESTcpUri(), - $"ESC-{Interlocked.Increment(ref _nextConnId)}"); - } - - public static IEventStoreConnection CreateMiniNodeClient(IPEndPoint endPoint, TcpType tcpType = TcpType.Ssl, - UserCredentials userCredentials = null) - { - return EventStoreConnection.Create(Settings( - tcpType, - userCredentials, - limitAttemptsForOperationTo: 10, - reconnectionDelay: TimeSpan.FromMilliseconds(100)), - endPoint.ToESTcpUri(), - $"ESC-{Interlocked.Increment(ref _nextConnId)}"); - } - - public static IEventStoreConnection To(MiniNode miniNode, TcpType tcpType, - UserCredentials userCredentials = null) - { - return EventStoreConnection.Create(Settings(tcpType, userCredentials), - miniNode.TcpEndPoint.ToESTcpUri(), - $"ESC-{Interlocked.Increment(ref _nextConnId)}"); - } - - private static ConnectionSettingsBuilder Settings( - TcpType tcpType, - UserCredentials userCredentials, - int limitAttemptsForOperationTo = 1, - TimeSpan? reconnectionDelay = null) - { - var settings = ConnectionSettings.Create() - .SetDefaultUserCredentials(userCredentials) - .UseCustomLogger(ClientApiLoggerBridge.Default) - .EnableVerboseLogging() - .LimitReconnectionsTo(10) - .LimitAttemptsForOperationTo(limitAttemptsForOperationTo) - .SetTimeoutCheckPeriodTo(TimeSpan.FromMilliseconds(100)) - .SetReconnectionDelayTo(reconnectionDelay ?? TimeSpan.Zero) - .FailOnNoServerResponse() - //.SetOperationTimeoutTo(TimeSpan.FromDays(1)) - ; - if (tcpType == TcpType.Ssl) - { - settings.DisableServerCertificateValidation(); - } - else - { - settings.DisableTls(); - } - - return settings; - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/Helpers/TestConnectionLifecycle.cs b/src/EventStore.Core.Tests/ClientAPI/Helpers/TestConnectionLifecycle.cs deleted file mode 100644 index 9ea1365f78..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/Helpers/TestConnectionLifecycle.cs +++ /dev/null @@ -1,77 +0,0 @@ -using System; -using System.Threading.Tasks; -using EventStore.ClientAPI; - -namespace EventStore.Core.Tests.ClientAPI.Helpers; - -public static class TestConnectionLifecycle -{ - public static async Task ReconnectUntilReady( - Func createConnection, - Func readinessProbe, - TimeSpan timeout) - { - var deadline = DateTime.UtcNow + timeout; - - while (true) - { - IEventStoreConnection connection = null; - - try - { - connection = createConnection(); - await connection.ConnectAsync(); - await readinessProbe(connection); - return connection; - } - catch (Exception ex) - { - if (connection != null) - { - TryCloseConnection(connection); - } - - if (IsTransientConnectionFailure(ex) && DateTime.UtcNow < deadline) - { - await Task.Delay(250); - continue; - } - - throw; - } - } - } - - public static async Task CloseConnectionAndWait(IEventStoreConnection connection, TimeSpan timeout) - { - var closed = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - connection.Closed += (_, _) => closed.TrySetResult(); - connection.Close(); - await closed.Task.WithTimeout(timeout); - } - - public static bool IsTransientConnectionFailure(Exception ex) => - ex.GetType().Name is "ConnectionClosedException" - or "RetriesLimitReachedException" - or "NotAuthenticatedException" - or "AccessDeniedException"; - - public static void TryCloseConnection(IEventStoreConnection connection) - { - try - { - connection.Close(); - } - catch - { - } - } - - public static void DisposeIfNeeded(object candidate) - { - if (candidate is IDisposable disposable) - { - disposable.Dispose(); - } - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/Helpers/TestEvent.cs b/src/EventStore.Core.Tests/ClientAPI/Helpers/TestEvent.cs deleted file mode 100644 index 8fb92bedf3..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/Helpers/TestEvent.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using System.Text; -using EventStore.ClientAPI; -using EventStore.Common.Utils; - -namespace EventStore.Core.Tests.ClientAPI.Helpers; - -public class TestEvent -{ - public static EventData NewTestEvent(string data = null, string metadata = null, string eventName = "TestEvent") - { - return NewTestEvent(Guid.NewGuid(), data, metadata, eventName); - } - - public static EventData NewTestEvent(Guid eventId, string data = null, string metadata = null, string eventName = "TestEvent") - { - var encodedData = Helper.UTF8NoBom.GetBytes(data ?? eventId.ToString()); - var encodedMetadata = Helper.UTF8NoBom.GetBytes(metadata ?? "metadata"); - - return new EventData(eventId, eventName, false, encodedData, encodedMetadata); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/Helpers/Writer.cs b/src/EventStore.Core.Tests/ClientAPI/Helpers/Writer.cs deleted file mode 100644 index 236e47ccb6..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/Helpers/Writer.cs +++ /dev/null @@ -1,98 +0,0 @@ -using System.Threading.Tasks; -using EventStore.ClientAPI; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI.Helpers; - -internal class StreamWriter -{ - private readonly IEventStoreConnection _store; - private readonly string _stream; - private readonly long _version; - - public StreamWriter(IEventStoreConnection store, string stream, long version) - { - _store = store; - _stream = stream; - _version = version; - } - - public async Task Append(params EventData[] events) - { - for (var i = 0; i < events.Length; i++) - { - var expVer = _version == ExpectedVersion.Any ? ExpectedVersion.Any : _version + i; - var nextExpVer = (await _store.AppendToStreamAsync(_stream, expVer, new[] { events[i] })).NextExpectedVersion; - if (_version != ExpectedVersion.Any) - { - Assert.AreEqual(expVer + 1, nextExpVer); - } - } - - return new TailWriter(_store, _stream); - } -} - -internal class TailWriter -{ - private readonly IEventStoreConnection _store; - private readonly string _stream; - - public TailWriter(IEventStoreConnection store, string stream) - { - _store = store; - _stream = stream; - } - - public async Task Then(EventData @event, long expectedVersion) - { - await _store.AppendToStreamAsync(_stream, expectedVersion, new[] { @event }); - return this; - } -} - -internal class TransactionalWriter -{ - private readonly IEventStoreConnection _store; - private readonly string _stream; - - public TransactionalWriter(IEventStoreConnection store, string stream) - { - _store = store; - _stream = stream; - } - - public async Task StartTransaction(long expectedVersion) - { - return new OngoingTransaction(await _store.StartTransactionAsync(_stream, expectedVersion)); - } - - public OngoingTransaction ContinueTransaction(long transactionId) - { - return new OngoingTransaction(_store.ContinueTransaction(transactionId)); - } -} - -//TODO GFY this should be removed and merged with the public idea of a transaction. -internal class OngoingTransaction -{ - private readonly EventStoreTransaction _transaction; - - public long TransactionId => _transaction.TransactionId; - - public OngoingTransaction(EventStoreTransaction transaction) - { - _transaction = transaction; - } - - public async Task Write(params EventData[] events) - { - await _transaction.WriteAsync(events); - return this; - } - - public Task Commit() - { - return _transaction.CommitAsync(); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/SpecificationWithMiniNode.cs b/src/EventStore.Core.Tests/ClientAPI/SpecificationWithMiniNode.cs deleted file mode 100644 index ddd01c25d0..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/SpecificationWithMiniNode.cs +++ /dev/null @@ -1,102 +0,0 @@ -using System; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.Core.Tests.ClientAPI.Helpers; -using EventStore.Core.Tests.Helpers; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI; - -public abstract class SpecificationWithMiniNode : SpecificationWithDirectoryPerTestFixture -{ - private readonly int _chunkSize; - protected MiniNode _node; - protected IEventStoreConnection _conn; - protected virtual TimeSpan Timeout { get; } = TimeSpan.FromMinutes(1); - protected virtual TimeSpan StartupTimeout => TimeSpan.FromMinutes(5); - - protected virtual Task Given() => Task.CompletedTask; - - protected abstract Task When(); - - protected virtual IEventStoreConnection BuildConnection(MiniNode node) - { - return TestConnection.CreateMiniNodeClient(node.TcpEndPoint, TcpType.Ssl); - } - - protected Task CloseConnectionAndWait(IEventStoreConnection connection) => - TestConnectionLifecycle.CloseConnectionAndWait(connection, Timeout); - - protected SpecificationWithMiniNode() : this(chunkSize: 1024 * 1024) { } - - protected SpecificationWithMiniNode(int chunkSize) - { - _chunkSize = chunkSize; - } - - [OneTimeSetUp] - public override async Task TestFixtureSetUp() - { - - MiniNodeLogging.Setup(); - - try - { - await base.TestFixtureSetUp(); - } - catch (Exception ex) - { - throw new Exception("TestFixtureSetUp Failed", ex); - } - - try - { - _node = new MiniNode(PathName, chunkSize: _chunkSize); - await _node.Start(StartupTimeout); - await _node.WaitForTcpEndPoint().WithTimeout(StartupTimeout); - _conn = await TestConnectionLifecycle.ReconnectUntilReady( - () => BuildConnection(_node), - connection => connection.ReadAllEventsForwardAsync(Position.Start, 1, false, DefaultData.AdminCredentials), - StartupTimeout); - } - catch (Exception ex) - { - MiniNodeLogging.WriteLogs(); - throw new Exception("MiniNodeSetUp Failed", ex); - } - - try - { - await Given().WithTimeout(Timeout); - } - catch (Exception ex) - { - MiniNodeLogging.WriteLogs(); - throw new Exception("Given Failed", ex); - } - - try - { - await When().WithTimeout(Timeout); - } - catch (Exception ex) - { - MiniNodeLogging.WriteLogs(); - throw new Exception("When Failed", ex); - } - } - - [OneTimeTearDown] - public override async Task TestFixtureTearDown() - { - if (_conn != null) - { - await TestConnectionLifecycle.CloseConnectionAndWait(_conn, Timeout); - } - - await _node.Shutdown(); - await base.TestFixtureTearDown(); - - MiniNodeLogging.Clear(); - } -} diff --git a/src/EventStore.Core.Tests/Cluster/MemberInfoTests.cs b/src/EventStore.Core.Tests/Cluster/MemberInfoTests.cs index 14d94644ce..b2bf2b68ea 100644 --- a/src/EventStore.Core.Tests/Cluster/MemberInfoTests.cs +++ b/src/EventStore.Core.Tests/Cluster/MemberInfoTests.cs @@ -1,5 +1,6 @@ using System; using System.Net; +using System.Reflection; using EventStore.Core.Data; using NUnit.Framework; @@ -16,12 +17,7 @@ public void member_with_dns_endpoint_should_equal() var memberWithDnsEndPoint = EventStore.Core.Cluster.MemberInfo.Initial(Guid.Empty, DateTime.UtcNow, VNodeState.Unknown, true, new DnsEndPoint(ipAddress, port), - new DnsEndPoint(ipAddress, port), - new DnsEndPoint(ipAddress, port), - new DnsEndPoint(ipAddress, port), - new DnsEndPoint(ipAddress, port), - null, 0, 0, - 0, false); + null, 0, 0, false); var ipEndPoint = new IPEndPoint(IPAddress.Parse(ipAddress), port); var dnsEndPoint = new DnsEndPoint(ipAddress, port); @@ -38,11 +34,7 @@ public void member_with_ip_endpoint_should_equal() var memberWithDnsEndPoint = EventStore.Core.Cluster.MemberInfo.Initial(Guid.Empty, DateTime.UtcNow, VNodeState.Unknown, true, new IPEndPoint(IPAddress.Parse(ipAddress), port), - new IPEndPoint(IPAddress.Parse(ipAddress), port), - new IPEndPoint(IPAddress.Parse(ipAddress), port), - new IPEndPoint(IPAddress.Parse(ipAddress), port), - new IPEndPoint(IPAddress.Parse(ipAddress), port), - null, 0, 0, 0, false); + null, 0, 0, false); var ipEndPoint = new IPEndPoint(IPAddress.Parse(ipAddress), port); var dnsEndPoint = new DnsEndPoint(ipAddress, port); @@ -50,4 +42,61 @@ public void member_with_ip_endpoint_should_equal() Assert.True(memberWithDnsEndPoint.Is(ipEndPoint)); Assert.True(memberWithDnsEndPoint.Is(dnsEndPoint)); } + + [Test] + public void internal_gossip_round_trip_preserves_the_grpc_replication_endpoint() + { + var replicationEndPoint = new DnsEndPoint("replication-node", 1112); + var httpEndPoint = new DnsEndPoint("public-node", 2113); + var member = EventStore.Core.Cluster.MemberInfo.Initial( + Guid.NewGuid(), + DateTime.UtcNow, + VNodeState.Unknown, + true, + httpEndPoint, + null, + 0, + 0, + false, + replicationEndPoint: replicationEndPoint); + + var grpc = ToGrpcClusterInfo(new EventStore.Core.Cluster.ClusterInfo(member)); + var roundTrip = FromGrpcClusterInfo(grpc); + + Assert.That(roundTrip.Members, Has.Length.EqualTo(1)); + Assert.That(roundTrip.Members[0].HttpEndPoint, Is.EqualTo(httpEndPoint)); + Assert.That(roundTrip.Members[0].ReplicationEndPoint, Is.EqualTo(replicationEndPoint)); + } + + [Test] + public void member_without_a_replication_endpoint_uses_its_http_endpoint() + { + var httpEndPoint = new DnsEndPoint("mixed-version-node", 2113); + var member = EventStore.Core.Cluster.MemberInfo.Initial( + Guid.NewGuid(), + DateTime.UtcNow, + VNodeState.Unknown, + true, + httpEndPoint, + null, + 0, + 0, + false); + var grpc = ToGrpcClusterInfo(new EventStore.Core.Cluster.ClusterInfo(member)); + grpc.Members[0].ReplicationEndPoint = null; + + var roundTrip = FromGrpcClusterInfo(grpc); + + Assert.That(roundTrip.Members[0].ReplicationEndPoint, Is.EqualTo(httpEndPoint)); + } + + private static EventStore.Cluster.ClusterInfo ToGrpcClusterInfo(EventStore.Core.Cluster.ClusterInfo clusterInfo) => + (EventStore.Cluster.ClusterInfo)typeof(EventStore.Core.Cluster.ClusterInfo) + .GetMethod("ToGrpcClusterInfo", BindingFlags.Static | BindingFlags.NonPublic) + .Invoke(null, [clusterInfo]); + + private static EventStore.Core.Cluster.ClusterInfo FromGrpcClusterInfo(EventStore.Cluster.ClusterInfo clusterInfo) => + (EventStore.Core.Cluster.ClusterInfo)typeof(EventStore.Core.Cluster.ClusterInfo) + .GetMethod("FromGrpcClusterInfo", BindingFlags.Static | BindingFlags.NonPublic) + .Invoke(null, [clusterInfo, null]); } diff --git a/src/EventStore.Core.Tests/DefaultData.cs b/src/EventStore.Core.Tests/DefaultData.cs index 3c647b08fc..262f7312dc 100644 --- a/src/EventStore.Core.Tests/DefaultData.cs +++ b/src/EventStore.Core.Tests/DefaultData.cs @@ -1,5 +1,4 @@ using System.Net; -using EventStore.ClientAPI.SystemData; using EventStore.Core.Services; namespace EventStore.Core.Tests; @@ -8,7 +7,6 @@ public class DefaultData { public static string AdminUsername = SystemUsers.Admin; public static string AdminPassword = SystemUsers.DefaultAdminPassword; - public static UserCredentials AdminCredentials = new UserCredentials(AdminUsername, AdminPassword); public static NetworkCredential AdminNetworkCredentials = new NetworkCredential(AdminUsername, AdminPassword); public static ClusterVNodeOptions.DefaultUserOptions DefaultUserOptions = new ClusterVNodeOptions.DefaultUserOptions() { diff --git a/src/EventStore.Core.Tests/Helpers/ClientApiLoggerBridge.cs b/src/EventStore.Core.Tests/Helpers/ClientApiLoggerBridge.cs deleted file mode 100644 index 862b03493f..0000000000 --- a/src/EventStore.Core.Tests/Helpers/ClientApiLoggerBridge.cs +++ /dev/null @@ -1,92 +0,0 @@ -using System; -using EventStore.Common.Utils; -using ILogger = Serilog.ILogger; - -namespace EventStore.Core.Tests.Helpers; - -public class ClientApiLoggerBridge : EventStore.ClientAPI.ILogger -{ - public static readonly ClientApiLoggerBridge Default = - new ClientApiLoggerBridge(Serilog.Log.ForContext(Serilog.Core.Constants.SourceContextPropertyName, - "client-api")); - - private readonly Serilog.ILogger _log; - - public ClientApiLoggerBridge(ILogger log) - { - Ensure.NotNull(log, "log"); - _log = log; - } - - public void Error(string format, params object[] args) - { - if (args.Length == 0) - { - _log.Error(format); - } - else - { - _log.Error(format, args); - } - } - - public void Error(Exception ex, string format, params object[] args) - { - if (args.Length == 0) - { - _log.Error(ex, format); - } - else - { - _log.Error(ex, format, args); - } - } - - public void Info(string format, params object[] args) - { - if (args.Length == 0) - { - _log.Information(format); - } - else - { - _log.Information(format, args); - } - } - - public void Info(Exception ex, string format, params object[] args) - { - if (args.Length == 0) - { - _log.Information(ex, format); - } - else - { - _log.Information(ex, format, args); - } - } - - public void Debug(string format, params object[] args) - { - if (args.Length == 0) - { - _log.Debug(format); - } - else - { - _log.Debug(format, args); - } - } - - public void Debug(Exception ex, string format, params object[] args) - { - if (args.Length == 0) - { - _log.Debug(ex, format); - } - else - { - _log.Debug(ex, format, args); - } - } -} diff --git a/src/EventStore.Core.Tests/Helpers/MiniClusterNode.cs b/src/EventStore.Core.Tests/Helpers/MiniClusterNode.cs index 444ab592d2..d5b3f61487 100644 --- a/src/EventStore.Core.Tests/Helpers/MiniClusterNode.cs +++ b/src/EventStore.Core.Tests/Helpers/MiniClusterNode.cs @@ -20,11 +20,10 @@ using EventStore.Core.Services.Monitoring; using EventStore.Core.Services.PersistentSubscription.ConsumerStrategy; using EventStore.Core.Services.Storage.ReaderIndex; -using EventStore.Core.Tests.Services.Transport.Tcp; using EventStore.Core.TransactionLog.Chunks; using EventStore.Plugins.Subsystems; -using EventStore.TcpUnitTestPlugin; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.AspNetCore.Server.Kestrel.Https; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; @@ -42,9 +41,8 @@ public class MiniClusterNode private static readonly ILogger Log = Serilog.Log.ForContext>(); - public IPEndPoint InternalTcpEndPoint { get; } - public IPEndPoint ExternalTcpEndPoint { get; } public IPEndPoint HttpEndPoint { get; } + public IPEndPoint ReplicationEndPoint { get; } public readonly int DebugIndex; @@ -62,11 +60,11 @@ public class MiniClusterNode public VNodeState NodeState = VNodeState.Unknown; private readonly IHost _host; - public MiniClusterNode(string pathname, int debugIndex, IPEndPoint internalTcp, IPEndPoint externalTcp, - IPEndPoint httpEndPoint, EndPoint[] gossipSeeds, ISubsystem[] subsystems = null, + public MiniClusterNode(string pathname, int debugIndex, IPEndPoint nodeEndPoint, IPEndPoint replicationEndPoint, + EndPoint[] gossipSeeds, ISubsystem[] subsystems = null, bool enableTrustedAuth = false, int memTableSize = 1000, bool disableFlushToDisk = false, bool readOnlyReplica = false, int nodePriority = 0, - string intHostAdvertiseAs = null, IExpiryStrategy expiryStrategy = null, + IExpiryStrategy expiryStrategy = null, ArchiveOptions archiveOptions = null, bool archiver = false, int clusterSize = 3, bool unsafeAllowSurplusNodes = false) { @@ -75,19 +73,17 @@ public MiniClusterNode(string pathname, int debugIndex, IPEndPoint internalTcp, RunCount += 1; DebugIndex = debugIndex; - InternalTcpEndPoint = internalTcp; - ExternalTcpEndPoint = externalTcp; - HttpEndPoint = httpEndPoint; + HttpEndPoint = nodeEndPoint; + ReplicationEndPoint = replicationEndPoint; _dbPath = Path.Combine( pathname, - $"mini-cluster-node-db-{externalTcp.Port}-{httpEndPoint.Port}"); + $"mini-cluster-node-db-{nodeEndPoint.Port}"); Directory.CreateDirectory(_dbPath); FileStreamExtensions.ConfigureFlush(disableFlushToDisk); subsystems ??= []; - subsystems = [.. subsystems, new TcpApiTestPlugin()]; var options = new ClusterVNodeOptions { @@ -117,14 +113,11 @@ public MiniClusterNode(string pathname, int debugIndex, IPEndPoint internalTcp, }, Interface = new() { - ReplicationIp = InternalTcpEndPoint.Address, - NodeIp = ExternalTcpEndPoint.Address, - ReplicationPort = InternalTcpEndPoint.Port, + NodeIp = HttpEndPoint.Address, NodePort = HttpEndPoint.Port, - ReplicationHeartbeatTimeout = 2_000, - ReplicationHeartbeatInterval = 2_000, - EnableTrustedAuth = enableTrustedAuth, - ReplicationHostAdvertiseAs = intHostAdvertiseAs + ReplicationIp = ReplicationEndPoint.Address, + ReplicationPort = ReplicationEndPoint.Port, + EnableTrustedAuth = enableTrustedAuth }, Database = new() { @@ -149,14 +142,7 @@ public MiniClusterNode(string pathname, int debugIndex, IPEndPoint internalTcp, PlugableComponents = subsystems }; - var configuration = new List> { - new("EventStore:TcpPlugin:NodeTcpPort", externalTcp.Port.ToString()), - new("EventStore:TcpPlugin:EnableExternalTcp", "true"), - new("EventStore:TcpUnitTestPlugin:NodeTcpPort", externalTcp.Port.ToString()), - new("EventStore:TcpUnitTestPlugin:NodeHeartbeatInterval", "10000"), - new("EventStore:TcpUnitTestPlugin:NodeHeartbeatTimeout", "10000"), - new("EventStore:TcpUnitTestPlugin:Insecure", options.Application.Insecure.ToString()), - }; + var configuration = new List>(); if (archiveOptions is not null) { @@ -176,9 +162,9 @@ public MiniClusterNode(string pathname, int debugIndex, IPEndPoint internalTcp, var inMemConf = new ConfigurationBuilder() .AddInMemoryCollection(configuration) .Build(); - var serverCertificate = ssl_connections.GetServerCertificate(); + var serverCertificate = TestCertificates.GetServerCertificate(); var trustedRootCertificates = - new X509Certificate2Collection(ssl_connections.GetRootCertificate()); + new X509Certificate2Collection(TestCertificates.GetRootCertificate()); options = options.Secure(trustedRootCertificates, serverCertificate); _isReadOnlyReplica = readOnlyReplica; @@ -191,8 +177,8 @@ public MiniClusterNode(string pathname, int debugIndex, IPEndPoint internalTcp, RuntimeInformation.RuntimeMode, "GC:", GC.MaxGeneration == 0 ? "NON-GENERATION (PROBABLY BOEHM)" - : $"{GC.MaxGeneration + 1} GENERATIONS", "DBPATH:", _dbPath, "ExTCP ENDPOINT:", - ExternalTcpEndPoint, "ExHTTP ENDPOINT:", HttpEndPoint); + : $"{GC.MaxGeneration + 1} GENERATIONS", "DBPATH:", _dbPath, "NODE ENDPOINT:", + HttpEndPoint, "HTTP ENDPOINT:", HttpEndPoint); var logFormatFactory = LogFormatHelper.LogFormatFactory; Node = new ClusterVNode(options, logFormatFactory, new AuthenticationProviderFactory( @@ -215,7 +201,7 @@ public MiniClusterNode(string pathname, int debugIndex, IPEndPoint internalTcp, webHost .UseKestrel(o => { - o.Listen(HttpEndPoint, options => + void ConfigureHttps(ListenOptions options) { options.UseHttps(new HttpsConnectionAdapterOptions { @@ -233,7 +219,10 @@ public MiniClusterNode(string pathname, int debugIndex, IPEndPoint internalTcp, return isValid; } }); - }); + } + + o.Listen(HttpEndPoint, ConfigureHttps); + o.Listen(ReplicationEndPoint, ConfigureHttps); }) .UseStartup(Node.Startup); }) diff --git a/src/EventStore.Core.Tests/Helpers/MiniNode.cs b/src/EventStore.Core.Tests/Helpers/MiniNode.cs index 9a41dc772b..95dd4d7d5e 100644 --- a/src/EventStore.Core.Tests/Helpers/MiniNode.cs +++ b/src/EventStore.Core.Tests/Helpers/MiniNode.cs @@ -5,7 +5,6 @@ using System.Linq; using System.Net; using System.Net.Http; -using System.Net.Sockets; using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; using EventStore.Common.Utils; @@ -20,13 +19,11 @@ using EventStore.Core.Services.Monitoring; using EventStore.Core.Services.Storage.ReaderIndex; using EventStore.Core.Tests.Index.Hashers; -using EventStore.Core.Tests.Services.Transport.Tcp; using EventStore.Core.TransactionLog.Chunks; using EventStore.Plugins.Authentication; using EventStore.Plugins.Authorization; using EventStore.Plugins.Subsystems; using EventStore.Plugins.Transforms; -using EventStore.TcpUnitTestPlugin; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.AspNetCore.Server.Kestrel.Https; @@ -45,8 +42,6 @@ public class MiniNode public const int CachedChunkSize = ChunkSize + ChunkHeader.Size + ChunkFooter.Size; protected static readonly ILogger Log = Serilog.Log.ForContext(); - public IPEndPoint TcpEndPoint { get; protected set; } - public IPEndPoint IntTcpEndPoint { get; protected set; } public IPEndPoint HttpEndPoint { get; protected set; } } @@ -71,7 +66,7 @@ public class MiniNode : MiniNode, IAsyncDisposable public Task AdminUserCreated => _adminUserCreated.Task; public MiniNode(string pathname, - int? tcpPort = null, int? httpPort = null, + int? httpPort = null, ISubsystem[] subsystems = null, int chunkSize = ChunkSize, int cachedChunkSize = CachedChunkSize, bool enableTrustedAuth = false, int memTableSize = 1000, @@ -97,26 +92,21 @@ public MiniNode(string pathname, var ip = IPAddress.Loopback; - int extTcpPort = tcpPort ?? PortsHelper.GetAvailablePort(ip); int httpEndPointPort = httpPort ?? PortsHelper.GetAvailablePort(ip); - int intTcpPort = PortsHelper.GetAvailablePort(ip); if (string.IsNullOrEmpty(dbPath)) { DbPath = Path.Combine(pathname, - $"mini-node-db-{extTcpPort}-{httpEndPointPort}"); + $"mini-node-db-{httpEndPointPort}"); } else { DbPath = dbPath; } - TcpEndPoint = new IPEndPoint(ip, extTcpPort); - IntTcpEndPoint = new IPEndPoint(ip, intTcpPort); HttpEndPoint = new IPEndPoint(ip, httpEndPointPort); subsystems ??= []; - subsystems = [.. subsystems, new TcpApiTestPlugin()]; var options = new ClusterVNodeOptions { @@ -130,8 +120,6 @@ public MiniNode(string pathname, }, Interface = new() { - ReplicationHeartbeatInterval = 10_000, - ReplicationHeartbeatTimeout = 10_000, EnableTrustedAuth = enableTrustedAuth }, Cluster = new() @@ -162,21 +150,11 @@ public MiniNode(string pathname, LoadedOptions = ClusterVNodeOptions.GetLoadedOptions(new ConfigurationBuilder() .AddEventStoreDefaultValues() .Build()), - }.Secure(new X509Certificate2Collection(ssl_connections.GetRootCertificate()), - ssl_connections.GetServerCertificate()) - .WithReplicationEndpointOn(IntTcpEndPoint) - .WithExternalTcpOn(TcpEndPoint) + }.Secure(new X509Certificate2Collection(TestCertificates.GetRootCertificate()), + TestCertificates.GetServerCertificate()) .WithNodeEndpointOn(HttpEndPoint); - var inMemConf = new ConfigurationBuilder() - .AddInMemoryCollection(new KeyValuePair[] { - new("EventStore:TcpPlugin:NodeTcpPort", extTcpPort.ToString()), - new("EventStore:TcpPlugin:EnableExternalTcp", "true"), - new("EventStore:TcpUnitTestPlugin:NodeTcpPort", extTcpPort.ToString()), - new("EventStore:TcpUnitTestPlugin:NodeHeartbeatInterval", "10000"), - new("EventStore:TcpUnitTestPlugin:NodeHeartbeatTimeout", "10000"), - new("EventStore:TcpUnitTestPlugin:Insecure", options.Application.Insecure.ToString()), - }).Build(); + var inMemConf = new ConfigurationBuilder().Build(); if (advertisedExtHostAddress != null) { @@ -200,7 +178,7 @@ public MiniNode(string pathname, ? "NON-GENERATION (PROBABLY BOEHM)" : $"{GC.MaxGeneration + 1} GENERATIONS", "DBPATH:", DbPath, - "TCP ENDPOINT:", TcpEndPoint, + "NODE ENDPOINT:", HttpEndPoint, "HTTP ENDPOINT:", HttpEndPoint); var logFormatFactory = LogFormatHelper.LogFormatFactory @@ -244,12 +222,12 @@ public MiniNode(string pathname, { options.UseHttps(new HttpsConnectionAdapterOptions { - ServerCertificate = ssl_connections.GetServerCertificate(), + ServerCertificate = TestCertificates.GetServerCertificate(), ClientCertificateMode = ClientCertificateMode.AllowCertificate, ClientCertificateValidation = (certificate, chain, sslPolicyErrors) => { var (isValid, error) = - ClusterVNode.ValidateClientCertificate(certificate, chain, sslPolicyErrors, () => null, () => new X509Certificate2Collection(ssl_connections.GetRootCertificate())); + ClusterVNode.ValidateClientCertificate(certificate, chain, sslPolicyErrors, () => null, () => new X509Certificate2Collection(TestCertificates.GetRootCertificate())); if (!isValid && error != null) { Log.Error("Client certificate validation error: {e}", error); @@ -335,25 +313,6 @@ void WaitForAdminUser(StorageMessage.EventCommitted m) Log.Information("MiniNode successfully started!"); } - public async Task WaitForTcpEndPoint() - { - while (true) - { - using var client = new TcpClient(); - - try - { - await client.ConnectAsync(TcpEndPoint.Address, TcpEndPoint.Port) - .WaitAsync(TimeSpan.FromMilliseconds(250)); - return; - } - catch (Exception ex) when (ex is SocketException or TimeoutException) - { - await Task.Delay(100); - } - } - } - public async Task Shutdown(bool keepDb = false) { diff --git a/src/EventStore.Core.Tests/Integration/Archive/when_archiving_and_restoring_a_cluster.cs b/src/EventStore.Core.Tests/Integration/Archive/when_archiving_and_restoring_a_cluster.cs index 781da7f1ce..d8e41d20c9 100644 --- a/src/EventStore.Core.Tests/Integration/Archive/when_archiving_and_restoring_a_cluster.cs +++ b/src/EventStore.Core.Tests/Integration/Archive/when_archiving_and_restoring_a_cluster.cs @@ -2,11 +2,12 @@ using System.IO; using System.Linq; using System.Net; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; using Amazon.S3; using Amazon.S3.Model; -using EventStore.ClientAPI; +using EventStore.Client.Streams; using EventStore.Core.Data; using EventStore.Core.Messages; using EventStore.Core.Messaging; @@ -18,7 +19,11 @@ using EventStore.Core.Tests.Helpers; using EventStore.Core.TransactionLog.Chunks.TFChunk; using EventStore.Core.TransactionLog.FileNamingStrategy; +using Google.Protobuf; +using Grpc.Net.Client; using NUnit.Framework; +using GrpcMetadata = EventStore.Core.Services.Transport.Grpc.Constants.Metadata; +using StreamsClient = EventStore.Client.Streams.Streams.StreamsClient; namespace EventStore.Core.Tests.Integration.Archive; @@ -44,6 +49,8 @@ public class when_archiving_and_restoring_a_cluster private long _archivedCheckpoint; private int _restoredNodeIndex; private int _completedIterations; + private GrpcChannel _channel; + private StreamsClient _client; protected override int NodeCount => 4; protected override TimeSpan GivenTimeout => SoakTimeout; @@ -117,19 +124,13 @@ protected override MiniClusterNode CreateNode( new( PathName, index, - endpoints.InternalTcp, - endpoints.ExternalTcp, - endpoints.HttpEndPoint, + endpoints.NodeEndPoint, + endpoints.ReplicationEndPoint, gossipSeeds, readOnlyReplica: index == ArchiverNodeIndex, archiveOptions: _archiveOptions.Enabled ? _archiveOptions : null, archiver: index == ArchiverNodeIndex); - protected override IEventStoreConnection CreateConnection() => - EventStoreConnection.Create( - ConnectionSettings.Create().DisableServerCertificateValidation(), - GetLeader().ExternalTcpEndPoint); - protected override async Task Given() { var payload = new byte[256 * 1024]; @@ -141,10 +142,30 @@ protected override async Task Given() var leader = await ReconnectToLeader(); for (var eventNumber = 0; eventNumber < EventsPerIteration; eventNumber++) { - await _conn.AppendToStreamAsync( - Stream, - EventStore.ClientAPI.ExpectedVersion.Any, - new EventData(Guid.NewGuid(), "archive-event", isJson: false, payload, Array.Empty())); + using var call = _client.Append(); + await call.RequestStream.WriteAsync(new AppendReq + { + Options = new() + { + Any = new(), + StreamIdentifier = new() { StreamName = ByteString.CopyFromUtf8(Stream) } + } + }); + await call.RequestStream.WriteAsync(new AppendReq + { + ProposedMessage = new() + { + Id = Core.Services.Transport.Grpc.Uuid.NewUuid().ToDto(), + Data = ByteString.CopyFrom(payload), + CustomMetadata = ByteString.Empty, + Metadata = { + { GrpcMetadata.Type, "archive-event" }, + { GrpcMetadata.ContentType, GrpcMetadata.ContentTypes.ApplicationOctetStream } + } + } + }); + await call.RequestStream.CompleteAsync(); + await call.ResponseAsync; } AssertEx.IsOrBecomesTrue( @@ -175,11 +196,16 @@ await _conn.AppendToStreamAsync( private async Task> ReconnectToLeader() { var leader = GetLeader(); - _conn?.Close(); - _conn = EventStoreConnection.Create( - ConnectionSettings.Create().DisableServerCertificateValidation(), - leader.ExternalTcpEndPoint); - await _conn.ConnectAsync(); + _channel?.Dispose(); + _channel = GrpcChannel.ForAddress(new Uri($"https://{leader.HttpEndPoint}"), + new GrpcChannelOptions + { + HttpHandler = new SocketsHttpHandler + { + SslOptions = { RemoteCertificateValidationCallback = delegate { return true; } } + } + }); + _client = new StreamsClient(_channel); return leader; } @@ -235,7 +261,7 @@ private async Task RestoreNode(int nodeIndex, int coldChunkNumber) private EndPoint[] GossipSeedsFor(int nodeIndex) => _nodeEndpoints .Where((_, index) => index != nodeIndex) - .Select(x => (EndPoint)x.HttpEndPoint) + .Select(x => (EndPoint)x.NodeEndPoint) .ToArray(); private async Task WaitForArchiveCheckpoint(long minimum) @@ -258,6 +284,7 @@ private async Task WaitForArchiveCheckpoint(long minimum) [OneTimeTearDown] public override async Task TestFixtureTearDown() { + _channel?.Dispose(); await base.TestFixtureTearDown(); if (_s3Client is null) { diff --git a/src/EventStore.Core.Tests/Integration/specification_with_cluster.cs b/src/EventStore.Core.Tests/Integration/specification_with_cluster.cs index 6b8aa33884..b88b48a586 100644 --- a/src/EventStore.Core.Tests/Integration/specification_with_cluster.cs +++ b/src/EventStore.Core.Tests/Integration/specification_with_cluster.cs @@ -4,7 +4,6 @@ using System.Net; using System.Net.Sockets; using System.Threading.Tasks; -using EventStore.ClientAPI; using EventStore.Core.Data; using EventStore.Core.Tests.Helpers; using EventStore.Plugins.Subsystems; @@ -17,7 +16,6 @@ public abstract class specification_with_cluster : Specif { protected MiniClusterNode[] _nodes; protected Endpoints[] _nodeEndpoints; - protected IEventStoreConnection _conn; protected virtual TimeSpan GivenTimeout { get; } = TimeSpan.FromMinutes(2); protected virtual int NodeCount => 3; @@ -25,15 +23,13 @@ public abstract class specification_with_cluster : Specif protected class Endpoints { - public readonly IPEndPoint InternalTcp; - public readonly IPEndPoint ExternalTcp; - public readonly IPEndPoint HttpEndPoint; + public readonly IPEndPoint NodeEndPoint; + public readonly IPEndPoint ReplicationEndPoint; public IEnumerable Ports() { - yield return InternalTcp.Port; - yield return ExternalTcp.Port; - yield return HttpEndPoint.Port; + yield return NodeEndPoint.Port; + yield return ReplicationEndPoint.Port; } private readonly List _sockets; @@ -44,21 +40,15 @@ public Endpoints() var defaultLoopBack = new IPEndPoint(IPAddress.Loopback, 0); - var internalTcp = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - internalTcp.Bind(defaultLoopBack); - _sockets.Add(internalTcp); + var nodeEndpoint = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + nodeEndpoint.Bind(defaultLoopBack); + _sockets.Add(nodeEndpoint); + var replicationEndpoint = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + replicationEndpoint.Bind(defaultLoopBack); + _sockets.Add(replicationEndpoint); - var externalTcp = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - externalTcp.Bind(defaultLoopBack); - _sockets.Add(externalTcp); - - var httpEndPoint = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - httpEndPoint.Bind(defaultLoopBack); - _sockets.Add(httpEndPoint); - - InternalTcp = CopyEndpoint((IPEndPoint)internalTcp.LocalEndPoint); - ExternalTcp = CopyEndpoint((IPEndPoint)externalTcp.LocalEndPoint); - HttpEndPoint = CopyEndpoint((IPEndPoint)httpEndPoint.LocalEndPoint); + NodeEndPoint = CopyEndpoint((IPEndPoint)nodeEndpoint.LocalEndPoint); + ReplicationEndPoint = CopyEndpoint((IPEndPoint)replicationEndpoint.LocalEndPoint); } public void DisposeSockets() @@ -102,7 +92,7 @@ public override async Task TestFixtureSetUp() nodeIndex, _nodeEndpoints[nodeIndex], _nodeEndpoints.Where((_, otherIndex) => otherIndex != nodeIndex) - .Select(x => (EndPoint)x.HttpEndPoint) + .Select(x => (EndPoint)x.NodeEndPoint) .ToArray(), wait)); _nodes[nodeIndex] = _nodeCreationFactory[nodeIndex](true); @@ -144,9 +134,6 @@ public override async Task TestFixtureSetUp() onFail: MiniNodeLogging.WriteLogs, msg: $"Waiting for followers timed out! States={string.Join(", ", _nodes.Select(n => n.NodeState))}"); - _conn = CreateConnection(); - await _conn.ConnectAsync(); - try { await Given().WithTimeout(GivenTimeout); @@ -158,9 +145,6 @@ public override async Task TestFixtureSetUp() } } - protected virtual IEventStoreConnection CreateConnection() => - EventStoreConnection.Create(_nodes[0].ExternalTcpEndPoint); - protected virtual void BeforeNodesStart() { } @@ -171,8 +155,7 @@ protected virtual void BeforeNodesStart() protected virtual MiniClusterNode CreateNode(int index, Endpoints endpoints, EndPoint[] gossipSeeds, bool wait = true) => new( - PathName, index, endpoints.InternalTcp, - endpoints.ExternalTcp, endpoints.HttpEndPoint, + PathName, index, endpoints.NodeEndPoint, endpoints.ReplicationEndPoint, subsystems: Array.Empty(), gossipSeeds: gossipSeeds); [TearDown] @@ -187,7 +170,6 @@ public void AfterEachTest() [OneTimeTearDown] public override async Task TestFixtureTearDown() { - _conn?.Close(); if (_nodes is not null) { await Task.WhenAll(_nodes.Where(node => node is not null).Select(node => node.Shutdown())); diff --git a/src/EventStore.Core.Tests/Integration/when_cluster_nodes_are_restarted.cs b/src/EventStore.Core.Tests/Integration/when_cluster_nodes_are_restarted.cs index 53791c1be1..ee99b6ff2d 100644 --- a/src/EventStore.Core.Tests/Integration/when_cluster_nodes_are_restarted.cs +++ b/src/EventStore.Core.Tests/Integration/when_cluster_nodes_are_restarted.cs @@ -102,7 +102,7 @@ private int SelectRestartNode(bool[] restartedNodes, bool restartLeader) private EndPoint[] GossipSeedsFor(int restartedNodeIndex) => _nodeEndpoints .Where((_, index) => index != restartedNodeIndex) - .Select(x => (EndPoint)x.HttpEndPoint) + .Select(x => (EndPoint)x.NodeEndPoint) .ToArray(); [Test] diff --git a/src/EventStore.Core.Tests/Services/ElectionsService/ClusterSettingsFactory.cs b/src/EventStore.Core.Tests/Services/ElectionsService/ClusterSettingsFactory.cs index f63c8c9319..b072443420 100644 --- a/src/EventStore.Core.Tests/Services/ElectionsService/ClusterSettingsFactory.cs +++ b/src/EventStore.Core.Tests/Services/ElectionsService/ClusterSettingsFactory.cs @@ -1,9 +1,7 @@ using System; using System.Linq; using System.Net; -using System.Runtime.InteropServices; using EventStore.Core.Cluster.Settings; -using EventStore.Core.Tests.Services.Transport.Tcp; namespace EventStore.Core.Tests.Services.ElectionsService; @@ -14,13 +12,9 @@ public class ClusterSettingsFactory private static ClusterVNodeSettings CreateVNode(int nodeNumber, bool isReadOnlyReplica) { - int tcpIntPort = StartingPort + nodeNumber * 2, - tcpExtPort = tcpIntPort + 1, - httpPort = tcpIntPort + 11; + var httpPort = StartingPort + nodeNumber; return new ClusterVNodeSettings(Guid.NewGuid(), 0, - GetLoopbackForPort(tcpIntPort), null, - GetLoopbackForPort(tcpExtPort), null, GetLoopbackForPort(httpPort), 0, isReadOnlyReplica); } diff --git a/src/EventStore.Core.Tests/Services/ElectionsService/ClusterVNodeSettings.cs b/src/EventStore.Core.Tests/Services/ElectionsService/ClusterVNodeSettings.cs index e1fb14badc..b9c4a888d9 100644 --- a/src/EventStore.Core.Tests/Services/ElectionsService/ClusterVNodeSettings.cs +++ b/src/EventStore.Core.Tests/Services/ElectionsService/ClusterVNodeSettings.cs @@ -16,24 +16,14 @@ public class ClusterVNodeSettings public readonly bool ReadOnlyReplica; public ClusterVNodeSettings(Guid instanceId, int debugIndex, - IPEndPoint internalTcpEndPoint, - IPEndPoint internalSecureTcpEndPoint, - IPEndPoint externalTcpEndPoint, - IPEndPoint externalSecureTcpEndPoint, IPEndPoint httpEndPoint, int nodePriority, bool readOnlyReplica) { Ensure.NotEmptyGuid(instanceId, "instanceId"); - Ensure.Equal(false, internalTcpEndPoint == null && internalSecureTcpEndPoint == null, "Both internal TCP endpoints are null"); - Ensure.NotNull(httpEndPoint, nameof(httpEndPoint)); - NodeInfo = new VNodeInfo(instanceId, debugIndex, - internalTcpEndPoint, internalSecureTcpEndPoint, - externalTcpEndPoint, externalSecureTcpEndPoint, - httpEndPoint, - readOnlyReplica); + NodeInfo = new VNodeInfo(instanceId, debugIndex, httpEndPoint, readOnlyReplica); NodePriority = nodePriority; diff --git a/src/EventStore.Core.Tests/Services/ElectionsService/ElectionServiceUnit.cs b/src/EventStore.Core.Tests/Services/ElectionsService/ElectionServiceUnit.cs index b24a1e875e..a07c837474 100644 --- a/src/EventStore.Core.Tests/Services/ElectionsService/ElectionServiceUnit.cs +++ b/src/EventStore.Core.Tests/Services/ElectionsService/ElectionServiceUnit.cs @@ -47,11 +47,7 @@ public ElectionsServiceUnit(ClusterSettings clusterSettings) _bus = new(GetType().Name); var memberInfo = MemberInfo.Initial(clusterSettings.Self.NodeInfo.InstanceId, InitialDate, VNodeState.Unknown, true, - clusterSettings.Self.NodeInfo.InternalTcp, - clusterSettings.Self.NodeInfo.InternalSecureTcp, - clusterSettings.Self.NodeInfo.ExternalTcp, - clusterSettings.Self.NodeInfo.ExternalSecureTcp, - clusterSettings.Self.NodeInfo.HttpEndPoint, null, 0, 0, + clusterSettings.Self.NodeInfo.HttpEndPoint, null, 0, clusterSettings.Self.NodePriority, clusterSettings.Self.ReadOnlyReplica); ElectionsService = new Core.Services.ElectionsService(Publisher, @@ -83,11 +79,7 @@ private ClusterInfo BuildClusterInfo(ClusterSettings clusterSettings) InitialDate, VNodeState.Unknown, true, - clusterSettings.Self.NodeInfo.InternalTcp, - clusterSettings.Self.NodeInfo.InternalSecureTcp, - clusterSettings.Self.NodeInfo.ExternalTcp, - clusterSettings.Self.NodeInfo.ExternalSecureTcp, - clusterSettings.Self.NodeInfo.HttpEndPoint, null, 0, 0, + clusterSettings.Self.NodeInfo.HttpEndPoint, null, 0, LastCommitPosition, WriterCheckpoint, ChaserCheckpoint, -1, -1, @@ -98,11 +90,7 @@ private ClusterInfo BuildClusterInfo(ClusterSettings clusterSettings) InitialDate, VNodeState.Unknown, true, - x.NodeInfo.InternalTcp, - x.NodeInfo.InternalSecureTcp, - x.NodeInfo.ExternalTcp, - x.NodeInfo.ExternalSecureTcp, - x.NodeInfo.HttpEndPoint, null, 0, 0, + x.NodeInfo.HttpEndPoint, null, 0, LastCommitPosition, WriterCheckpoint, ChaserCheckpoint, -1, -1, @@ -196,9 +184,7 @@ public IEnumerable ListMembers(Func predicate = nu ? MemberInfo.ForManager(x.InstanceId, x.TimeStamp, x.IsAlive, x.HttpEndPoint) : MemberInfo.ForVNode(x.InstanceId, x.TimeStamp, x.State, x.IsAlive, - x.InternalTcpEndPoint, x.InternalSecureTcpEndPoint, - x.ExternalTcpEndPoint, x.ExternalSecureTcpEndPoint, - x.HttpEndPoint, null, 0, 0, + x.HttpEndPoint, null, 0, x.LastCommitPosition, x.WriterCheckpoint, x.ChaserCheckpoint, x.EpochPosition, x.EpochNumber, x.EpochId, x.NodePriority, x.IsReadOnlyReplica)); } diff --git a/src/EventStore.Core.Tests/Services/ElectionsService/ElectionsServiceTests.cs b/src/EventStore.Core.Tests/Services/ElectionsService/ElectionsServiceTests.cs index bcf9e0938f..378cc8075c 100644 --- a/src/EventStore.Core.Tests/Services/ElectionsService/ElectionsServiceTests.cs +++ b/src/EventStore.Core.Tests/Services/ElectionsService/ElectionsServiceTests.cs @@ -29,18 +29,12 @@ public abstract class ElectionsFixture protected static Func NodeFactory = (id) => new VNodeInfo( Guid.Parse($"00000000-0000-0000-0000-00000000000{id}"), id, - new IPEndPoint(IPAddress.Loopback, id), - new IPEndPoint(IPAddress.Loopback, id), - new IPEndPoint(IPAddress.Loopback, id), - new IPEndPoint(IPAddress.Loopback, id), new IPEndPoint(IPAddress.Loopback, id), false); protected static readonly Func MemberInfoFromVNode = (nodeInfo, timestamp, state, isAlive, epochNumber, epochId, priority) => MemberInfo.ForVNode( nodeInfo.InstanceId, timestamp, state, isAlive, - nodeInfo.InternalTcp, - nodeInfo.InternalSecureTcp, nodeInfo.ExternalTcp, nodeInfo.ExternalSecureTcp, - nodeInfo.HttpEndPoint, null, 0, 0, + nodeInfo.HttpEndPoint, null, 0, 0, 0, 0, 0, epochNumber, epochId, priority, nodeInfo.IsReadOnlyReplica); @@ -924,9 +918,7 @@ public void should_send_an_acceptance_to_other_members() new ElectionMessage.ElectionsDone(0,0, MemberInfo.ForVNode( _nodeThree.InstanceId, _timeProvider.UtcNow, VNodeState.Unknown, true, - _nodeThree.InternalTcp, - _nodeThree.InternalSecureTcp, _nodeThree.ExternalTcp, _nodeThree.ExternalSecureTcp, - _nodeThree.HttpEndPoint, null, 0, 0, 0, 0, 0, 0, 0, _epochId, 0, + _nodeThree.HttpEndPoint, null, 0, 0, 0, 0, 0, 0, _epochId, 0, _nodeThree.IsReadOnlyReplica)), new GrpcMessage.SendOverGrpc(_nodeThree.HttpEndPoint, new ElectionMessage.Accept(_node.InstanceId, _node.HttpEndPoint, @@ -1077,9 +1069,7 @@ public void should_complete_elections() new ElectionMessage.ElectionsDone(0,0, MemberInfo.ForVNode( _nodeTwo.InstanceId, _timeProvider.UtcNow, VNodeState.Unknown, true, - _nodeTwo.InternalTcp, - _nodeTwo.InternalSecureTcp, _nodeTwo.ExternalTcp, _nodeTwo.ExternalSecureTcp, - _nodeTwo.HttpEndPoint, null, 0, 0, 0, 0, 0, 0, 0, _epochId, 0, + _nodeTwo.HttpEndPoint, null, 0, 0, 0, 0, 0, 0, _epochId, 0, _nodeTwo.IsReadOnlyReplica)), }; _publisher.Messages.Should().BeEquivalentTo(expected); @@ -1314,9 +1304,7 @@ public void should_attempt_not_to_elect_previously_elected_leader() new ElectionMessage.ElectionsDone(3,1, MemberInfo.ForVNode( _nodeTwo.InstanceId, _timeProvider.UtcNow, VNodeState.Unknown, true, - _nodeTwo.InternalTcp, - _nodeTwo.InternalSecureTcp, _nodeTwo.ExternalTcp, _nodeTwo.ExternalSecureTcp, - _nodeTwo.HttpEndPoint, null, 0, 0, 0, 0, 0, 0, 0, _epochId, 0, + _nodeTwo.HttpEndPoint, null, 0, 0, 0, 0, 0, 0, _epochId, 0, _nodeTwo.IsReadOnlyReplica)), }; _publisher.Messages.Should().BeEquivalentTo(expected); @@ -1357,11 +1345,7 @@ public void should_throw_argument_exception() var endpoint = new IPEndPoint(IPAddress.Loopback, 1234); var nodeInfo = MemberInfo.Initial(Guid.NewGuid(), DateTime.UtcNow, VNodeState.ReadOnlyLeaderless, true, - endpoint, - endpoint, - endpoint, - endpoint, - endpoint, null, 0, 0, + endpoint, null, 0, 0, true); @@ -1405,9 +1389,7 @@ public void previous_leader_should_be_elected() new ElectionMessage.ElectionsDone(0,0, MemberInfo.ForVNode( _nodeThree.InstanceId, _timeProvider.UtcNow, VNodeState.Unknown, true, - _nodeThree.InternalTcp, - _nodeThree.InternalSecureTcp, _nodeThree.ExternalTcp, _nodeThree.ExternalSecureTcp, - _nodeThree.HttpEndPoint, null, 0, 0, + _nodeThree.HttpEndPoint, null, 0, 0, 0, 0, 0, 0, _epochId, 0, _nodeThree.IsReadOnlyReplica)), }; @@ -1505,9 +1487,7 @@ public void previous_leader_should_not_be_elected() new ElectionMessage.ElectionsDone(0,0, MemberInfo.ForVNode( _nodeTwo.InstanceId, _timeProvider.UtcNow, VNodeState.Unknown, true, - _nodeTwo.InternalTcp, - _nodeTwo.InternalSecureTcp, _nodeTwo.ExternalTcp, _nodeTwo.ExternalSecureTcp, - _nodeTwo.HttpEndPoint, null, 0, 0, + _nodeTwo.HttpEndPoint, null, 0, 0, 0, 0, 0, 0, _epochId, 0, _nodeTwo.IsReadOnlyReplica)), }; @@ -1541,9 +1521,7 @@ public void previous_leader_should_not_be_elected() new ElectionMessage.ElectionsDone(0,0, MemberInfo.ForVNode( _nodeTwo.InstanceId, _timeProvider.UtcNow, VNodeState.Unknown, true, - _nodeTwo.InternalTcp, - _nodeTwo.InternalSecureTcp, _nodeTwo.ExternalTcp, _nodeTwo.ExternalSecureTcp, - _nodeTwo.HttpEndPoint, null, 0, 0, + _nodeTwo.HttpEndPoint, null, 0, 0, 0, 0, 0, 0, _epochId, 0, _nodeTwo.IsReadOnlyReplica)), }; @@ -1598,9 +1576,7 @@ public void previous_leader_should_not_be_elected() new ElectionMessage.ElectionsDone(0,0, MemberInfo.ForVNode( _nodeTwo.InstanceId, _timeProvider.UtcNow, VNodeState.Unknown, true, - _nodeTwo.InternalTcp, - _nodeTwo.InternalSecureTcp, _nodeTwo.ExternalTcp, _nodeTwo.ExternalSecureTcp, - _nodeTwo.HttpEndPoint, null, 0, 0, + _nodeTwo.HttpEndPoint, null, 0, 0, 0, 0, 0, 0, _epochId, 0, _nodeTwo.IsReadOnlyReplica)), }; @@ -1634,9 +1610,7 @@ public void previous_leader_should_not_be_elected() new ElectionMessage.ElectionsDone(0,0, MemberInfo.ForVNode( _nodeTwo.InstanceId, _timeProvider.UtcNow, VNodeState.Unknown, true, - _nodeTwo.InternalTcp, - _nodeTwo.InternalSecureTcp, _nodeTwo.ExternalTcp, _nodeTwo.ExternalSecureTcp, - _nodeTwo.HttpEndPoint, null, 0, 0, + _nodeTwo.HttpEndPoint, null, 0, 0, 0, 0, 0, 0, _epochId, 0, _nodeTwo.IsReadOnlyReplica)), }; diff --git a/src/EventStore.Core.Tests/Services/ElectionsService/LeaderNode/ElectionsServiceUnitTests.cs b/src/EventStore.Core.Tests/Services/ElectionsService/LeaderNode/ElectionsServiceUnitTests.cs index 11c8462e3d..6e3a013cf5 100644 --- a/src/EventStore.Core.Tests/Services/ElectionsService/LeaderNode/ElectionsServiceUnitTests.cs +++ b/src/EventStore.Core.Tests/Services/ElectionsService/LeaderNode/ElectionsServiceUnitTests.cs @@ -40,7 +40,7 @@ public void Setup() seeds.Add(endPoint); var instanceId = Guid.Parse($"101EFD13-F9CD-49BE-9C6D-E6AF9AF5540{i}"); var memberInfo = MemberInfo.ForVNode(instanceId, DateTime.UtcNow, VNodeState.Unknown, true, - endPoint, null, endPoint, null, endPoint, null, 0, 0, -1, 0, 0, -1, -1, Guid.Empty, 0, false); + endPoint, null, 0, -1, 0, 0, -1, -1, Guid.Empty, 0, false); members.Add(memberInfo); _fakeTimeProvider = new FakeTimeProvider(); _scheduler = new FakeScheduler(new FakeTimer(), _fakeTimeProvider); @@ -379,7 +379,7 @@ Func epochNumber { var id = IdForNode(i); var ep = EndpointForNode(i); - return MemberInfo.ForVNode(id, DateTime.Now, VNodeState.Follower, true, ep, ep, ep, ep, ep, null, 0, 0, + return MemberInfo.ForVNode(id, DateTime.Now, VNodeState.Follower, true, ep, null, 0, -1, writerCheckpoint(i), chaserCheckpoint(i), 1, epochNumber(i), epochId, nodePriority(i), false); } diff --git a/src/EventStore.Core.Tests/Services/ElectionsService/Randomized/RandomizedElectionsTestCase.cs b/src/EventStore.Core.Tests/Services/ElectionsService/Randomized/RandomizedElectionsTestCase.cs index 68b6ce5fd9..168f65a466 100644 --- a/src/EventStore.Core.Tests/Services/ElectionsService/Randomized/RandomizedElectionsTestCase.cs +++ b/src/EventStore.Core.Tests/Services/ElectionsService/Randomized/RandomizedElectionsTestCase.cs @@ -74,7 +74,7 @@ public virtual void Init() var outputBus = new SynchronousScheduler($"ELECTIONS-OUTPUT-BUS-{i}"); var endPoint = new IPEndPoint(BaseEndPoint.Address, BaseEndPoint.Port + i); var memberInfo = MemberInfo.Initial(Guid.NewGuid(), DateTime.UtcNow, VNodeState.Unknown, true, - endPoint, endPoint, endPoint, endPoint, endPoint, null, 0, 0, 0, false); + endPoint, null, 0, 0, false); _instances.Add(new ElectionsInstance(memberInfo.InstanceId, endPoint, inputBus, outputBus)); sendOverHttpHandler.RegisterEndPoint(endPoint, inputBus); @@ -125,8 +125,7 @@ protected virtual GossipMessage.GossipUpdated GetInitialGossipFor(ElectionsInsta { var members = allInstances.Select( x => MemberInfo.ForVNode(x.InstanceId, DateTime.UtcNow, VNodeState.Unknown, true, - x.EndPoint, null, x.EndPoint, null, - x.EndPoint, null, 0, 0, -1, 0, 0, -1, -1, Guid.Empty, 0, false)); + x.EndPoint, null, 0, -1, 0, 0, -1, -1, Guid.Empty, 0, false)); var gossip = new GossipMessage.GossipUpdated(new ClusterInfo(members.ToArray())); return gossip; } diff --git a/src/EventStore.Core.Tests/Services/ElectionsService/Randomized/UpdateGossipProcessor.cs b/src/EventStore.Core.Tests/Services/ElectionsService/Randomized/UpdateGossipProcessor.cs index 7ed5cd5fc9..b9381ca8dc 100644 --- a/src/EventStore.Core.Tests/Services/ElectionsService/Randomized/UpdateGossipProcessor.cs +++ b/src/EventStore.Core.Tests/Services/ElectionsService/Randomized/UpdateGossipProcessor.cs @@ -63,7 +63,7 @@ public void Process(int iteration, RandTestQueueItem item) previousMembers[leaderIndex] = MemberInfo.ForVNode(previousLeaderInfo.InstanceId, DateTime.UtcNow, VNodeState.Leader, - previousLeaderInfo.IsAlive, leaderEndPoint, null, leaderEndPoint, null, leaderEndPoint, null, 0, 0, + previousLeaderInfo.IsAlive, leaderEndPoint, null, 0, -1, 0, 0, -1, -1, Guid.Empty, 0, false); } } @@ -82,7 +82,7 @@ public void Process(int iteration, RandTestQueueItem item) foreach (var memberInfo in updatedGossip) { - _sendOverGrpcProcessor.RegisterEndpointToSkip(memberInfo.ExternalTcpEndPoint, !memberInfo.IsAlive); + _sendOverGrpcProcessor.RegisterEndpointToSkip(memberInfo.HttpEndPoint, !memberInfo.IsAlive); } var updateGossipMessage = new GossipMessage.GossipUpdated(new ClusterInfo(updatedGossip)); diff --git a/src/EventStore.Core.Tests/Services/ElectionsService/Randomized/elections_service_5_nodes_with_1_known_when_started_and_set_full_imediately.cs b/src/EventStore.Core.Tests/Services/ElectionsService/Randomized/elections_service_5_nodes_with_1_known_when_started_and_set_full_imediately.cs index 339ca873bd..6722010f0f 100644 --- a/src/EventStore.Core.Tests/Services/ElectionsService/Randomized/elections_service_5_nodes_with_1_known_when_started_and_set_full_imediately.cs +++ b/src/EventStore.Core.Tests/Services/ElectionsService/Randomized/elections_service_5_nodes_with_1_known_when_started_and_set_full_imediately.cs @@ -37,7 +37,7 @@ private MemberInfo[] CreateInitialGossip(ElectionsInstance instance, ElectionsIn { return new[] { MemberInfo.ForVNode(instance.InstanceId, DateTime.UtcNow, VNodeState.Unknown, true, - instance.EndPoint, null, instance.EndPoint, null, instance.EndPoint, null, 0, 0, + instance.EndPoint, null, 0, -1, 0, 0, -1, -1, Guid.Empty, 0, false) }; } @@ -53,7 +53,7 @@ private MemberInfo[] CreateUpdatedGossip(int iteration, Console.WriteLine("Update item: {0} : {1}", iteration, item.EndPoint.GetPort()); return instances.Select((x, i) => MemberInfo.ForVNode(x.InstanceId, DateTime.UtcNow, VNodeState.Unknown, true, - x.EndPoint, null, x.EndPoint, null, x.EndPoint, null, 0, 0, + x.EndPoint, null, 0, -1, 0, 0, -1, -1, Guid.Empty, 0, false)).ToArray(); } diff --git a/src/EventStore.Core.Tests/Services/ElectionsService/Randomized/elections_service_5_nodes_with_1_known_when_started_and_set_to_full_later.cs b/src/EventStore.Core.Tests/Services/ElectionsService/Randomized/elections_service_5_nodes_with_1_known_when_started_and_set_to_full_later.cs index 05f165cfab..cdf621e6ab 100644 --- a/src/EventStore.Core.Tests/Services/ElectionsService/Randomized/elections_service_5_nodes_with_1_known_when_started_and_set_to_full_later.cs +++ b/src/EventStore.Core.Tests/Services/ElectionsService/Randomized/elections_service_5_nodes_with_1_known_when_started_and_set_to_full_later.cs @@ -37,7 +37,7 @@ private MemberInfo[] CreateInitialGossip(ElectionsInstance instance, ElectionsIn { return new[] { MemberInfo.ForVNode(instance.InstanceId, DateTime.UtcNow, VNodeState.Unknown, true, - instance.EndPoint, null, instance.EndPoint, null, instance.EndPoint, null, 0, 0, + instance.EndPoint, null, 0, -1, 0, 0, -1, -1, Guid.Empty, 0, false) }; } @@ -57,7 +57,7 @@ private MemberInfo[] CreateUpdatedGossip(int iteration, { return instances.Select((x, i) => MemberInfo.ForVNode(x.InstanceId, DateTime.UtcNow, VNodeState.Unknown, true, - x.EndPoint, null, x.EndPoint, null, x.EndPoint, null, 0, 0, + x.EndPoint, null, 0, -1, 0, 0, -1, -1, Guid.Empty, 0, false)) .ToArray(); } diff --git a/src/EventStore.Core.Tests/Services/GossipService/NodeGossipServiceTests.cs b/src/EventStore.Core.Tests/Services/GossipService/NodeGossipServiceTests.cs index 11ebbfbf88..2c877c6645 100644 --- a/src/EventStore.Core.Tests/Services/GossipService/NodeGossipServiceTests.cs +++ b/src/EventStore.Core.Tests/Services/GossipService/NodeGossipServiceTests.cs @@ -43,31 +43,15 @@ public NodeGossipServiceTestFixture() _currentNode = new VNodeInfo( Guid.Parse("00000000-0000-0000-0000-000000000001"), 1, - new IPEndPoint(IPAddress.Loopback, 1111), - new IPEndPoint(IPAddress.Loopback, 1111), - new IPEndPoint(IPAddress.Loopback, 1111), - new IPEndPoint(IPAddress.Loopback, 1111), new IPEndPoint(IPAddress.Loopback, 1111), false); _nodeTwo = new VNodeInfo( Guid.Parse("00000000-0000-0000-0000-000000000002"), 2, - new IPEndPoint(IPAddress.Loopback, 2222), - new IPEndPoint(IPAddress.Loopback, 2222), - new IPEndPoint(IPAddress.Loopback, 2222), - new IPEndPoint(IPAddress.Loopback, 2222), new IPEndPoint(IPAddress.Loopback, 2222), false); _nodeThree = new VNodeInfo( Guid.Parse("00000000-0000-0000-0000-000000000003"), 3, - new IPEndPoint(IPAddress.Loopback, 3333), - new IPEndPoint(IPAddress.Loopback, 3333), - new IPEndPoint(IPAddress.Loopback, 3333), - new IPEndPoint(IPAddress.Loopback, 3333), new IPEndPoint(IPAddress.Loopback, 3333), false); _nodeFour = new VNodeInfo( Guid.Parse("00000000-0000-0000-0000-000000000004"), 4, - new IPEndPoint(IPAddress.Loopback, 4444), - new IPEndPoint(IPAddress.Loopback, 4444), - new IPEndPoint(IPAddress.Loopback, 4444), - new IPEndPoint(IPAddress.Loopback, 4444), new IPEndPoint(IPAddress.Loopback, 4444), false); _getNodeToGossipTo = infos => infos.First(x => Equals(x.HttpEndPoint, _nodeTwo.HttpEndPoint)); @@ -120,8 +104,7 @@ protected static MemberInfo MemberInfoForVNode(VNodeInfo nodeInfo, DateTime utcN VNodeState nodeState = VNodeState.Initializing, string esVersion = VersionInfo.DefaultVersion, bool isAlive = true) { return MemberInfo.ForVNode(nodeInfo.InstanceId, utcNow, nodeState, isAlive, - nodeInfo.InternalTcp, nodeInfo.InternalSecureTcp, nodeInfo.ExternalTcp, - nodeInfo.ExternalSecureTcp, nodeInfo.HttpEndPoint, null, 0, 0, + nodeInfo.HttpEndPoint, null, 0, 0, writerCheckpoint ?? 0, 0, -1, epochNumber ?? -1, Guid.Empty, nodePriority ?? 0, false, esVersion); } @@ -743,7 +726,7 @@ protected override Message[] Given() => _nodeTwo.HttpEndPoint); [Test] - public void should_ignore_message_and_wait_for_tcp_to_decide() + public void should_ignore_message_and_wait_for_connection_state_to_change() { ExpectNoMessages(); } @@ -1031,7 +1014,7 @@ private static MemberInfo TestNodeFor(int identifier, bool isAlive, DateTime tim { var ipEndpoint = new IPEndPoint(IPAddress.Loopback, identifier); return MemberInfo.ForVNode(Guid.NewGuid(), timeStamp, VNodeState.Initializing, isAlive, - ipEndpoint, ipEndpoint, ipEndpoint, ipEndpoint, ipEndpoint, null, 0, 0, + ipEndpoint, null, 0, 0, 0, 0, -1, -1, Guid.Empty, 0, false); } @@ -1108,7 +1091,7 @@ private static MemberInfo TestNodeFor(int identifier, bool isAlive, DateTime tim { var ipEndpoint = new IPEndPoint(IPAddress.Loopback, identifier); return MemberInfo.ForVNode(Guid.NewGuid(), timeStamp, nodeState, isAlive, - ipEndpoint, ipEndpoint, ipEndpoint, ipEndpoint, ipEndpoint, null, 0, 0, + ipEndpoint, null, 0, 0, 0, 0, -1, -1, Guid.Empty, 0, false); } diff --git a/src/EventStore.Core.Tests/Services/Replication/LogReplication/LogReplicationFixture.cs b/src/EventStore.Core.Tests/Services/Replication/LogReplication/LogReplicationFixture.cs index a7514a3a51..1dfac124f9 100644 --- a/src/EventStore.Core.Tests/Services/Replication/LogReplication/LogReplicationFixture.cs +++ b/src/EventStore.Core.Tests/Services/Replication/LogReplication/LogReplicationFixture.cs @@ -157,14 +157,9 @@ private async ValueTask> CreateLeader(TFChunkDb db, Cancel timeStamp: DateTime.Now, state: VNodeState.Leader, isAlive: true, - internalTcpEndPoint: FakeEndPoint, - internalSecureTcpEndPoint: null, - externalTcpEndPoint: null, - externalSecureTcpEndPoint: null, httpEndPoint: FakeEndPoint, advertiseHostToClientAs: null, advertiseHttpPortToClientAs: 0, - advertiseTcpPortToClientAs: 0, lastCommitPosition: 0, writerCheckpoint: 0, chaserCheckpoint: 0, diff --git a/src/EventStore.Core.Tests/Services/Replication/ReadOnlyReplica/connecting_to_read_only_replica.cs b/src/EventStore.Core.Tests/Services/Replication/ReadOnlyReplica/connecting_to_read_only_replica.cs index f8e76a11ab..9faa415dc9 100644 --- a/src/EventStore.Core.Tests/Services/Replication/ReadOnlyReplica/connecting_to_read_only_replica.cs +++ b/src/EventStore.Core.Tests/Services/Replication/ReadOnlyReplica/connecting_to_read_only_replica.cs @@ -1,11 +1,18 @@ +using System; using System.Net; +using System.Net.Http; +using System.Text; using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.ClientAPI.Exceptions; -using EventStore.Core.Tests.ClientAPI.Helpers; +using EventStore.Client.Streams; +using EventStore.Core.Services.Transport.Grpc; using EventStore.Core.Tests.Helpers; using EventStore.Core.Tests.Integration; +using Google.Protobuf; +using Grpc.Core; +using Grpc.Net.Client; using NUnit.Framework; +using Empty = EventStore.Client.Empty; +using GrpcMetadata = EventStore.Core.Services.Transport.Grpc.Constants.Metadata; namespace EventStore.Core.Tests.Replication.ReadOnlyReplica; @@ -13,13 +20,15 @@ namespace EventStore.Core.Tests.Replication.ReadOnlyReplica; [TestFixture(typeof(LogFormat.V2), typeof(string))] public class connecting_to_read_only_replica : specification_with_cluster { + protected override async Task Given() => + await _nodes[2].AdminUserCreated.WithTimeout(TimeSpan.FromSeconds(30)); + protected override MiniClusterNode CreateNode(int index, Endpoints endpoints, EndPoint[] gossipSeeds, bool wait = true) { var isReadOnly = index == 2; var node = new MiniClusterNode( - PathName, index, endpoints.InternalTcp, - endpoints.ExternalTcp, endpoints.HttpEndPoint, gossipSeeds, + PathName, index, endpoints.NodeEndPoint, endpoints.ReplicationEndPoint, gossipSeeds, readOnlyReplica: isReadOnly); if (wait && !isReadOnly) { @@ -29,35 +38,82 @@ protected override MiniClusterNode CreateNode(int index, return node; } - protected override IEventStoreConnection CreateConnection() + private static CallOptions GetCallOptions() { - var settings = ConnectionSettings.Create() - .DisableServerCertificateValidation() - .PerformOnAnyNode(); - return EventStoreConnection.Create(settings, _nodes[2].ExternalTcpEndPoint); + var credentials = CallCredentials.FromInterceptor((_, metadata) => + { + metadata.Add("authorization", + $"Basic {Convert.ToBase64String(Encoding.ASCII.GetBytes("admin:changeit"))}"); + return Task.CompletedTask; + }); + return new CallOptions(credentials: credentials, deadline: DateTime.UtcNow.AddSeconds(30)); } - [Test] - public async Task append_to_stream_should_fail_with_not_supported_exception() + private Streams.StreamsClient CreateClient(out GrpcChannel channel, out HttpClient httpClient) { - const string stream = "append_to_stream_should_fail_with_not_supported_exception"; - await AssertEx.ThrowsAsync( - () => _conn.AppendToStreamAsync(stream, ExpectedVersion.Any, TestEvent.NewTestEvent())); + httpClient = new HttpClient(new SocketsHttpHandler + { + SslOptions = { RemoteCertificateValidationCallback = delegate { return true; } } + }); + channel = GrpcChannel.ForAddress(new Uri($"https://{_nodes[2].HttpEndPoint}"), + new GrpcChannelOptions { HttpClient = httpClient }); + return new Streams.StreamsClient(channel); } [Test] - public async Task delete_stream_should_fail_with_not_supported_exception() + public async Task append_to_stream_is_rejected() { - const string stream = "delete_stream_should_fail_with_not_supported_exception"; - await AssertEx.ThrowsAsync(() => - _conn.DeleteStreamAsync(stream, ExpectedVersion.Any)); + var client = CreateClient(out var channel, out var httpClient); + using (channel) + using (httpClient) + using (var call = client.Append(GetCallOptions())) + { + await call.RequestStream.WriteAsync(new AppendReq + { + Options = new() + { + Any = new Empty(), + StreamIdentifier = new() { StreamName = ByteString.CopyFromUtf8(nameof(append_to_stream_is_rejected)) } + } + }); + await call.RequestStream.WriteAsync(new AppendReq + { + ProposedMessage = new() + { + Id = Uuid.NewUuid().ToDto(), + Data = ByteString.Empty, + CustomMetadata = ByteString.Empty, + Metadata = + { + [GrpcMetadata.Type] = "test", + [GrpcMetadata.ContentType] = GrpcMetadata.ContentTypes.ApplicationJson + } + } + }); + await call.RequestStream.CompleteAsync(); + + var exception = Assert.ThrowsAsync(async () => await call.ResponseAsync); + Assert.That(exception.StatusCode, Is.EqualTo(StatusCode.NotFound)); + } } [Test] - public async Task start_transaction_should_fail_with_not_supported_exception() + public async Task delete_stream_is_rejected() { - const string stream = "start_transaction_should_fail_with_not_supported_exception"; - await AssertEx.ThrowsAsync(() => - _conn.StartTransactionAsync(stream, ExpectedVersion.Any)); + var client = CreateClient(out var channel, out var httpClient); + using (channel) + using (httpClient) + using (var call = client.DeleteAsync(new DeleteReq + { + Options = new() + { + Any = new Empty(), + StreamIdentifier = new() { StreamName = ByteString.CopyFromUtf8(nameof(delete_stream_is_rejected)) } + } + }, GetCallOptions())) + { + var exception = Assert.ThrowsAsync(async () => await call.ResponseAsync); + Assert.That(exception.StatusCode, Is.EqualTo(StatusCode.NotFound)); + } } } diff --git a/src/EventStore.Core.Tests/Services/RequestForwarding/GrpcRequestForwardingServiceTests.cs b/src/EventStore.Core.Tests/Services/RequestForwarding/GrpcRequestForwardingServiceTests.cs index e861b74159..70d7ef4a93 100644 --- a/src/EventStore.Core.Tests/Services/RequestForwarding/GrpcRequestForwardingServiceTests.cs +++ b/src/EventStore.Core.Tests/Services/RequestForwarding/GrpcRequestForwardingServiceTests.cs @@ -791,10 +791,6 @@ private static MemberInfo CreateLeader(Guid? instanceId = null, int httpPort = 2 DateTime.UtcNow, VNodeState.Leader, true, - new DnsEndPoint("leader-replication.internal", 1112), - new DnsEndPoint("leader-replication.internal", 1113), - null, - null, new DnsEndPoint("leader.internal", httpPort), null, 0, @@ -803,7 +799,6 @@ private static MemberInfo CreateLeader(Guid? instanceId = null, int httpPort = 2 0, 0, 0, - 0, Guid.NewGuid(), 0, false); diff --git a/src/EventStore.Core.Tests/Services/RequestForwarding/GrpcRequestForwardingTransportSecurityTests.cs b/src/EventStore.Core.Tests/Services/RequestForwarding/GrpcRequestForwardingTransportSecurityTests.cs index 17fe60ab3f..50186afc83 100644 --- a/src/EventStore.Core.Tests/Services/RequestForwarding/GrpcRequestForwardingTransportSecurityTests.cs +++ b/src/EventStore.Core.Tests/Services/RequestForwarding/GrpcRequestForwardingTransportSecurityTests.cs @@ -53,7 +53,7 @@ public async Task cleartext_credential_rejection_completes_the_proxy_correlation supervisor.Handle(new ClientMessage.ForwardMessage(request)); - var response = publisher.Messages.OfType().Single(); + var response = publisher.Messages.OfType().Single(); Assert.That(response.CorrelationId, Is.EqualTo(request.InternalCorrId)); } @@ -119,10 +119,6 @@ private static MemberInfo CreateLeader() => MemberInfo.ForVNode( DateTime.UtcNow, VNodeState.Leader, true, - new DnsEndPoint("leader-replication.internal", 1112), - new DnsEndPoint("leader-replication.internal", 1113), - null, - null, new DnsEndPoint("leader.internal", 2113), null, 0, @@ -131,7 +127,6 @@ private static MemberInfo CreateLeader() => MemberInfo.ForVNode( 0, 0, 0, - 0, Guid.NewGuid(), 0, false); diff --git a/src/EventStore.Core.Tests/Services/RequestForwarding/RequestForwardingServiceTests.cs b/src/EventStore.Core.Tests/Services/RequestForwarding/RequestForwardingServiceTests.cs index d0ed4eb5e5..8eb4dccb3c 100644 --- a/src/EventStore.Core.Tests/Services/RequestForwarding/RequestForwardingServiceTests.cs +++ b/src/EventStore.Core.Tests/Services/RequestForwarding/RequestForwardingServiceTests.cs @@ -55,13 +55,13 @@ public void not_authenticated_survives_the_client_correlation_rewrite() clientCorrelationId, new CallbackEnvelope(message => response = message), TimeSpan.FromMinutes(1), - new TcpMessage.NotAuthenticated(clientCorrelationId, "timeout")); + new ClientMessage.NotAuthenticated(clientCorrelationId, "timeout")); var service = new RequestForwardingService( new NoopPublisher(), forwardingProxy, TimeSpan.FromSeconds(1)); - service.Handle(new TcpMessage.NotAuthenticated(internalCorrelationId, "not authenticated")); + service.Handle(new ClientMessage.NotAuthenticated(internalCorrelationId, "not authenticated")); - var completion = (TcpMessage.NotAuthenticated)response; + var completion = (ClientMessage.NotAuthenticated)response; Assert.Multiple(() => { Assert.That(completion.CorrelationId, Is.EqualTo(clientCorrelationId)); diff --git a/src/EventStore.Core.Tests/Services/RequestManagement/Service/when_writing_and_deposed_as_leader.cs b/src/EventStore.Core.Tests/Services/RequestManagement/Service/when_writing_and_deposed_as_leader.cs index 8ef3d8d03b..6f3bf4ff30 100644 --- a/src/EventStore.Core.Tests/Services/RequestManagement/Service/when_writing_and_deposed_as_leader.cs +++ b/src/EventStore.Core.Tests/Services/RequestManagement/Service/when_writing_and_deposed_as_leader.cs @@ -42,10 +42,6 @@ private static MemberInfo FakeMemberInfo() return EventStore.Core.Cluster.MemberInfo.Initial(Guid.Empty, DateTime.UtcNow, VNodeState.Unknown, true, new IPEndPoint(IPAddress.Parse(ipAddress), port), - new IPEndPoint(IPAddress.Parse(ipAddress), port), - new IPEndPoint(IPAddress.Parse(ipAddress), port), - new IPEndPoint(IPAddress.Parse(ipAddress), port), - new IPEndPoint(IPAddress.Parse(ipAddress), port), - null, 0, 0, 0, false); + null, 0, 0, false); } } diff --git a/src/EventStore.Core.Tests/Services/RequestManagement/Service/when_writing_and_deposed_as_leader_and_replica_moves_forward.cs b/src/EventStore.Core.Tests/Services/RequestManagement/Service/when_writing_and_deposed_as_leader_and_replica_moves_forward.cs index 994a62f28d..4b7ba26312 100644 --- a/src/EventStore.Core.Tests/Services/RequestManagement/Service/when_writing_and_deposed_as_leader_and_replica_moves_forward.cs +++ b/src/EventStore.Core.Tests/Services/RequestManagement/Service/when_writing_and_deposed_as_leader_and_replica_moves_forward.cs @@ -37,10 +37,6 @@ private static MemberInfo FakeMemberInfo() return EventStore.Core.Cluster.MemberInfo.Initial(Guid.Empty, DateTime.UtcNow, VNodeState.Unknown, true, new IPEndPoint(IPAddress.Parse(ipAddress), port), - new IPEndPoint(IPAddress.Parse(ipAddress), port), - new IPEndPoint(IPAddress.Parse(ipAddress), port), - new IPEndPoint(IPAddress.Parse(ipAddress), port), - new IPEndPoint(IPAddress.Parse(ipAddress), port), - null, 0, 0, 0, false); + null, 0, 0, false); } } diff --git a/src/EventStore.Core.Tests/Services/Transport/Grpc/Forwarding/ForwardingGrpcCodecTests.cs b/src/EventStore.Core.Tests/Services/Transport/Grpc/Forwarding/ForwardingGrpcCodecTests.cs index 828810fab5..e4396f5693 100644 --- a/src/EventStore.Core.Tests/Services/Transport/Grpc/Forwarding/ForwardingGrpcCodecTests.cs +++ b/src/EventStore.Core.Tests/Services/Transport/Grpc/Forwarding/ForwardingGrpcCodecTests.cs @@ -415,8 +415,6 @@ public void not_handled_leader_info_round_trips() Guid.NewGuid(), ClientMessage.NotHandled.Types.NotHandledReason.NotLeader, new ClientMessage.NotHandled.Types.LeaderInfo( - new DnsEndPoint("leader-tcp.internal", 1113), - true, new DnsEndPoint("leader-http.internal", 2113))); var decoded = RoundTripResponse(message); @@ -425,28 +423,6 @@ public void not_handled_leader_info_round_trips() { Assert.That(decoded.CorrelationId, Is.EqualTo(message.CorrelationId)); Assert.That(decoded.Reason, Is.EqualTo(message.Reason)); - Assert.That(decoded.LeaderInfo.IsSecure, Is.True); - Assert.That(decoded.LeaderInfo.ExternalTcp, Is.EqualTo(message.LeaderInfo.ExternalTcp)); - Assert.That(decoded.LeaderInfo.Http, Is.EqualTo(message.LeaderInfo.Http)); - }); - } - - [Test] - public void not_handled_leader_info_without_external_tcp_round_trips_as_null() - { - var message = new ClientMessage.NotHandled( - Guid.NewGuid(), - ClientMessage.NotHandled.Types.NotHandledReason.NotLeader, - new ClientMessage.NotHandled.Types.LeaderInfo( - null, - false, - new DnsEndPoint("leader-http.internal", 2113))); - - var decoded = RoundTripResponse(message); - - Assert.Multiple(() => - { - Assert.That(decoded.LeaderInfo.ExternalTcp, Is.Null); Assert.That(decoded.LeaderInfo.Http, Is.EqualTo(message.LeaderInfo.Http)); }); } @@ -454,9 +430,9 @@ public void not_handled_leader_info_without_external_tcp_round_trips_as_null() [Test] public void not_authenticated_round_trips() { - var message = new TcpMessage.NotAuthenticated(Guid.NewGuid(), "not authenticated"); + var message = new ClientMessage.NotAuthenticated(Guid.NewGuid(), "not authenticated"); - var decoded = RoundTripResponse(message); + var decoded = RoundTripResponse(message); Assert.Multiple(() => { diff --git a/src/EventStore.Core.Tests/Services/Transport/Grpc/Replication/GrpcReplicaServiceFactoryTests.cs b/src/EventStore.Core.Tests/Services/Transport/Grpc/Replication/GrpcReplicaServiceFactoryTests.cs index 19697515ac..91ba8c4a35 100644 --- a/src/EventStore.Core.Tests/Services/Transport/Grpc/Replication/GrpcReplicaServiceFactoryTests.cs +++ b/src/EventStore.Core.Tests/Services/Transport/Grpc/Replication/GrpcReplicaServiceFactoryTests.cs @@ -37,7 +37,30 @@ public void replication_client_uses_node_certificate_names_and_owns_its_http_cli Assert.That(nodeHttpClientFactory.AdditionalCertificateNames, Is.EqualTo(new[] { "cluster.internal" })); client.Dispose(); - Assert.That(nodeHttpClientFactory.Handler.Disposed, Is.True); + Assert.That(nodeHttpClientFactory.TransportHandler.Disposed, Is.True); + } + + [Test] + public void replication_client_normalizes_legacy_sub_second_heartbeat_values() + { + var nodeHttpClientFactory = new RecordingNodeHttpClientFactory(); + var factory = new ReplicationGrpcClientFactory( + Uri.UriSchemeHttps, + nodeHttpClientFactory, + TimeSpan.FromMilliseconds(700), + TimeSpan.FromMilliseconds(700)); + + using var client = factory.Create(new DnsEndPoint("leader.internal", 1112)); + + Assert.Multiple(() => + { + Assert.That(nodeHttpClientFactory.SocketsHandler.KeepAlivePingDelay, + Is.EqualTo(TimeSpan.FromSeconds(1))); + Assert.That(nodeHttpClientFactory.SocketsHandler.KeepAlivePingTimeout, + Is.EqualTo(TimeSpan.FromSeconds(1))); + Assert.That(nodeHttpClientFactory.SocketsHandler.KeepAlivePingPolicy, + Is.EqualTo(HttpKeepAlivePingPolicy.Always)); + }); } [Test] @@ -99,7 +122,8 @@ private sealed class TrackingReplicationGrpcClientFactory : IReplicationGrpcClie private sealed class RecordingNodeHttpClientFactory : INodeHttpClientFactory { - public RecordingHttpMessageHandler Handler { get; } = new(); + public RecordingHttpMessageHandler TransportHandler { get; } = new(); + public SocketsHttpHandler SocketsHandler { get; } = new(); public string[] AdditionalCertificateNames { get; private set; } public HttpClient CreateHttpClient( @@ -107,7 +131,8 @@ public HttpClient CreateHttpClient( Action configureSocketsHttpHandler = null) { AdditionalCertificateNames = additionalCertificateNames; - return new HttpClient(Handler); + configureSocketsHttpHandler?.Invoke(SocketsHandler); + return new HttpClient(TransportHandler); } } diff --git a/src/EventStore.Core.Tests/Services/Transport/Grpc/Replication/GrpcReplicaServiceSupervisorTests.cs b/src/EventStore.Core.Tests/Services/Transport/Grpc/Replication/GrpcReplicaServiceSupervisorTests.cs index a6eaedf281..dcd662b4f8 100644 --- a/src/EventStore.Core.Tests/Services/Transport/Grpc/Replication/GrpcReplicaServiceSupervisorTests.cs +++ b/src/EventStore.Core.Tests/Services/Transport/Grpc/Replication/GrpcReplicaServiceSupervisorTests.cs @@ -22,7 +22,7 @@ public class GrpcReplicaServiceSupervisorTests [TestCase(false)] [TestCase(true)] - public async Task pre_replica_state_starts_and_tracks_a_stream_for_the_advertised_http_endpoints( + public async Task pre_replica_state_starts_and_tracks_a_stream_for_the_advertised_replication_endpoints( bool readOnlyReplica) { var fixture = CreateFixture(); @@ -37,7 +37,7 @@ public async Task pre_replica_state_starts_and_tracks_a_stream_for_the_advertise var request = fixture.Factory.Requests.Single(); Assert.Multiple(() => { - Assert.That(request.Endpoints.LeaderEndPoint, Is.EqualTo(fixture.Leader.HttpEndPoint)); + Assert.That(request.Endpoints.LeaderEndPoint, Is.EqualTo(fixture.Leader.ReplicationEndPoint)); Assert.That(request.Endpoints.AdvertisedReplicaEndPoint, Is.EqualTo(fixture.AdvertisedEndPoint)); Assert.That(request.Service.StartCalls, Is.EqualTo(1)); Assert.That(fixture.TrackedTasks.Single(), Is.SameAs(request.Service.Task)); @@ -410,7 +410,7 @@ private static Fixture CreateFixture( startException, createException, beforeCreateReturns); - var advertisedEndPoint = new DnsEndPoint("replica.internal", 2113); + var advertisedEndPoint = new DnsEndPoint("replica.internal", 1112); var trackedTasks = new List(); var supervisor = new GrpcReplicaServiceSupervisor( publisher, @@ -432,10 +432,6 @@ private static MemberInfo CreateLeader() => MemberInfo.ForVNode( DateTime.UtcNow, VNodeState.Leader, true, - new DnsEndPoint("leader-replication.internal", 1112), - null, - null, - null, new DnsEndPoint("leader.internal", 2113), null, 0, @@ -444,10 +440,10 @@ private static MemberInfo CreateLeader() => MemberInfo.ForVNode( 0, 0, 0, - 0, Guid.NewGuid(), 0, - false); + false, + replicationEndPoint: new DnsEndPoint("leader.replication.internal", 1112)); private static SystemMessage.StateChangeMessage CreateReplicaState( VNodeState state, diff --git a/src/EventStore.Core.Tests/Services/Transport/Tcp/core_tcp_package.cs b/src/EventStore.Core.Tests/Services/Transport/Tcp/core_tcp_package.cs deleted file mode 100644 index ffcb22e037..0000000000 --- a/src/EventStore.Core.Tests/Services/Transport/Tcp/core_tcp_package.cs +++ /dev/null @@ -1,199 +0,0 @@ -using System; -using EventStore.Core.Services.Transport.Tcp; -using NUnit.Framework; - -namespace EventStore.Core.Tests.Services.Transport.Tcp; - -[TestFixture] -public class core_tcp_package -{ - [Test] - public void should_throw_argument_null_exception_when_created_as_authorized_but_login_not_provided() - { - Assert.Throws(() => - new TcpPackage(TcpCommand.BadRequest, TcpFlags.Authenticated, Guid.NewGuid(), null, "pa$$", - new byte[] { 1, 2, 3 })); - } - - [Test] - public void should_throw_argument_null_exception_when_created_as_authorized_but_password_not_provided() - { - Assert.Throws(() => - new TcpPackage(TcpCommand.BadRequest, TcpFlags.Authenticated, Guid.NewGuid(), "login", null, - new byte[] { 1, 2, 3 })); - } - - [Test] - public void should_throw_argument_null_exception_when_created_as_authorized_but_token_not_provided() - { - Assert.Throws(() => - new TcpPackage(TcpCommand.BadRequest, TcpFlags.Authenticated, Guid.NewGuid(), null, - new byte[] { 1, 2, 3 })); - } - - [Test] - public void should_throw_argument_exception_when_created_as_not_authorized_but_login_is_provided() - { - Assert.Throws(() => - new TcpPackage(TcpCommand.BadRequest, TcpFlags.None, Guid.NewGuid(), "login", null, - new byte[] { 1, 2, 3 })); - } - - [Test] - public void should_throw_argument_exception_when_created_as_not_authorized_but_password_is_provided() - { - Assert.Throws(() => - new TcpPackage(TcpCommand.BadRequest, TcpFlags.None, Guid.NewGuid(), null, "pa$$", - new byte[] { 1, 2, 3 })); - } - - [Test] - public void should_throw_argument_exception_when_created_as_not_authorized_but_token_is_provided() - { - Assert.Throws(() => - new TcpPackage(TcpCommand.BadRequest, TcpFlags.None, Guid.NewGuid(), "token", - new byte[] { 1, 2, 3 })); - } - - [Test] - public void not_authorized_with_data_should_serialize_and_deserialize_correctly() - { - var corrId = Guid.NewGuid(); - var refPkg = new TcpPackage(TcpCommand.BadRequest, TcpFlags.None, corrId, null, null, new byte[] { 1, 2, 3 }); - var bytes = refPkg.AsArraySegment(); - - var pkg = TcpPackage.FromArraySegment(bytes); - Assert.AreEqual(TcpCommand.BadRequest, pkg.Command); - Assert.AreEqual(TcpFlags.None, pkg.Flags); - Assert.AreEqual(corrId, pkg.CorrelationId); - Assert.False(pkg.Tokens.TryGetValue("uid", out _)); - Assert.False(pkg.Tokens.TryGetValue("pwd", out _)); - Assert.False(pkg.Tokens.TryGetValue("jwt", out _)); - - Assert.AreEqual(3, pkg.Data.Count); - Assert.AreEqual(1, pkg.Data.Array[pkg.Data.Offset + 0]); - Assert.AreEqual(2, pkg.Data.Array[pkg.Data.Offset + 1]); - Assert.AreEqual(3, pkg.Data.Array[pkg.Data.Offset + 2]); - } - - [Test] - public void not_authorized_with_empty_data_should_serialize_and_deserialize_correctly() - { - var corrId = Guid.NewGuid(); - var refPkg = new TcpPackage(TcpCommand.BadRequest, TcpFlags.None, corrId, null, null, new byte[0]); - var bytes = refPkg.AsArraySegment(); - - var pkg = TcpPackage.FromArraySegment(bytes); - Assert.AreEqual(TcpCommand.BadRequest, pkg.Command); - Assert.AreEqual(TcpFlags.None, pkg.Flags); - Assert.AreEqual(corrId, pkg.CorrelationId); - Assert.False(pkg.Tokens.TryGetValue("uid", out _)); - Assert.False(pkg.Tokens.TryGetValue("pwd", out _)); - Assert.False(pkg.Tokens.TryGetValue("jwt", out _)); - - Assert.AreEqual(0, pkg.Data.Count); - } - - [Test] - public void authorized_with_data_should_serialize_and_deserialize_correctly() - { - var corrId = Guid.NewGuid(); - var refPkg = new TcpPackage(TcpCommand.BadRequest, TcpFlags.Authenticated, corrId, "login", "pa$$", - new byte[] { 1, 2, 3 }); - var bytes = refPkg.AsArraySegment(); - - var pkg = TcpPackage.FromArraySegment(bytes); - Assert.AreEqual(TcpCommand.BadRequest, pkg.Command); - Assert.AreEqual(TcpFlags.Authenticated, pkg.Flags); - Assert.AreEqual(corrId, pkg.CorrelationId); - Assert.AreEqual("login", pkg.Tokens["uid"]); - Assert.AreEqual("pa$$", pkg.Tokens["pwd"]); - Assert.False(pkg.Tokens.TryGetValue("jwt", out _)); - - Assert.AreEqual(3, pkg.Data.Count); - Assert.AreEqual(1, pkg.Data.Array[pkg.Data.Offset + 0]); - Assert.AreEqual(2, pkg.Data.Array[pkg.Data.Offset + 1]); - Assert.AreEqual(3, pkg.Data.Array[pkg.Data.Offset + 2]); - } - - [Test] - public void authorized_with_empty_data_should_serialize_and_deserialize_correctly() - { - var corrId = Guid.NewGuid(); - var refPkg = new TcpPackage(TcpCommand.BadRequest, TcpFlags.Authenticated, corrId, "login", "pa$$", - new byte[0]); - var bytes = refPkg.AsArraySegment(); - - var pkg = TcpPackage.FromArraySegment(bytes); - Assert.AreEqual(TcpCommand.BadRequest, pkg.Command); - Assert.AreEqual(TcpFlags.Authenticated, pkg.Flags); - Assert.AreEqual(corrId, pkg.CorrelationId); - Assert.AreEqual("login", pkg.Tokens["uid"]); - Assert.AreEqual("pa$$", pkg.Tokens["pwd"]); - Assert.False(pkg.Tokens.TryGetValue("jwt", out _)); - - Assert.AreEqual(0, pkg.Data.Count); - } - - [Test] - public void token_authorized_with_data_should_serialize_and_deserialize_correctly() - { - var corrId = Guid.NewGuid(); - var refPkg = new TcpPackage(TcpCommand.BadRequest, TcpFlags.Authenticated, corrId, "token", - new byte[] { 1, 2, 3 }); - var bytes = refPkg.AsArraySegment(); - - var pkg = TcpPackage.FromArraySegment(bytes); - Assert.AreEqual(TcpCommand.BadRequest, pkg.Command); - Assert.AreEqual(TcpFlags.Authenticated, pkg.Flags); - Assert.AreEqual(corrId, pkg.CorrelationId); - Assert.AreEqual("token", pkg.Tokens["jwt"]); - Assert.False(pkg.Tokens.TryGetValue("uid", out _)); - Assert.False(pkg.Tokens.TryGetValue("pwd", out _)); - - Assert.AreEqual(3, pkg.Data.Count); - Assert.AreEqual(1, pkg.Data.Array[pkg.Data.Offset + 0]); - Assert.AreEqual(2, pkg.Data.Array[pkg.Data.Offset + 1]); - Assert.AreEqual(3, pkg.Data.Array[pkg.Data.Offset + 2]); - } - - [Test] - public void token_authorized_with_empty_data_should_serialize_and_deserialize_correctly() - { - var corrId = Guid.NewGuid(); - var refPkg = new TcpPackage(TcpCommand.BadRequest, TcpFlags.Authenticated, corrId, "token", - new byte[0]); - var bytes = refPkg.AsArraySegment(); - - var pkg = TcpPackage.FromArraySegment(bytes); - Assert.AreEqual(TcpCommand.BadRequest, pkg.Command); - Assert.AreEqual(TcpFlags.Authenticated, pkg.Flags); - Assert.AreEqual(corrId, pkg.CorrelationId); - Assert.AreEqual("token", pkg.Tokens["jwt"]); - Assert.False(pkg.Tokens.TryGetValue("uid", out _)); - Assert.False(pkg.Tokens.TryGetValue("pwd", out _)); - - Assert.AreEqual(0, pkg.Data.Count); - } - - [Test] - public void should_throw_argument_exception_when_login_too_long() - { - Assert.Throws(() => new TcpPackage(TcpCommand.BadRequest, TcpFlags.Authenticated, - Guid.NewGuid(), new string('*', TcpPackage.MaxLoginLength + 1), "pa$$", new byte[] { 1, 2, 3 })); - } - - [Test] - public void should_throw_argument_exception_when_password_too_long() - { - Assert.Throws(() => new TcpPackage(TcpCommand.BadRequest, TcpFlags.Authenticated, - Guid.NewGuid(), "login", new string('*', TcpPackage.MaxPasswordLength + 1), new byte[] { 1, 2, 3 })); - } - - [Test] - public void should_throw_argument_exception_when_token_too_long() - { - Assert.Throws(() => new TcpPackage(TcpCommand.BadRequest, TcpFlags.Authenticated, - Guid.NewGuid(), new string('*', TcpPackage.MaxTokenLength + 1), new byte[] { 1, 2, 3 })); - } -} diff --git a/src/EventStore.Core.Tests/Services/Transport/Tcp/ssl_connection.cs b/src/EventStore.Core.Tests/Services/Transport/Tcp/ssl_connection.cs deleted file mode 100644 index f6165d0f82..0000000000 --- a/src/EventStore.Core.Tests/Services/Transport/Tcp/ssl_connection.cs +++ /dev/null @@ -1,174 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Net; -using System.Reflection; -using System.Security.Cryptography; -using System.Security.Cryptography.X509Certificates; -using System.Text; -using System.Threading; -using EventStore.Common.Utils; -using EventStore.Core.Services.Transport.Tcp; -using EventStore.Core.Tests.Helpers; -using EventStore.Transport.Tcp; -using NUnit.Framework; -using ILogger = Serilog.ILogger; - -namespace EventStore.Core.Tests.Services.Transport.Tcp; - -[TestFixture] -public class ssl_connections -{ - private static readonly ILogger Log = Serilog.Log.ForContext(); - private IPAddress _ip; - private int _port; - - [SetUp] - public void SetUp() - { - _ip = IPAddress.Loopback; - _port = PortsHelper.GetAvailablePort(_ip); - } - - [Test] - public void should_connect_to_each_other_and_send_data() - { - var serverEndPoint = new IPEndPoint(_ip, _port); - X509Certificate2 cert = GetServerCertificate(); - - var sent = new byte[1000]; - new Random().NextBytes(sent); - - using var received = new MemoryStream(); - - var done = new ManualResetEventSlim(); - - var listener = new TcpServerListener(serverEndPoint); - listener.StartListening((endPoint, socket) => - { - var ssl = TcpConnectionSsl.CreateServerFromSocket(Guid.NewGuid(), endPoint, socket, - () => cert, null, delegate - { return (true, null); }, - verbose: true); - ssl.ConnectionClosed += (x, y) => done.Set(); - if (ssl.IsClosed) - { - done.Set(); - } - - Action>> callback = null; - callback = (x, y) => - { - foreach (var arraySegment in y) - { - received.Write(arraySegment.Array, arraySegment.Offset, arraySegment.Count); - Log.Information("Received: {0} bytes, total: {1}.", arraySegment.Count, received.Length); - } - - if (received.Length >= sent.Length) - { - Log.Information("Done receiving..."); - done.Set(); - } - else - { - Log.Information("Receiving..."); - ssl.ReceiveAsync(callback); - } - }; - Log.Information("Receiving..."); - ssl.ReceiveAsync(callback); - }, "Secure"); - - var clientSsl = TcpConnectionSsl.CreateConnectingConnection( - Guid.NewGuid(), - serverEndPoint.GetHost(), - null, - serverEndPoint, - delegate - { return (true, null); }, - null, - new TcpClientConnector(), - TcpConnectionManager.ConnectionTimeout, - conn => - { - Log.Information("Sending bytes..."); - conn.EnqueueSend(new[] { new ArraySegment(sent) }); - }, - (conn, err) => - { - Log.Error("Connecting failed: {0}.", err); - done.Set(); - }, - verbose: true); - - Assert.IsTrue(done.Wait(20000), "Took too long to receive completion."); - - Log.Information("Stopping listener..."); - listener.Stop(); - Log.Information("Closing client TLS connection..."); - clientSsl.Close("Normal close."); - Log.Information("Checking received data..."); - Assert.AreEqual(sent, received.ToArray()); - } - - private static X509Certificate2 _root, _server, _otherServer, _untrusted; - public static X509Certificate2 GetRootCertificate() - { - _root ??= GetCertificate("ca", loadKey: false); - return new X509Certificate2(_root); - } - - public static X509Certificate2 GetServerCertificate() - { - _server ??= GetCertificate("node1"); - return new X509Certificate2(_server); - } - - public static X509Certificate2 GetOtherServerCertificate() - { - _otherServer ??= GetCertificate("node2"); - return new X509Certificate2(_otherServer); - } - - public static X509Certificate2 GetUntrustedCertificate() - { - _untrusted ??= GetCertificate("untrusted"); - return new X509Certificate2(_untrusted); - } - - private static X509Certificate2 GetCertificate(string name, bool loadKey = true) - { - const string resourcePath = "EventStore.Core.Tests.Services.Transport.Tcp.test_certificates"; - - var certBytes = LoadResource($"{resourcePath}.{name}.{name}.crt"); - var certificate = X509Certificate2.CreateFromPem(Encoding.UTF8.GetString(certBytes)); - - if (!loadKey) - { - return certificate; - } - - var keyBytes = LoadResource($"{resourcePath}.{name}.{name}.key"); - using var rsa = RSA.Create(); - rsa.ImportFromPem(Encoding.UTF8.GetString(keyBytes)); - - using X509Certificate2 certWithKey = certificate.CopyWithPrivateKey(rsa); - - // recreate the certificate from a PKCS #12 bundle to work around: https://github.com/dotnet/runtime/issues/23749 - return X509CertificateLoader.LoadPkcs12(certWithKey.ExportToPkcs12(), string.Empty, X509KeyStorageFlags.Exportable); - } - - private static byte[] LoadResource(string resource) - { - using var resourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resource); - if (resourceStream == null) - { - return null; - } - - using var memStream = new MemoryStream(); - resourceStream.CopyTo(memStream); - return memStream.ToArray(); - } -} diff --git a/src/EventStore.Core.Tests/Services/Transport/Tcp/ssl_connections_mutual_auth.cs b/src/EventStore.Core.Tests/Services/Transport/Tcp/ssl_connections_mutual_auth.cs deleted file mode 100644 index 1a80ae435e..0000000000 --- a/src/EventStore.Core.Tests/Services/Transport/Tcp/ssl_connections_mutual_auth.cs +++ /dev/null @@ -1,149 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net; -using System.Net.Security; -using System.Reflection; -using System.Security.Cryptography.X509Certificates; -using System.Threading; -using EventStore.Common.Utils; -using EventStore.Core.Services.Transport.Tcp; -using EventStore.Core.Tests.Helpers; -using EventStore.Transport.Tcp; -using NUnit.Framework; -using Serilog; - -namespace EventStore.Core.Tests.Services.Transport.Tcp; - -[TestFixture] -public class ssl_connections_mutual_auth -{ - private static readonly ILogger Log = Serilog.Log.ForContext(); - private IPAddress _ip; - private int _port; - - [SetUp] - public void SetUp() - { - _ip = IPAddress.Loopback; - _port = PortsHelper.GetAvailablePort(_ip); - } - - [TestCase(true, true, true, true, true)] //require valid server and client certificate - [TestCase(true, false, true, true, false)] //require valid server and client certificate - [TestCase(false, true, true, true, false)] //require valid server and client certificate - [TestCase(false, false, true, true, false)] //require valid server and client certificate - [TestCase(true, true, true, false, true)] //require valid server certificate only - [TestCase(true, false, true, false, true)] //require valid server certificate only - [TestCase(false, true, true, false, false)] //require valid server certificate only - [TestCase(false, false, true, false, false)] //require valid server certificate only - [TestCase(true, true, false, true, true)] //require valid client certificate only - [TestCase(true, false, false, true, false)] //require valid client certificate only - [TestCase(false, true, false, true, true)] //require valid client certificate only - [TestCase(false, false, false, true, false)] //require valid client certificate only - [TestCase(true, true, false, false, true)] //do not require valid client or server certificate - [TestCase(true, false, false, false, true)] //do not require valid client or server certificate - [TestCase(false, true, false, false, true)] //do not require valid client or server certificate - [TestCase(false, false, false, false, true)] //do not require valid client or server certificate - public void should_connect_to_each_other_and_send_data_depending_on_certificate_validity_and_settings( - bool useValidServerCertificate, - bool useValidClientCertificate, - bool validateServerCertificate, - bool validateClientCertificate, - bool shouldConnectSuccessfully - ) - { - var serverEndPoint = new IPEndPoint(_ip, _port); - var serverCertificate = useValidServerCertificate - ? ssl_connections.GetServerCertificate() - : ssl_connections.GetUntrustedCertificate(); - var clientCertificate = useValidClientCertificate - ? ssl_connections.GetOtherServerCertificate() - : ssl_connections.GetUntrustedCertificate(); - var rootCertificates = new X509Certificate2Collection(ssl_connections.GetRootCertificate()); - - - var sent = new byte[1000]; - new Random().NextBytes(sent); - - var received = new MemoryStream(); - - var done = new ManualResetEventSlim(); - - var listener = new TcpServerListener(serverEndPoint); - listener.StartListening((endPoint, socket) => - { - var ssl = TcpConnectionSsl.CreateServerFromSocket(Guid.NewGuid(), endPoint, socket, () => serverCertificate, - null, (cert, chain, err) => validateClientCertificate ? - ClusterVNode.ValidateClientCertificate(cert, chain, err, () => null, () => rootCertificates) : (true, null), - verbose: true); - ssl.ConnectionClosed += (x, y) => done.Set(); - if (ssl.IsClosed) - { - done.Set(); - } - - Action>> callback = null; - callback = (x, y) => - { - foreach (var arraySegment in y) - { - received.Write(arraySegment.Array, arraySegment.Offset, arraySegment.Count); - Log.Information("Received: {0} bytes, total: {1}.", arraySegment.Count, received.Length); - } - - if (received.Length >= sent.Length) - { - Log.Information("Done receiving..."); - done.Set(); - } - else - { - Log.Information("Receiving..."); - ssl.ReceiveAsync(callback); - } - }; - Log.Information("Receiving..."); - ssl.ReceiveAsync(callback); - }, "Secure"); - - var clientSsl = TcpConnectionSsl.CreateConnectingConnection( - Guid.NewGuid(), - serverEndPoint.GetHost(), - null, - serverEndPoint, - (cert, chain, err, _) => validateServerCertificate ? ClusterVNode.ValidateServerCertificate(cert, chain, err, () => null, () => rootCertificates, null) : (true, null), - () => new X509CertificateCollection { clientCertificate }, - new TcpClientConnector(), - TcpConnectionManager.ConnectionTimeout, - conn => - { - Log.Information("Sending bytes..."); - conn.EnqueueSend(new[] { new ArraySegment(sent) }); - }, - (conn, err) => - { - Log.Error("Connecting failed: {0}.", err); - done.Set(); - }, - verbose: true); - - Assert.IsTrue(done.Wait(20000), "Took too long to receive completion."); - - Log.Information("Stopping listener..."); - listener.Stop(); - Log.Information("Closing client TLS connection..."); - clientSsl.Close("Normal close."); - Log.Information("Checking received data..."); - - if (shouldConnectSuccessfully) - { - Assert.AreEqual(sent, received.ToArray()); - } - else - { - Assert.AreEqual(new byte[0], received.ToArray()); - } - } -} diff --git a/src/EventStore.Core.Tests/Services/VNode/InaugurationManager/InaugurationManagerTests.cs b/src/EventStore.Core.Tests/Services/VNode/InaugurationManager/InaugurationManagerTests.cs index 8ae7429f0c..fc9ce572a8 100644 --- a/src/EventStore.Core.Tests/Services/VNode/InaugurationManager/InaugurationManagerTests.cs +++ b/src/EventStore.Core.Tests/Services/VNode/InaugurationManager/InaugurationManagerTests.cs @@ -20,8 +20,7 @@ public abstract class InaugurationManagerTests protected readonly MemberInfo _leader = MemberInfo.ForVNode( default, default, default, default, - new DnsEndPoint("localhost", default), default, default, default, - new DnsEndPoint("localhost", default), default, default, default, + new DnsEndPoint("localhost", default), default, default, default, default, default, default, default, default, default, default); protected readonly long _replicationTarget = 400; protected readonly long _indexTarget = 400; diff --git a/src/EventStore.Core.Tests/Services/VNode/ShutdownServiceTests.cs b/src/EventStore.Core.Tests/Services/VNode/ShutdownServiceTests.cs index f2adb06c98..d1011fa934 100644 --- a/src/EventStore.Core.Tests/Services/VNode/ShutdownServiceTests.cs +++ b/src/EventStore.Core.Tests/Services/VNode/ShutdownServiceTests.cs @@ -1,5 +1,4 @@ using System; -using System.Net; using DotNext.Net.Http; using EventStore.Core.Data; using EventStore.Core.Messages; @@ -16,10 +15,6 @@ public class ShutdownServiceTests = new( Guid.NewGuid(), 0, - new IPEndPoint(0, 0), - new IPEndPoint(IPAddress.Loopback, 1), - new IPEndPoint(IPAddress.Loopback, 2), - new IPEndPoint(IPAddress.Loopback, 3), new HttpEndPoint(new Uri("http://www.trogondb.com")), true); [Test] diff --git a/src/EventStore.Core.Tests/Services/VNode/leader_info_provider.cs b/src/EventStore.Core.Tests/Services/VNode/leader_info_provider.cs index 68de1f0112..9a34d143df 100644 --- a/src/EventStore.Core.Tests/Services/VNode/leader_info_provider.cs +++ b/src/EventStore.Core.Tests/Services/VNode/leader_info_provider.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Net; -using EventStore.Common.Utils; using EventStore.Core.Cluster; using EventStore.Core.Data; using EventStore.Core.Services.VNode; @@ -13,252 +11,45 @@ namespace EventStore.Core.Tests.Services.VNode; [TestFixture] public class leader_info_provider { - private const string DefaultHttpEndPoint = "9.9.9.9:9"; - - public static IEnumerable TestCases() - { - var leaderInfoCases = new TestCase[] { - new ("Leader: NoEndPoints", - Given: new (Leader: new()), - Expected: new (TcpEndPoint: null)), - new ("Leader: TcpEndPoint", - Given: new (Leader: new(TcpEndPoint: "1.1.1.1:1")), - Expected: new (TcpEndPoint: "1.1.1.1:1", IsTcpSecure: false)), - new ("Leader: SecureTcpEndPoint", - Given: new (Leader: new(SecureTcpEndPoint: "2.2.2.2:2")), - Expected: new (TcpEndPoint: "2.2.2.2:2", IsTcpSecure: true)), - new ("Leader: TcpEndPoint + SecureTcpEndPoint", - Given: new (Leader: new(TcpEndPoint: "1.1.1.1:1", SecureTcpEndPoint: "2.2.2.2:2")), - Expected: new (TcpEndPoint: "1.1.1.1:1", IsTcpSecure: true)), - // AdvertiseTcpPort - new ("Leader: AdvertiseTcpPort & NoEndPoints", - Given: new (Leader: new(AdvertiseTcpPort: 9)), - Expected: new (TcpEndPoint: null, IsTcpSecure: false)), - new ("Leader: AdvertiseTcpPort & TcpEndPoint", - Given: new (Leader: new(AdvertiseTcpPort: 9, TcpEndPoint: "1.1.1.1:1")), - Expected: new (TcpEndPoint: "1.1.1.1:9")), - new ("Leader: AdvertiseTcpPort & SecureTcpEndPoint", - Given: new (Leader: new(AdvertiseTcpPort: 9, SecureTcpEndPoint: "2.2.2.2:2")), - Expected: new (TcpEndPoint: "2.2.2.2:9", IsTcpSecure: true)), - new ("Leader: AdvertiseTcpPort & TcpEndPoint + SecureTcpEndPoint", - Given: new (Leader: new(TcpEndPoint: "1.1.1.1:1", SecureTcpEndPoint: "2.2.2.2:2", AdvertiseTcpPort: 9)), - Expected: new (TcpEndPoint: "1.1.1.1:9", IsTcpSecure: true)), - // AdvertiseHost - new ("Leader: AdvertiseHost & NoEndPoints", - Given: new (Leader: new(AdvertiseHost: "host", HttpEndPoint: "3.3.3.3:3")), - Expected: new (TcpEndPoint: null, IsTcpSecure: false, HttpEndPoint: "host:3")), - new ("Leader: AdvertiseHost & TcpEndPoint", - Given: new (Leader: new(AdvertiseHost: "host", TcpEndPoint: "1.1.1.1:1", HttpEndPoint: "3.3.3.3:3")), - Expected: new (TcpEndPoint: "host:1", HttpEndPoint: "host:3")), - new ("Leader: AdvertiseHost & SecureTcpEndPoint", - Given: new (Leader: new(AdvertiseHost: "host", SecureTcpEndPoint: "2.2.2.2:2", HttpEndPoint: "3.3.3.3:3")), - Expected: new (TcpEndPoint: "host:2", IsTcpSecure: true, HttpEndPoint: "host:3")), - new ("Leader: AdvertiseHost & TcpEndPoint + SecureTcpEndPoint", - Given: new (Leader: new(AdvertiseHost: "host", TcpEndPoint: "1.1.1.1:1", SecureTcpEndPoint: "2.2.2.2:2", HttpEndPoint: "3.3.3.3:3")), - Expected: new (TcpEndPoint: "host:1", IsTcpSecure: true, HttpEndPoint: "host:3")), //?? is this intended, secure with port of insecure - // AdvertiseHost + AdvertisePort - new ("Leader: AdvertiseHost + Port & NoEndPoints", - Given: new (Leader: new(AdvertiseHost: "host", AdvertiseTcpPort: 9, HttpEndPoint: "3.3.3.3:3")), - Expected: new (TcpEndPoint: null, IsTcpSecure: false, HttpEndPoint: "host:3")), - new ("Leader: AdvertiseHost + Port & TcpEndPoint", - Given: new (Leader: new(AdvertiseHost: "host", AdvertiseTcpPort: 9, TcpEndPoint: "1.1.1.1:1", HttpEndPoint: "3.3.3.3:3")), - Expected: new (TcpEndPoint: "host:9", IsTcpSecure: false, HttpEndPoint: "host:3")), - new ("Leader: AdvertiseHost + Port & SecureTcpEndPoint", - Given: new (Leader: new(AdvertiseHost: "host", AdvertiseTcpPort: 9, SecureTcpEndPoint: "2.2.2.2:2", HttpEndPoint: "3.3.3.3:3")), - Expected: new (TcpEndPoint: "host:9", IsTcpSecure: true, HttpEndPoint: "host:3")), - // AdvertiseHttpPort - new ("Leader: AdvertiseHttpPort & NoEndPoints", - Given: new (Leader: new(AdvertiseHttpPort: 8, HttpEndPoint: "3.3.3.3:3")), - Expected: new (HttpEndPoint: "3.3.3.3:8")), - new ("Leader: AdvertiseHttpPort & AdvertiseHost", - Given: new (Leader: new(AdvertiseHttpPort: 8, AdvertiseHost: "host", HttpEndPoint: "3.3.3.3:3")), - Expected: new (HttpEndPoint: "host:8")), - }; - - var nodeInfoCases = new TestCase[] { - new ("Gossip: NoEndPoints", - Given: new (Gossip: new()), - Expected: new (TcpEndPoint: null)), - new ("Gossip: TcpEndPoint", - Given: new (Gossip: new(TcpEndPoint: "4.4.4.4:4")), - Expected: new (TcpEndPoint: "4.4.4.4:4", IsTcpSecure: false)), - new ("Gossip: SecureTcpEndPoint", - Given: new (Gossip: new(SecureTcpEndPoint: "5.5.5.5:5")), - Expected: new (TcpEndPoint: "5.5.5.5:5", IsTcpSecure: true)), - // AnyIP SecureTcpEndPoint - new ("Gossip: TcpEndPoint + SecureTcpEndPoint", - Given: new (Gossip: new(TcpEndPoint: "4.4.4.4:4", SecureTcpEndPoint: "5.5.5.5:5")), - Expected: new (TcpEndPoint: "4.4.4.4:4", IsTcpSecure: true)), - // AnyIP & HttpEndPoint - new ("Gossip: HttpEndPoint", - Given: new (Gossip: new(HttpEndPoint: "8.8.8.8:8")), - Expected: new (HttpEndPoint: "8.8.8.8:8")), - new ("Gossip: HttpEndPoint & AdvertiseHost", - Given: new (Gossip: new(HttpEndPoint: "8.8.8.8:8", AdvertiseHost: "host")), - Expected: new (HttpEndPoint: "host:8")), - new ("Gossip: HttpEndPoint & AdvertisePort", - Given: new (Gossip: new(HttpEndPoint: "8.8.8.8:8", AdvertiseHttpPort: 9)), - Expected: new (HttpEndPoint: "8.8.8.8:9")), - new ("Gossip: HttpEndPoint & AdvertiseHost + Port", - Given: new (Gossip: new(HttpEndPoint: "8.8.8.8:8", AdvertiseHost: "host", AdvertiseHttpPort: 9)), - Expected: new (HttpEndPoint: "host:9")), - }; - - foreach (var testCase in leaderInfoCases.Concat(nodeInfoCases)) - { - yield return new object[] { testCase }; - } - } - - [TestCaseSource(nameof(TestCases))] - public void should_provide_as_expected(TestCase t) - { - - var given = t.BuildGiven(); - var expected = t.BuildExpected(); - - LeaderInfoProvider leaderInfoProvider = new LeaderInfoProvider( - given.GossipInfo, - given.LeaderInfo); - - var result = leaderInfoProvider.GetLeaderInfoEndPoints(); - - AssertAreEqual(expected.TcpEndPoint, result.AdvertisedTcpEndPoint, "TcpEndPoint"); - AssertAreEqual(expected.HttpEndPoint, result.AdvertisedHttpEndPoint, "HttpEndPoint"); - Assert.AreEqual(expected.IsSecure, result.IsTcpEndPointSecure); - } - - private void AssertAreEqual(EndPoint expected, EndPoint actual, string msg) - { - - if (expected == null) - { - Assert.IsNull(actual); - return; - } - - Assert.AreEqual(expected.GetHost(), actual.GetHost(), $"{msg} host"); - Assert.AreEqual(expected.GetPort(), actual.GetPort(), $"{msg} port"); - } - - public record TestCase(string Test, GivenInput Given, ExpectedInput Expected) - { - public override string ToString() => Test; - - public Given BuildGiven() => Given.Build(); - public Expected BuildExpected() => Expected.Build(); - } - - public record Given(MemberInfo LeaderInfo, GossipAdvertiseInfo GossipInfo); - - public record Expected(EndPoint TcpEndPoint, bool IsSecure, EndPoint HttpEndPoint); - - public record LeaderInput( - string TcpEndPoint = null, - string SecureTcpEndPoint = null, - string HttpEndPoint = null, - int AdvertiseTcpPort = 0, - int AdvertiseHttpPort = 0, - string AdvertiseHost = null); - - public record GossipInput( - string TcpEndPoint = null, - string SecureTcpEndPoint = null, - string HttpEndPoint = null, - int AdvertiseTcpPort = 0, - int AdvertiseHttpPort = 0, - string AdvertiseHost = null); - - public record GivenInput( - LeaderInput Leader = null, - GossipInput Gossip = null) - { - - public MemberInfo BuildLeaderInfo() - { - if (Leader == null) - { - return null; - } - - return MemberInfo.Initial( - instanceId: Guid.NewGuid(), - timeStamp: DateTime.Now, - state: VNodeState.Initializing, - isAlive: false, - internalTcpEndPoint: IPEndPoint.Parse("1.1.2.2:3"), - internalSecureTcpEndPoint: IPEndPoint.Parse("4.4.5.5:6"), - externalTcpEndPoint: Leader.TcpEndPoint == null ? null : IPEndPoint.Parse(Leader.TcpEndPoint), - externalSecureTcpEndPoint: Leader.SecureTcpEndPoint == null ? null : IPEndPoint.Parse(Leader.SecureTcpEndPoint), - httpEndPoint: IPEndPoint.Parse(Leader.HttpEndPoint ?? DefaultHttpEndPoint), - advertiseHostToClientAs: Leader.AdvertiseHost, - advertiseHttpPortToClientAs: Leader.AdvertiseHttpPort, - advertiseTcpPortToClientAs: Leader.AdvertiseTcpPort, - nodePriority: 1, - isReadOnlyReplica: false); - } - - public GossipAdvertiseInfo BuildGossipInfo() - { - return new GossipAdvertiseInfo( - externalTcp: ParseDnsEndPoint(Gossip?.TcpEndPoint), - externalSecureTcp: ParseDnsEndPoint(Gossip?.SecureTcpEndPoint), - httpEndPoint: ParseDnsEndPoint(Gossip?.HttpEndPoint ?? DefaultHttpEndPoint), - advertiseHostToClientAs: Gossip?.AdvertiseHost, - advertiseHttpPortToClientAs: Gossip?.AdvertiseHttpPort ?? 8, - advertiseTcpPortToClientAs: Gossip?.AdvertiseTcpPort ?? 9, - internalTcp: new DnsEndPoint("internal.tcp", 1), - internalSecureTcp: new DnsEndPoint("secure.tcp", 2), - advertiseInternalHostAs: null, - advertiseExternalHostAs: null, - advertiseHttpPortAs: 0); - } - - public Given Build() - { - return new Given( - BuildLeaderInfo(), - BuildGossipInfo()); - } - } - - public record ExpectedInput(string TcpEndPoint = null, string HttpEndPoint = null, bool IsTcpSecure = false) - { - public Expected Build() - { - return new Expected( - ParseEndPoint(TcpEndPoint), - IsTcpSecure, - ParseEndPoint(HttpEndPoint ?? DefaultHttpEndPoint)); - } - } - - private static EndPoint ParseEndPoint(string s) - { - if (s == null) - { - return null; - } - - if (IPEndPoint.TryParse(s, out var ipEndPoint)) - { - return ipEndPoint; - } - - return ParseDnsEndPoint(s); - } - - private static DnsEndPoint ParseDnsEndPoint(string s) - { - if (s == null) - { - return null; - } - - var parts = s.Split(":"); - var host = parts[0]; - var port = int.Parse(parts[1]); - - return new DnsEndPoint(host, port); - } + public static IEnumerable Cases() + { + yield return Case("leader endpoint", Leader("1.1.1.1", 2113), Gossip("2.2.2.2", 2113), "1.1.1.1", 2113); + yield return Case("leader advertised host", Leader("1.1.1.1", 2113, "leader.example"), + Gossip("2.2.2.2", 2113), "leader.example", 2113); + yield return Case("leader advertised port", Leader("1.1.1.1", 2113, advertisePort: 3113), + Gossip("2.2.2.2", 2113), "1.1.1.1", 3113); + yield return Case("leader advertised endpoint", Leader("1.1.1.1", 2113, "leader.example", 3113), + Gossip("2.2.2.2", 2113), "leader.example", 3113); + yield return Case("local gossip endpoint", null, Gossip("2.2.2.2", 2113), "2.2.2.2", 2113); + yield return Case("local advertised endpoint", null, Gossip("2.2.2.2", 2113, "node.example", 3113), + "node.example", 3113); + } + + [TestCaseSource(nameof(Cases))] + public void returns_the_advertised_http_endpoint(MemberInfo leader, GossipAdvertiseInfo gossip, EndPoint expected) + { + var result = new LeaderInfoProvider(gossip, leader).GetLeaderInfoEndPoint(); + + Assert.AreEqual(expected, result); + } + + private static TestCaseData Case(string name, MemberInfo leader, GossipAdvertiseInfo gossip, + string expectedHost, int expectedPort) => + new TestCaseData(leader, gossip, new DnsEndPoint(expectedHost, expectedPort)).SetName(name); + + private static MemberInfo Leader(string host, int port, string advertiseHost = null, int advertisePort = 0) => + MemberInfo.Initial( + Guid.NewGuid(), + DateTime.UtcNow, + VNodeState.Leader, + true, + new DnsEndPoint(host, port), + advertiseHost, + advertisePort, + 0, + false); + + private static GossipAdvertiseInfo Gossip(string host, int port, string advertiseHost = null, + int advertisePort = 0) => + new(new DnsEndPoint(host, port), advertiseHost, advertisePort); } diff --git a/src/EventStore.Core.Tests/TcpApiTestPlugin/PublicTcpApiTestService.cs b/src/EventStore.Core.Tests/TcpApiTestPlugin/PublicTcpApiTestService.cs deleted file mode 100644 index 2cc5eb14dd..0000000000 --- a/src/EventStore.Core.Tests/TcpApiTestPlugin/PublicTcpApiTestService.cs +++ /dev/null @@ -1,123 +0,0 @@ -#nullable enable - -using System; -using System.Net; -using System.Security.Cryptography.X509Certificates; -using System.Threading; -using System.Threading.Tasks; -using EventStore.Core; -using EventStore.Core.Bus; -using EventStore.Core.Certificates; -using EventStore.Core.Messages; -using EventStore.Core.Services; -using EventStore.Core.Services.Transport.Tcp; -using EventStore.Plugins.Authentication; -using Microsoft.Extensions.Hosting; -using Serilog; - -namespace EventStore.TcpUnitTestPlugin; - -public class PublicTcpApiTestService : IHostedService -{ - static readonly ILogger Logger = Log.ForContext(); - private readonly TcpService _tcpService; - private int _systemInitialized; - - PublicTcpApiTestService(TcpService tcpService, ISubscriber bus) - { - _tcpService = tcpService; - - bus.Subscribe(new AdHocHandler(_ => StartTcpService())); - bus.Subscribe(new AdHocHandler(_ => StartTcpService())); - bus.Subscribe(tcpService); - - _ = Task.Run(async () => - { - await Task.Delay(TimeSpan.FromHours(1)); - Logger.Warning("Shutting down TCP unit tests"); - tcpService.Handle(new SystemMessage.BecomeShuttingDown(Guid.NewGuid(), true, true)); - }); - } - - public static PublicTcpApiTestService Insecure( - TcpApiTestOptions options, - IAuthenticationProvider authProvider, - AuthorizationGateway authGateway, - StandardComponents components - ) - { - var endpoint = new IPEndPoint(IPAddress.Loopback, options.NodeTcpPort); - - var tcpService = new TcpService( - publisher: components.MainQueue, - serverEndPoint: endpoint, - networkSendQueue: components.NetworkSendService, - serviceType: TcpServiceType.External, securityType: TcpSecurityType.Normal, - dispatcher: new ClientTcpDispatcher(options.WriteTimeoutMs), - heartbeatInterval: TimeSpan.FromMilliseconds(options.NodeHeartbeatInterval), - heartbeatTimeout: TimeSpan.FromMilliseconds(options.NodeHeartbeatTimeout), - authProvider: authProvider, - authorizationGateway: authGateway, - certificateSelector: null, - intermediatesSelector: null, - sslClientCertValidator: null, - connectionPendingSendBytesThreshold: options.ConnectionPendingSendBytesThreshold, - connectionQueueSizeThreshold: options.ConnectionQueueSizeThreshold - ); - - return new(tcpService, components.MainBus); - } - - public static PublicTcpApiTestService Secure( - TcpApiTestOptions options, - IAuthenticationProvider authProvider, - AuthorizationGateway authGateway, - StandardComponents components, - CertificateProvider? certificateProvider - ) - { - var endpoint = new IPEndPoint(IPAddress.Loopback, options.NodeTcpPort); - - var tcpService = new TcpService( - publisher: components.MainQueue, - serverEndPoint: endpoint, - networkSendQueue: components.NetworkSendService, - serviceType: TcpServiceType.External, securityType: TcpSecurityType.Secure, - dispatcher: new ClientTcpDispatcher(options.WriteTimeoutMs), - heartbeatInterval: TimeSpan.FromMilliseconds(options.NodeHeartbeatInterval), - heartbeatTimeout: TimeSpan.FromMilliseconds(options.NodeHeartbeatTimeout), - authProvider: authProvider, - authorizationGateway: authGateway, - certificateSelector: () => certificateProvider?.Certificate, - intermediatesSelector: () => - { - var intermediates = certificateProvider?.IntermediateCerts; - return intermediates == null ? null : new X509Certificate2Collection(intermediates); - }, - sslClientCertValidator: delegate - { return (true, null); }, - connectionPendingSendBytesThreshold: options.ConnectionPendingSendBytesThreshold, - connectionQueueSizeThreshold: options.ConnectionQueueSizeThreshold - ); - - return new(tcpService, components.MainBus); - } - - public Task StartAsync(CancellationToken cancellationToken) - { - StartTcpService(); - return Task.CompletedTask; - } - - public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; - - private void StartTcpService() - { - if (Interlocked.Exchange(ref _systemInitialized, 1) == 1) - { - return; - } - - _tcpService.Handle(new SystemMessage.SystemInit()); - } -} diff --git a/src/EventStore.Core.Tests/TcpApiTestPlugin/TcpApiTestOptions.cs b/src/EventStore.Core.Tests/TcpApiTestPlugin/TcpApiTestOptions.cs deleted file mode 100644 index a2be50f225..0000000000 --- a/src/EventStore.Core.Tests/TcpApiTestPlugin/TcpApiTestOptions.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace EventStore.TcpUnitTestPlugin; - -public class TcpApiTestOptions -{ - public int NodeTcpPort { get; init; } = 1113; - public int NodeHeartbeatInterval { get; init; } = 2_000; - public int NodeHeartbeatTimeout { get; init; } = 1_000; - public int ConnectionPendingSendBytesThreshold { get; set; } = 10 * 1_024 * 1_024; - public int ConnectionQueueSizeThreshold { get; set; } = 50_000; - public int WriteTimeoutMs { get; set; } = 2_000; - public bool Insecure { get; init; } = false; -} diff --git a/src/EventStore.Core.Tests/TcpApiTestPlugin/TcpApiTestPlugin.cs b/src/EventStore.Core.Tests/TcpApiTestPlugin/TcpApiTestPlugin.cs deleted file mode 100644 index 543fe02ca7..0000000000 --- a/src/EventStore.Core.Tests/TcpApiTestPlugin/TcpApiTestPlugin.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.Threading.Tasks; -using EventStore.Core; -using EventStore.Core.Certificates; -using EventStore.Core.Services; -using EventStore.Plugins; -using EventStore.Plugins.Authentication; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Serilog; - -namespace EventStore.TcpUnitTestPlugin; - -public class TcpApiTestPlugin() : SubsystemsPlugin(name: "TcpTestApi") -{ - static readonly ILogger Logger = Log.ForContext(); - - public override void ConfigureServices(IServiceCollection services, IConfiguration configuration) - { - var options = configuration.GetSection("EventStore:TcpUnitTestPlugin").Get() ?? new(); - - services.AddHostedService(serviceProvider => - { - var components = serviceProvider.GetRequiredService(); - var authGateway = serviceProvider.GetRequiredService(); - var authProvider = serviceProvider.GetRequiredService(); - - return options.Insecure - ? PublicTcpApiTestService.Insecure(options, authProvider, authGateway, components) - : PublicTcpApiTestService.Secure(options, authProvider, authGateway, components, serviceProvider.GetService()); - }); - } - - public override Task Start() - { - Logger.Debug("{Name}-{Version} test plugin is loaded", Name, Version); - return Task.CompletedTask; - } -} diff --git a/src/EventStore.Core.XUnit.Tests/Configuration/ClusterNodeOptionsTests/when_building/with_default_settings.cs b/src/EventStore.Core.XUnit.Tests/Configuration/ClusterNodeOptionsTests/when_building/with_default_settings.cs index 3ec6433bfd..179fd7e1d8 100644 --- a/src/EventStore.Core.XUnit.Tests/Configuration/ClusterNodeOptionsTests/when_building/with_default_settings.cs +++ b/src/EventStore.Core.XUnit.Tests/Configuration/ClusterNodeOptionsTests/when_building/with_default_settings.cs @@ -35,7 +35,6 @@ public void should_create_single_cluster_node() [Test] public void should_have_default_endpoints() { - Assert.AreEqual(new IPEndPoint(IPAddress.Loopback, 1112), _node.NodeInfo.InternalSecureTcp); Assert.AreEqual(new IPEndPoint(IPAddress.Loopback, 2113), _node.NodeInfo.HttpEndPoint); } @@ -69,11 +68,6 @@ public void should_set_command_line_args_to_default_values() Assert.AreEqual(Opts.MaxProjectionStateSizeDefault, _options.Projection.MaxProjectionStateSize, "MaxProjectionStateSize"); - Assert.AreEqual(700, _options.Interface.ReplicationHeartbeatInterval, "ReplicationHeartbeatInterval"); - - Assert.AreEqual(700, _options.Interface.ReplicationHeartbeatTimeout, - "ReplicationHeartbeatTimeout"); - Assert.AreEqual(TFConsts.ChunkSize, _node.Db.Config.ChunkSize, "ChunkSize"); Assert.AreEqual(TFConsts.ChunksCacheSize, _node.Db.Config.MaxChunksCacheSize, "MaxChunksCacheSize"); } @@ -92,15 +86,14 @@ public void should_create_single_cluster_node() } [Test] - public void should_have_default_secure_endpoints() + public void should_have_default_endpoint() { - var internalTcp = new IPEndPoint(IPAddress.Loopback, 1112); + var replicationEndPoint = new IPEndPoint(IPAddress.Loopback, 1112); var httpEndPoint = new IPEndPoint(IPAddress.Loopback, 2113); - Assert.AreEqual(internalTcp, _node.NodeInfo.InternalSecureTcp); + Assert.AreEqual(replicationEndPoint, _node.NodeInfo.ReplicationEndPoint); Assert.AreEqual(httpEndPoint, _node.NodeInfo.HttpEndPoint); - - Assert.AreEqual(internalTcp.ToDnsEndPoint(), _node.GossipAdvertiseInfo.InternalSecureTcp); + Assert.AreEqual(replicationEndPoint.ToDnsEndPoint(), _node.GossipAdvertiseInfo.ReplicationEndPoint); Assert.AreEqual(httpEndPoint.ToDnsEndPoint(), _node.GossipAdvertiseInfo.HttpEndPoint); } @@ -125,13 +118,12 @@ public void should_create_single_cluster_node() [Test] public void should_have_default_endpoints() { - var internalTcp = new IPEndPoint(IPAddress.Loopback, 1112); + var replicationEndPoint = new IPEndPoint(IPAddress.Loopback, 1112); var httpEndPoint = new IPEndPoint(IPAddress.Loopback, 2113); - Assert.AreEqual(internalTcp, _node.NodeInfo.InternalTcp); + Assert.AreEqual(replicationEndPoint, _node.NodeInfo.ReplicationEndPoint); Assert.AreEqual(httpEndPoint, _node.NodeInfo.HttpEndPoint); - - Assert.AreEqual(internalTcp.ToDnsEndPoint(), _node.GossipAdvertiseInfo.InternalTcp); + Assert.AreEqual(replicationEndPoint.ToDnsEndPoint(), _node.GossipAdvertiseInfo.ReplicationEndPoint); Assert.AreEqual(httpEndPoint.ToDnsEndPoint(), _node.GossipAdvertiseInfo.HttpEndPoint); } diff --git a/src/EventStore.Core.XUnit.Tests/Configuration/ClusterVNodeOptionsTests.cs b/src/EventStore.Core.XUnit.Tests/Configuration/ClusterVNodeOptionsTests.cs index 5aa70e05a0..f18a6018cf 100644 --- a/src/EventStore.Core.XUnit.Tests/Configuration/ClusterVNodeOptionsTests.cs +++ b/src/EventStore.Core.XUnit.Tests/Configuration/ClusterVNodeOptionsTests.cs @@ -95,6 +95,75 @@ public void valid_parameters() Assert.Empty(values); } + [Fact] + public void replication_port_advertise_as_is_configurable() + { + var options = GetOptions("--replication-port-advertise-as 2112"); + + options.Interface.ReplicationPortAdvertiseAs.Should().Be(2112); + options.Interface.GetReplicationPortAdvertiseAs().Should().Be(2112); + Assert.Empty(options.Unknown.Options); + } + + [Fact] + public void replication_port_advertise_as_is_configurable_from_the_environment() + { + var configuration = new ConfigurationBuilder() + .AddEventStoreDefaultValues() + .AddEventStoreEnvironmentVariables(( + "EVENTSTORE_REPLICATION_PORT_ADVERTISE_AS", + "2112")) + .Build(); + + var options = ClusterVNodeOptions.FromConfiguration(configuration); + + options.Interface.ReplicationPortAdvertiseAs.Should().Be(2112); + options.Interface.GetReplicationPortAdvertiseAs().Should().Be(2112); + } + + [Fact] + public void replication_port_advertise_as_is_configurable_from_yaml() + { + var yamlPath = Path.Combine( + Path.GetTempPath(), + $"eventstore-replication-advertise-{Guid.NewGuid():N}.conf"); + + try + { + File.WriteAllText(yamlPath, "ReplicationPortAdvertiseAs: 2112"); + var configuration = EventStoreConfiguration.Build(["--config", yamlPath], new Hashtable()); + + var options = ClusterVNodeOptions.FromConfiguration(configuration); + + options.Interface.ReplicationPortAdvertiseAs.Should().Be(2112); + options.Interface.GetReplicationPortAdvertiseAs().Should().Be(2112); + } + finally + { + File.Delete(yamlPath); + } + } + + [Fact] + public void deprecated_replication_tcp_port_advertise_as_remains_compatible() + { + var options = GetOptions("--replication-tcp-port-advertise-as 3112"); + + options.Interface.GetReplicationPortAdvertiseAs().Should().Be(3112); + options.GetDeprecationWarnings().Should().Contain( + "ReplicationTcpPortAdvertiseAs setting has been deprecated"); + Assert.Empty(options.Unknown.Options); + } + + [Fact] + public void replication_port_advertise_as_takes_precedence_over_deprecated_alias() + { + var options = GetOptions( + "--replication-port-advertise-as 2112 --replication-tcp-port-advertise-as 3112"); + + options.Interface.GetReplicationPortAdvertiseAs().Should().Be(2112); + } + [Fact] public void grpc_compression_level_defaults_to_optimal() { diff --git a/src/EventStore.Core.XUnit.Tests/Configuration/ClusterVNodeOptionsValidatorTests.cs b/src/EventStore.Core.XUnit.Tests/Configuration/ClusterVNodeOptionsValidatorTests.cs index 3caee89cc6..0278976316 100644 --- a/src/EventStore.Core.XUnit.Tests/Configuration/ClusterVNodeOptionsValidatorTests.cs +++ b/src/EventStore.Core.XUnit.Tests/Configuration/ClusterVNodeOptionsValidatorTests.cs @@ -1,4 +1,5 @@ using System; +using System.Net; using EventStore.Common.Exceptions; using EventStore.Core.Authentication; using EventStore.Core.Services; @@ -9,6 +10,43 @@ namespace EventStore.Core.XUnit.Tests.Configuration; // Some other tests are in ClusterNodeOptionsTests/when_building public class ClusterVNodeOptionsValidatorTests { + [Theory] + [InlineData("127.0.0.1", "127.0.0.1")] + [InlineData("0.0.0.0", "127.0.0.1")] + [InlineData("127.0.0.1", "0.0.0.0")] + public void node_and_replication_listeners_cannot_overlap(string nodeIp, string replicationIp) + { + var options = new ClusterVNodeOptions + { + Interface = new() + { + NodeIp = IPAddress.Parse(nodeIp), + NodePort = 2113, + ReplicationIp = IPAddress.Parse(replicationIp), + ReplicationPort = 2113, + } + }; + + Assert.Throws(() => ClusterVNodeOptionsValidator.Validate(options)); + } + + [Fact] + public void node_and_replication_listeners_can_use_the_same_port_on_distinct_addresses() + { + var options = new ClusterVNodeOptions + { + Interface = new() + { + NodeIp = IPAddress.Parse("127.0.0.1"), + NodePort = 2113, + ReplicationIp = IPAddress.Parse("127.0.0.2"), + ReplicationPort = 2113, + } + }; + + ClusterVNodeOptionsValidator.Validate(options); + } + [Theory] [InlineData(false, false, true)] [InlineData(false, true, true)] diff --git a/src/EventStore.Core.XUnit.Tests/Metrics/ElectionsCounterTrackerTests.cs b/src/EventStore.Core.XUnit.Tests/Metrics/ElectionsCounterTrackerTests.cs index 4b2e6ba989..8e47744ed8 100644 --- a/src/EventStore.Core.XUnit.Tests/Metrics/ElectionsCounterTrackerTests.cs +++ b/src/EventStore.Core.XUnit.Tests/Metrics/ElectionsCounterTrackerTests.cs @@ -25,8 +25,7 @@ public ElectionsCounterTrackerTests() var endPoint = new DnsEndPoint("127.0.0.1", 1113); var memberInfo = Cluster.MemberInfo.Initial(Guid.Empty, DateTime.UtcNow, VNodeState.Unknown, true, - endPoint, endPoint, endPoint, endPoint, endPoint, - null, 0, 0, 0, false); + endPoint, null, 0, 0, false); _electionsDoneMessage = new ElectionMessage.ElectionsDone(1, 1, memberInfo); } diff --git a/src/EventStore.Core.XUnit.Tests/Services/Storage/InMemory/GossipListenerServiceTests.cs b/src/EventStore.Core.XUnit.Tests/Services/Storage/InMemory/GossipListenerServiceTests.cs index 84e972bfba..888b7ddda2 100644 --- a/src/EventStore.Core.XUnit.Tests/Services/Storage/InMemory/GossipListenerServiceTests.cs +++ b/src/EventStore.Core.XUnit.Tests/Services/Storage/InMemory/GossipListenerServiceTests.cs @@ -47,14 +47,9 @@ public async Task notify_state_change() timeStamp: DateTime.Now, state: Data.VNodeState.DiscoverLeader, isAlive: true, - internalTcpEndPoint: default, - internalSecureTcpEndPoint: new DnsEndPoint("myhost", random()), - externalTcpEndPoint: default, - externalSecureTcpEndPoint: new DnsEndPoint("myhost", random()), httpEndPoint: new DnsEndPoint("myhost", random()), advertiseHostToClientAs: "advertiseHostToClientAs", advertiseHttpPortToClientAs: random(), - advertiseTcpPortToClientAs: random(), lastCommitPosition: random(), writerCheckpoint: random(), chaserCheckpoint: random(), diff --git a/src/EventStore.Core.XUnit.Tests/Telemetry/TelemetryServiceTests.cs b/src/EventStore.Core.XUnit.Tests/Telemetry/TelemetryServiceTests.cs index 1193feeb54..ccaa036e64 100644 --- a/src/EventStore.Core.XUnit.Tests/Telemetry/TelemetryServiceTests.cs +++ b/src/EventStore.Core.XUnit.Tests/Telemetry/TelemetryServiceTests.cs @@ -77,14 +77,9 @@ private static MemberInfo CreateMemberInfo(Guid instanceId, VNodeState state, bo timeStamp: DateTime.Now, state: state, isAlive: true, - internalTcpEndPoint: default, - internalSecureTcpEndPoint: new DnsEndPoint("myhost", random()), - externalTcpEndPoint: default, - externalSecureTcpEndPoint: new DnsEndPoint("myhost", random()), httpEndPoint: new DnsEndPoint("myhost", random()), advertiseHostToClientAs: "advertiseHostToClientAs", advertiseHttpPortToClientAs: random(), - advertiseTcpPortToClientAs: random(), lastCommitPosition: random(), writerCheckpoint: random(), chaserCheckpoint: random(), diff --git a/src/EventStore.Core/Cluster/ClientClusterInfo.cs b/src/EventStore.Core/Cluster/ClientClusterInfo.cs index 442abee0d0..3c0f6bc3ce 100644 --- a/src/EventStore.Core/Cluster/ClientClusterInfo.cs +++ b/src/EventStore.Core/Cluster/ClientClusterInfo.cs @@ -37,14 +37,6 @@ public class ClientMemberInfo public VNodeState State { get; set; } public bool IsAlive { get; set; } - public string InternalTcpIp { get; set; } - public int InternalTcpPort { get; set; } - public int InternalSecureTcpPort { get; set; } - - public string ExternalTcpIp { get; set; } - public int ExternalTcpPort { get; set; } - public int ExternalSecureTcpPort { get; set; } - public string InternalHttpEndPointIp { get; set; } public int InternalHttpEndPointPort { get; set; } @@ -76,12 +68,6 @@ public ClientMemberInfo(MemberInfo member) State = member.State; IsAlive = member.IsAlive; - InternalTcpIp = member.InternalTcpEndPoint is null - ? member.InternalSecureTcpEndPoint.GetHost() - : member.InternalTcpEndPoint.GetHost(); - InternalSecureTcpPort = member.InternalSecureTcpEndPoint?.GetPort() ?? 0; - InternalTcpPort = member.InternalTcpEndPoint?.GetPort() ?? 0; - InternalHttpEndPointIp = member.HttpEndPoint.GetHost(); InternalHttpEndPointPort = member.HttpEndPoint.GetPort(); @@ -92,18 +78,6 @@ public ClientMemberInfo(MemberInfo member) ? member.HttpEndPoint.GetPort() : member.AdvertiseHttpPortToClientAs; - ExternalTcpIp = string.IsNullOrEmpty(member.AdvertiseHostToClientAs) - ? member.ExternalSecureTcpEndPoint?.GetHost() ?? - member.ExternalTcpEndPoint?.GetHost() ?? member.HttpEndPoint.GetHost() - : member.AdvertiseHostToClientAs; - - ExternalTcpPort = member.AdvertiseTcpPortToClientAs == 0 - ? member.ExternalTcpEndPoint?.GetPort() ?? 0 - : member.AdvertiseTcpPortToClientAs; - ExternalSecureTcpPort = member.AdvertiseTcpPortToClientAs == 0 - ? member.ExternalSecureTcpEndPoint?.GetPort() ?? 0 - : member.AdvertiseTcpPortToClientAs; - LastCommitPosition = member.LastCommitPosition; WriterCheckpoint = member.WriterCheckpoint; ChaserCheckpoint = member.ChaserCheckpoint; @@ -122,8 +96,6 @@ public override string ToString() { return $"InstanceId: {InstanceId:B}, TimeStamp: {TimeStamp:yyyy-MM-dd HH:mm:ss.fff}, State: {State}, IsAlive: {IsAlive}, " + - $"InternalTcpIp: {InternalTcpIp}, InternalTcpPort: {InternalTcpPort}, InternalSecureTcpPort: {InternalSecureTcpPort}, " + - $"ExternalTcpIp: {ExternalTcpIp}, ExternalTcpPort: {ExternalTcpPort}, ExternalSecureTcpPort: {ExternalSecureTcpPort}, " + $"InternalHttpEndPointIp: {InternalHttpEndPointIp}, InternalHttpEndPointPort: {InternalHttpEndPointPort}, " + $"HttpEndPointIp: {HttpEndPointIp}, HttpEndPointPort: {HttpEndPointPort}, " + $"LastCommitPosition: {LastCommitPosition}, WriterCheckpoint: {WriterCheckpoint}, ChaserCheckpoint: {ChaserCheckpoint}, " + @@ -133,4 +105,3 @@ public override string ToString() } } } - diff --git a/src/EventStore.Core/Cluster/ClusterInfo.cs b/src/EventStore.Core/Cluster/ClusterInfo.cs index 42bf18fb0e..a9b9cd9119 100644 --- a/src/EventStore.Core/Cluster/ClusterInfo.cs +++ b/src/EventStore.Core/Cluster/ClusterInfo.cs @@ -5,7 +5,6 @@ using EventStore.Client; using EventStore.Common.Utils; using EventStore.Core.Data; -using EventStore.Core.Messages; using EventStore.Core.Services.Transport.Grpc; namespace EventStore.Core.Cluster @@ -26,12 +25,6 @@ public ClusterInfo(IEnumerable members) .ToArray(); } - public ClusterInfo(ClusterInfoDto dto) - { - Members = dto.Members.Safe().Select(x => new MemberInfo(x)) - .OrderByDescending(x => x.HttpEndPoint, Comparer).ToArray(); - } - public override string ToString() { return string.Join("\n", Members.Select(s => s.ToString())); @@ -71,19 +64,15 @@ internal static ClusterInfo FromGrpcClusterInfo(EventStore.Cluster.ClusterInfo g new MemberInfo( Uuid.FromDto(x.InstanceId).ToGuid(), x.TimeStamp.FromTicksSinceEpoch(), (VNodeState)x.State, x.IsAlive, - !x.InternalTcpUsesTls ? new DnsEndPoint(x.InternalTcp.Address, (int)x.InternalTcp.Port).WithClusterDns(clusterDns) : null, - x.InternalTcpUsesTls ? new DnsEndPoint(x.InternalTcp.Address, (int)x.InternalTcp.Port).WithClusterDns(clusterDns) : null, - !x.ExternalTcpUsesTls && x.ExternalTcp != null - ? new DnsEndPoint(x.ExternalTcp.Address, (int)x.ExternalTcp.Port).WithClusterDns(clusterDns) - : null, - x.ExternalTcpUsesTls && x.ExternalTcp != null - ? new DnsEndPoint(x.ExternalTcp.Address, (int)x.ExternalTcp.Port).WithClusterDns(clusterDns) - : null, new DnsEndPoint(x.HttpEndPoint.Address, (int)x.HttpEndPoint.Port).WithClusterDns(clusterDns), - x.AdvertiseHostToClientAs, (int)x.AdvertiseHttpPortToClientAs, (int)x.AdvertiseTcpPortToClientAs, + x.AdvertiseHostToClientAs, (int)x.AdvertiseHttpPortToClientAs, x.LastCommitPosition, x.WriterCheckpoint, x.ChaserCheckpoint, x.EpochPosition, x.EpochNumber, Uuid.FromDto(x.EpochId).ToGuid(), x.NodePriority, - x.IsReadOnlyReplica, x.EsVersion == String.Empty ? null : x.EsVersion + x.IsReadOnlyReplica, x.EsVersion == String.Empty ? null : x.EsVersion, + x.ReplicationEndPoint is null + ? null + : new DnsEndPoint(x.ReplicationEndPoint.Address, (int)x.ReplicationEndPoint.Port) + .WithClusterDns(clusterDns) )).ToArray(); return new ClusterInfo(receivedMembers); } @@ -99,23 +88,9 @@ internal static EventStore.Cluster.ClusterInfo ToGrpcClusterInfo(ClusterInfo clu HttpEndPoint = new EventStore.Cluster.EndPoint( x.HttpEndPoint.GetHost(), (uint)x.HttpEndPoint.GetPort()), - InternalTcp = x.InternalSecureTcpEndPoint != null ? - new EventStore.Cluster.EndPoint( - x.InternalSecureTcpEndPoint.GetHost(), - (uint)x.InternalSecureTcpEndPoint.GetPort()) : - new EventStore.Cluster.EndPoint( - x.InternalTcpEndPoint.GetHost(), - (uint)x.InternalTcpEndPoint.GetPort()), - InternalTcpUsesTls = x.InternalSecureTcpEndPoint != null, - ExternalTcp = x.ExternalSecureTcpEndPoint != null ? - new EventStore.Cluster.EndPoint( - x.ExternalSecureTcpEndPoint.GetHost(), - (uint)x.ExternalSecureTcpEndPoint.GetPort()) : - x.ExternalTcpEndPoint != null ? - new EventStore.Cluster.EndPoint( - x.ExternalTcpEndPoint.GetHost(), - (uint)x.ExternalTcpEndPoint.GetPort()) : null, - ExternalTcpUsesTls = x.ExternalSecureTcpEndPoint != null, + ReplicationEndPoint = new EventStore.Cluster.EndPoint( + x.ReplicationEndPoint.GetHost(), + (uint)x.ReplicationEndPoint.GetPort()), LastCommitPosition = x.LastCommitPosition, WriterCheckpoint = x.WriterCheckpoint, ChaserCheckpoint = x.ChaserCheckpoint, @@ -126,7 +101,6 @@ internal static EventStore.Cluster.ClusterInfo ToGrpcClusterInfo(ClusterInfo clu IsReadOnlyReplica = x.IsReadOnlyReplica, AdvertiseHostToClientAs = x.AdvertiseHostToClientAs ?? "", AdvertiseHttpPortToClientAs = (uint)x.AdvertiseHttpPortToClientAs, - AdvertiseTcpPortToClientAs = (uint)x.AdvertiseTcpPortToClientAs, EsVersion = x.ESVersion ?? String.Empty }).ToArray(); var info = new EventStore.Cluster.ClusterInfo(); diff --git a/src/EventStore.Core/Cluster/MemberInfo.cs b/src/EventStore.Core/Cluster/MemberInfo.cs index f006448cc1..4fe81259c2 100644 --- a/src/EventStore.Core/Cluster/MemberInfo.cs +++ b/src/EventStore.Core/Cluster/MemberInfo.cs @@ -2,7 +2,6 @@ using System.Net; using EventStore.Common.Utils; using EventStore.Core.Data; -using EventStore.Core.Messages; using EventStore.Core.TransactionLog.LogRecords; namespace EventStore.Core.Cluster @@ -15,14 +14,10 @@ public class MemberInfo : IEquatable public readonly VNodeState State; public readonly bool IsAlive; - public readonly EndPoint InternalTcpEndPoint; - public readonly EndPoint InternalSecureTcpEndPoint; - public readonly EndPoint ExternalTcpEndPoint; - public readonly EndPoint ExternalSecureTcpEndPoint; + public readonly EndPoint ReplicationEndPoint; public readonly EndPoint HttpEndPoint; public readonly string AdvertiseHostToClientAs; public readonly int AdvertiseHttpPortToClientAs; - public readonly int AdvertiseTcpPortToClientAs; public readonly long LastCommitPosition; public readonly long WriterCheckpoint; @@ -37,26 +32,21 @@ public class MemberInfo : IEquatable public readonly string ESVersion; public static MemberInfo ForManager(Guid instanceId, DateTime timeStamp, bool isAlive, - EndPoint httpEndPoint, string esVersion = VersionInfo.UnknownVersion) + EndPoint httpEndPoint, string esVersion = VersionInfo.UnknownVersion, + EndPoint replicationEndPoint = null) { return new MemberInfo(instanceId, timeStamp, VNodeState.Manager, isAlive, - httpEndPoint, null, httpEndPoint, null, - httpEndPoint, null, 0, 0, - -1, -1, -1, -1, -1, Guid.Empty, 0, false, esVersion); + httpEndPoint, null, 0, + -1, -1, -1, -1, -1, Guid.Empty, 0, false, esVersion, replicationEndPoint); } public static MemberInfo ForVNode(Guid instanceId, DateTime timeStamp, VNodeState state, bool isAlive, - EndPoint internalTcpEndPoint, - EndPoint internalSecureTcpEndPoint, - EndPoint externalTcpEndPoint, - EndPoint externalSecureTcpEndPoint, EndPoint httpEndPoint, string advertiseHostToClientAs, int advertiseHttpPortToClientAs, - int advertiseTcpPortToClientAs, long lastCommitPosition, long writerCheckpoint, long chaserCheckpoint, @@ -64,7 +54,8 @@ public static MemberInfo ForVNode(Guid instanceId, int epochNumber, Guid epochId, int nodePriority, - bool isReadOnlyReplica, string esVersion = VersionInfo.UnknownVersion) + bool isReadOnlyReplica, string esVersion = VersionInfo.UnknownVersion, + EndPoint replicationEndPoint = null) { if (state == VNodeState.Manager) { @@ -72,27 +63,22 @@ public static MemberInfo ForVNode(Guid instanceId, } return new MemberInfo(instanceId, timeStamp, state, isAlive, - internalTcpEndPoint, internalSecureTcpEndPoint, - externalTcpEndPoint, externalSecureTcpEndPoint, - httpEndPoint, advertiseHostToClientAs, advertiseHttpPortToClientAs, advertiseTcpPortToClientAs, + httpEndPoint, advertiseHostToClientAs, advertiseHttpPortToClientAs, lastCommitPosition, writerCheckpoint, chaserCheckpoint, - epochPosition, epochNumber, epochId, nodePriority, isReadOnlyReplica, esVersion); + epochPosition, epochNumber, epochId, nodePriority, isReadOnlyReplica, esVersion, + replicationEndPoint); } public static MemberInfo Initial(Guid instanceId, DateTime timeStamp, VNodeState state, bool isAlive, - EndPoint internalTcpEndPoint, - EndPoint internalSecureTcpEndPoint, - EndPoint externalTcpEndPoint, - EndPoint externalSecureTcpEndPoint, EndPoint httpEndPoint, string advertiseHostToClientAs, int advertiseHttpPortToClientAs, - int advertiseTcpPortToClientAs, int nodePriority, - bool isReadOnlyReplica, string esVersion = VersionInfo.UnknownVersion) + bool isReadOnlyReplica, string esVersion = VersionInfo.UnknownVersion, + EndPoint replicationEndPoint = null) { if (state == VNodeState.Manager) { @@ -100,20 +86,17 @@ public static MemberInfo Initial(Guid instanceId, } return new MemberInfo(instanceId, timeStamp, state, isAlive, - internalTcpEndPoint, internalSecureTcpEndPoint, - externalTcpEndPoint, externalSecureTcpEndPoint, - httpEndPoint, advertiseHostToClientAs, advertiseHttpPortToClientAs, advertiseTcpPortToClientAs, - -1, -1, -1, -1, -1, Guid.Empty, nodePriority, isReadOnlyReplica, esVersion); + httpEndPoint, advertiseHostToClientAs, advertiseHttpPortToClientAs, + -1, -1, -1, -1, -1, Guid.Empty, nodePriority, isReadOnlyReplica, esVersion, + replicationEndPoint); } internal MemberInfo(Guid instanceId, DateTime timeStamp, VNodeState state, bool isAlive, - EndPoint internalTcpEndPoint, EndPoint internalSecureTcpEndPoint, - EndPoint externalTcpEndPoint, EndPoint externalSecureTcpEndPoint, - EndPoint httpEndPoint, string advertiseHostToClientAs, int advertiseHttpPortToClientAs, int advertiseTcpPortToClientAs, + EndPoint httpEndPoint, string advertiseHostToClientAs, int advertiseHttpPortToClientAs, long lastCommitPosition, long writerCheckpoint, long chaserCheckpoint, - long epochPosition, int epochNumber, Guid epochId, int nodePriority, bool isReadOnlyReplica, string esVersion = null) + long epochPosition, int epochNumber, Guid epochId, int nodePriority, bool isReadOnlyReplica, + string esVersion = null, EndPoint replicationEndPoint = null) { - Ensure.Equal(false, internalTcpEndPoint == null && internalSecureTcpEndPoint == null, "Both internal TCP endpoints are null"); Ensure.NotNull(httpEndPoint, nameof(httpEndPoint)); InstanceId = instanceId; @@ -122,14 +105,10 @@ internal MemberInfo(Guid instanceId, DateTime timeStamp, VNodeState state, bool State = state; IsAlive = isAlive; - InternalTcpEndPoint = internalTcpEndPoint; - InternalSecureTcpEndPoint = internalSecureTcpEndPoint; - ExternalTcpEndPoint = externalTcpEndPoint; - ExternalSecureTcpEndPoint = externalSecureTcpEndPoint; + ReplicationEndPoint = replicationEndPoint ?? httpEndPoint; HttpEndPoint = httpEndPoint; AdvertiseHostToClientAs = advertiseHostToClientAs; AdvertiseHttpPortToClientAs = advertiseHttpPortToClientAs; - AdvertiseTcpPortToClientAs = advertiseTcpPortToClientAs; LastCommitPosition = lastCommitPosition; WriterCheckpoint = writerCheckpoint; @@ -145,42 +124,10 @@ internal MemberInfo(Guid instanceId, DateTime timeStamp, VNodeState state, bool ESVersion = esVersion; } - internal MemberInfo(MemberInfoDto dto) - { - InstanceId = dto.InstanceId; - TimeStamp = dto.TimeStamp; - State = dto.State; - IsAlive = dto.IsAlive; - InternalTcpEndPoint = new DnsEndPoint(dto.InternalTcpIp, dto.InternalTcpPort); - InternalSecureTcpEndPoint = dto.InternalSecureTcpPort > 0 - ? new DnsEndPoint(dto.InternalTcpIp, dto.InternalSecureTcpPort) - : null; - ExternalTcpEndPoint = dto.ExternalTcpIp != null ? new DnsEndPoint(dto.ExternalTcpIp, dto.ExternalTcpPort) : null; - ExternalSecureTcpEndPoint = dto.ExternalTcpIp != null && dto.ExternalSecureTcpPort > 0 - ? new DnsEndPoint(dto.ExternalTcpIp, dto.ExternalSecureTcpPort) - : null; - HttpEndPoint = new DnsEndPoint(dto.HttpEndPointIp, dto.HttpEndPointPort); - AdvertiseHostToClientAs = dto.AdvertiseHostToClientAs; - AdvertiseHttpPortToClientAs = dto.AdvertiseHttpPortToClientAs; - AdvertiseTcpPortToClientAs = dto.AdvertiseTcpPortToClientAs; - LastCommitPosition = dto.LastCommitPosition; - WriterCheckpoint = dto.WriterCheckpoint; - ChaserCheckpoint = dto.ChaserCheckpoint; - EpochPosition = dto.EpochPosition; - EpochNumber = dto.EpochNumber; - EpochId = dto.EpochId; - NodePriority = dto.NodePriority; - IsReadOnlyReplica = dto.IsReadOnlyReplica; - } - public bool Is(EndPoint endPoint) { - return endPoint != null - && HttpEndPoint.EndPointEquals(endPoint) - || (InternalTcpEndPoint != null && InternalTcpEndPoint.EndPointEquals(endPoint)) - || (InternalSecureTcpEndPoint != null && InternalSecureTcpEndPoint.EndPointEquals(endPoint)) - || (ExternalTcpEndPoint != null && ExternalTcpEndPoint.EndPointEquals(endPoint)) - || (ExternalSecureTcpEndPoint != null && ExternalSecureTcpEndPoint.EndPointEquals(endPoint)); + return endPoint != null && + (HttpEndPoint.EndPointEquals(endPoint) || ReplicationEndPoint.EndPointEquals(endPoint)); } public MemberInfo Updated(DateTime utcNow, @@ -196,14 +143,9 @@ public MemberInfo Updated(DateTime utcNow, utcNow, state ?? State, isAlive ?? IsAlive, - InternalTcpEndPoint, - InternalSecureTcpEndPoint, - ExternalTcpEndPoint, - ExternalSecureTcpEndPoint, HttpEndPoint, AdvertiseHostToClientAs, AdvertiseHttpPortToClientAs, - AdvertiseTcpPortToClientAs, lastCommitPosition ?? LastCommitPosition, writerCheckpoint ?? WriterCheckpoint, chaserCheckpoint ?? ChaserCheckpoint, @@ -211,7 +153,7 @@ public MemberInfo Updated(DateTime utcNow, epoch != null ? epoch.EpochNumber : EpochNumber, epoch != null ? epoch.EpochId : EpochId, nodePriority ?? NodePriority, - IsReadOnlyReplica, esVersion ?? ESVersion); + IsReadOnlyReplica, esVersion ?? ESVersion, ReplicationEndPoint); } public override string ToString() @@ -219,16 +161,12 @@ public override string ToString() if (State == VNodeState.Manager) { return - $"MAN {InstanceId:B} <{(IsAlive ? "LIVE" : "DEAD")}> [{State}, {HttpEndPoint}] | {TimeStamp:yyyy-MM-dd HH:mm:ss.fff}"; + $"MAN {InstanceId:B} <{(IsAlive ? "LIVE" : "DEAD")}> [{State}, {ReplicationEndPoint}, {HttpEndPoint}] | {TimeStamp:yyyy-MM-dd HH:mm:ss.fff}"; } return $"Priority: {NodePriority} VND {InstanceId:B} <{(IsAlive ? "LIVE" : "DEAD")}> [{State}, " + - $"{(InternalTcpEndPoint == null ? "n/a" : InternalTcpEndPoint.ToString())}, " + - $"{(InternalSecureTcpEndPoint == null ? "n/a" : InternalSecureTcpEndPoint.ToString())}, " + - $"{(ExternalTcpEndPoint == null ? "n/a" : ExternalTcpEndPoint.ToString())}, " + - $"{(ExternalSecureTcpEndPoint == null ? "n/a" : ExternalSecureTcpEndPoint.ToString())}, " + - $"{HttpEndPoint}, (ADVERTISED: HTTP:{AdvertiseHostToClientAs}:{AdvertiseHttpPortToClientAs}, TCP:{AdvertiseHostToClientAs}:{AdvertiseTcpPortToClientAs}), " + + $"Replication:{ReplicationEndPoint}, {HttpEndPoint}, (ADVERTISED: HTTP:{AdvertiseHostToClientAs}:{AdvertiseHttpPortToClientAs}), " + $"Version: {ESVersion}] " + $"{LastCommitPosition}/{WriterCheckpoint}/{ChaserCheckpoint}/E{EpochNumber}@{EpochPosition}:{EpochId:B} | {TimeStamp:yyyy-MM-dd HH:mm:ss.fff}"; } @@ -249,14 +187,10 @@ public bool Equals(MemberInfo other) return other.InstanceId == InstanceId && other.State == State && other.IsAlive == IsAlive - && Equals(other.InternalTcpEndPoint, InternalTcpEndPoint) - && Equals(other.InternalSecureTcpEndPoint, InternalSecureTcpEndPoint) - && Equals(other.ExternalTcpEndPoint, ExternalTcpEndPoint) - && Equals(other.ExternalSecureTcpEndPoint, ExternalSecureTcpEndPoint) + && Equals(other.ReplicationEndPoint, ReplicationEndPoint) && Equals(other.HttpEndPoint, HttpEndPoint) && other.AdvertiseHostToClientAs == AdvertiseHostToClientAs && other.AdvertiseHttpPortToClientAs == AdvertiseHttpPortToClientAs - && other.AdvertiseTcpPortToClientAs == AdvertiseTcpPortToClientAs && other.EpochPosition == EpochPosition && other.EpochNumber == EpochNumber && other.EpochId == EpochId @@ -292,16 +226,10 @@ public override int GetHashCode() int result = InstanceId.GetHashCode(); result = (result * 397) ^ State.GetHashCode(); result = (result * 397) ^ IsAlive.GetHashCode(); - result = (result * 397) ^ (InternalTcpEndPoint != null ? InternalTcpEndPoint.GetHashCode() : 0); - result = (result * 397) ^ - (InternalSecureTcpEndPoint != null ? InternalSecureTcpEndPoint.GetHashCode() : 0); - result = (result * 397) ^ (ExternalTcpEndPoint != null ? ExternalTcpEndPoint.GetHashCode() : 0); - result = (result * 397) ^ - (ExternalSecureTcpEndPoint != null ? ExternalSecureTcpEndPoint.GetHashCode() : 0); + result = (result * 397) ^ ReplicationEndPoint.GetHashCode(); result = (result * 397) ^ HttpEndPoint.GetHashCode(); result = (result * 397) ^ (AdvertiseHostToClientAs != null ? AdvertiseHostToClientAs.GetHashCode() : 0); result = (result * 397) ^ AdvertiseHttpPortToClientAs.GetHashCode(); - result = (result * 397) ^ AdvertiseTcpPortToClientAs.GetHashCode(); result = (result * 397) ^ EpochPosition.GetHashCode(); result = (result * 397) ^ EpochNumber.GetHashCode(); result = (result * 397) ^ EpochId.GetHashCode(); diff --git a/src/EventStore.Core/ClusterVNode.cs b/src/EventStore.Core/ClusterVNode.cs index 327e52189a..7332610589 100644 --- a/src/EventStore.Core/ClusterVNode.cs +++ b/src/EventStore.Core/ClusterVNode.cs @@ -55,7 +55,6 @@ using EventStore.Core.Services.Transport.Http; using EventStore.Core.Services.Transport.Http.Authentication; using EventStore.Core.Services.Transport.Http.NodeHttpClientFactory; -using EventStore.Core.Services.Transport.Tcp; using EventStore.Core.Services.VNode; using EventStore.Core.Settings; using EventStore.Core.Synchronization; @@ -291,47 +290,15 @@ public ClusterVNode(ClusterVNodeOptions options, archiveOptions.Validate(); OptionsFormatter.LogConfig("Archive", archiveOptions); - var disableInternalTcpTls = options.Application.TlsDisabled(); - var disableExternalTcpTls = options.Application.TlsDisabled(); - var nodeTcpOptions = configuration.GetSection("EventStore:TcpPlugin").Get() ?? new(); - var enableExternalTcp = nodeTcpOptions.EnableExternalTcp; - var httpEndPoint = new IPEndPoint(options.Interface.NodeIp, options.Interface.NodePort); - - var intTcp = disableInternalTcpTls - ? new IPEndPoint(options.Interface.ReplicationIp, - options.Interface.ReplicationPort) - : null; - var intSecIp = !disableInternalTcpTls - ? new IPEndPoint(options.Interface.ReplicationIp, - options.Interface.ReplicationPort) - : null; - - var extTcp = disableExternalTcpTls && enableExternalTcp - ? new IPEndPoint(options.Interface.NodeIp, - nodeTcpOptions.NodeTcpPort) - : null; - var extSecIp = !disableExternalTcpTls && enableExternalTcp - ? new IPEndPoint(options.Interface.NodeIp, - nodeTcpOptions.NodeTcpPort) - : null; - - var intTcpPortAdvertiseAs = disableInternalTcpTls ? options.Interface.ReplicationTcpPortAdvertiseAs : 0; - var intSecTcpPortAdvertiseAs = !disableInternalTcpTls ? options.Interface.ReplicationTcpPortAdvertiseAs : 0; - - var extTcpPortAdvertiseAs = - enableExternalTcp && disableExternalTcpTls && nodeTcpOptions.NodeTcpPortAdvertiseAs.HasValue - ? nodeTcpOptions.NodeTcpPortAdvertiseAs.Value! - : 0; - var extSecTcpPortAdvertiseAs = - enableExternalTcp && !disableExternalTcpTls && nodeTcpOptions.NodeTcpPortAdvertiseAs.HasValue - ? nodeTcpOptions.NodeTcpPortAdvertiseAs.Value! - : 0; + var replicationEndPoint = new IPEndPoint( + options.Interface.ReplicationIp, + options.Interface.ReplicationPort); Log.Information("Quorum size set to {quorum}.", options.Cluster.QuorumSize); - NodeInfo = new VNodeInfo(instanceId.Value, debugIndex, intTcp, intSecIp, extTcp, extSecIp, - httpEndPoint, options.Cluster.ReadOnlyReplica); + NodeInfo = new VNodeInfo(instanceId.Value, debugIndex, httpEndPoint, options.Cluster.ReadOnlyReplica, + replicationEndPoint); var metricsConfiguration = MetricsConfiguration.Get(configuration); var trackers = new Trackers(); @@ -631,8 +598,6 @@ void StartSubsystems() TimeSpan.FromSeconds(options.Application.StatsPeriodSec), NodeInfo.HttpEndPoint, options.Database.StatsStorage, - NodeInfo.ExternalTcp, - NodeInfo.ExternalSecureTcp, statsHelper); _mainBus.Subscribe(monitoringQueue); @@ -646,7 +611,6 @@ void StartSubsystems() monitoringInnerBus.Subscribe(monitoring); monitoringInnerBus.Subscribe(monitoring); monitoringInnerBus.Subscribe(monitoring); - monitoringInnerBus.Subscribe(monitoring); _threadPoolBacklogMonitor = new ThreadPoolBacklogMonitor(_queueStatsManager, trackers.QueueTrackers); _threadPoolBacklogMonitor.Start(); @@ -914,64 +878,42 @@ void StartSubsystems() GossipAdvertiseInfo GetGossipAdvertiseInfo() { - IPAddress intIpAddress = options.Interface.ReplicationIp; - - IPAddress extIpAddress = options.Interface.NodeIp; - - var intHostToAdvertise = options.Interface.ReplicationHostAdvertiseAs ?? intIpAddress.ToString(); - var extHostToAdvertise = options.Interface.NodeHostAdvertiseAs ?? extIpAddress.ToString(); - - if (intIpAddress.Equals(IPAddress.Any) || extIpAddress.Equals(IPAddress.Any)) + var nodeIpAddress = options.Interface.NodeIp; + var hostToAdvertise = options.Interface.NodeHostAdvertiseAs ?? nodeIpAddress.ToString(); + var replicationIpAddress = options.Interface.ReplicationIp; + var replicationHostToAdvertise = + options.Interface.ReplicationHostAdvertiseAs ?? replicationIpAddress.ToString(); + + if ((nodeIpAddress.Equals(IPAddress.Any) && options.Interface.NodeHostAdvertiseAs == null) || + (replicationIpAddress.Equals(IPAddress.Any) && options.Interface.ReplicationHostAdvertiseAs == null)) { IPAddress nonLoopbackAddress = IPFinder.GetNonLoopbackAddress(); - IPAddress addressToAdvertise = - options.Cluster.ClusterSize > 1 ? nonLoopbackAddress : IPAddress.Loopback; - - if (intIpAddress.Equals(IPAddress.Any) && options.Interface.ReplicationHostAdvertiseAs == null) + var addressToAdvertise = + (options.Cluster.ClusterSize > 1 ? nonLoopbackAddress : IPAddress.Loopback).ToString(); + if (nodeIpAddress.Equals(IPAddress.Any) && options.Interface.NodeHostAdvertiseAs == null) { - intHostToAdvertise = addressToAdvertise.ToString(); + hostToAdvertise = addressToAdvertise; } - if (extIpAddress.Equals(IPAddress.Any) && options.Interface.NodeHostAdvertiseAs == null) + if (replicationIpAddress.Equals(IPAddress.Any) && + options.Interface.ReplicationHostAdvertiseAs == null) { - extHostToAdvertise = addressToAdvertise.ToString(); + replicationHostToAdvertise = addressToAdvertise; } } - var intTcpEndPoint = NodeInfo.InternalTcp == null - ? null - : new DnsEndPoint(intHostToAdvertise, intTcpPortAdvertiseAs > 0 - ? (options.Interface.ReplicationTcpPortAdvertiseAs) - : NodeInfo.InternalTcp.Port); - - var intSecureTcpEndPoint = NodeInfo.InternalSecureTcp == null - ? null - : new DnsEndPoint(intHostToAdvertise, intSecTcpPortAdvertiseAs > 0 - ? intSecTcpPortAdvertiseAs - : NodeInfo.InternalSecureTcp.Port); - - var extTcpEndPoint = NodeInfo.ExternalTcp == null - ? null - : new DnsEndPoint(extHostToAdvertise, extTcpPortAdvertiseAs > 0 - ? extTcpPortAdvertiseAs - : NodeInfo.ExternalTcp.Port); - - var extSecureTcpEndPoint = NodeInfo.ExternalSecureTcp == null - ? null - : new DnsEndPoint(extHostToAdvertise, extSecTcpPortAdvertiseAs > 0 - ? extSecTcpPortAdvertiseAs - : NodeInfo.ExternalSecureTcp.Port); - - var httpEndPoint = new DnsEndPoint(extHostToAdvertise, + var httpEndPoint = new DnsEndPoint(hostToAdvertise, options.Interface.NodePortAdvertiseAs > 0 ? options.Interface.NodePortAdvertiseAs : NodeInfo.HttpEndPoint.GetPort()); - - return new GossipAdvertiseInfo(intTcpEndPoint, intSecureTcpEndPoint, extTcpEndPoint, - extSecureTcpEndPoint, httpEndPoint, options.Interface.ReplicationHostAdvertiseAs, - options.Interface.NodeHostAdvertiseAs, options.Interface.NodePortAdvertiseAs, - options.Interface.AdvertiseHostToClientAs, options.Interface.AdvertiseNodePortToClientAs, - nodeTcpOptions?.NodeTcpPortAdvertiseAs ?? 0); + var replicationPortAdvertiseAs = options.Interface.GetReplicationPortAdvertiseAs(); + var advertisedReplicationEndPoint = new DnsEndPoint(replicationHostToAdvertise, + replicationPortAdvertiseAs > 0 + ? replicationPortAdvertiseAs + : NodeInfo.ReplicationEndPoint.GetPort()); + + return new GossipAdvertiseInfo(httpEndPoint, options.Interface.AdvertiseHostToClientAs, + options.Interface.AdvertiseNodePortToClientAs, advertisedReplicationEndPoint); } _httpService = new KestrelHttpService(_mainQueue, NodeInfo.HttpEndPoint); @@ -1015,15 +957,6 @@ GossipAdvertiseInfo GetGossipAdvertiseInfo() var authorizationGateway = new AuthorizationGateway(_authorizationProvider); - SubscribeWorkers(bus => - { - var tcpSendService = new TcpSendService(); - // ReSharper disable RedundantTypeArgumentsOfMethod - bus.Subscribe(tcpSendService); - // ReSharper restore RedundantTypeArgumentsOfMethod - }); - - var httpAuthenticationProviders = new List(); foreach (var authenticationScheme in _authenticationProvider.GetSupportedAuthenticationSchemes() ?? @@ -1098,6 +1031,7 @@ GossipAdvertiseInfo GetGossipAdvertiseInfo() _mainBus.Subscribe(forwardingService); _mainBus.Subscribe(forwardingService); _mainBus.Subscribe(forwardingService); + _mainBus.Subscribe(forwardingService); _mainBus.Subscribe(forwardingService); _mainBus.Subscribe(forwardingService); _mainBus.Subscribe(forwardingService); @@ -1145,7 +1079,6 @@ GossipAdvertiseInfo GetGossipAdvertiseInfo() subscriptionQueueSlowMessageThreshold); _mainBus.Subscribe(subscrQueue); _mainBus.Subscribe(subscrQueue); - _mainBus.Subscribe(subscrQueue); _mainBus.Subscribe(subscrQueue); _mainBus.Subscribe(subscrQueue); _mainBus.Subscribe(subscrQueue); @@ -1160,7 +1093,6 @@ GossipAdvertiseInfo GetGossipAdvertiseInfo() virtualStreamReader); subscrBus.Subscribe(subscription); subscrBus.Subscribe(subscription); - subscrBus.Subscribe(subscription); subscrBus.Subscribe(subscription); subscrBus.Subscribe(subscription); subscrBus.Subscribe(subscription); @@ -1195,7 +1127,6 @@ GossipAdvertiseInfo GetGossipAdvertiseInfo() perSubscrBus.Subscribe(psubDispatcher); perSubscrBus.Subscribe(psubDispatcher); _mainBus.Subscribe(perSubscrQueue); - _mainBus.Subscribe(perSubscrQueue); _mainBus.Subscribe(perSubscrQueue); _mainBus.Subscribe(perSubscrQueue); _mainBus.Subscribe(perSubscrQueue); @@ -1229,7 +1160,6 @@ GossipAdvertiseInfo GetGossipAdvertiseInfo() perSubscrBus.Subscribe(persistentSubscription); perSubscrBus.Subscribe(persistentSubscription); perSubscrBus.Subscribe(persistentSubscription); - perSubscrBus.Subscribe(persistentSubscription); perSubscrBus.Subscribe(persistentSubscription); perSubscrBus.Subscribe(persistentSubscription); perSubscrBus.Subscribe(persistentSubscription); @@ -1476,15 +1406,11 @@ GossipAdvertiseInfo GetGossipAdvertiseInfo() _mainBus.Subscribe(_timerService); var memberInfo = MemberInfo.Initial(NodeInfo.InstanceId, _timeProvider.UtcNow, VNodeState.Unknown, true, - GossipAdvertiseInfo.InternalTcp, - GossipAdvertiseInfo.InternalSecureTcp, - GossipAdvertiseInfo.ExternalTcp, - GossipAdvertiseInfo.ExternalSecureTcp, GossipAdvertiseInfo.HttpEndPoint, GossipAdvertiseInfo.AdvertiseHostToClientAs, GossipAdvertiseInfo.AdvertiseHttpPortToClientAs, - GossipAdvertiseInfo.AdvertiseTcpPortToClientAs, - options.Cluster.NodePriority, options.Cluster.ReadOnlyReplica, VersionInfo.Version); + options.Cluster.NodePriority, options.Cluster.ReadOnlyReplica, VersionInfo.Version, + GossipAdvertiseInfo.ReplicationEndPoint); // ELECTIONS TRACKER _mainBus.Subscribe(trackers.ElectionCounterTracker); @@ -1528,13 +1454,17 @@ GossipAdvertiseInfo GetGossipAdvertiseInfo() _grpcReplicaServiceSupervisor = new GrpcReplicaServiceSupervisor( _mainQueue, new GrpcReplicaServiceFactory( - new ReplicationGrpcClientFactory(uriScheme, _nodeHttpClientFactory), + new ReplicationGrpcClientFactory( + uriScheme, + _nodeHttpClientFactory, + TimeSpan.FromMilliseconds(options.Interface.ReplicationHeartbeatInterval), + TimeSpan.FromMilliseconds(options.Interface.ReplicationHeartbeatTimeout)), new ReplicaSubscriptionDataSource(Db, epochManager), NodeInfo.InstanceId, options.Cluster.ReadOnlyReplica ? ReplicaPromotability.NonPromotable : ReplicaPromotability.Promotable), - GossipAdvertiseInfo.HttpEndPoint, + GossipAdvertiseInfo.ReplicationEndPoint, AddTask); _mainBus.Subscribe(_grpcReplicaServiceSupervisor); _mainBus.Subscribe(_grpcReplicaServiceSupervisor); @@ -2263,5 +2193,5 @@ private void ReloadCertificates(ClusterVNodeOptions options) } public override string ToString() => - $"[{NodeInfo.InstanceId:B}, {NodeInfo.InternalTcp}, {NodeInfo.ExternalTcp}, {NodeInfo.HttpEndPoint}]"; + $"[{NodeInfo.InstanceId:B}, {NodeInfo.ReplicationEndPoint}, {NodeInfo.HttpEndPoint}]"; } diff --git a/src/EventStore.Core/Configuration/ClusterVNodeOptions.cs b/src/EventStore.Core/Configuration/ClusterVNodeOptions.cs index f4d51719a8..e9341c51d3 100644 --- a/src/EventStore.Core/Configuration/ClusterVNodeOptions.cs +++ b/src/EventStore.Core/Configuration/ClusterVNodeOptions.cs @@ -178,10 +178,10 @@ public record ApplicationOptions [Description("The maximum size of appends, in bytes. May not exceed 16MB.")] public int MaxAppendSize { get; init; } = 1_024 * 1_024; - [Description("Disable Authentication, Authorization and TLS on all TCP/HTTP interfaces.")] + [Description("Disable authentication, authorization, and TLS on the HTTP/gRPC listeners.")] public bool Insecure { get; init; } = false; - [Description("Disable TLS on all TCP/HTTP interfaces while keeping authentication and authorization enabled.")] + [Description("Disable TLS on the HTTP/gRPC listeners while keeping authentication and authorization enabled.")] public bool DisableTls { get; init; } = false; public bool AuthDisabled() => Insecure; @@ -620,7 +620,7 @@ public record GrpcOptions [Description("Interface Options")] public record InterfaceOptions { - [Description("The IP Address used by internal replication between nodes in the cluster.")] + [Description("The IP address used by the gRPC replication listener.")] public IPAddress ReplicationIp { get; init; } = IPAddress.Loopback; [Description("The IP Address for the node.")] @@ -629,13 +629,13 @@ public record InterfaceOptions [Description("The Port to run the HTTP server on.")] public int NodePort { get; init; } = 2113; - [Description("The TCP port used by internal replication between nodes in the cluster.")] + [Description("The port used by the gRPC replication listener.")] public int ReplicationPort { get; init; } = 1112; [Description("Advertise the Node's host name to other nodes and external clients as.")] public string? NodeHostAdvertiseAs { get; init; } = null; - [Description("Advertise the Replication host name to other nodes in the cluster as.")] + [Description("Advertise the gRPC replication host name to other nodes in the cluster as.")] public string? ReplicationHostAdvertiseAs { get; init; } = null; [Description("Advertise Host in Gossip to Client As.")] @@ -647,26 +647,31 @@ public record InterfaceOptions [Description("Advertise Http Port As.")] public int NodePortAdvertiseAs { get; init; } = 0; - [Description("Advertise Replication Tcp Port As.")] + [Description("Advertise the gRPC replication port as.")] + public int ReplicationPortAdvertiseAs { get; init; } = 0; + + [Description("Advertise the gRPC replication port as.")] + [Deprecated( + "The ReplicationTcpPortAdvertiseAs setting has been deprecated because replication uses gRPC. " + + "Use ReplicationPortAdvertiseAs instead.")] public int ReplicationTcpPortAdvertiseAs { get; init; } = 0; - [Description("Heartbeat timeout for Replication TCP sockets."), + public int GetReplicationPortAdvertiseAs() => + ReplicationPortAdvertiseAs > 0 + ? ReplicationPortAdvertiseAs + : ReplicationTcpPortAdvertiseAs; + + [Description("Keepalive ping timeout for gRPC replication connections. Values below 1000 ms use the HTTP/2 minimum of 1000 ms."), Unit("ms")] public int ReplicationHeartbeatTimeout { get; init; } = 700; - [Description("Heartbeat interval for Replication TCP sockets."), + [Description("Keepalive ping interval for gRPC replication connections. Values below 1000 ms use the HTTP/2 minimum of 1000 ms."), Unit("ms")] public int ReplicationHeartbeatInterval { get; init; } = 700; [Description("Whether to allow local connections via a UNIX domain socket.")] public bool EnableUnixSocket { get; init; } = false; - [Description("The maximum number of pending send bytes allowed before a connection is closed.")] - public int ConnectionPendingSendBytesThreshold { get; init; } = 10 * 1_024 * 1_024; - - [Description("The maximum number of pending connection operations allowed before a connection is closed.")] - public int ConnectionQueueSizeThreshold { get; init; } = 50_000; - [Description("Disables the admin ui on the HTTP endpoint.")] public bool DisableAdminUi { get; init; } = false; diff --git a/src/EventStore.Core/Configuration/ClusterVNodeOptionsExtensions.cs b/src/EventStore.Core/Configuration/ClusterVNodeOptionsExtensions.cs index 562cbcd82a..9a4ef773da 100644 --- a/src/EventStore.Core/Configuration/ClusterVNodeOptionsExtensions.cs +++ b/src/EventStore.Core/Configuration/ClusterVNodeOptionsExtensions.cs @@ -89,21 +89,18 @@ options with }; /// - /// Sets the external tcp endpoint to the specified value + /// Sets the http endpoint to the specified value /// /// The - /// The external endpoint to use + /// The http endpoint to use /// A with the options set - public static ClusterVNodeOptions WithExternalTcpOn( + public static ClusterVNodeOptions WithNodeEndpointOn( this ClusterVNodeOptions options, IPEndPoint endPoint) => - options with { Interface = options.Interface with { NodeIp = endPoint.Address, } }; + options with { Interface = options.Interface with { NodeIp = endPoint.Address, NodePort = endPoint.Port } }; /// - /// Sets the internal tcp endpoint to the specified value + /// Sets the endpoint used by node-to-node gRPC replication. /// - /// The - /// The internal endpoint to use - /// A with the options set public static ClusterVNodeOptions WithReplicationEndpointOn( this ClusterVNodeOptions options, IPEndPoint endPoint) => options with @@ -111,16 +108,6 @@ options with Interface = options.Interface with { ReplicationIp = endPoint.Address, ReplicationPort = endPoint.Port } }; - /// - /// Sets the http endpoint to the specified value - /// - /// The - /// The http endpoint to use - /// A with the options set - public static ClusterVNodeOptions WithNodeEndpointOn( - this ClusterVNodeOptions options, IPEndPoint endPoint) => - options with { Interface = options.Interface with { NodeIp = endPoint.Address, NodePort = endPoint.Port } }; - /// /// Sets up the External Host that would be advertised /// @@ -132,19 +119,16 @@ public static ClusterVNodeOptions options with { Interface = options.Interface with { NodeHostAdvertiseAs = endPoint.GetHost(), } }; /// - /// Sets up the Internal Host that would be advertised + /// Sets the endpoint advertised to other nodes for gRPC replication. /// - /// The - /// The advertised host - /// A with the options set - public static ClusterVNodeOptions - AdvertiseInternalHostAs(this ClusterVNodeOptions options, EndPoint endPoint) => + public static ClusterVNodeOptions AdvertiseReplicationHostAs( + this ClusterVNodeOptions options, EndPoint endPoint) => options with { Interface = options.Interface with { ReplicationHostAdvertiseAs = endPoint.GetHost(), - ReplicationTcpPortAdvertiseAs = endPoint.GetPort() + ReplicationPortAdvertiseAs = endPoint.GetPort() } }; diff --git a/src/EventStore.Core/Configuration/ClusterVNodeOptionsValidator.cs b/src/EventStore.Core/Configuration/ClusterVNodeOptionsValidator.cs index 41c7b4b39f..35d4a1312a 100644 --- a/src/EventStore.Core/Configuration/ClusterVNodeOptionsValidator.cs +++ b/src/EventStore.Core/Configuration/ClusterVNodeOptionsValidator.cs @@ -32,6 +32,23 @@ public static void Validate(ClusterVNodeOptions options) throw new ArgumentNullException(nameof(options.Interface.ReplicationIp)); } + if (options.Interface.NodePort == options.Interface.ReplicationPort && + EndpointsOverlap(options.Interface.NodeIp, options.Interface.ReplicationIp)) + { + throw new ArgumentException( + $"{nameof(options.Interface.NodePort)} and {nameof(options.Interface.ReplicationPort)} cannot bind the same endpoint."); + } + + if (options.Interface.ReplicationHeartbeatInterval <= 0) + { + throw new ArgumentOutOfRangeException(nameof(options.Interface.ReplicationHeartbeatInterval)); + } + + if (options.Interface.ReplicationHeartbeatTimeout <= 0) + { + throw new ArgumentOutOfRangeException(nameof(options.Interface.ReplicationHeartbeatTimeout)); + } + if (options.Cluster.ClusterSize <= 0) { throw new ArgumentOutOfRangeException(nameof(options.Cluster.ClusterSize), options.Cluster.ClusterSize, @@ -174,4 +191,11 @@ public static bool ValidateForStartup(ClusterVNodeOptions options) return true; } + private static bool EndpointsOverlap(System.Net.IPAddress first, System.Net.IPAddress second) => + first.Equals(second) || + first.Equals(System.Net.IPAddress.Any) || + second.Equals(System.Net.IPAddress.Any) || + first.Equals(System.Net.IPAddress.IPv6Any) || + second.Equals(System.Net.IPAddress.IPv6Any); + } diff --git a/src/EventStore.Core/Data/GossipAdvertiseInfo.cs b/src/EventStore.Core/Data/GossipAdvertiseInfo.cs index 4c2c09cd25..dc8de85350 100644 --- a/src/EventStore.Core/Data/GossipAdvertiseInfo.cs +++ b/src/EventStore.Core/Data/GossipAdvertiseInfo.cs @@ -5,47 +5,25 @@ namespace EventStore.Core.Data { public class GossipAdvertiseInfo { - public DnsEndPoint InternalTcp { get; } - public DnsEndPoint InternalSecureTcp { get; } - public DnsEndPoint ExternalTcp { get; } - public DnsEndPoint ExternalSecureTcp { get; } + public DnsEndPoint ReplicationEndPoint { get; } public DnsEndPoint HttpEndPoint { get; } - public string AdvertiseInternalHostAs { get; } - public string AdvertiseExternalHostAs { get; } - public int AdvertiseHttpPortAs { get; } public string AdvertiseHostToClientAs { get; } public int AdvertiseHttpPortToClientAs { get; } - public int AdvertiseTcpPortToClientAs { get; } - public GossipAdvertiseInfo(DnsEndPoint internalTcp, DnsEndPoint internalSecureTcp, - DnsEndPoint externalTcp, DnsEndPoint externalSecureTcp, - DnsEndPoint httpEndPoint, - string advertiseInternalHostAs, string advertiseExternalHostAs, int advertiseHttpPortAs, - string advertiseHostToClientAs, int advertiseHttpPortToClientAs, int advertiseTcpPortToClientAs) + public GossipAdvertiseInfo(DnsEndPoint httpEndPoint, + string advertiseHostToClientAs, int advertiseHttpPortToClientAs, + DnsEndPoint replicationEndPoint = null) { - Ensure.Equal(false, internalTcp == null && internalSecureTcp == null, "Both internal TCP endpoints are null"); - - InternalTcp = internalTcp; - InternalSecureTcp = internalSecureTcp; - ExternalTcp = externalTcp; - ExternalSecureTcp = externalSecureTcp; + Ensure.NotNull(httpEndPoint, nameof(httpEndPoint)); + ReplicationEndPoint = replicationEndPoint ?? httpEndPoint; HttpEndPoint = httpEndPoint; - AdvertiseInternalHostAs = advertiseInternalHostAs; - AdvertiseExternalHostAs = advertiseExternalHostAs; - AdvertiseHttpPortAs = advertiseHttpPortAs; AdvertiseHostToClientAs = advertiseHostToClientAs; AdvertiseHttpPortToClientAs = advertiseHttpPortToClientAs; - AdvertiseTcpPortToClientAs = advertiseTcpPortToClientAs; } public override string ToString() { - return string.Format( - $"IntTcp: {InternalTcp}, IntSecureTcp: {InternalSecureTcp}\n" + - $"ExtTcp: {ExternalTcp}, ExtSecureTcp: {ExternalSecureTcp}\n" + - $"Http: {HttpEndPoint}, HttpAdvertiseAs: {AdvertiseExternalHostAs}:{AdvertiseHttpPortAs},\n" + - $"HttpAdvertiseToClientAs: {AdvertiseHostToClientAs}:{AdvertiseHttpPortToClientAs},\n" + - $"TcpAdvertiseToClientAs: {AdvertiseHostToClientAs}:{AdvertiseTcpPortToClientAs}"); + return $"Replication: {ReplicationEndPoint}, Http: {HttpEndPoint}, HttpAdvertiseToClientAs: {AdvertiseHostToClientAs}:{AdvertiseHttpPortToClientAs}"; } } } diff --git a/src/EventStore.Core/Data/VNodeInfo.cs b/src/EventStore.Core/Data/VNodeInfo.cs index a217f5bbd5..9361344b21 100644 --- a/src/EventStore.Core/Data/VNodeInfo.cs +++ b/src/EventStore.Core/Data/VNodeInfo.cs @@ -8,55 +8,32 @@ public class VNodeInfo { public readonly Guid InstanceId; public readonly int DebugIndex; - public readonly IPEndPoint InternalTcp; - public readonly IPEndPoint InternalSecureTcp; - public readonly IPEndPoint ExternalTcp; - public readonly IPEndPoint ExternalSecureTcp; + public readonly EndPoint ReplicationEndPoint; public readonly EndPoint HttpEndPoint; public readonly bool IsReadOnlyReplica; - public VNodeInfo(Guid instanceId, int debugIndex, - IPEndPoint internalTcp, IPEndPoint internalSecureTcp, - IPEndPoint externalTcp, IPEndPoint externalSecureTcp, - EndPoint httpEndPoint, - bool isReadOnlyReplica) + public VNodeInfo(Guid instanceId, int debugIndex, EndPoint httpEndPoint, + bool isReadOnlyReplica, EndPoint replicationEndPoint = null) { Ensure.NotEmptyGuid(instanceId, "instanceId"); - Ensure.Equal(false, internalTcp == null && internalSecureTcp == null, "Both internal TCP endpoints are null"); Ensure.NotNull(httpEndPoint, nameof(httpEndPoint)); DebugIndex = debugIndex; InstanceId = instanceId; - InternalTcp = internalTcp; - InternalSecureTcp = internalSecureTcp; - ExternalTcp = externalTcp; - ExternalSecureTcp = externalSecureTcp; + ReplicationEndPoint = replicationEndPoint ?? httpEndPoint; HttpEndPoint = httpEndPoint; IsReadOnlyReplica = isReadOnlyReplica; } public bool Is(EndPoint endPoint) { - return endPoint != null - && HttpEndPoint.Equals(endPoint) - || (InternalTcp != null && InternalTcp.Equals(endPoint)) - || (InternalSecureTcp != null && InternalSecureTcp.Equals(endPoint)) - || (ExternalTcp != null && ExternalTcp.Equals(endPoint)) - || (ExternalSecureTcp != null && ExternalSecureTcp.Equals(endPoint)); + return endPoint != null && + (HttpEndPoint.Equals(endPoint) || ReplicationEndPoint.Equals(endPoint)); } public override string ToString() { - return string.Format("InstanceId: {0:B}, InternalTcp: {1}, InternalSecureTcp: {2}, " + - "ExternalTcp: {3}, ExternalSecureTcp: {4}, HttpEndPoint: {5}," + - "IsReadOnlyReplica: {6}", - InstanceId, - InternalTcp, - InternalSecureTcp, - ExternalTcp, - ExternalSecureTcp, - HttpEndPoint, - IsReadOnlyReplica); + return $"InstanceId: {InstanceId:B}, ReplicationEndPoint: {ReplicationEndPoint}, HttpEndPoint: {HttpEndPoint}, IsReadOnlyReplica: {IsReadOnlyReplica}"; } } } diff --git a/src/EventStore.Core/Messages/ClientMessage.cs b/src/EventStore.Core/Messages/ClientMessage.cs index 3b0f349bfa..b9632f1f3a 100644 --- a/src/EventStore.Core/Messages/ClientMessage.cs +++ b/src/EventStore.Core/Messages/ClientMessage.cs @@ -203,6 +203,11 @@ public enum NotHandledReason public class LeaderInfo { + public LeaderInfo(EndPoint http) + { + Http = http; + } + public LeaderInfo(EndPoint externalTcp, bool isSecure, EndPoint http) { ExternalTcp = externalTcp; @@ -217,6 +222,19 @@ public LeaderInfo(EndPoint externalTcp, bool isSecure, EndPoint http) } } + [DerivedMessage(CoreMessage.Client)] + public partial class NotAuthenticated : Message + { + public readonly Guid CorrelationId; + public readonly string Reason; + + public NotAuthenticated(Guid correlationId, string reason) + { + CorrelationId = correlationId; + Reason = reason; + } + } + [DerivedMessage(CoreMessage.Client)] public partial class WriteEvents : WriteRequestMessage { diff --git a/src/EventStore.Core/Messages/ClusterInfoDto.cs b/src/EventStore.Core/Messages/ClusterInfoDto.cs deleted file mode 100644 index 68ab95c1e1..0000000000 --- a/src/EventStore.Core/Messages/ClusterInfoDto.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System.Linq; -using System.Net; -using EventStore.Common.Utils; -using EventStore.Core.Cluster; - -namespace EventStore.Core.Messages -{ - public class ClusterInfoDto - { - public MemberInfoDto[] Members { get; set; } - public string ServerIp { get; set; } - public int ServerPort { get; set; } - - public ClusterInfoDto() - { - } - - public ClusterInfoDto(ClusterInfo clusterInfo, EndPoint serverEndPoint) - { - Members = clusterInfo.Members.Select(x => new MemberInfoDto(x)).ToArray(); - ServerIp = serverEndPoint.GetHost(); - ServerPort = serverEndPoint.GetPort(); - } - - public override string ToString() - { - return string.Format("Server: {0}:{1}, Members: [{2}]", - ServerIp, ServerPort, - Members != null ? string.Join(",", Members.Select(m => m.ToString())) : "null"); - } - } -} diff --git a/src/EventStore.Core/Messages/MemberInfoDto.cs b/src/EventStore.Core/Messages/MemberInfoDto.cs deleted file mode 100644 index eb491f64d6..0000000000 --- a/src/EventStore.Core/Messages/MemberInfoDto.cs +++ /dev/null @@ -1,95 +0,0 @@ -using System; -using EventStore.Common.Utils; -using EventStore.Core.Cluster; -using EventStore.Core.Data; - -namespace EventStore.Core.Messages -{ - public class MemberInfoDto - { - public Guid InstanceId { get; set; } - - public DateTime TimeStamp { get; set; } - public VNodeState State { get; set; } - public bool IsAlive { get; set; } - - public string InternalTcpIp { get; set; } - public int InternalTcpPort { get; set; } - public int InternalSecureTcpPort { get; set; } - - public string ExternalTcpIp { get; set; } - public int ExternalTcpPort { get; set; } - public int ExternalSecureTcpPort { get; set; } - - public string HttpEndPointIp { get; set; } - public int HttpEndPointPort { get; set; } - public string AdvertiseHostToClientAs { get; set; } - public int AdvertiseHttpPortToClientAs { get; set; } - public int AdvertiseTcpPortToClientAs { get; set; } - - public long LastCommitPosition { get; set; } - public long WriterCheckpoint { get; set; } - public long ChaserCheckpoint { get; set; } - - public long EpochPosition { get; set; } - public int EpochNumber { get; set; } - public Guid EpochId { get; set; } - - public int NodePriority { get; set; } - public bool IsReadOnlyReplica { get; set; } - - public MemberInfoDto() - { - } - - public MemberInfoDto(MemberInfo member) - { - InstanceId = member.InstanceId; - - TimeStamp = member.TimeStamp; - State = member.State; - IsAlive = member.IsAlive; - - InternalTcpIp = member.InternalTcpEndPoint?.GetHost() ?? member.InternalSecureTcpEndPoint?.GetHost(); - InternalTcpPort = member.InternalTcpEndPoint == null ? 0 : member.InternalTcpEndPoint.GetPort(); - InternalSecureTcpPort = - member.InternalSecureTcpEndPoint == null ? 0 : member.InternalSecureTcpEndPoint.GetPort(); - - ExternalTcpIp = member.ExternalTcpEndPoint?.GetHost() ?? member.ExternalSecureTcpEndPoint?.GetHost(); - ExternalTcpPort = member.ExternalTcpEndPoint == null ? 0 : member.ExternalTcpEndPoint.GetPort(); - ExternalSecureTcpPort = - member.ExternalSecureTcpEndPoint == null ? 0 : member.ExternalSecureTcpEndPoint.GetPort(); - - HttpEndPointIp = member.HttpEndPoint.GetHost(); - HttpEndPointPort = member.HttpEndPoint.GetPort(); - AdvertiseHostToClientAs = member.AdvertiseHostToClientAs; - AdvertiseHttpPortToClientAs = member.AdvertiseHttpPortToClientAs; - AdvertiseTcpPortToClientAs = member.AdvertiseTcpPortToClientAs; - - LastCommitPosition = member.LastCommitPosition; - WriterCheckpoint = member.WriterCheckpoint; - ChaserCheckpoint = member.ChaserCheckpoint; - - EpochPosition = member.EpochPosition; - EpochNumber = member.EpochNumber; - EpochId = member.EpochId; - - NodePriority = member.NodePriority; - IsReadOnlyReplica = member.IsReadOnlyReplica; - } - - public override string ToString() - { - return - $"InstanceId: {InstanceId:B}, TimeStamp: {TimeStamp:yyyy-MM-dd HH:mm:ss.fff}, State: {State}, IsAlive: {IsAlive}, " + - $"InternalTcpIp: {InternalTcpIp}, InternalTcpPort: {InternalTcpPort}, InternalSecureTcpPort: {InternalSecureTcpPort}, " + - $"ExternalTcpIp: {ExternalTcpIp}, ExternalTcpPort: {ExternalTcpPort}, ExternalSecureTcpPort: {ExternalSecureTcpPort}, " + - $"HttpEndPointIp: {HttpEndPointIp}, HttpEndPointPort: {HttpEndPointPort}, " + - $"{nameof(AdvertiseHostToClientAs)}: {AdvertiseHostToClientAs}, {nameof(AdvertiseHttpPortToClientAs)}: {AdvertiseHttpPortToClientAs}, " + - $"{nameof(AdvertiseTcpPortToClientAs)}: {AdvertiseTcpPortToClientAs}, " + - $"LastCommitPosition: {LastCommitPosition}, WriterCheckpoint: {WriterCheckpoint}, ChaserCheckpoint: {ChaserCheckpoint}, " + - $"EpochPosition: {EpochPosition}, EpochNumber: {EpochNumber}, EpochId: {EpochId:B}, NodePriority: {NodePriority}, " + - $"IsReadOnlyReplica: {IsReadOnlyReplica}"; - } - } -} diff --git a/src/EventStore.Core/Services/ElectionsService.cs b/src/EventStore.Core/Services/ElectionsService.cs index d865b66e26..a220a232ce 100644 --- a/src/EventStore.Core/Services/ElectionsService.cs +++ b/src/EventStore.Core/Services/ElectionsService.cs @@ -137,13 +137,11 @@ public ElectionsService(IPublisher publisher, _timeProvider.UtcNow, VNodeState.Initializing, true, - memberInfo.InternalTcpEndPoint, memberInfo.InternalSecureTcpEndPoint, - memberInfo.ExternalTcpEndPoint, memberInfo.ExternalSecureTcpEndPoint, memberInfo.HttpEndPoint, - memberInfo.AdvertiseHostToClientAs, memberInfo.AdvertiseHttpPortToClientAs, memberInfo.AdvertiseTcpPortToClientAs, + memberInfo.AdvertiseHostToClientAs, memberInfo.AdvertiseHttpPortToClientAs, ownInfo.LastCommitPosition, ownInfo.WriterCheckpoint, ownInfo.ChaserCheckpoint, ownInfo.EpochPosition, ownInfo.EpochNumber, ownInfo.EpochId, ownInfo.NodePriority, - memberInfo.IsReadOnlyReplica, VersionInfo.Version) + memberInfo.IsReadOnlyReplica, VersionInfo.Version, memberInfo.ReplicationEndPoint) }; } diff --git a/src/EventStore.Core/Services/Gossip/GossipServiceBase.cs b/src/EventStore.Core/Services/Gossip/GossipServiceBase.cs index 643147e6ed..9b1720c796 100644 --- a/src/EventStore.Core/Services/Gossip/GossipServiceBase.cs +++ b/src/EventStore.Core/Services/Gossip/GossipServiceBase.cs @@ -274,7 +274,7 @@ public void Handle(GossipMessage.GossipSendFailed message) if (node.InstanceId == CurrentLeader?.InstanceId) { Log.Information( - "Leader [{leaderEndPoint}, {instanceId:B}] appears to be DEAD (Gossip send failed); wait for TCP to decide.", + "Leader [{leaderEndPoint}, {instanceId:B}] appears to be DEAD (Gossip send failed); wait for replication transport to decide.", message.Recipient, node.InstanceId); return; } @@ -302,7 +302,7 @@ public void Handle(SystemMessage.VNodeConnectionLost message) return; } - Log.Information("Looks like node [{nodeEndPoint}] is DEAD (TCP connection lost). Issuing a gossip to confirm.", + Log.Information("Looks like node [{nodeEndPoint}] is DEAD (replication connection lost). Issuing a gossip to confirm.", message.VNodeEndPoint); _bus.Publish(new GrpcMessage.SendOverGrpc(node.HttpEndPoint, new GossipMessage.GetGossip(), @@ -354,7 +354,7 @@ public void Handle(GossipMessage.GetGossipFailed message) if (_cluster.HasChangedSince(oldCluster)) { LogClusterChange(oldCluster, _cluster, - string.Format("TCP connection lost to [{0}]", message.Recipient)); + string.Format("Replication connection lost to [{0}]", message.Recipient)); } _bus.Publish(new GossipMessage.GossipUpdated(_cluster)); @@ -371,7 +371,7 @@ public void Handle(SystemMessage.VNodeConnectionEstablished message) if (_cluster.HasChangedSince(oldCluster)) { LogClusterChange(oldCluster, _cluster, - string.Format("TCP connection established to [{0}]", message.VNodeEndPoint)); + string.Format("Replication connection established to [{0}]", message.VNodeEndPoint)); } _bus.Publish(new GossipMessage.GossipUpdated(_cluster)); diff --git a/src/EventStore.Core/Services/Gossip/NodeGossipService.cs b/src/EventStore.Core/Services/Gossip/NodeGossipService.cs index 803e69d77d..cca9ede01e 100644 --- a/src/EventStore.Core/Services/Gossip/NodeGossipService.cs +++ b/src/EventStore.Core/Services/Gossip/NodeGossipService.cs @@ -57,14 +57,9 @@ protected override MemberInfo GetInitialMe() _timeProvider.UtcNow, initialState, true, - _memberInfo.InternalTcpEndPoint, - _memberInfo.InternalSecureTcpEndPoint, - _memberInfo.ExternalTcpEndPoint, - _memberInfo.ExternalSecureTcpEndPoint, _memberInfo.HttpEndPoint, _memberInfo.AdvertiseHostToClientAs, _memberInfo.AdvertiseHttpPortToClientAs, - _memberInfo.AdvertiseTcpPortToClientAs, _getLastCommitPosition(), _writerCheckpoint.Read(), _chaserCheckpoint.Read(), @@ -72,7 +67,8 @@ protected override MemberInfo GetInitialMe() lastEpoch == null ? -1 : lastEpoch.EpochNumber, lastEpoch == null ? Guid.Empty : lastEpoch.EpochId, _nodePriority, - _memberInfo.IsReadOnlyReplica, _memberInfo.ESVersion); + _memberInfo.IsReadOnlyReplica, _memberInfo.ESVersion, + _memberInfo.ReplicationEndPoint); } protected override MemberInfo GetUpdatedMe(MemberInfo me) diff --git a/src/EventStore.Core/Services/Monitoring/MonitoringService.cs b/src/EventStore.Core/Services/Monitoring/MonitoringService.cs index f342fdfcdc..c4db88ef49 100644 --- a/src/EventStore.Core/Services/Monitoring/MonitoringService.cs +++ b/src/EventStore.Core/Services/Monitoring/MonitoringService.cs @@ -13,7 +13,6 @@ using EventStore.Core.Messaging; using EventStore.Core.Services.Monitoring.Stats; using EventStore.Core.Services.UserManagement; -using EventStore.Transport.Tcp; using ILogger = Serilog.ILogger; using Timeout = System.Threading.Timeout; @@ -33,8 +32,7 @@ public class MonitoringService : IHandle, IAsyncHandle, IHandle, IHandle, - IAsyncHandle, - IHandle + IAsyncHandle { private static readonly ILogger RegularLog = Serilog.Log.ForContext(Serilog.Core.Constants.SourceContextPropertyName, "REGULAR-STATS-LOGGER"); @@ -62,19 +60,12 @@ public class MonitoringService : IHandle, private readonly string _nodeStatsStream; private bool _statsStreamCreated; private Guid _streamMetadataWriteCorrId; - private IMonitoredTcpConnection[] _memoizedTcpConnections; - private DateTime _lastTcpConnectionsRequestTime; - private IPEndPoint _tcpEndpoint; - private IPEndPoint _tcpSecureEndpoint; - public MonitoringService(IQueuedHandler monitoringQueue, IAsyncHandle statsCollectionDispatcher, IPublisher mainQueue, TimeSpan statsCollectionPeriod, EndPoint nodeEndpoint, StatsStorage statsStorage, - IPEndPoint tcpEndpoint, - IPEndPoint tcpSecureEndpoint, SystemStatsHelper systemStatsHelper) { Ensure.NotNull(monitoringQueue, "monitoringQueue"); @@ -100,8 +91,6 @@ public MonitoringService(IQueuedHandler monitoringQueue, } _nodeStatsStream = $"{SystemStreams.StatsStreamPrefix}-{nodeEndpoint}"; - _tcpEndpoint = tcpEndpoint; - _tcpSecureEndpoint = tcpSecureEndpoint; _timer = Task.CompletedTask; _systemStats = systemStatsHelper; @@ -343,74 +332,6 @@ public async ValueTask HandleAsync(MonitoringMessage.GetFreshStats message, Canc } } - public void Handle(MonitoringMessage.GetFreshTcpConnectionStats message) - { - try - { - IMonitoredTcpConnection[] connections = null; - if (!TryGetMemoizedTcpConnections(out connections)) - { - connections = TcpConnectionMonitor.Default.GetTcpConnectionStats(); - if (connections != null) - { - _memoizedTcpConnections = connections; - _lastTcpConnectionsRequestTime = DateTime.UtcNow; - } - } - - List connStats = new List(); - foreach (var conn in connections) - { - var tcpConn = conn as TcpConnection; - if (tcpConn != null) - { - var isExternalConnection = _tcpEndpoint != null && _tcpEndpoint.Port == tcpConn.LocalEndPoint.GetPort(); - connStats.Add(new MonitoringMessage.TcpConnectionStats - { - IsExternalConnection = isExternalConnection, - RemoteEndPoint = tcpConn.RemoteEndPoint.ToString(), - LocalEndPoint = tcpConn.LocalEndPoint.ToString(), - ConnectionId = tcpConn.ConnectionId, - ClientConnectionName = tcpConn.ClientConnectionName, - TotalBytesSent = tcpConn.TotalBytesSent, - TotalBytesReceived = tcpConn.TotalBytesReceived, - PendingSendBytes = tcpConn.PendingSendBytes, - PendingReceivedBytes = tcpConn.PendingReceivedBytes, - IsSslConnection = false - }); - } - - var tcpConnSsl = conn as TcpConnectionSsl; - if (tcpConnSsl != null) - { - var isExternalConnection = _tcpSecureEndpoint != null && - _tcpSecureEndpoint.Port == tcpConnSsl.LocalEndPoint.GetPort(); - connStats.Add(new MonitoringMessage.TcpConnectionStats - { - IsExternalConnection = isExternalConnection, - RemoteEndPoint = tcpConnSsl.RemoteEndPoint.ToString(), - LocalEndPoint = tcpConnSsl.LocalEndPoint.ToString(), - ConnectionId = tcpConnSsl.ConnectionId, - ClientConnectionName = tcpConnSsl.ClientConnectionName, - TotalBytesSent = tcpConnSsl.TotalBytesSent, - TotalBytesReceived = tcpConnSsl.TotalBytesReceived, - PendingSendBytes = tcpConnSsl.PendingSendBytes, - PendingReceivedBytes = tcpConnSsl.PendingReceivedBytes, - IsSslConnection = true - }); - } - } - - message.Envelope.ReplyWith( - new MonitoringMessage.GetFreshTcpConnectionStatsCompleted(connStats) - ); - } - catch (Exception ex) - { - Log.Error(ex, "Error on getting fresh tcp connection stats"); - } - } - private bool TryGetMemoizedStats(out StatsContainer stats) { if (_memoizedStats == null || DateTime.UtcNow - _lastStatsRequestTime > MemoizePeriod) @@ -423,16 +344,5 @@ private bool TryGetMemoizedStats(out StatsContainer stats) return true; } - private bool TryGetMemoizedTcpConnections(out IMonitoredTcpConnection[] connections) - { - if (_memoizedTcpConnections == null || DateTime.UtcNow - _lastTcpConnectionsRequestTime > MemoizePeriod) - { - connections = null; - return false; - } - - connections = _memoizedTcpConnections; - return true; - } } } diff --git a/src/EventStore.Core/Services/Replication/GrpcReplicaServiceSupervisor.cs b/src/EventStore.Core/Services/Replication/GrpcReplicaServiceSupervisor.cs index 52d2f29e3d..d4b8bf3c17 100644 --- a/src/EventStore.Core/Services/Replication/GrpcReplicaServiceSupervisor.cs +++ b/src/EventStore.Core/Services/Replication/GrpcReplicaServiceSupervisor.cs @@ -277,7 +277,7 @@ private async ValueTask ReplaceActiveAsync( { active.Service = _factory.Create( new FencedPublisher(this, active), - new GrpcReplicaConnectionEndpoints(leader.HttpEndPoint, _advertisedReplicaEndPoint)); + new GrpcReplicaConnectionEndpoints(leader.ReplicationEndPoint, _advertisedReplicaEndPoint)); SetActive(active); var task = active.Service.Start(); _trackTask(task); @@ -296,7 +296,8 @@ private async ValueTask ReplaceActiveAsync( { ClearActive(active); await StopAsync(active.Service); - Log.Warning(exception, "Failed to start replication stream to [{leaderEndPoint}].", leader.HttpEndPoint); + Log.Warning(exception, "Failed to start replication stream to [{leaderEndPoint}].", + leader.ReplicationEndPoint); _publisher.Publish(new ReplicationMessage.LeaderConnectionFailed( leaderConnectionCorrelationId, leader)); } diff --git a/src/EventStore.Core/Services/Replication/ReplicationGrpcClient.cs b/src/EventStore.Core/Services/Replication/ReplicationGrpcClient.cs index 4c8f0ea9e9..463f63a119 100644 --- a/src/EventStore.Core/Services/Replication/ReplicationGrpcClient.cs +++ b/src/EventStore.Core/Services/Replication/ReplicationGrpcClient.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Net; +using System.Net.Http; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; @@ -32,24 +33,37 @@ public interface IReplicationGrpcCall : IDisposable public sealed class ReplicationGrpcClientFactory : IReplicationGrpcClientFactory { + private static readonly TimeSpan DefaultKeepAlivePingDelay = TimeSpan.FromMilliseconds(700); + private static readonly TimeSpan DefaultKeepAlivePingTimeout = TimeSpan.FromMilliseconds(700); + private static readonly TimeSpan MinimumKeepAlivePingValue = TimeSpan.FromSeconds(1); private readonly string _uriScheme; private readonly INodeHttpClientFactory _nodeHttpClientFactory; + private readonly TimeSpan _keepAlivePingDelay; + private readonly TimeSpan _keepAlivePingTimeout; public ReplicationGrpcClientFactory( string uriScheme, - INodeHttpClientFactory nodeHttpClientFactory) + INodeHttpClientFactory nodeHttpClientFactory, + TimeSpan? keepAlivePingDelay = null, + TimeSpan? keepAlivePingTimeout = null) { Ensure.NotNullOrEmpty(uriScheme, nameof(uriScheme)); Ensure.NotNull(nodeHttpClientFactory, nameof(nodeHttpClientFactory)); _uriScheme = uriScheme; _nodeHttpClientFactory = nodeHttpClientFactory; + _keepAlivePingDelay = NormalizeKeepAliveValue(keepAlivePingDelay ?? DefaultKeepAlivePingDelay); + _keepAlivePingTimeout = NormalizeKeepAliveValue(keepAlivePingTimeout ?? DefaultKeepAlivePingTimeout); } + private static TimeSpan NormalizeKeepAliveValue(TimeSpan value) => + value < MinimumKeepAlivePingValue ? MinimumKeepAlivePingValue : value; + public IReplicationGrpcClient Create(EndPoint leaderEndPoint) { Ensure.NotNull(leaderEndPoint, nameof(leaderEndPoint)); - return new ReplicationGrpcClient(_uriScheme, leaderEndPoint, _nodeHttpClientFactory); + return new ReplicationGrpcClient(_uriScheme, leaderEndPoint, _nodeHttpClientFactory, + _keepAlivePingDelay, _keepAlivePingTimeout); } } @@ -60,9 +74,18 @@ internal sealed class ReplicationGrpcClient : IReplicationGrpcClient public ReplicationGrpcClient( string uriScheme, EndPoint leaderEndPoint, - INodeHttpClientFactory nodeHttpClientFactory) + INodeHttpClientFactory nodeHttpClientFactory, + TimeSpan keepAlivePingDelay, + TimeSpan keepAlivePingTimeout) { - var httpClient = nodeHttpClientFactory.CreateHttpClient(leaderEndPoint.GetOtherNames()); + var httpClient = nodeHttpClientFactory.CreateHttpClient( + leaderEndPoint.GetOtherNames(), + handler => + { + handler.KeepAlivePingDelay = keepAlivePingDelay; + handler.KeepAlivePingTimeout = keepAlivePingTimeout; + handler.KeepAlivePingPolicy = HttpKeepAlivePingPolicy.Always; + }); httpClient.Timeout = Timeout.InfiniteTimeSpan; httpClient.DefaultRequestVersion = new Version(2, 0); diff --git a/src/EventStore.Core/Services/RequestForwarding/GrpcRequestForwardingSupervisor.cs b/src/EventStore.Core/Services/RequestForwarding/GrpcRequestForwardingSupervisor.cs index 0d91272c93..cfd7d7a51b 100644 --- a/src/EventStore.Core/Services/RequestForwarding/GrpcRequestForwardingSupervisor.cs +++ b/src/EventStore.Core/Services/RequestForwarding/GrpcRequestForwardingSupervisor.cs @@ -136,7 +136,7 @@ public void Handle(ClientMessage.ForwardMessage message) "Request forwarding stream is closed.")); break; case RequestForwardingAdmission.CredentialsRequireTls: - PublishIfActive(active, new TcpMessage.NotAuthenticated( + PublishIfActive(active, new ClientMessage.NotAuthenticated( request.InternalCorrId, "Credentials cannot be forwarded unless transport security is enabled.")); break; diff --git a/src/EventStore.Core/Services/RequestForwardingService.cs b/src/EventStore.Core/Services/RequestForwardingService.cs index 00ec4690c2..9a47f63629 100644 --- a/src/EventStore.Core/Services/RequestForwardingService.cs +++ b/src/EventStore.Core/Services/RequestForwardingService.cs @@ -10,6 +10,7 @@ namespace EventStore.Core.Services public class RequestForwardingService : IHandle, IHandle, IHandle, + IHandle, IHandle, IHandle, IHandle, @@ -54,6 +55,13 @@ public void Handle(ClientMessage.NotHandled message) (clientCorrId, m) => new ClientMessage.NotHandled(clientCorrId, m.Reason, m.LeaderInfo)); } + public void Handle(ClientMessage.NotAuthenticated message) + { + _forwardingProxy.TryForwardReply( + message.CorrelationId, message, + (clientCorrId, m) => new ClientMessage.NotAuthenticated(clientCorrId, m.Reason)); + } + public void Handle(TcpMessage.NotAuthenticated message) { _forwardingProxy.TryForwardReply( diff --git a/src/EventStore.Core/Services/Transport/Grpc/Forwarding/ForwardingGrpcCodec.cs b/src/EventStore.Core/Services/Transport/Grpc/Forwarding/ForwardingGrpcCodec.cs index be24786c2e..c9e857c9de 100644 --- a/src/EventStore.Core/Services/Transport/Grpc/Forwarding/ForwardingGrpcCodec.cs +++ b/src/EventStore.Core/Services/Transport/Grpc/Forwarding/ForwardingGrpcCodec.cs @@ -172,7 +172,7 @@ public static ClientMessage.WriteRequestMessage FromGrpc( ClientMessage.TransactionCommitCompleted completed => ToGrpc(completed), ClientMessage.DeleteStreamCompleted completed => ToGrpc(completed), ClientMessage.NotHandled notHandled => ToGrpc(notHandled), - TcpMessage.NotAuthenticated notAuthenticated => ToGrpc(notAuthenticated), + ClientMessage.NotAuthenticated notAuthenticated => ToGrpc(notAuthenticated), _ => throw new ArgumentOutOfRangeException(nameof(message), message.GetType().FullName, "Unsupported forwarding response") } @@ -195,7 +195,7 @@ public static Message FromGrpc(Proto.LeaderFrame frame) Proto.ForwardResponse.PayloadOneofCase.DeleteStream => FromGrpc(correlationId, response.DeleteStream), Proto.ForwardResponse.PayloadOneofCase.NotHandled => FromGrpc(correlationId, response.NotHandled), Proto.ForwardResponse.PayloadOneofCase.NotAuthenticated => - new TcpMessage.NotAuthenticated(correlationId, response.NotAuthenticated.Reason), + new ClientMessage.NotAuthenticated(correlationId, response.NotAuthenticated.Reason), _ => throw new ArgumentOutOfRangeException(nameof(frame), response.PayloadCase, "Unknown forwarding response") }; @@ -457,8 +457,6 @@ private static Proto.ForwardResponse ToGrpc(ClientMessage.NotHandled message) { notHandled.LeaderInfo = new Proto.LeaderInfo { - ExternalTcp = ToGrpc(message.LeaderInfo.ExternalTcp), - IsSecure = message.LeaderInfo.IsSecure, Http = ToGrpc(message.LeaderInfo.Http) }; } @@ -471,7 +469,7 @@ private static Proto.ForwardResponse ToGrpc(ClientMessage.NotHandled message) return response; } - private static Proto.ForwardResponse ToGrpc(TcpMessage.NotAuthenticated message) + private static Proto.ForwardResponse ToGrpc(ClientMessage.NotAuthenticated message) { var response = NewResponse(message.CorrelationId); response.NotAuthenticated = new Proto.NotAuthenticated { Reason = message.Reason ?? string.Empty }; @@ -552,8 +550,6 @@ private static ClientMessage.NotHandled FromGrpc(Guid correlationId, Proto.NotHa correlationId, reason, new ClientMessage.NotHandled.Types.LeaderInfo( - FromGrpc(message.LeaderInfo.ExternalTcp), - message.LeaderInfo.IsSecure, FromGrpc(message.LeaderInfo.Http))), Proto.NotHandled.DetailOneofCase.Description => new ClientMessage.NotHandled(correlationId, reason, message.Description), diff --git a/src/EventStore.Core/Services/Transport/Grpc/Forwarding/ForwardingService.cs b/src/EventStore.Core/Services/Transport/Grpc/Forwarding/ForwardingService.cs index 39b5ef358d..c08f7d546f 100644 --- a/src/EventStore.Core/Services/Transport/Grpc/Forwarding/ForwardingService.cs +++ b/src/EventStore.Core/Services/Transport/Grpc/Forwarding/ForwardingService.cs @@ -355,7 +355,7 @@ public static ForwardedAuthentication Authenticated( public static ForwardedAuthentication NotAuthenticated(string reason) => new( null, null, - requestId => new TcpMessage.NotAuthenticated(requestId, reason)); + requestId => new ClientMessage.NotAuthenticated(requestId, reason)); public static ForwardedAuthentication NotReady(string reason) => new( null, diff --git a/src/EventStore.Core/Services/VNode/ClusterVNodeController.cs b/src/EventStore.Core/Services/VNode/ClusterVNodeController.cs index f75bcb77ff..eb126bf830 100644 --- a/src/EventStore.Core/Services/VNode/ClusterVNodeController.cs +++ b/src/EventStore.Core/Services/VNode/ClusterVNodeController.cs @@ -1101,14 +1101,10 @@ private ValueTask ForwardRequest(ClientMessage.WriteRequestMessage msg, Message private void DenyRequestBecauseNotLeader(Guid correlationId, IEnvelope envelope) { LeaderInfoProvider leaderInfoProvider = new LeaderInfoProvider(_node.GossipAdvertiseInfo, _leader); - var endpoints = leaderInfoProvider.GetLeaderInfoEndPoints(); envelope.ReplyWith( new ClientMessage.NotHandled(correlationId, ClientMessage.NotHandled.Types.NotHandledReason.NotLeader, - new ClientMessage.NotHandled.Types.LeaderInfo(endpoints.AdvertisedTcpEndPoint, - endpoints.IsTcpEndPointSecure, - endpoints.AdvertisedHttpEndPoint - ))); + new ClientMessage.NotHandled.Types.LeaderInfo(leaderInfoProvider.GetLeaderInfoEndPoint()))); } private ValueTask HandleAsReadOnlyReplica(ClientMessage.WriteEvents message, CancellationToken token) @@ -1217,14 +1213,10 @@ private ValueTask HandleAsReadOnlyReplica(ClientMessage.DeleteStream message, Ca private void DenyRequestBecauseReadOnly(Guid correlationId, IEnvelope envelope) { LeaderInfoProvider leaderInfoProvider = new LeaderInfoProvider(_node.GossipAdvertiseInfo, _leader); - var endpoints = leaderInfoProvider.GetLeaderInfoEndPoints(); envelope.ReplyWith( new ClientMessage.NotHandled(correlationId, ClientMessage.NotHandled.Types.NotHandledReason.IsReadOnly, - new ClientMessage.NotHandled.Types.LeaderInfo(endpoints.AdvertisedTcpEndPoint, - endpoints.IsTcpEndPointSecure, - endpoints.AdvertisedHttpEndPoint - ))); + new ClientMessage.NotHandled.Types.LeaderInfo(leaderInfoProvider.GetLeaderInfoEndPoint()))); } private void DenyRequestBecauseNotReady(IEnvelope envelope, Guid correlationId) @@ -1536,11 +1528,8 @@ private async ValueTask Handle(ReplicationMessage.FollowerAssignment message, Ca if (IsLegitimateReplicationMessage(message)) { Log.Information( - "========== [{httpEndPoint}] FOLLOWER ASSIGNMENT RECEIVED FROM [{internalTcp},{internalSecureTcp},{leaderId:B}].", - _nodeInfo.HttpEndPoint, - _leader.InternalTcpEndPoint == null ? "n/a" : _leader.InternalTcpEndPoint.ToString(), - _leader.InternalSecureTcpEndPoint == null ? "n/a" : _leader.InternalSecureTcpEndPoint.ToString(), - message.LeaderId); + "========== [{httpEndPoint}] FOLLOWER ASSIGNMENT RECEIVED FROM [{leaderEndPoint},{leaderId:B}].", + _nodeInfo.HttpEndPoint, _leader.HttpEndPoint, message.LeaderId); await _outputBus.DispatchAsync(message, token); await _fsm.HandleAsync(new SystemMessage.BecomeFollower(_stateCorrelationId, _leader), token); } @@ -1551,11 +1540,8 @@ private async ValueTask Handle(ReplicationMessage.CloneAssignment message, Cance if (IsLegitimateReplicationMessage(message)) { Log.Information( - "========== [{httpEndPoint}] CLONE ASSIGNMENT RECEIVED FROM [{internalTcp},{internalSecureTcp},{leaderId:B}].", - _nodeInfo.HttpEndPoint, - _leader.InternalTcpEndPoint == null ? "n/a" : _leader.InternalTcpEndPoint.ToString(), - _leader.InternalSecureTcpEndPoint == null ? "n/a" : _leader.InternalSecureTcpEndPoint.ToString(), - message.LeaderId); + "========== [{httpEndPoint}] CLONE ASSIGNMENT RECEIVED FROM [{leaderEndPoint},{leaderId:B}].", + _nodeInfo.HttpEndPoint, _leader.HttpEndPoint, message.LeaderId); await _outputBus.DispatchAsync(message, token); await _fsm.HandleAsync(new SystemMessage.BecomeClone(_stateCorrelationId, _leader), token); } @@ -1569,11 +1555,8 @@ private ValueTask Handle(ReplicationMessage.DropSubscription message, Cancellati if (IsLegitimateReplicationMessage(message)) { Log.Information( - "========== [{httpEndPoint}] DROP SUBSCRIPTION REQUEST RECEIVED FROM [{internalTcp},{internalSecureTcp},{leaderId:B}]. THIS MEANS THAT THERE IS A SURPLUS OF NODES IN THE CLUSTER, SHUTTING DOWN.", - _nodeInfo.HttpEndPoint, - _leader.InternalTcpEndPoint == null ? "n/a" : _leader.InternalTcpEndPoint.ToString(), - _leader.InternalSecureTcpEndPoint == null ? "n/a" : _leader.InternalSecureTcpEndPoint.ToString(), - message.LeaderId); + "========== [{httpEndPoint}] DROP SUBSCRIPTION REQUEST RECEIVED FROM [{leaderEndPoint},{leaderId:B}]. THIS MEANS THAT THERE IS A SURPLUS OF NODES IN THE CLUSTER, SHUTTING DOWN.", + _nodeInfo.HttpEndPoint, _leader.HttpEndPoint, message.LeaderId); task = _outputBus.DispatchAsync( new ClientMessage.RequestShutdown(exitProcess: true, shutdownHttp: true), token); } diff --git a/src/EventStore.Core/Services/VNode/LeaderInfoProvider.cs b/src/EventStore.Core/Services/VNode/LeaderInfoProvider.cs index 8189b16503..f4a6e75e4b 100644 --- a/src/EventStore.Core/Services/VNode/LeaderInfoProvider.cs +++ b/src/EventStore.Core/Services/VNode/LeaderInfoProvider.cs @@ -20,38 +20,21 @@ public LeaderInfoProvider(GossipAdvertiseInfo gossipInfo, Cluster.MemberInfo lea _leaderInfo = leaderInfo; } - public (EndPoint AdvertisedTcpEndPoint, bool IsTcpEndPointSecure, EndPoint AdvertisedHttpEndPoint) - GetLeaderInfoEndPoints() + public EndPoint GetLeaderInfoEndPoint() { - var endpoints = _leaderInfo != null - ? (TcpEndPoint: _leaderInfo.ExternalTcpEndPoint ?? _leaderInfo.ExternalSecureTcpEndPoint, - IsTcpEndPointSecure: _leaderInfo.ExternalSecureTcpEndPoint != null, - HttpEndPoint: _leaderInfo.HttpEndPoint, + ? (HttpEndPoint: _leaderInfo.HttpEndPoint, AdvertiseHost: _leaderInfo.AdvertiseHostToClientAs, - AdvertiseHttpPort: _leaderInfo.AdvertiseHttpPortToClientAs, - AdvertiseTcpPort: _leaderInfo.AdvertiseTcpPortToClientAs) - : (TcpEndPoint: _gossipInfo.ExternalTcp ?? _gossipInfo.ExternalSecureTcp, - IsTcpEndPointSecure: _gossipInfo.ExternalSecureTcp != null, - HttpEndPoint: _gossipInfo.HttpEndPoint, + AdvertiseHttpPort: _leaderInfo.AdvertiseHttpPortToClientAs) + : (HttpEndPoint: (EndPoint)_gossipInfo.HttpEndPoint, AdvertiseHost: _gossipInfo.AdvertiseHostToClientAs, - AdvertiseHttpPort: _gossipInfo.AdvertiseHttpPortToClientAs, - AdvertiseTcpPort: _gossipInfo.AdvertiseTcpPortToClientAs); + AdvertiseHttpPort: _gossipInfo.AdvertiseHttpPortToClientAs); - var advertisedTcpEndPoint = endpoints.TcpEndPoint == null - ? null - : new DnsEndPoint( - string.IsNullOrEmpty(endpoints.AdvertiseHost) - ? endpoints.TcpEndPoint.GetHost() - : endpoints.AdvertiseHost, - endpoints.AdvertiseTcpPort == 0 ? endpoints.TcpEndPoint.GetPort() : endpoints.AdvertiseTcpPort); - var advertisedHttpEndPoint = new DnsEndPoint( + return new DnsEndPoint( string.IsNullOrEmpty(endpoints.AdvertiseHost) ? endpoints.HttpEndPoint.GetHost() : endpoints.AdvertiseHost, endpoints.AdvertiseHttpPort == 0 ? endpoints.HttpEndPoint.GetPort() : endpoints.AdvertiseHttpPort); - - return (advertisedTcpEndPoint, endpoints.IsTcpEndPointSecure, advertisedHttpEndPoint); } } } diff --git a/src/EventStore.Projections.Core.Tests/Services/projections_system/when_starting_up.cs b/src/EventStore.Projections.Core.Tests/Services/projections_system/when_starting_up.cs index 83cfb94daf..f970cd0c05 100644 --- a/src/EventStore.Projections.Core.Tests/Services/projections_system/when_starting_up.cs +++ b/src/EventStore.Projections.Core.Tests/Services/projections_system/when_starting_up.cs @@ -59,11 +59,7 @@ protected override IEnumerable PreWhen() { yield return (new SystemMessage.BecomeFollower(Guid.NewGuid(), MemberInfo.Initial(Guid.NewGuid(), DateTime.UtcNow, VNodeState.Unknown, true, - new IPEndPoint(IPAddress.Loopback, 1111), - new IPEndPoint(IPAddress.Loopback, 1112), - new IPEndPoint(IPAddress.Loopback, 1113), - new IPEndPoint(IPAddress.Loopback, 1114), - new IPEndPoint(IPAddress.Loopback, 1115), null, 0, 0, + new IPEndPoint(IPAddress.Loopback, 1115), null, 0, 1, false ))); diff --git a/src/Protos/Grpc/cluster.proto b/src/Protos/Grpc/cluster.proto index e7e5cd25a1..e53b81756b 100644 --- a/src/Protos/Grpc/cluster.proto +++ b/src/Protos/Grpc/cluster.proto @@ -128,10 +128,8 @@ message MemberInfo { VNodeState state = 3; bool is_alive = 4; EndPoint http_end_point = 5; - EndPoint internal_tcp = 6; - EndPoint external_tcp = 7; - bool internal_tcp_uses_tls = 8; - bool external_tcp_uses_tls = 9; + reserved 6, 7, 8, 9; + reserved "internal_tcp", "external_tcp", "internal_tcp_uses_tls", "external_tcp_uses_tls"; int64 last_commit_position = 10; int64 writer_checkpoint = 11; @@ -142,8 +140,10 @@ message MemberInfo { int32 node_priority = 16; bool is_read_only_replica = 17; - string advertise_host_to_client_as = 18; - uint32 advertise_http_port_to_client_as = 19; - uint32 advertise_tcp_port_to_client_as = 20; + string advertise_host_to_client_as = 18; + uint32 advertise_http_port_to_client_as = 19; + reserved 20; + reserved "advertise_tcp_port_to_client_as"; string es_version = 21; + EndPoint replication_end_point = 22; } diff --git a/src/Protos/Grpc/forwarding.proto b/src/Protos/Grpc/forwarding.proto index 621ceb77d4..47387edf76 100644 --- a/src/Protos/Grpc/forwarding.proto +++ b/src/Protos/Grpc/forwarding.proto @@ -188,8 +188,8 @@ enum NotHandledReason { } message LeaderInfo { - EndPoint external_tcp = 1; - bool is_secure = 2; + reserved 1, 2; + reserved "external_tcp", "is_secure"; EndPoint http = 3; }