From 36e95124ca51cfabc65a6c80edf990908da87db2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paulus=20P=C3=A4rssinen?= Date: Mon, 3 Feb 2025 21:57:52 +0000 Subject: [PATCH] Add Unix socket support (#944) * Add Unix socket support * More cluster test adjustments * Remove warnings * dotnet format whitespace * fix merge * fix merge * Add very basic test for the unix socket support * Try fix long-path error & sync GarnetClientSession connection logic with GarnetClient * Dispose the GarnetClient objects in tests * Sync LightClient connection logic with GarnetClient(Session) * fix * Add exception message to StoreWrapper.GetIp * Fix merge * Add TLS test path * Add more Unix socket tests with SE.Redis as client * Add config parsing for --unixsocketperm and tests * attempt fix import ordering * Set unix socket permission after listener is bound * attempt fix import ordering * attempt fix import ordering * attempt fix import ordering * use ILogger in LightClient --------- Co-authored-by: Vasileios Zois <96085550+vazois@users.noreply.github.com> --- .../BDN.benchmark/Cluster/ClusterContext.cs | 3 +- .../Embedded/GarnetServerEmbedded.cs | 7 +- benchmark/Resp.benchmark/Program.cs | 5 +- benchmark/Resp.benchmark/RespOnlineBench.cs | 21 +- benchmark/Resp.benchmark/RespPerfBench.cs | 7 +- benchmark/Resp.benchmark/TxnPerfBench.cs | 5 +- .../ClientSession/GarnetClientSession.cs | 153 +++++++++---- libs/client/GarnetClient.cs | 100 +++++---- libs/cluster/Server/ClusterConfig.cs | 27 +-- libs/cluster/Server/ClusterManager.cs | 11 +- libs/cluster/Server/ClusterProvider.cs | 2 +- .../Server/Failover/FailoverSession.cs | 5 +- .../Server/Failover/ReplicaFailoverSession.cs | 11 +- .../Server/GarnetClusterConnectionStore.cs | 8 +- libs/cluster/Server/GarnetServerNode.cs | 20 +- libs/cluster/Server/Gossip.cs | 22 +- .../Server/Migration/MigrateSession.cs | 4 +- .../Replication/PrimaryOps/AofTaskStore.cs | 4 +- .../PrimaryOps/ReplicaSyncSession.cs | 4 +- .../ReplicaOps/ReplicaReceiveCheckpoint.cs | 4 +- .../Session/RespClusterBasicCommands.cs | 4 +- libs/common/ClientBase.cs | 29 +-- libs/common/LightClient.cs | 120 ++++++---- libs/common/LightClientRequest.cs | 11 +- .../Networking/TcpNetworkHandlerBase.cs | 8 +- libs/host/Configuration/Options.cs | 35 ++- libs/host/Configuration/OptionsValidators.cs | 41 ++++ libs/host/Configuration/Redis/RedisOptions.cs | 6 + libs/host/GarnetServer.cs | 25 ++- libs/host/defaults.conf | 8 +- libs/server/Metrics/GarnetServerMonitor.cs | 8 +- libs/server/Metrics/Info/GarnetInfoMetrics.cs | 2 +- libs/server/Resp/PurgeBPCommand.cs | 2 +- libs/server/Servers/GarnetServerBase.cs | 51 ++--- libs/server/Servers/GarnetServerOptions.cs | 10 + libs/server/Servers/GarnetServerTcp.cs | 63 ++++-- libs/server/Servers/ServerOptions.cs | 10 +- libs/server/StoreWrapper.cs | 6 +- .../ClusterStress/ShardedRespOnlineBench.cs | 6 +- .../ClusterStress/ShardedRespPerfBench.cs | 9 +- .../CommandInfoUpdater/CommandInfoUpdater.cs | 2 +- playground/GarnetClientStress/Options.cs | 2 +- .../GarnetClientStress/SimpleStressTests.cs | 4 +- playground/GarnetClientStress/TaskScaling.cs | 4 +- playground/MigrateBench/MigrateRequest.cs | 4 +- playground/MigrateBench/MigrateSlotWalk.cs | 3 +- .../GarnetClientSample/GarnetClientSamples.cs | 33 ++- samples/GarnetClientSample/Program.cs | 7 +- .../ClusterAuthCommsTests.cs | 2 +- .../Garnet.test.cluster/ClusterConfigTests.cs | 2 +- .../ClusterManagementTests.cs | 8 +- .../ClusterMigrateTests.cs | 139 ++++++------ .../ClusterRedirectTests.cs | 26 +-- .../Garnet.test.cluster/ClusterTestContext.cs | 107 ++++----- test/Garnet.test.cluster/ClusterTestUtils.cs | 81 ++++--- .../ClusterReplicationBaseTests.cs | 32 +-- test/Garnet.test/CacheSizeTrackerTests.cs | 2 +- test/Garnet.test/GarnetClientTests.cs | 45 ++-- .../GarnetJSON/JsonCommandsTest.cs | 2 +- test/Garnet.test/GarnetServerConfigTests.cs | 51 ++++- test/Garnet.test/IndexGrowthTests.cs | 4 +- test/Garnet.test/LuaScriptTests.cs | 4 +- test/Garnet.test/ReadCacheTests.cs | 4 +- test/Garnet.test/Resp/ACL/RespCommandTests.cs | 3 +- test/Garnet.test/RespAdminCommandsTests.cs | 24 +- test/Garnet.test/RespAofAzureTests.cs | 30 +-- test/Garnet.test/RespAofTests.cs | 4 +- test/Garnet.test/RespEtagTests.cs | 10 +- test/Garnet.test/RespHashTests.cs | 2 +- test/Garnet.test/RespInfoTests.cs | 2 +- test/Garnet.test/RespListGarnetClientTests.cs | 16 +- test/Garnet.test/RespLowMemoryTests.cs | 2 +- .../RespSortedSetGarnetClientTests.cs | 26 +-- test/Garnet.test/RespSortedSetTests.cs | 3 +- test/Garnet.test/RespTests.cs | 14 +- test/Garnet.test/RespTlsTests.cs | 2 +- test/Garnet.test/TestUtils.cs | 211 ++++++++++-------- test/Garnet.test/UnixSocketTests.cs | 94 ++++++++ 78 files changed, 1108 insertions(+), 780 deletions(-) create mode 100644 test/Garnet.test/UnixSocketTests.cs diff --git a/benchmark/BDN.benchmark/Cluster/ClusterContext.cs b/benchmark/BDN.benchmark/Cluster/ClusterContext.cs index 5805a185de..fcfc9a265c 100644 --- a/benchmark/BDN.benchmark/Cluster/ClusterContext.cs +++ b/benchmark/BDN.benchmark/Cluster/ClusterContext.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. +using System.Net; using System.Runtime.InteropServices; using System.Text; using BDN.benchmark.CustomProcs; @@ -34,7 +35,7 @@ public void SetupSingleInstance(bool disableSlotVerification = false) { QuietMode = true, EnableCluster = !disableSlotVerification, - Port = port, + EndPoint = new IPEndPoint(IPAddress.Loopback, port), CleanClusterConfig = true, }; if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) diff --git a/benchmark/BDN.benchmark/Embedded/GarnetServerEmbedded.cs b/benchmark/BDN.benchmark/Embedded/GarnetServerEmbedded.cs index 63fe7d90ec..24655aa1f5 100644 --- a/benchmark/BDN.benchmark/Embedded/GarnetServerEmbedded.cs +++ b/benchmark/BDN.benchmark/Embedded/GarnetServerEmbedded.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. using System; +using System.Net; using System.Net.Security; using System.Threading; using Garnet.common; @@ -13,7 +14,7 @@ namespace Embedded.server { internal class GarnetServerEmbedded : GarnetServerBase, IServerHook { - public GarnetServerEmbedded() : base("0.0.0.0", 0, 1 << 10) + public GarnetServerEmbedded() : base(new IPEndPoint(IPAddress.Loopback, 0), 1 << 10) { } @@ -36,7 +37,7 @@ public EmbeddedNetworkHandler CreateNetworkHandler(SslClientAuthenticationOption throw new Exception("Unable to add handler to dictionary"); handler.Start(tlsOptions, remoteEndpointName); - incr_conn_recv(); + IncrementConnectionsReceived(); return handler; } catch (Exception ex) @@ -59,7 +60,7 @@ public void DisposeMessageConsumer(INetworkHandler session) if (activeHandlers.TryRemove(session, out _)) { Interlocked.Decrement(ref activeHandlerCount); - incr_conn_disp(); + IncrementConnectionsDisposed(); try { session.Session?.Dispose(); diff --git a/benchmark/Resp.benchmark/Program.cs b/benchmark/Resp.benchmark/Program.cs index 42a41f8de2..3dd2ce5d3b 100644 --- a/benchmark/Resp.benchmark/Program.cs +++ b/benchmark/Resp.benchmark/Program.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Net; using System.Threading; using CommandLine; using Garnet.client; @@ -195,7 +196,7 @@ static void Main(string[] args) static void WaitForServer(Options opts) { - using var client = new GarnetClientSession(opts.Address, opts.Port, new(), tlsOptions: opts.EnableTLS ? BenchUtils.GetTlsOptions(opts.TlsHost, opts.CertFileName, opts.CertPassword) : null); + using var client = new GarnetClientSession(new IPEndPoint(IPAddress.Parse(opts.Address), opts.Port), new(), tlsOptions: opts.EnableTLS ? BenchUtils.GetTlsOptions(opts.TlsHost, opts.CertFileName, opts.CertPassword) : null); while (true) { try @@ -228,7 +229,7 @@ static void RunBasicCommandsBenchmark(Options opts) unsafe { var onResponseDelegate = new LightClient.OnResponseDelegateUnsafe(ReqGen.OnResponse); - using var client = new LightClient(opts.Address, opts.Port, (int)OpType.GET, onResponseDelegate, opts.DbSize, opts.EnableTLS ? BenchUtils.GetTlsOptions(opts.TlsHost, opts.CertFileName, opts.CertPassword) : null); + using var client = new LightClient(new IPEndPoint(IPAddress.Parse(opts.Address), opts.Port), (int)OpType.GET, onResponseDelegate, opts.DbSize, opts.EnableTLS ? BenchUtils.GetTlsOptions(opts.TlsHost, opts.CertFileName, opts.CertPassword) : null); client.Connect(); client.Authenticate(opts.Auth); BenchUtils.LoadSetGetScripts(client, out BenchUtils.sha1SetScript, out BenchUtils.sha1GetScript); diff --git a/benchmark/Resp.benchmark/RespOnlineBench.cs b/benchmark/Resp.benchmark/RespOnlineBench.cs index ddb61f0b02..20aa0c3aa2 100644 --- a/benchmark/Resp.benchmark/RespOnlineBench.cs +++ b/benchmark/Resp.benchmark/RespOnlineBench.cs @@ -5,6 +5,7 @@ using System.Buffers; using System.Diagnostics; using System.Linq; +using System.Net; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; @@ -33,8 +34,7 @@ internal class RespOnlineBench static bool IsValidRange(long value) => value < HISTOGRAM_UPPER_BOUND && value > HISTOGRAM_LOWER_BOUND; - readonly string address; - readonly int port; + readonly EndPoint endpoint; readonly int NumThreads; readonly OpType op; readonly Options opts; @@ -71,8 +71,7 @@ public RespOnlineBench(Options opts, int resetInterval = 30, int runDuration = i { this.runDuration = runDuration; this.resetInterval = resetInterval; - this.address = opts.Address; - this.port = opts.Port; + this.endpoint = new IPEndPoint(IPAddress.Parse(opts.Address), opts.Port); this.op = opts.Op; this.opts = opts; this.auth = opts.Auth; @@ -157,7 +156,7 @@ private void InitializeClients() { gcsPool = new AsyncPool(opts.NumThreads.First(), () => { - var c = new GarnetClientSession(address, port, new(), tlsOptions: opts.EnableTLS ? BenchUtils.GetTlsOptions(opts.TlsHost, opts.CertFileName, opts.CertPassword) : null); + var c = new GarnetClientSession(endpoint, new(), tlsOptions: opts.EnableTLS ? BenchUtils.GetTlsOptions(opts.TlsHost, opts.CertFileName, opts.CertPassword) : null); c.Connect(); if (auth != null) { @@ -173,7 +172,7 @@ private void InitializeClients() { gdbPool = new AsyncPool(opts.NumThreads.First(), () => { - var gdb = new GarnetClient(address, port, opts.EnableTLS ? BenchUtils.GetTlsOptions(opts.TlsHost, opts.CertFileName, opts.CertPassword) : null, recordLatency: opts.ClientHistogram); + var gdb = new GarnetClient(endpoint, opts.EnableTLS ? BenchUtils.GetTlsOptions(opts.TlsHost, opts.CertFileName, opts.CertPassword) : null, recordLatency: opts.ClientHistogram); gdb.Connect(); if (auth != null) { @@ -184,7 +183,7 @@ private void InitializeClients() } else { - garnetClient = new GarnetClient(address, port, opts.EnableTLS ? BenchUtils.GetTlsOptions(opts.TlsHost, opts.CertFileName, opts.CertPassword) : null, recordLatency: opts.ClientHistogram); + garnetClient = new GarnetClient(endpoint, opts.EnableTLS ? BenchUtils.GetTlsOptions(opts.TlsHost, opts.CertFileName, opts.CertPassword) : null, recordLatency: opts.ClientHistogram); garnetClient.Connect(); if (auth != null) { @@ -427,7 +426,7 @@ public unsafe void OpRunnerLightClient(int thread_id) var onResponseDelegate = new LightClient.OnResponseDelegateUnsafe(ReqGen.OnResponse); - var client = new LightClient(address, port, (int)op, onResponseDelegate, size, opts.EnableTLS ? BenchUtils.GetTlsOptions(opts.TlsHost, opts.CertFileName, opts.CertPassword) : null); + var client = new LightClient(endpoint, (int)op, onResponseDelegate, size, opts.EnableTLS ? BenchUtils.GetTlsOptions(opts.TlsHost, opts.CertFileName, opts.CertPassword) : null); client.Connect(); client.Authenticate(auth); @@ -571,8 +570,7 @@ public async void OpRunnerGarnetClientSession(int thread_id) if (!opts.Pool) { client = new GarnetClientSession( - address, - port, + endpoint, new(Math.Max(bufferSizeValue, opts.ValueLength * opts.IntraThreadParallelism)), tlsOptions: opts.EnableTLS ? BenchUtils.GetTlsOptions(opts.TlsHost, opts.CertFileName, opts.CertPassword) : null); client.Connect(); @@ -671,8 +669,7 @@ public async void OpRunnerGarnetClientSessionParallel(int thread_id, int paralle if (!opts.Pool) { client = new GarnetClientSession( - address, - port, + endpoint, new NetworkBufferSettings(Math.Max(131072, opts.IntraThreadParallelism * opts.ValueLength)), tlsOptions: opts.EnableTLS ? BenchUtils.GetTlsOptions(opts.TlsHost, opts.CertFileName, opts.CertPassword) : null); client.Connect(); diff --git a/benchmark/Resp.benchmark/RespPerfBench.cs b/benchmark/Resp.benchmark/RespPerfBench.cs index 6de8506fc5..261bdc93b7 100644 --- a/benchmark/Resp.benchmark/RespPerfBench.cs +++ b/benchmark/Resp.benchmark/RespPerfBench.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using System.Net; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -86,7 +87,7 @@ private unsafe void GetDBSIZE(int loadDbThreads) { var req = Encoding.ASCII.GetBytes("*1\r\n$6\r\nDBSIZE\r\n"); var lighClientOnResponseDelegate = new LightClient.OnResponseDelegateUnsafe(ReqGen.OnResponse); - using LightClient client = new(opts.Address, opts.Port, (int)OpType.DBSIZE, lighClientOnResponseDelegate, 128, opts.EnableTLS ? BenchUtils.GetTlsOptions(opts.TlsHost, opts.CertFileName, opts.CertPassword) : null); + using LightClient client = new(new IPEndPoint(IPAddress.Parse(opts.Address), opts.Port), (int)OpType.DBSIZE, lighClientOnResponseDelegate, 128, opts.EnableTLS ? BenchUtils.GetTlsOptions(opts.TlsHost, opts.CertFileName, opts.CertPassword) : null); client.Connect(); client.Authenticate(opts.Auth); @@ -373,7 +374,7 @@ public ReqGen LightOperate( private unsafe void LightOperateThreadRunner(int NumOps, OpType opType, ReqGen rg) { var lighClientOnResponseDelegate = new LightClient.OnResponseDelegateUnsafe(ReqGen.OnResponse); - using ClientBase client = new LightClient(opts.Address, opts.Port, (int)opType, lighClientOnResponseDelegate, rg.GetBufferSize(), opts.EnableTLS ? BenchUtils.GetTlsOptions(opts.TlsHost, opts.CertFileName, opts.CertPassword) : null); + using ClientBase client = new LightClient(new IPEndPoint(IPAddress.Parse(opts.Address), opts.Port), (int)opType, lighClientOnResponseDelegate, rg.GetBufferSize(), opts.EnableTLS ? BenchUtils.GetTlsOptions(opts.TlsHost, opts.CertFileName, opts.CertPassword) : null); client.Connect(); client.Authenticate(opts.Auth); @@ -407,7 +408,7 @@ private void GarnetClientSessionOperateThreadRunner(int NumOps, OpType opType, R default: throw new Exception($"opType: {opType} benchmark not supported with GarnetClientSession!"); } - var c = new GarnetClientSession(opts.Address, opts.Port, new(), tlsOptions: opts.EnableTLS ? BenchUtils.GetTlsOptions(opts.TlsHost, opts.CertFileName, opts.CertPassword) : null); + var c = new GarnetClientSession(new IPEndPoint(IPAddress.Parse(opts.Address), opts.Port), new(), tlsOptions: opts.EnableTLS ? BenchUtils.GetTlsOptions(opts.TlsHost, opts.CertFileName, opts.CertPassword) : null); c.Connect(); if (opts.Auth != null) { diff --git a/benchmark/Resp.benchmark/TxnPerfBench.cs b/benchmark/Resp.benchmark/TxnPerfBench.cs index 89fa74e759..6312ae7b26 100644 --- a/benchmark/Resp.benchmark/TxnPerfBench.cs +++ b/benchmark/Resp.benchmark/TxnPerfBench.cs @@ -4,6 +4,7 @@ using System; using System.Diagnostics; using System.Linq; +using System.Net; using System.Threading; using Garnet.client; using Garnet.common; @@ -107,7 +108,7 @@ public void Run() { gcsPool = new AsyncPool(opts.NumThreads.First(), () => { - var c = new GarnetClientSession(address, port, new(), tlsOptions: opts.EnableTLS ? BenchUtils.GetTlsOptions(opts.TlsHost, opts.CertFileName, opts.CertPassword) : null); + var c = new GarnetClientSession(new IPEndPoint(IPAddress.Parse(address), port), new(), tlsOptions: opts.EnableTLS ? BenchUtils.GetTlsOptions(opts.TlsHost, opts.CertFileName, opts.CertPassword) : null); c.Connect(); if (auth != null) { @@ -325,7 +326,7 @@ public void OpRunnerSERedis(int thread_id) public void LoadData() { var req = new OnlineReqGen(0, opts.DbSize, true, opts.Zipf, opts.KeyLength, opts.ValueLength); - GarnetClientSession client = new(address, port, new(), tlsOptions: opts.EnableTLS ? BenchUtils.GetTlsOptions(opts.TlsHost, opts.CertFileName, opts.CertPassword) : null); + GarnetClientSession client = new(new IPEndPoint(IPAddress.Parse(address), port), new(), tlsOptions: opts.EnableTLS ? BenchUtils.GetTlsOptions(opts.TlsHost, opts.CertFileName, opts.CertPassword) : null); client.Connect(); if (auth != null) { diff --git a/libs/client/ClientSession/GarnetClientSession.cs b/libs/client/ClientSession/GarnetClientSession.cs index 6c76d77c02..e396299858 100644 --- a/libs/client/ClientSession/GarnetClientSession.cs +++ b/libs/client/ClientSession/GarnetClientSession.cs @@ -19,10 +19,8 @@ namespace Garnet.client /// Mono-threaded remote client session for Garnet (a session makes a single network connection, and /// expects mono-threaded client access, i.e., no concurrent invocations of API by client) /// - public sealed unsafe partial class GarnetClientSession : IServerHook, IMessageConsumer + public sealed partial class GarnetClientSession : IServerHook, IMessageConsumer { - readonly string address; - readonly int port; readonly int bufferSizeDigits; INetworkSender networkSender; readonly ElasticCircularBuffer tasksTypes = new(); @@ -47,11 +45,16 @@ public sealed unsafe partial class GarnetClientSession : IServerHook, IMessageCo int disposed; // Send - byte* offset, end; + unsafe byte* offset, end; // Num outstanding commands volatile int numCommands; + /// + /// The host endpoint + /// + public EndPoint EndPoint { get; } + /// public bool Disposed => disposed > 0; @@ -88,8 +91,7 @@ public sealed unsafe partial class GarnetClientSession : IServerHook, IMessageCo /// /// Create client instance /// - /// IP address of server - /// Port of server + /// Endpoint of the server /// TLS options /// Username to authenticate with /// Password to authenticate with @@ -98,8 +100,7 @@ public sealed unsafe partial class GarnetClientSession : IServerHook, IMessageCo /// Max outstanding network sends allowed /// Logger public GarnetClientSession( - string address, - int port, + EndPoint endpoint, NetworkBufferSettings networkBufferSettings, LimitedFixedBufferPool networkPool = null, SslClientAuthenticationOptions tlsOptions = null, @@ -108,8 +109,7 @@ public GarnetClientSession( int networkSendThrottleMax = 8, ILogger logger = null) { - this.address = address; - this.port = port; + EndPoint = endpoint; this.usingManagedNetworkPool = networkPool != null; this.networkBufferSettings = networkBufferSettings; @@ -129,9 +129,9 @@ public GarnetClientSession( /// /// Timeout in milliseconds (default 0 for immediate timeout) /// - public void Connect(int timeoutMs = 0, CancellationToken token = default) + public unsafe void Connect(int timeoutMs = 0, CancellationToken token = default) { - socket = GetSendSocket(address, port, timeoutMs); + socket = ConnectSendSocketAsync(timeoutMs, token).ConfigureAwait(false).GetAwaiter().GetResult(); networkHandler = new GarnetClientSessionTcpNetworkHandler( this, socket, @@ -141,7 +141,7 @@ public void Connect(int timeoutMs = 0, CancellationToken token = default) messageConsumer: this, networkSendThrottleMax: networkSendThrottleMax, logger: logger); - networkHandler.StartAsync(sslOptions, $"{address}:{port}", token).ConfigureAwait(false).GetAwaiter().GetResult(); + networkHandler.StartAsync(sslOptions, EndPoint.ToString(), token).ConfigureAwait(false).GetAwaiter().GetResult(); networkSender = networkHandler.GetNetworkSender(); networkSender.GetResponseObject(); offset = networkSender.GetResponseObjectHead(); @@ -166,6 +166,94 @@ public void Connect(int timeoutMs = 0, CancellationToken token = default) } } + /// + /// Connect client send socket + /// + /// + /// + /// + private async Task ConnectSendSocketAsync(int millisecondsTimeout = 0, CancellationToken cancellationToken = default) + { + if (EndPoint is DnsEndPoint dnsEndpoint) + { + var hostEntries = await Dns.GetHostEntryAsync(dnsEndpoint.Host, cancellationToken).ConfigureAwait(false); + // Try all available DNS entries if a hostName is provided + foreach (var addressEntry in hostEntries.AddressList) + { + var endpoint = new IPEndPoint(addressEntry, dnsEndpoint.Port); + var socket = new Socket(endpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp) + { + NoDelay = true + }; + + if (await TryConnectSocketAsync(socket, endpoint, millisecondsTimeout, cancellationToken)) + return socket; + } + } + else + { + var socket = new Socket(EndPoint.AddressFamily, SocketType.Stream, ProtocolType.Unspecified); + if (EndPoint is not UnixDomainSocketEndPoint) + socket.NoDelay = true; + + if (await TryConnectSocketAsync(socket, EndPoint, millisecondsTimeout, cancellationToken)) + return socket; + } + + logger?.LogWarning("Failed to connect at {endpoint}", EndPoint); + throw new Exception($"Failed to connect at {EndPoint}"); + } + + /// + /// Try to establish connection for using + /// + /// + /// + /// + /// The cancellation token + /// + private async Task TryConnectSocketAsync(Socket socket, EndPoint endpoint, int millisecondsTimeout, CancellationToken cancellationToken = default) + { + try + { + if (millisecondsTimeout > 0) + { + using var timeoutCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); + + var connectTask = socket.ConnectAsync(endpoint, timeoutCts.Token).AsTask(); + if (await Task.WhenAny(connectTask, Task.Delay(millisecondsTimeout, timeoutCts.Token)) == connectTask) + { + // Task completed within timeout. + // Consider that the task may have faulted or been canceled. + // We re-await the task so that any exceptions/cancellation is rethrown. + await connectTask; + } + else + { + timeoutCts.Cancel(); + } + + if (!socket.Connected) + { + socket.Close(); + throw new Exception($"Failed to connect server {endpoint}."); + } + } + else + { + await socket.ConnectAsync(endpoint, cancellationToken).ConfigureAwait(false); + } + } + catch (Exception ex) + { + logger?.LogWarning(ex, "Failed at GarnetClient.TryConnectSocketAsync"); + socket.Dispose(); + return false; + } + + return true; + } + /// /// Reconnect to server /// @@ -236,7 +324,7 @@ public void ExecuteForArray(params string[] command) /// /// ClusterAppendLog /// - public void ExecuteClusterAppendLog(string nodeId, long previousAddress, long currentAddress, long nextAddress, long payloadPtr, int payloadLength) + public unsafe void ExecuteClusterAppendLog(string nodeId, long previousAddress, long currentAddress, long nextAddress, long payloadPtr, int payloadLength) { Debug.Assert(nodeId != null); @@ -334,7 +422,7 @@ public void Wait() /// Issue command for execution /// /// - private void InternalExecute(params string[] command) + private unsafe void InternalExecute(params string[] command) { byte* curr = offset; while (!RespWriteUtils.TryWriteArrayLength(command.Length, ref curr, end)) @@ -358,7 +446,7 @@ private void InternalExecute(params string[] command) return; } - private int ProcessReplies(byte* recvBufferPtr, int bytesRead) + private unsafe int ProcessReplies(byte* recvBufferPtr, int bytesRead) { // Debug.WriteLine("RECV: [" + Encoding.UTF8.GetString(new Span(recvBufferPtr, bytesRead)).Replace("\n", "|").Replace("\r", "") + "]"); @@ -426,7 +514,7 @@ private int ProcessReplies(byte* recvBufferPtr, int bytesRead) /// /// Flush current buffer of outgoing messages. Does not wait for responses. /// - private void Flush() + private unsafe void Flush() { if (offset > networkSender.GetResponseObjectHead()) { @@ -448,35 +536,6 @@ private void Flush() } } - private static Socket GetSendSocket(string address, int port, int millisecondsTimeout) - { - var ip = IPAddress.Parse(address); - var endPoint = new IPEndPoint(ip, port); - var socket = new Socket(ip.AddressFamily, SocketType.Stream, ProtocolType.Tcp) - { - NoDelay = true - }; - - if (millisecondsTimeout > 0) - { - IAsyncResult result = socket.BeginConnect(endPoint, null, null); - result.AsyncWaitHandle.WaitOne(millisecondsTimeout, true); - if (socket.Connected) - socket.EndConnect(result); - else - { - socket.Close(); - throw new Exception("Failed to connect server."); - } - } - else - { - socket.Connect(endPoint); - } - - return socket; - } - /// public bool TryCreateMessageConsumer(Span bytesReceived, INetworkSender networkSender, out IMessageConsumer session) => throw new NotSupportedException(); @@ -497,7 +556,7 @@ public void DisposeMessageConsumer(INetworkHandler session) } /// - public int TryConsumeMessages(byte* reqBuffer, int bytesRead) + public unsafe int TryConsumeMessages(byte* reqBuffer, int bytesRead) => ProcessReplies(reqBuffer, bytesRead); } } \ No newline at end of file diff --git a/libs/client/GarnetClient.cs b/libs/client/GarnetClient.cs index ba2089b2c9..951ecfad96 100644 --- a/libs/client/GarnetClient.cs +++ b/libs/client/GarnetClient.cs @@ -44,8 +44,6 @@ public sealed partial class GarnetClient : IServerHook, IMessageConsumer, IDispo static readonly Memory AUTH = "$4\r\nAUTH\r\n"u8.ToArray(); static readonly MemoryResult RESP_OK = new(default(OK_MEM)); - readonly string address; - readonly int port; readonly int sendPageSize; readonly int bufferSize; readonly int maxOutstandingTasks; @@ -95,6 +93,11 @@ public sealed partial class GarnetClient : IServerHook, IMessageConsumer, IDispo /// static readonly Exception disposeException = new GarnetClientDisposedException(); + /// + /// The host endpoint + /// + public EndPoint EndPoint { get; } + /// /// Whether we are connected to the server /// @@ -113,8 +116,7 @@ public sealed partial class GarnetClient : IServerHook, IMessageConsumer, IDispo /// /// Create client instance /// - /// IP address of server - /// Port of server + /// Endpoint of the server /// TLS options /// Username to authenticate with /// Password to authenticate with @@ -127,8 +129,7 @@ public sealed partial class GarnetClient : IServerHook, IMessageConsumer, IDispo /// Max outstanding network sends allowed /// Logger instance public GarnetClient( - string address, - int port, + EndPoint endpoint, SslClientAuthenticationOptions tlsOptions = null, string authUsername = null, string authPassword = null, @@ -142,8 +143,7 @@ public GarnetClient( int networkSendThrottleMax = 8, ILogger logger = null) { - this.address = address; - this.port = port; + EndPoint = endpoint; this.sendPageSize = (int)Utility.PreviousPowerOf2(sendPageSize); this.bufferSize = bufferSize; this.authUsername = authUsername; @@ -187,9 +187,9 @@ public GarnetClient( /// public void Connect(CancellationToken token = default) { - socket = CreateSendSocket(timeoutMilliseconds); + socket = ConnectSendSocketAsync(timeoutMilliseconds).ConfigureAwait(false).GetAwaiter().GetResult(); networkWriter = new NetworkWriter(this, socket, bufferSize, sslOptions, out networkHandler, sendPageSize, networkSendThrottleMax, logger); - networkHandler.StartAsync(sslOptions, $"{address}:{port}", token).ConfigureAwait(false).GetAwaiter().GetResult(); + networkHandler.StartAsync(sslOptions, EndPoint.ToString(), token).ConfigureAwait(false).GetAwaiter().GetResult(); if (timeoutMilliseconds > 0) { @@ -219,9 +219,9 @@ public void Connect(CancellationToken token = default) /// public async Task ConnectAsync(CancellationToken token = default) { - socket = CreateSendSocket(timeoutMilliseconds); + socket = await ConnectSendSocketAsync(timeoutMilliseconds, token).ConfigureAwait(false); networkWriter = new NetworkWriter(this, socket, bufferSize, sslOptions, out networkHandler, sendPageSize, networkSendThrottleMax, logger); - await networkHandler.StartAsync(sslOptions, $"{address}:{port}", token).ConfigureAwait(false); + await networkHandler.StartAsync(sslOptions, EndPoint.ToString(), token).ConfigureAwait(false); if (timeoutMilliseconds > 0) { @@ -247,81 +247,87 @@ public async Task ConnectAsync(CancellationToken token = default) } /// - /// Create client send socket + /// Connect client send socket /// /// /// /// - Socket CreateSendSocket(int millisecondsTimeout = 0) + private async Task ConnectSendSocketAsync(int millisecondsTimeout = 0, CancellationToken cancellationToken = default) { - if (!IPAddress.TryParse(address, out var ip)) + if (EndPoint is DnsEndPoint dnsEndpoint) { - var hostEntries = Dns.GetHostEntry(address); + var hostEntries = await Dns.GetHostEntryAsync(dnsEndpoint.Host, cancellationToken).ConfigureAwait(false); // Try all available DNS entries if a hostName is provided foreach (var addressEntry in hostEntries.AddressList) { - var endPoint = new IPEndPoint(addressEntry, port); - if (!TryConnectSocket(endPoint, millisecondsTimeout, out var socket)) - continue; - return socket; - } + var endpoint = new IPEndPoint(addressEntry, dnsEndpoint.Port); + var socket = new Socket(endpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp) + { + NoDelay = true + }; - // Reaching this point means we failed to establish connection from any of the provided addresses - throw new Exception($"Failed to connect at {address}:{port}"); + if (await TryConnectSocketAsync(socket, endpoint, millisecondsTimeout, cancellationToken)) + return socket; + } } else { - var endPoint = new IPEndPoint(ip, port); - if (!TryConnectSocket(endPoint, millisecondsTimeout, out var socket)) - { - // If failed here then provided endpoint does not accept connections - logger?.LogWarning("Failed to connect at {address}:{port}", ip.ToString(), port); - throw new Exception($"Failed to connect at {ip.ToString()}:{port}"); - } - return socket; + var socket = new Socket(EndPoint.AddressFamily, SocketType.Stream, ProtocolType.Unspecified); + if (EndPoint is not UnixDomainSocketEndPoint) + socket.NoDelay = true; + + if (await TryConnectSocketAsync(socket, EndPoint, millisecondsTimeout, cancellationToken)) + return socket; } + + logger?.LogWarning("Failed to connect at {endpoint}", EndPoint); + throw new Exception($"Failed to connect at {EndPoint}"); } /// - /// Try to establish connection for socket using endPoint + /// Try to establish connection for using /// - /// - /// /// + /// + /// + /// The cancellation token /// - bool TryConnectSocket(IPEndPoint endPoint, int millisecondsTimeout, out Socket socket) + private async Task TryConnectSocketAsync(Socket socket, EndPoint endpoint, int millisecondsTimeout, CancellationToken cancellationToken = default) { - socket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp) - { - NoDelay = true - }; try { if (millisecondsTimeout > 0) { - var result = socket.BeginConnect(endPoint, null, null); - result.AsyncWaitHandle.WaitOne(millisecondsTimeout, true); + using var timeoutCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); - if (socket.Connected) + var connectTask = socket.ConnectAsync(endpoint, timeoutCts.Token).AsTask(); + if (await Task.WhenAny(connectTask, Task.Delay(millisecondsTimeout, timeoutCts.Token)) == connectTask) { - socket.EndConnect(result); + // Task completed within timeout. + // Consider that the task may have faulted or been canceled. + // We re-await the task so that any exceptions/cancellation is rethrown. + await connectTask; } else + { + timeoutCts.Cancel(); + } + + if (!socket.Connected) { socket.Close(); - throw new Exception($"Failed to connect server {address}:{port}."); + throw new Exception($"Failed to connect server {endpoint}."); } } else { - socket.Connect(endPoint); + await socket.ConnectAsync(endpoint, cancellationToken).ConfigureAwait(false); } } catch (Exception ex) { - logger?.LogWarning(ex, "Failed at GarnetClient.TryConnectSocket"); + logger?.LogWarning(ex, "Failed at GarnetClient.TryConnectSocketAsync"); socket.Dispose(); - socket = null; return false; } diff --git a/libs/cluster/Server/ClusterConfig.cs b/libs/cluster/Server/ClusterConfig.cs index c97a66aa1d..6f00322b81 100644 --- a/libs/cluster/Server/ClusterConfig.cs +++ b/libs/cluster/Server/ClusterConfig.cs @@ -9,6 +9,7 @@ using System.Runtime.CompilerServices; using System.Diagnostics; using Microsoft.Extensions.Logging; +using System.Net; namespace Garnet.cluster { @@ -253,14 +254,14 @@ public bool IsKnown(string nodeid) /// Get list of endpoints for all replicas of this node. /// /// List of (address,port) pairs. - public List<(string, int)> GetLocalNodeReplicaEndpoints() + public List GetLocalNodeReplicaEndpoints() { - List<(string, int)> replicas = new(); + List replicas = new(); for (ushort i = 2; i < workers.Length; i++) { var replicaOf = workers[i].ReplicaOfNodeId; if (replicaOf != null && replicaOf.Equals(workers[1].Nodeid, StringComparison.OrdinalIgnoreCase)) - replicas.Add((workers[i].Address, workers[i].Port)); + replicas.Add(new(IPAddress.Parse(workers[i].Address), workers[i].Port)); } return replicas; } @@ -270,17 +271,17 @@ public bool IsKnown(string nodeid) /// /// /// List of pairs (address,port) representing known primary endpoints - public List<(string, int)> GetLocalNodePrimaryEndpoints(bool includeMyPrimaryFirst = false) + public List GetLocalNodePrimaryEndpoints(bool includeMyPrimaryFirst = false) { string myPrimaryId = includeMyPrimaryFirst ? LocalNodePrimaryId : ""; - List<(string, int)> primaries = new(); + List primaries = new(); for (ushort i = 2; i < workers.Length; i++) { if (workers[i].Role == NodeRole.PRIMARY && !workers[i].Nodeid.Equals(myPrimaryId, StringComparison.OrdinalIgnoreCase)) - primaries.Add((workers[i].Address, workers[i].Port)); + primaries.Add(new(IPAddress.Parse(workers[i].Address), workers[i].Port)); if (workers[i].Nodeid.Equals(myPrimaryId, StringComparison.OrdinalIgnoreCase)) - primaries.Insert(0, (workers[i].Address, workers[i].Port)); + primaries.Insert(0, new(IPAddress.Parse(workers[i].Address), workers[i].Port)); } return primaries; } @@ -475,10 +476,10 @@ private static void slotBitmapSetBit(ref byte[] bitmap, int pos) /// Node-id. /// Pair of (string,integer) representing endpoint. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public (string address, int port) GetEndpointFromNodeId(string nodeid) + public IPEndPoint GetEndpointFromNodeId(string nodeid) { var workerId = GetWorkerIdFromNodeId(nodeid); - return (workers[workerId].Address, workers[workerId].Port); + return new(IPAddress.Parse(workers[workerId].Address), workers[workerId].Port); } #endregion @@ -788,18 +789,18 @@ public List GetReplicas(string nodeid, ClusterProvider clusterProvider) /// /// Get all know node ids /// - public void GetAllNodeIds(out List<(string, string, int)> allNodeIds) + public void GetAllNodeIds(out List<(string NodeId, IPEndPoint EndPoint)> allNodeIds) { allNodeIds = []; for (ushort i = 2; i < workers.Length; i++) - allNodeIds.Add((workers[i].Nodeid, workers[i].Address, workers[i].Port)); + allNodeIds.Add((workers[i].Nodeid, new IPEndPoint(IPAddress.Parse(workers[i].Address), workers[i].Port))); } /// /// Get node-ids for nodes in the local shard /// /// - public void GetNodeIdsForShard(out List<(string, string, int)> shardNodeIds) + public void GetNodeIdsForShard(out List<(string NodeId, IPEndPoint EndPoint)> shardNodeIds) { var primaryId = LocalNodeRole == NodeRole.PRIMARY ? LocalNodeId : workers[1].ReplicaOfNodeId; shardNodeIds = []; @@ -807,7 +808,7 @@ public void GetNodeIdsForShard(out List<(string, string, int)> shardNodeIds) { var replicaOf = workers[i].ReplicaOfNodeId; if (primaryId != null && ((replicaOf != null && replicaOf.Equals(primaryId, StringComparison.OrdinalIgnoreCase)) || primaryId.Equals(workers[i].Nodeid))) - shardNodeIds.Add((workers[i].Nodeid, workers[i].Address, workers[i].Port)); + shardNodeIds.Add((workers[i].Nodeid, new IPEndPoint(IPAddress.Parse(workers[i].Address), workers[i].Port))); } } diff --git a/libs/cluster/Server/ClusterManager.cs b/libs/cluster/Server/ClusterManager.cs index 1454add9d9..9d95c4d665 100644 --- a/libs/cluster/Server/ClusterManager.cs +++ b/libs/cluster/Server/ClusterManager.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. using System; +using System.Net; using System.Threading; using Garnet.common; using Garnet.server.TLS; @@ -49,7 +50,11 @@ public unsafe ClusterManager(ClusterProvider clusterProvider, ILogger logger = n clusterConfigDevice = deviceFactory.Get(new FileDescriptor(directoryName: "", fileName: "nodes.conf")); pool = new(1, (int)clusterConfigDevice.SectorSize); + if (opts.EndPoint is not IPEndPoint endpoint) + throw new NotImplementedException("Cluster mode for unix domain sockets has not been implemented."); + var address = clusterProvider.storeWrapper.GetIp(); + this.logger = logger; var recoverConfig = clusterConfigDevice.GetFileSize(0) > 0 && !opts.CleanClusterConfig; @@ -65,14 +70,14 @@ public unsafe ClusterManager(ClusterProvider clusterProvider, ILogger logger = n var config = ClusterUtils.ReadDevice(clusterConfigDevice, pool, logger); currentConfig = ClusterConfig.FromByteArray(config); // Used to update endpoint if it change when running inside a container. - if (address != currentConfig.LocalNodeIp || opts.Port != currentConfig.LocalNodePort) + if (address != currentConfig.LocalNodeIp || endpoint.Port != currentConfig.LocalNodePort) { logger?.LogInformation( "Updating local Endpoint: From {currentConfig.GetLocalNodeIp()}:{currentConfig.GetLocalNodePort()} to {address}:{opts.Port}", currentConfig.LocalNodeIp, currentConfig.LocalNodePort, address, - opts.Port); + endpoint.Port); } } else @@ -82,7 +87,7 @@ public unsafe ClusterManager(ClusterProvider clusterProvider, ILogger logger = n } clusterConnectionStore = new GarnetClusterConnectionStore(logger: logger); - InitLocal(address, opts.Port, recoverConfig); + InitLocal(address, endpoint.Port, recoverConfig); logger?.LogInformation("{NodeInfoStartup}", CurrentConfig.GetClusterInfo(clusterProvider).TrimEnd('\n')); gossipDelay = TimeSpan.FromSeconds(opts.GossipDelay); clusterTimeout = opts.ClusterTimeout <= 0 ? Timeout.InfiniteTimeSpan : TimeSpan.FromSeconds(opts.ClusterTimeout); diff --git a/libs/cluster/Server/ClusterProvider.cs b/libs/cluster/Server/ClusterProvider.cs index 89ba7acc8d..172c081949 100644 --- a/libs/cluster/Server/ClusterProvider.cs +++ b/libs/cluster/Server/ClusterProvider.cs @@ -425,7 +425,7 @@ internal ReplicationLogCheckpointManager GetReplicationLogCheckpointManager(Stor /// internal bool BumpAndWaitForEpochTransition() { - var server = storeWrapper.GetTcpServer(); + var server = storeWrapper.TcpServer; BumpCurrentEpoch(); while (true) { diff --git a/libs/cluster/Server/Failover/FailoverSession.cs b/libs/cluster/Server/Failover/FailoverSession.cs index 66df02e383..d37b2a8119 100644 --- a/libs/cluster/Server/Failover/FailoverSession.cs +++ b/libs/cluster/Server/Failover/FailoverSession.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. using System; +using System.Net; using System.Threading; using Garnet.client; using Garnet.common; @@ -68,12 +69,12 @@ public FailoverSession( { for (var i = 0; i < endpoints.Count; i++) { - clients[i] = new GarnetClient(endpoints[i].Item1, endpoints[i].Item2, clusterProvider.serverOptions.TlsOptions?.TlsClientOptions, authUsername: clusterProvider.ClusterUsername, authPassword: clusterProvider.ClusterPassword, logger: logger); + clients[i] = new GarnetClient(endpoints[i], clusterProvider.serverOptions.TlsOptions?.TlsClientOptions, authUsername: clusterProvider.ClusterUsername, authPassword: clusterProvider.ClusterPassword, logger: logger); } } else { - clients[0] = new GarnetClient(hostAddress, hostPort, clusterProvider.serverOptions.TlsOptions?.TlsClientOptions, authUsername: clusterProvider.ClusterUsername, authPassword: clusterProvider.ClusterPassword, logger: logger); + clients[0] = new GarnetClient(new IPEndPoint(IPAddress.Parse(hostAddress), hostPort), clusterProvider.serverOptions.TlsOptions?.TlsClientOptions, authUsername: clusterProvider.ClusterUsername, authPassword: clusterProvider.ClusterPassword, logger: logger); } } diff --git a/libs/cluster/Server/Failover/ReplicaFailoverSession.cs b/libs/cluster/Server/Failover/ReplicaFailoverSession.cs index f5994c9985..223d804c9a 100644 --- a/libs/cluster/Server/Failover/ReplicaFailoverSession.cs +++ b/libs/cluster/Server/Failover/ReplicaFailoverSession.cs @@ -40,13 +40,13 @@ private async Task GetOrAddConnectionAsync(string nodeId) { _ = clusterProvider.clusterManager.clusterConnectionStore.GetConnection(nodeId, out var gsn); - var (address, port) = oldConfig.GetEndpointFromNodeId(nodeId); - while (!clusterProvider.clusterManager.clusterConnectionStore.GetOrAdd(clusterProvider, address, port, clusterProvider.serverOptions.TlsOptions, nodeId, out gsn, logger: logger)) + var endpoint = oldConfig.GetEndpointFromNodeId(nodeId); + while (!clusterProvider.clusterManager.clusterConnectionStore.GetOrAdd(clusterProvider, endpoint, clusterProvider.serverOptions.TlsOptions, nodeId, out gsn, logger: logger)) _ = System.Threading.Thread.Yield(); if (gsn == null) { - logger?.LogWarning("TryMeet: Could not establish connection to remote node [{nodeId} {address}:{port}] failed", nodeId, address, port); + logger?.LogWarning("TryMeet: Could not establish connection to remote node [{nodeId} {endpoint}] failed", nodeId, endpoint); return null; } @@ -62,10 +62,9 @@ private async Task GetOrAddConnectionAsync(string nodeId) /// private async Task CreateConnectionAsync(string nodeId) { - var (address, port) = oldConfig.GetEndpointFromNodeId(nodeId); + var endpoint = oldConfig.GetEndpointFromNodeId(nodeId); var client = new GarnetClient( - address, - port, + endpoint, clusterProvider.serverOptions.TlsOptions?.TlsClientOptions, sendPageSize: sendPageSize, maxOutstandingTasks: 8, diff --git a/libs/cluster/Server/GarnetClusterConnectionStore.cs b/libs/cluster/Server/GarnetClusterConnectionStore.cs index 6c50c2fc5e..535e7e61be 100644 --- a/libs/cluster/Server/GarnetClusterConnectionStore.cs +++ b/libs/cluster/Server/GarnetClusterConnectionStore.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. using System; +using System.Net; using System.Security.Cryptography; using Garnet.common; using Garnet.server.TLS; @@ -97,14 +98,13 @@ public bool AddConnection(GarnetServerNode conn) /// Get or add a connection to the store. /// /// - /// - /// + /// The cluster node endpoint /// /// /// /// /// - public bool GetOrAdd(ClusterProvider clusterProvider, string address, int port, IGarnetTlsOptions tlsOptions, string nodeId, out GarnetServerNode conn, ILogger logger = null) + public bool GetOrAdd(ClusterProvider clusterProvider, IPEndPoint endpoint, IGarnetTlsOptions tlsOptions, string nodeId, out GarnetServerNode conn, ILogger logger = null) { conn = null; try @@ -114,7 +114,7 @@ public bool GetOrAdd(ClusterProvider clusterProvider, string address, int port, if (UnsafeGetConnection(nodeId, out conn)) return true; - conn = new GarnetServerNode(clusterProvider, address, port, tlsOptions?.TlsClientOptions, logger: logger) + conn = new GarnetServerNode(clusterProvider, endpoint, tlsOptions?.TlsClientOptions, logger: logger) { NodeId = nodeId }; diff --git a/libs/cluster/Server/GarnetServerNode.cs b/libs/cluster/Server/GarnetServerNode.cs index 0a735588a7..529b9098b7 100644 --- a/libs/cluster/Server/GarnetServerNode.cs +++ b/libs/cluster/Server/GarnetServerNode.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. using System; +using System.Net; using System.Net.Security; using System.Threading; using System.Threading.Tasks; @@ -51,14 +52,9 @@ internal sealed class GarnetServerNode public string NodeId; /// - /// Address of remote node + /// EndPoint of remote node /// - public string Address; - - /// - /// Port of remote node - /// - public int Port; + public EndPoint EndPoint; /// /// Default send page size for GarnetClient @@ -74,18 +70,16 @@ internal sealed class GarnetServerNode /// GarnetServerNode constructor /// /// - /// - /// + /// The endpoint of the remote node /// /// - public GarnetServerNode(ClusterProvider clusterProvider, string address, int port, SslClientAuthenticationOptions tlsOptions, ILogger logger = null) + public GarnetServerNode(ClusterProvider clusterProvider, EndPoint endpoint, SslClientAuthenticationOptions tlsOptions, ILogger logger = null) { var opts = clusterProvider.storeWrapper.serverOptions; this.clusterProvider = clusterProvider; - this.Address = address; - this.Port = port; + this.EndPoint = endpoint; this.gc = new GarnetClient( - address, port, tlsOptions, + endpoint, tlsOptions, sendPageSize: opts.DisablePubSub ? defaultSendPageSize : Math.Max(defaultSendPageSize, (int)opts.PubSubPageSizeBytes()), maxOutstandingTasks: defaultMaxOutstandingTask, timeoutMilliseconds: opts.ClusterTimeout <= 0 ? 0 : TimeSpan.FromSeconds(opts.ClusterTimeout).Milliseconds, diff --git a/libs/cluster/Server/Gossip.cs b/libs/cluster/Server/Gossip.cs index a6c9bcb573..c9c3d41b57 100644 --- a/libs/cluster/Server/Gossip.cs +++ b/libs/cluster/Server/Gossip.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Net; using System.Threading; using System.Threading.Tasks; using Garnet.common; @@ -160,7 +161,7 @@ public async Task TryMeetAsync(string address, int port, bool acquireLock = true if (gsn == null) { - gsn = new GarnetServerNode(clusterProvider, address, port, tlsOptions?.TlsClientOptions, logger: logger); + gsn = new GarnetServerNode(clusterProvider, new IPEndPoint(IPAddress.Parse(address), port), tlsOptions?.TlsClientOptions, logger: logger); created = true; } @@ -210,7 +211,7 @@ public async Task TryMeetAsync(string address, int port, bool acquireLock = true public void TryClusterPublish(RespCommand cmd, ref Span channel, ref Span message) { var conf = CurrentConfig; - List<(string, string, int)> nodeEntries = null; + List<(string NodeId, IPEndPoint Endpoint)> nodeEntries = null; if (cmd == RespCommand.PUBLISH) conf.GetAllNodeIds(out nodeEntries); else @@ -219,11 +220,10 @@ public void TryClusterPublish(RespCommand cmd, ref Span channel, ref Span< { try { - var nodeId = entry.Item1; - var address = entry.Item2; - var port = entry.Item3; + var nodeId = entry.NodeId; + var endpoint = entry.Endpoint; GarnetServerNode gsn = null; - while (!clusterConnectionStore.GetOrAdd(clusterProvider, address, port, tlsOptions, nodeId, out gsn, logger: logger)) + while (!clusterConnectionStore.GetOrAdd(clusterProvider, endpoint, tlsOptions, nodeId, out gsn, logger: logger)) Thread.Yield(); if (gsn == null) @@ -303,7 +303,7 @@ async Task InitConnections() try { GarnetServerNode gsn = null; - while (!clusterConnectionStore.GetOrAdd(clusterProvider, address, port, tlsOptions, nodeId, out gsn, logger: logger)) + while (!clusterConnectionStore.GetOrAdd(clusterProvider, new IPEndPoint(IPAddress.Parse(address), port), tlsOptions, nodeId, out gsn, logger: logger)) await Task.Yield(); if (gsn == null) @@ -363,12 +363,12 @@ void BroadcastGossipSend() } gossipStats.gossip_timeout_count++; - logger?.LogWarning("GOSSIP to remote node [{nodeId} {address}:{port}] timeout!", currNode.NodeId, currNode.Address, currNode.Port); + logger?.LogWarning("GOSSIP to remote node [{nodeId} {endpoint}] timeout!", currNode.NodeId, currNode.EndPoint); _ = clusterConnectionStore.TryRemove(currNode.NodeId); } catch (Exception ex) { - logger?.LogWarning(ex, "GOSSIP to remote node [{nodeId} {address} {port}] failed!", currNode.NodeId, currNode.Address, currNode.Port); + logger?.LogWarning(ex, "GOSSIP to remote node [{nodeId} {endpoint}] failed!", currNode.NodeId, currNode.EndPoint); _ = clusterConnectionStore.TryRemove(currNode.NodeId); gossipStats.gossip_failed_count++; } @@ -412,12 +412,12 @@ void GossipSampleSend() } gossipStats.gossip_timeout_count++; - logger?.LogWarning("GOSSIP to remote node [{nodeId} {address}:{port}] timeout!", currNode.NodeId, currNode.Address, currNode.Port); + logger?.LogWarning("GOSSIP to remote node [{nodeId} {endpoint}] timeout!", currNode.NodeId, currNode.EndPoint); _ = clusterConnectionStore.TryRemove(currNode.NodeId); } catch (Exception ex) { - logger?.LogError(ex, "GOSSIP to remote node [{nodeId} {address} {port}] failed!", currNode.NodeId, currNode.Address, currNode.Port); + logger?.LogError(ex, "GOSSIP to remote node [{nodeId} {endpoint}] failed!", currNode.NodeId, currNode.EndPoint); _ = clusterConnectionStore.TryRemove(currNode.NodeId); gossipStats.gossip_failed_count++; } diff --git a/libs/cluster/Server/Migration/MigrateSession.cs b/libs/cluster/Server/Migration/MigrateSession.cs index 04612e8360..af226c6e87 100644 --- a/libs/cluster/Server/Migration/MigrateSession.cs +++ b/libs/cluster/Server/Migration/MigrateSession.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Net; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -150,8 +151,7 @@ internal MigrateSession( // Single key value size + few bytes for command header and arguments _gcs = new( - _targetAddress, - _targetPort, + new IPEndPoint(IPAddress.Parse(_targetAddress), _targetPort), networkBufferSettings: GetNetworkBufferSettings, networkPool: GetNetworkPool, clusterProvider?.serverOptions.TlsOptions?.TlsClientOptions, diff --git a/libs/cluster/Server/Replication/PrimaryOps/AofTaskStore.cs b/libs/cluster/Server/Replication/PrimaryOps/AofTaskStore.cs index ba5b8a4c58..abfbfa6aa3 100644 --- a/libs/cluster/Server/Replication/PrimaryOps/AofTaskStore.cs +++ b/libs/cluster/Server/Replication/PrimaryOps/AofTaskStore.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Net; using Garnet.client; using Garnet.common; using Garnet.server; @@ -135,8 +136,7 @@ public bool TryAddReplicationTask(string remoteNodeId, long startAddress, out Ao current.LocalNodeId, remoteNodeId, new GarnetClientSession( - address, - port, + new IPEndPoint(IPAddress.Parse(address), port), clusterProvider.replicationManager.GetAofSyncNetworkBufferSettings, clusterProvider.replicationManager.GetNetworkPool, tlsOptions: clusterProvider.serverOptions.TlsOptions?.TlsClientOptions, diff --git a/libs/cluster/Server/Replication/PrimaryOps/ReplicaSyncSession.cs b/libs/cluster/Server/Replication/PrimaryOps/ReplicaSyncSession.cs index 005c12df36..de853ca91c 100644 --- a/libs/cluster/Server/Replication/PrimaryOps/ReplicaSyncSession.cs +++ b/libs/cluster/Server/Replication/PrimaryOps/ReplicaSyncSession.cs @@ -4,6 +4,7 @@ using System; using System.ComponentModel; using System.Diagnostics; +using System.Net; using System.Threading; using System.Threading.Tasks; using Garnet.client; @@ -61,8 +62,7 @@ public async Task SendCheckpoint() } GarnetClientSession gcs = new( - address, - port, + new IPEndPoint(IPAddress.Parse(address), port), clusterProvider.replicationManager.GetRSSNetworkBufferSettings, clusterProvider.replicationManager.GetNetworkPool, tlsOptions: clusterProvider.serverOptions.TlsOptions?.TlsClientOptions, diff --git a/libs/cluster/Server/Replication/ReplicaOps/ReplicaReceiveCheckpoint.cs b/libs/cluster/Server/Replication/ReplicaOps/ReplicaReceiveCheckpoint.cs index cf476b9e5d..e872b9961b 100644 --- a/libs/cluster/Server/Replication/ReplicaOps/ReplicaReceiveCheckpoint.cs +++ b/libs/cluster/Server/Replication/ReplicaOps/ReplicaReceiveCheckpoint.cs @@ -4,6 +4,7 @@ using System; using System.Diagnostics; using System.IO; +using System.Net; using System.Text; using System.Threading.Tasks; using Garnet.client; @@ -150,8 +151,7 @@ private async Task InitiateReplicaSync() try { gcs = new( - address, - port, + new IPEndPoint(IPAddress.Parse(address), port), clusterProvider.replicationManager.GetIRSNetworkBufferSettings, clusterProvider.replicationManager.GetNetworkPool, tlsOptions: clusterProvider.serverOptions.TlsOptions?.TlsClientOptions, diff --git a/libs/cluster/Session/RespClusterBasicCommands.cs b/libs/cluster/Session/RespClusterBasicCommands.cs index 7bddbc7b0a..ef724a360f 100644 --- a/libs/cluster/Session/RespClusterBasicCommands.cs +++ b/libs/cluster/Session/RespClusterBasicCommands.cs @@ -241,8 +241,8 @@ private bool NetworkClusterEndpoint(out bool invalidParameters) var nodeId = parseState.GetString(0); var current = clusterProvider.clusterManager.CurrentConfig; - var (host, port) = current.GetEndpointFromNodeId(nodeId); - while (!RespWriteUtils.TryWriteAsciiBulkString($"{host}:{port}", ref dcurr, dend)) + var endpoint = current.GetEndpointFromNodeId(nodeId); + while (!RespWriteUtils.TryWriteAsciiBulkString(endpoint.ToString(), ref dcurr, dend)) SendAndReset(); return true; } diff --git a/libs/common/ClientBase.cs b/libs/common/ClientBase.cs index 4bd96c4296..aad8dbaea4 100644 --- a/libs/common/ClientBase.cs +++ b/libs/common/ClientBase.cs @@ -2,31 +2,26 @@ // Licensed under the MIT license. using System; +using System.Net; using System.Threading; using Garnet.networking; namespace Garnet.common { - /// /// Abstract base class for client session provider. /// public abstract unsafe class ClientBase : IDisposable { /// - /// host address - /// - protected readonly string address; - - /// - /// host port + /// The host endpoint /// - protected readonly int port; + protected readonly EndPoint endpoint; /// /// connection status /// - protected bool connected_; + protected bool connected; /// /// Buffersize @@ -44,17 +39,15 @@ public abstract unsafe class ClientBase : IDisposable protected INetworkSender networkSender; /// - /// Create + /// Create client /// - /// - /// - /// - public ClientBase(string address, int port, int BufferSize) + /// The host endpoint to connect to + /// The buffer size + public ClientBase(EndPoint endpoint, int bufferSize) { - this.address = address; - this.port = port; - this.Buffersize = BufferSize; - this.connected_ = false; + this.endpoint = endpoint; + this.Buffersize = bufferSize; + this.connected = false; this.numPendingRequests = 0; } diff --git a/libs/common/LightClient.cs b/libs/common/LightClient.cs index e4add4cdf4..0183f992d9 100644 --- a/libs/common/LightClient.cs +++ b/libs/common/LightClient.cs @@ -7,16 +7,17 @@ using System.Net.Sockets; using System.Text; using System.Threading; +using System.Threading.Tasks; using Garnet.networking; using Garnet.server; +using Microsoft.Extensions.Logging; namespace Garnet.common { - /// /// Light remote client /// - public unsafe class LightClient : ClientBase, IServerHook, IMessageConsumer + public class LightClient : ClientBase, IServerHook, IMessageConsumer { /// /// On response delegate function. @@ -25,7 +26,7 @@ public unsafe class LightClient : ClientBase, IServerHook, IMessageConsumer /// /// /// - public delegate (int, int) OnResponseDelegateUnsafe(byte* buf, int bytesRead, int opType); + public unsafe delegate (int, int) OnResponseDelegateUnsafe(byte* buf, int bytesRead, int opType); readonly OnResponseDelegateUnsafe onResponseDelegateUnsafe = null; readonly int BufferSize; @@ -39,24 +40,24 @@ public unsafe class LightClient : ClientBase, IServerHook, IMessageConsumer LightClientTcpNetworkHandler networkHandler; readonly NetworkBufferSettings networkBufferSettings; readonly LimitedFixedBufferPool networkPool; + readonly ILogger logger; /// /// Create client instance to connect to specfied destination /// - /// IP address - /// Port + /// The server endpoint /// Op type /// Callback that takes in a byte array and length, and returns the number of bytes read and the number of requests processed /// Message buffer size. /// SSL options - public LightClient( - string address, - int port, + public unsafe LightClient( + EndPoint endpoint, int opType, OnResponseDelegateUnsafe onResponseDelegateUnsafe = null, int BufferSize = 1 << 18, - SslClientAuthenticationOptions sslOptions = null) - : base(address, port, BufferSize) + SslClientAuthenticationOptions sslOptions = null, + ILogger logger = null) + : base(endpoint, BufferSize) { this.networkBufferSettings = new NetworkBufferSettings(BufferSize, BufferSize); this.networkPool = networkBufferSettings.CreateBufferPool(); @@ -64,6 +65,7 @@ public LightClient( this.opType = opType; this.BufferSize = BufferSize; this.sslOptions = sslOptions; + this.logger = logger; } /// @@ -109,20 +111,79 @@ public void ReadOnly() /// public override void Connect() { - socket = GetSendSocket(address, port); + socket = ConnectSendSocketAsync().ConfigureAwait(false).GetAwaiter().GetResult(); networkHandler = new LightClientTcpNetworkHandler(this, socket, networkBufferSettings, networkPool, sslOptions != null, this); - networkHandler.StartAsync(sslOptions, $"{address}:{port}").ConfigureAwait(false).GetAwaiter().GetResult(); + networkHandler.StartAsync(sslOptions, endpoint.ToString()).ConfigureAwait(false).GetAwaiter().GetResult(); networkSender = networkHandler.GetNetworkSender(); networkSender.GetResponseObject(); } + /// + /// Connect client send socket + /// + private async Task ConnectSendSocketAsync(CancellationToken cancellationToken = default) + { + if (endpoint is DnsEndPoint dnsEndpoint) + { + var hostEntries = await Dns.GetHostEntryAsync(dnsEndpoint.Host, cancellationToken).ConfigureAwait(false); + // Try all available DNS entries if a hostName is provided + foreach (var addressEntry in hostEntries.AddressList) + { + var endpoint = new IPEndPoint(addressEntry, dnsEndpoint.Port); + var socket = new Socket(endpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp) + { + NoDelay = true + }; + + if (await TryConnectSocketAsync(socket, endpoint, cancellationToken)) + return socket; + } + } + else + { + var socket = new Socket(endpoint.AddressFamily, SocketType.Stream, ProtocolType.Unspecified); + if (endpoint is not UnixDomainSocketEndPoint) + socket.NoDelay = true; + + if (await TryConnectSocketAsync(socket, endpoint, cancellationToken)) + return socket; + } + + logger?.LogWarning("Failed to connect at {endpoint}", endpoint); + throw new Exception($"Failed to connect at {endpoint}"); + } + + /// + /// Try to establish connection for using + /// + /// + /// + /// The cancellation token + /// + private async Task TryConnectSocketAsync(Socket socket, EndPoint endpoint, CancellationToken cancellationToken = default) + { + try + { + await socket.ConnectAsync(endpoint, cancellationToken).ConfigureAwait(false); + if (socket.Connected) + return true; + } + catch (Exception ex) + { + socket.Dispose(); + return false; + } + + return true; + } + /// /// Send len bytes from given buffer /// /// /// /// Number of symbols expected in the response - public override void Send(byte[] buf, int len, int numTokens = 1) + public override unsafe void Send(byte[] buf, int len, int numTokens = 1) { Interlocked.Add(ref numPendingRequests, numTokens); @@ -161,35 +222,6 @@ public override void Send(int len, int numTokens = 1) networkSender.GetResponseObject(); } - private static Socket GetSendSocket(string address, int port, int millisecondsTimeout = -2) - { - var ip = IPAddress.Parse(address); - var endPoint = new IPEndPoint(ip, port); - var socket = new Socket(ip.AddressFamily, SocketType.Stream, ProtocolType.Tcp) - { - NoDelay = true - }; - - if (millisecondsTimeout != -2) - { - IAsyncResult result = socket.BeginConnect(endPoint, null, null); - result.AsyncWaitHandle.WaitOne(millisecondsTimeout, true); - if (socket.Connected) - socket.EndConnect(result); - else - { - socket.Close(); - throw new Exception("Failed to connect server."); - } - } - else - { - socket.Connect(endPoint); - } - - return socket; - } - public override bool CompletePendingRequests(int timeout = -1, CancellationToken token = default) { var deadline = timeout == -1 ? DateTime.MaxValue.Ticks : DateTime.Now.AddMilliseconds(timeout).Ticks; @@ -206,7 +238,7 @@ public override bool CompletePendingRequests(int timeout = -1, CancellationToken return numPendingRequests == 0; } - private static (int, int) DefaultLightReceiveUnsafe(byte* buf, int bytesRead, int opType) => (bytesRead, 1); + private static unsafe (int, int) DefaultLightReceiveUnsafe(byte* buf, int bytesRead, int opType) => (bytesRead, 1); /// public override void Dispose() @@ -227,7 +259,7 @@ public void DisposeMessageConsumer(INetworkHandler session) } /// - public int TryConsumeMessages(byte* reqBuffer, int bytesRead) + public unsafe int TryConsumeMessages(byte* reqBuffer, int bytesRead) { (int readHead, int count) = onResponseDelegateUnsafe(reqBuffer, bytesRead, opType); Interlocked.Add(ref numPendingRequests, -count); diff --git a/libs/common/LightClientRequest.cs b/libs/common/LightClientRequest.cs index fef497f960..00e43f71ce 100644 --- a/libs/common/LightClientRequest.cs +++ b/libs/common/LightClientRequest.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. using System; +using System.Net; using System.Net.Security; using System.Text; using Garnet.networking; @@ -26,16 +27,14 @@ public unsafe class LightClientRequest : IDisposable /// public CountResponseType countResponseType; - public string Address { get; set; } - public int Port { get; set; } + public EndPoint EndPoint { get; set; } - public LightClientRequest(string address, int port, int optType, LightClient.OnResponseDelegateUnsafe onReceive = null, SslClientAuthenticationOptions sslOptions = null, CountResponseType countResponseType = CountResponseType.Tokens) + public LightClientRequest(EndPoint endpoint, int optType, LightClient.OnResponseDelegateUnsafe onReceive = null, SslClientAuthenticationOptions sslOptions = null, CountResponseType countResponseType = CountResponseType.Tokens) { this.countResponseType = countResponseType; - client = new LightClient(address, port, optType, onReceive == null ? LightReceive : onReceive, sslOptions: sslOptions); + client = new LightClient(endpoint, optType, onReceive == null ? LightReceive : onReceive, sslOptions: sslOptions); client.Connect(); - Address = address; - Port = port; + EndPoint = endpoint; } public byte[] SendCommand(string cmd, int numTokens = 1, bool returnAccumulatedBuffer = false) diff --git a/libs/common/Networking/TcpNetworkHandlerBase.cs b/libs/common/Networking/TcpNetworkHandlerBase.cs index 8801815d4c..43dd1e1769 100644 --- a/libs/common/Networking/TcpNetworkHandlerBase.cs +++ b/libs/common/Networking/TcpNetworkHandlerBase.cs @@ -23,8 +23,6 @@ public abstract class TcpNetworkHandlerBase : Netwo { readonly ILogger logger; readonly Socket socket; - readonly string remoteEndpoint; - readonly string localEndpoint; int closeRequested; /// @@ -37,16 +35,14 @@ public TcpNetworkHandlerBase(TServerHook serverHook, TNetworkSender networkSende this.socket = socket; this.closeRequested = 0; - remoteEndpoint = socket.RemoteEndPoint is IPEndPoint remote ? $"{remote.Address}:{remote.Port}" : ""; - localEndpoint = socket.LocalEndPoint is IPEndPoint local ? $"{local.Address}:{local.Port}" : ""; AllocateNetworkReceiveBuffer(); } /// - public override string RemoteEndpointName => remoteEndpoint; + public override string RemoteEndpointName => socket.RemoteEndPoint.ToString(); /// - public override string LocalEndpointName => localEndpoint; + public override string LocalEndpointName => socket.LocalEndPoint.ToString(); /// public override void Start(SslServerAuthenticationOptions tlsOptions = null, string remoteEndpointName = null, CancellationToken token = default) diff --git a/libs/host/Configuration/Options.cs b/libs/host/Configuration/Options.cs index 82a8b32f13..f55bfb2229 100644 --- a/libs/host/Configuration/Options.cs +++ b/libs/host/Configuration/Options.cs @@ -7,6 +7,7 @@ using System.IO; using System.Linq; using System.Net; +using System.Net.Sockets; using System.Reflection; using System.Runtime.InteropServices; using System.Security.Cryptography.X509Certificates; @@ -525,6 +526,16 @@ internal sealed class Options [Option("lua-script-memory-limit", Default = null, HelpText = "Memory limit for a Lua instances while running a script, lua-memory-management-mode must be set to something other than Native to use this flag")] public string LuaScriptMemoryLimit { get; set; } + [FilePathValidation(false, true, false)] + [Option("unixsocket", Required = false, HelpText = "Unix socket address path to bind server to")] + public string UnixSocketPath { get; set; } + + + [IntRangeValidation(0, 777, isRequired: false)] + [SupportedOSValidation(isRequired: false, nameof(OSPlatform.Linux), nameof(OSPlatform.OSX), nameof(OSPlatform.FreeBSD))] + [Option("unixsocketperm", Required = false, HelpText = "Unix socket permissions in octal (Unix platforms only)")] + public int UnixSocketPermission { get; set; } + /// /// This property contains all arguments that were not parsed by the command line argument parser /// @@ -584,9 +595,22 @@ public GarnetServerOptions GetServerOptions(ILogger logger = null) var checkpointDir = CheckpointDir; if (!useAzureStorage) checkpointDir = new DirectoryInfo(string.IsNullOrEmpty(checkpointDir) ? (string.IsNullOrEmpty(logDir) ? "." : logDir) : checkpointDir).FullName; - var address = !string.IsNullOrEmpty(this.Address) && this.Address.Equals("localhost", StringComparison.CurrentCultureIgnoreCase) - ? IPAddress.Loopback.ToString() - : this.Address; + EndPoint endpoint; + if (!string.IsNullOrEmpty(UnixSocketPath)) + { + endpoint = new UnixDomainSocketEndPoint(UnixSocketPath); + } + else + { + var address = string.IsNullOrEmpty(Address) || Address.Equals("localhost", StringComparison.CurrentCultureIgnoreCase) + ? IPAddress.Loopback + : IPAddress.Parse(Address); + + endpoint = new IPEndPoint(address, Port); + } + + // Unix file permission octal to UnixFileMode + var unixSocketPermissions = (UnixFileMode)Convert.ToInt32(UnixSocketPermission.ToString(), 8); var revivBinRecordSizes = this.RevivBinRecordSizes?.ToArray(); var revivBinRecordCounts = this.RevivBinRecordCounts?.ToArray(); @@ -632,8 +656,7 @@ public GarnetServerOptions GetServerOptions(ILogger logger = null) return new GarnetServerOptions(logger) { - Port = Port, - Address = address, + EndPoint = endpoint, MemorySize = MemorySize, PageSize = PageSize, SegmentSize = SegmentSize, @@ -739,6 +762,8 @@ public GarnetServerOptions GetServerOptions(ILogger logger = null) FailOnRecoveryError = FailOnRecoveryError.GetValueOrDefault(), SkipRDBRestoreChecksumValidation = SkipRDBRestoreChecksumValidation.GetValueOrDefault(), LuaOptions = EnableLua.GetValueOrDefault() ? new LuaOptions(LuaMemoryManagementMode, LuaScriptMemoryLimit, logger) : null, + UnixSocketPath = UnixSocketPath, + UnixSocketPermission = unixSocketPermissions }; } diff --git a/libs/host/Configuration/OptionsValidators.cs b/libs/host/Configuration/OptionsValidators.cs index 94d97cced8..ef218cfb5a 100644 --- a/libs/host/Configuration/OptionsValidators.cs +++ b/libs/host/Configuration/OptionsValidators.cs @@ -8,6 +8,7 @@ using System.Linq; using System.Net; using System.Reflection; +using System.Runtime.InteropServices; using System.Security; using System.Text; using System.Text.RegularExpressions; @@ -26,6 +27,17 @@ internal class OptionValidationAttribute : ValidationAttribute /// protected readonly bool IsRequired; + /// + /// Determine object default value at runtime + /// + protected static object GetDefault(Type t) + { + if (t.IsValueType && Nullable.GetUnderlyingType(t) == null) + return Activator.CreateInstance(t); + else + return null; + } + protected static object GetDefault(T t) => default(T); internal OptionValidationAttribute(bool isRequired = true) @@ -601,4 +613,33 @@ protected override ValidationResult IsValid(object value, ValidationContext vali return ValidationResult.Success; } } + + /// + /// Forbids a config option from being set if the current OS platform is not supported. + /// + [AttributeUsage(AttributeTargets.Property)] + internal sealed class SupportedOSValidationAttribute : OptionValidationAttribute + { + private readonly string[] supportedPlatforms; + + internal SupportedOSValidationAttribute(bool isRequired, params string[] supportedPlatforms) : base(isRequired) + { + this.supportedPlatforms = supportedPlatforms; + } + + /// + protected override ValidationResult IsValid(object value, ValidationContext validationContext) + { + if (!IsRequired && (Equals(value, GetDefault(value.GetType())) || (value is string strVal && string.IsNullOrEmpty(strVal)))) + return ValidationResult.Success; + + foreach (var platform in supportedPlatforms) + { + if (OperatingSystem.IsOSPlatform(platform)) + return ValidationResult.Success; + } + + return new ValidationResult($"{nameof(validationContext.DisplayName)} can only bet set on following platforms: {string.Join(',', supportedPlatforms)}"); + } + } } \ No newline at end of file diff --git a/libs/host/Configuration/Redis/RedisOptions.cs b/libs/host/Configuration/Redis/RedisOptions.cs index 48c696e21d..dc18fa6dfb 100644 --- a/libs/host/Configuration/Redis/RedisOptions.cs +++ b/libs/host/Configuration/Redis/RedisOptions.cs @@ -82,6 +82,12 @@ internal class RedisOptions [RedisOption("repl-diskless-sync-delay", nameof(Options.ReplicaSyncDelayMs))] public Option ReplicaDisklessSyncDelay { get; set; } + + [RedisOption("unixsocket", nameof(Options.UnixSocketPath))] + public Option UnixSocketPath { get; set; } + + [RedisOption("unixsocketperm", nameof(Options.UnixSocketPermission))] + public Option UnixSocketPermission { get; set; } } /// diff --git a/libs/host/GarnetServer.cs b/libs/host/GarnetServer.cs index 973399ad80..d523d88805 100644 --- a/libs/host/GarnetServer.cs +++ b/libs/host/GarnetServer.cs @@ -5,6 +5,7 @@ using System.Diagnostics; using System.IO; using System.Linq; +using System.Net.Sockets; using System.Reflection; using System.Text; using System.Threading; @@ -170,18 +171,20 @@ private void InitializeServer() var magenta = "\u001b[35m"; var normal = "\u001b[0m"; - Console.WriteLine($@"{red} _________ - /_||___||_\ {normal}Garnet {version} {(IntPtr.Size == 8 ? "64" : "32")} bit; {(opts.EnableCluster ? "cluster" : "standalone")} mode{red} - '. \ / .' {normal}Port: {opts.Port}{red} - '.\ /.' {magenta}https://aka.ms/GetGarnet{red} - '.' -{normal}"); + Console.WriteLine($""" + {red} _________ + /_||___||_\ {normal}Garnet {version} {(IntPtr.Size == 8 ? "64" : "32")} bit; {(opts.EnableCluster ? "cluster" : "standalone")} mode{red} + '. \ / .' {normal}Listening on: {opts.EndPoint}{red} + '.\ /.' {magenta}https://aka.ms/GetGarnet{red} + '.' + {normal} + """); } var clusterFactory = opts.EnableCluster ? new ClusterFactory() : null; this.logger = this.loggerFactory?.CreateLogger("GarnetServer"); - logger?.LogInformation("Garnet {version} {bits} bit; {clusterMode} mode; Port: {port}", version, IntPtr.Size == 8 ? "64" : "32", opts.EnableCluster ? "cluster" : "standalone", opts.Port); + logger?.LogInformation("Garnet {version} {bits} bit; {clusterMode} mode; Endpoint: {endpoint}", version, IntPtr.Size == 8 ? "64" : "32", opts.EnableCluster ? "cluster" : "standalone", opts.EndPoint); // Flush initialization logs from memory logger FlushMemoryLogger(this.initLogger, "ArgParser", this.loggerFactory); @@ -242,8 +245,14 @@ private void InitializeServer() logger.LogInformation("Total configured memory limit: {configMemoryLimit}", configMemoryLimit); } + if (opts.EndPoint is UnixDomainSocketEndPoint) + { + // Delete existing unix socket file, if it exists. + File.Delete(opts.UnixSocketPath); + } + // Create Garnet TCP server if none was provided. - this.server ??= new GarnetServerTcp(opts.Address, opts.Port, 0, opts.TlsOptions, opts.NetworkSendThrottleMax, opts.NetworkConnectionLimit, logger); + this.server ??= new GarnetServerTcp(opts.EndPoint, 0, opts.TlsOptions, opts.NetworkSendThrottleMax, opts.NetworkConnectionLimit, opts.UnixSocketPath, opts.UnixSocketPermission, logger); storeWrapper = new StoreWrapper(version, redisProtocolVersion, server, store, objectStore, objectStoreSizeTracker, customCommandManager, appendOnlyFile, opts, subscribeBroker, clusterFactory: clusterFactory, loggerFactory: loggerFactory); diff --git a/libs/host/defaults.conf b/libs/host/defaults.conf index 01c31e5c3f..f6bc0cebe3 100644 --- a/libs/host/defaults.conf +++ b/libs/host/defaults.conf @@ -354,5 +354,11 @@ "LuaMemoryManagementMode": "Native", /* Lua limits are ignored for Native, but can be set for other modes */ - "LuaScriptMemoryLimit": null + "LuaScriptMemoryLimit": null, + + /* Unix socket address path to bind the server to */ + "UnixSocketPath": null, + + /* Unix socket permissions in octal (Unix platforms only) */ + "UnixSocketPermission": 0 } \ No newline at end of file diff --git a/libs/server/Metrics/GarnetServerMonitor.cs b/libs/server/Metrics/GarnetServerMonitor.cs index 450c0b0a51..3ab5ab2c23 100644 --- a/libs/server/Metrics/GarnetServerMonitor.cs +++ b/libs/server/Metrics/GarnetServerMonitor.cs @@ -188,8 +188,8 @@ private void ResetStats() session.GetSessionMetrics.Reset(); } - garnetServer.reset_conn_recv(); - garnetServer.reset_conn_disp(); + garnetServer.ResetConnectionsReceived(); + garnetServer.ResetConnectionsDiposed(); storeWrapper.clusterProvider?.ResetGossipStats(); @@ -256,8 +256,8 @@ private async void MainMonitorTask(CancellationToken token) monitor_iterations++; var garnetServer = ((GarnetServerBase)server); - globalMetrics.total_connections_received = garnetServer.get_conn_recv(); - globalMetrics.total_connections_disposed = garnetServer.get_conn_disp(); + globalMetrics.total_connections_received = garnetServer.TotalConnectionsReceived; + globalMetrics.total_connections_disposed = garnetServer.TotalConnectionsDisposed; globalMetrics.total_connections_active = garnetServer.get_conn_active(); UpdateInstantaneousMetrics(); diff --git a/libs/server/Metrics/Info/GarnetInfoMetrics.cs b/libs/server/Metrics/Info/GarnetInfoMetrics.cs index 1203145682..94c05fdca3 100644 --- a/libs/server/Metrics/Info/GarnetInfoMetrics.cs +++ b/libs/server/Metrics/Info/GarnetInfoMetrics.cs @@ -291,7 +291,7 @@ private void PopulateKeyspaceInfo(StoreWrapper storeWrapper) private void PopulateClusterBufferPoolStats(StoreWrapper storeWrapper) { - bufferPoolStats = [new("server_socket", storeWrapper.GetTcpServer().GetBufferPoolStats())]; + bufferPoolStats = [new("server_socket", storeWrapper.TcpServer.GetBufferPoolStats())]; if (storeWrapper.clusterProvider != null) bufferPoolStats = [.. bufferPoolStats, .. storeWrapper.clusterProvider.GetBufferPoolStats()]; } diff --git a/libs/server/Resp/PurgeBPCommand.cs b/libs/server/Resp/PurgeBPCommand.cs index dba9327812..efc51e119f 100644 --- a/libs/server/Resp/PurgeBPCommand.cs +++ b/libs/server/Resp/PurgeBPCommand.cs @@ -69,7 +69,7 @@ private bool NetworkPurgeBP() success = ClusterPurgeBufferPool(managerType); break; case ManagerType.ServerListener: - storeWrapper.GetTcpServer().Purge(); + storeWrapper.TcpServer.Purge(); break; default: success = false; diff --git a/libs/server/Servers/GarnetServerBase.cs b/libs/server/Servers/GarnetServerBase.cs index 605a199493..b5424b0ff7 100644 --- a/libs/server/Servers/GarnetServerBase.cs +++ b/libs/server/Servers/GarnetServerBase.cs @@ -4,6 +4,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; +using System.Net; using System.Runtime.CompilerServices; using System.Threading; using Garnet.common; @@ -32,19 +33,12 @@ public abstract class GarnetServerBase : IGarnetServer /// readonly ConcurrentDictionary sessionProviders; - readonly string address; - readonly int port; readonly int networkBufferSize; /// - /// Server Address - /// - public string Address => address; - - /// - /// Server Port + /// The endpoint server listener socket is bound to. /// - public int Port => port; + public EndPoint EndPoint { get; } /// /// Server NetworkBufferSize @@ -62,20 +56,20 @@ public abstract class GarnetServerBase : IGarnetServer protected readonly ILogger logger; - long total_connections_received = 0; - long total_connections_disposed = 0; + long totalConnectionsReceived = 0; + long totalConnectionsDisposed = 0; /// /// Add to total_connections_received /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void incr_conn_recv() => Interlocked.Increment(ref total_connections_received); + public void IncrementConnectionsReceived() => Interlocked.Increment(ref totalConnectionsReceived); /// /// Add to total_connections_disposed /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void incr_conn_disp() => Interlocked.Increment(ref total_connections_disposed); + public void IncrementConnectionsDisposed() => Interlocked.Increment(ref totalConnectionsDisposed); /// /// Returns all the active message consumers. @@ -92,39 +86,28 @@ public virtual IEnumerable ActiveConsumers() public long get_conn_active() => this.activeHandlers.Count; /// - /// Get total_connections_received + /// Get the total number of connections received. /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public long get_conn_recv() => total_connections_received; + public long TotalConnectionsReceived => totalConnectionsReceived; /// - /// Get total_connections_disposed + /// Get the total number of connections received. /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public long get_conn_disp() => total_connections_disposed; + public long TotalConnectionsDisposed => totalConnectionsDisposed; /// - /// Reset connection recv counter. Multiplier for accounting for pub/sub + /// Reset connections received counter. Multiplier for accounting for pub/sub /// - public void reset_conn_recv() => Interlocked.Exchange(ref total_connections_received, activeHandlers.Count); + public void ResetConnectionsReceived() => Interlocked.Exchange(ref totalConnectionsReceived, activeHandlers.Count); /// - /// Reset connection disposed counter + /// Reset connections disposed counter /// - public void reset_conn_disp() => Interlocked.Exchange(ref total_connections_disposed, 0); + public void ResetConnectionsDiposed() => Interlocked.Exchange(ref totalConnectionsDisposed, 0); - /// - /// - /// - /// - /// - /// - /// - public GarnetServerBase(string address, int port, int networkBufferSize, ILogger logger = null) + public GarnetServerBase(EndPoint endpoint, int networkBufferSize, ILogger logger = null) { this.logger = logger; - this.address = address; - this.port = port; this.networkBufferSize = networkBufferSize; if (networkBufferSize == default) this.networkBufferSize = BufferSizeUtils.ClientBufferSize(new MaxSizeSettings()); @@ -132,6 +115,8 @@ public GarnetServerBase(string address, int port, int networkBufferSize, ILogger activeHandlers = new(); sessionProviders = new(); activeHandlerCount = 0; + + EndPoint = endpoint; Disposed = false; } diff --git a/libs/server/Servers/GarnetServerOptions.cs b/libs/server/Servers/GarnetServerOptions.cs index ecefa235a5..d248747d1e 100644 --- a/libs/server/Servers/GarnetServerOptions.cs +++ b/libs/server/Servers/GarnetServerOptions.cs @@ -411,6 +411,16 @@ public class GarnetServerOptions : ServerOptions public LuaOptions LuaOptions; + /// + /// Unix socket address path to bind server to + /// + public string UnixSocketPath { get; set; } + + /// + /// Unix socket file permissions + /// + public UnixFileMode UnixSocketPermission { get; set; } + /// /// Constructor /// diff --git a/libs/server/Servers/GarnetServerTcp.cs b/libs/server/Servers/GarnetServerTcp.cs index 09a1c4fd71..f0be88764c 100644 --- a/libs/server/Servers/GarnetServerTcp.cs +++ b/libs/server/Servers/GarnetServerTcp.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.IO; using System.Net; using System.Net.Sockets; using System.Threading; @@ -19,21 +20,14 @@ namespace Garnet.server public class GarnetServerTcp : GarnetServerBase, IServerHook { readonly SocketAsyncEventArgs acceptEventArg; - readonly Socket servSocket; + readonly Socket listenSocket; readonly IGarnetTlsOptions tlsOptions; readonly int networkSendThrottleMax; readonly NetworkBufferSettings networkBufferSettings; readonly LimitedFixedBufferPool networkPool; readonly int networkConnectionLimit; - - public IPEndPoint GetEndPoint - { - get - { - var ip = string.IsNullOrEmpty(Address) ? IPAddress.Any : IPAddress.Parse(Address); - return new IPEndPoint(ip, Port); - } - } + readonly string unixSocketPath; + readonly UnixFileMode unixSocketPermission; /// /// Get active consumers @@ -64,14 +58,21 @@ public IEnumerable ActiveClusterSessions() /// /// Constructor for server /// - /// - /// + /// Endpoint bound for listening for connections. /// /// /// /// - public GarnetServerTcp(string address, int port, int networkBufferSize = default, IGarnetTlsOptions tlsOptions = null, int networkSendThrottleMax = 8, int networkConnectionLimit = -1, ILogger logger = null) - : base(address, port, networkBufferSize, logger) + public GarnetServerTcp( + EndPoint endpoint, + int networkBufferSize = default, + IGarnetTlsOptions tlsOptions = null, + int networkSendThrottleMax = 8, + int networkConnectionLimit = -1, + string unixSocketPath = null, + UnixFileMode unixSocketPermission = default, + ILogger logger = null) + : base(endpoint, networkBufferSize, logger) { this.networkConnectionLimit = networkConnectionLimit; this.tlsOptions = tlsOptions; @@ -79,7 +80,16 @@ public GarnetServerTcp(string address, int port, int networkBufferSize = default var serverBufferSize = BufferSizeUtils.ServerBufferSize(new MaxSizeSettings()); this.networkBufferSettings = new NetworkBufferSettings(serverBufferSize, serverBufferSize); this.networkPool = networkBufferSettings.CreateBufferPool(logger: logger); - servSocket = new Socket(GetEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); + this.unixSocketPath = unixSocketPath; + this.unixSocketPermission = unixSocketPermission; + + listenSocket = endpoint switch + { + UnixDomainSocketEndPoint unix => new Socket(unix.AddressFamily, SocketType.Stream, ProtocolType.Unspecified), + + _ => new Socket(endpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp) + }; + acceptEventArg = new SocketAsyncEventArgs(); acceptEventArg.Completed += AcceptEventArg_Completed; } @@ -90,7 +100,7 @@ public GarnetServerTcp(string address, int port, int networkBufferSize = default public override void Dispose() { base.Dispose(); - servSocket.Dispose(); + listenSocket.Dispose(); acceptEventArg.UserToken = null; acceptEventArg.Dispose(); networkPool?.Dispose(); @@ -101,10 +111,14 @@ public override void Dispose() /// public override void Start() { - var endPoint = GetEndPoint; - servSocket.Bind(endPoint); - servSocket.Listen(512); - if (!servSocket.AcceptAsync(acceptEventArg)) + listenSocket.Bind(EndPoint); + if (EndPoint is UnixDomainSocketEndPoint && unixSocketPermission != default && !OperatingSystem.IsWindows()) + { + File.SetUnixFileMode(unixSocketPath, unixSocketPermission); + } + + listenSocket.Listen(512); + if (!listenSocket.AcceptAsync(acceptEventArg)) AcceptEventArg_Completed(null, acceptEventArg); } @@ -116,7 +130,7 @@ private void AcceptEventArg_Completed(object sender, SocketAsyncEventArgs e) { if (!HandleNewConnection(e)) break; e.AcceptSocket = null; - } while (!servSocket.AcceptAsync(e)); + } while (!listenSocket.AcceptAsync(e)); } // socket disposed catch (ObjectDisposedException) { } @@ -130,7 +144,8 @@ private unsafe bool HandleNewConnection(SocketAsyncEventArgs e) return false; } - e.AcceptSocket.NoDelay = true; + if (e.AcceptSocket.LocalEndPoint is not UnixDomainSocketEndPoint) + e.AcceptSocket.NoDelay = true; // Ok to create new event args on accept because we assume a connection to be long-running string remoteEndpointName = e.AcceptSocket.RemoteEndPoint?.ToString(); @@ -149,7 +164,7 @@ private unsafe bool HandleNewConnection(SocketAsyncEventArgs e) throw new Exception("Unable to add handler to dictionary"); handler.Start(tlsOptions?.TlsServerOptions, remoteEndpointName); - incr_conn_recv(); + IncrementConnectionsReceived(); return true; } catch (Exception ex) @@ -204,7 +219,7 @@ public void DisposeMessageConsumer(INetworkHandler session) if (activeHandlers.TryRemove(session, out _)) { Interlocked.Decrement(ref activeHandlerCount); - incr_conn_disp(); + IncrementConnectionsDisposed(); try { session.Session?.Dispose(); diff --git a/libs/server/Servers/ServerOptions.cs b/libs/server/Servers/ServerOptions.cs index 92795ba60d..297b257657 100644 --- a/libs/server/Servers/ServerOptions.cs +++ b/libs/server/Servers/ServerOptions.cs @@ -3,6 +3,7 @@ using System; using System.IO; +using System.Net; using Microsoft.Extensions.Logging; using Tsavorite.core; @@ -14,14 +15,9 @@ namespace Garnet.server public class ServerOptions { /// - /// Port to run server on. + /// Endpoint to bind server to. /// - public int Port = 6379; - - /// - /// IP address to bind server to. - /// - public string Address = "127.0.0.1"; + public EndPoint EndPoint { get; set; } = new IPEndPoint(IPAddress.Loopback, 6379); /// /// Total log memory used in bytes (rounds down to power of 2). diff --git a/libs/server/StoreWrapper.cs b/libs/server/StoreWrapper.cs index 0bef69a596..510b5d154b 100644 --- a/libs/server/StoreWrapper.cs +++ b/libs/server/StoreWrapper.cs @@ -58,7 +58,7 @@ public sealed class StoreWrapper /// /// Get server /// - public GarnetServerTcp GetTcpServer() => (GarnetServerTcp)server; + public GarnetServerTcp TcpServer => (GarnetServerTcp)server; /// /// Access control list governing all commands @@ -204,7 +204,9 @@ public StoreWrapper( /// public string GetIp() { - var localEndpoint = GetTcpServer().GetEndPoint; + if (TcpServer.EndPoint is not IPEndPoint localEndpoint) + throw new NotImplementedException("Cluster mode for unix domain sockets has not been implemented"); + if (localEndpoint.Address.Equals(IPAddress.Any)) { using (Socket socket = new(AddressFamily.InterNetwork, SocketType.Dgram, 0)) diff --git a/playground/ClusterStress/ShardedRespOnlineBench.cs b/playground/ClusterStress/ShardedRespOnlineBench.cs index 8bf1d944db..8768aa71e1 100644 --- a/playground/ClusterStress/ShardedRespOnlineBench.cs +++ b/playground/ClusterStress/ShardedRespOnlineBench.cs @@ -205,8 +205,7 @@ private void InitClients(ClusterNode[] nodes) for (int i = 0; i < nodes.Length; i++) { var endpoint = (IPEndPoint)nodes[i].EndPoint; - gclient[i] = new GarnetClient(endpoint.Address.ToString(), - endpoint.Port, + gclient[i] = new GarnetClient(endpoint, opts.EnableTLS ? BenchUtils.GetTlsOptions(opts.TlsHost, opts.CertFileName, opts.CertPassword) : null, recordLatency: opts.ClientHistogram); gclient[i].Connect(); @@ -221,8 +220,7 @@ private void InitClients(ClusterNode[] nodes) { var endpoint = (IPEndPoint)nodes[i].EndPoint; gcs[j][i] = new GarnetClientSession( - endpoint.Address.ToString(), - endpoint.Port, + endpoint, new(Math.Max(bufferSizeValue, opts.IntraThreadParallelism * opts.ValueLength)), tlsOptions: opts.EnableTLS ? BenchUtils.GetTlsOptions(opts.TlsHost, opts.CertFileName, opts.CertPassword) : null); gcs[j][i].Connect(); diff --git a/playground/ClusterStress/ShardedRespPerfBench.cs b/playground/ClusterStress/ShardedRespPerfBench.cs index 45df1fff64..1550f96697 100644 --- a/playground/ClusterStress/ShardedRespPerfBench.cs +++ b/playground/ClusterStress/ShardedRespPerfBench.cs @@ -99,8 +99,7 @@ private unsafe ClusterShardConnection[] InitConnections(OpType opType, int buffe clusterShards[i].count = 1 + (opts.ReplicaReads ? pNodes[i].Children.Count : 0); IPEndPoint pEndpoint = (IPEndPoint)pNodes[i].EndPoint; clusterShards[i].primary = new LightClient( - pEndpoint.Address.ToString(), - pEndpoint.Port, + pEndpoint, (int)opType, lighClientOnResponseDelegate, bufferSize, @@ -115,8 +114,7 @@ private unsafe ClusterShardConnection[] InitConnections(OpType opType, int buffe { IPEndPoint rEndpoint = (IPEndPoint)pNodes[i].Children[j].EndPoint; clusterShards[i].replicas[j] = new LightClient( - rEndpoint.Address.ToString(), - rEndpoint.Port, + rEndpoint, (int)opType, lighClientOnResponseDelegate, bufferSize, @@ -364,8 +362,7 @@ private unsafe void LightOperateThreadRunnerCluster(int threadid, int NumOps, Op { var endpoint = (IPEndPoint)nodes[i].EndPoint; clients[i] = new LightClient( - endpoint.Address.ToString(), - endpoint.Port, + endpoint, (int)opType, lighClientOnResponseDelegate, rg.GetBufferSize(), diff --git a/playground/CommandInfoUpdater/CommandInfoUpdater.cs b/playground/CommandInfoUpdater/CommandInfoUpdater.cs index 081124f901..ad1901c14a 100644 --- a/playground/CommandInfoUpdater/CommandInfoUpdater.cs +++ b/playground/CommandInfoUpdater/CommandInfoUpdater.cs @@ -165,7 +165,7 @@ private static unsafe bool TryGetCommandsInfo(string[] commandsToQuery, int resp byte[] response; try { - var lightClient = new LightClientRequest(respServerHost.ToString(), respServerPort, 0); + var lightClient = new LightClientRequest(new IPEndPoint(respServerHost, respServerPort), 0); response = lightClient.SendCommand($"COMMAND INFO {string.Join(' ', commandsToQuery)}"); } catch (Exception e) diff --git a/playground/GarnetClientStress/Options.cs b/playground/GarnetClientStress/Options.cs index 06aae99439..a4d8fe507d 100644 --- a/playground/GarnetClientStress/Options.cs +++ b/playground/GarnetClientStress/Options.cs @@ -11,7 +11,7 @@ public class Options public int Port { get; set; } [Option('h', "host", Required = false, Default = "127.0.0.1", HelpText = "IP address to connect to")] - public string? Address { get; set; } + public string Address { get; set; } = "127.0.0.1"; [Option("dbsize", Required = false, Default = 1 << 10, HelpText = "DB size")] public int DbSize { get; set; } diff --git a/playground/GarnetClientStress/SimpleStressTests.cs b/playground/GarnetClientStress/SimpleStressTests.cs index 05d919aa8b..663e9f855a 100644 --- a/playground/GarnetClientStress/SimpleStressTests.cs +++ b/playground/GarnetClientStress/SimpleStressTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. using System; +using System.Net; using Garnet.client; namespace GarnetClientStress @@ -16,8 +17,7 @@ public static void RunPingDispose(Options opts) for (int i = 0; i < clients.Length; i++) { clients[i] = new GarnetClient( - opts.Address, - opts.Port, + new IPEndPoint(IPAddress.Parse(opts.Address), opts.Port), opts.EnableTLS ? StressTestUtils.GetTlsOptions("GarnetTest") : null, timeoutMilliseconds: (int)timeout.TotalMilliseconds, maxOutstandingTasks: opts.ClientMaxOutstandingTasks, diff --git a/playground/GarnetClientStress/TaskScaling.cs b/playground/GarnetClientStress/TaskScaling.cs index 4f4a39b9e1..d55a58a369 100644 --- a/playground/GarnetClientStress/TaskScaling.cs +++ b/playground/GarnetClientStress/TaskScaling.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. using System; +using System.Net; using System.Threading; using System.Threading.Tasks; using Garnet.client; @@ -32,8 +33,7 @@ private async Task CreateClients() for (int i = 0; i < gclientArray.Length; i++) { gclientArray[i] = new GarnetClient( - opts.Address, - opts.Port, + new IPEndPoint(IPAddress.Parse(opts.Address), opts.Port), opts.EnableTLS ? StressTestUtils.GetTlsOptions("GarnetTest") : null, timeoutMilliseconds: (int)timeout.TotalMilliseconds, maxOutstandingTasks: opts.ClientMaxOutstandingTasks, diff --git a/playground/MigrateBench/MigrateRequest.cs b/playground/MigrateBench/MigrateRequest.cs index 5556ce5883..d1dc984f2b 100644 --- a/playground/MigrateBench/MigrateRequest.cs +++ b/playground/MigrateBench/MigrateRequest.cs @@ -57,8 +57,8 @@ public MigrateRequest(Options opts, ILogger logger = null) targetAddress = targetEndpoint[0]; targetPort = int.Parse(targetEndpoint[1]); - sourceNode = new(sourceAddress, sourcePort, new NetworkBufferSettings(1 << 22), logger: logger); - targetNode = new(targetAddress, targetPort, new NetworkBufferSettings(1 << 22), logger: logger); + sourceNode = new(new IPEndPoint(IPAddress.Parse(sourceAddress), sourcePort), new NetworkBufferSettings(1 << 22), logger: logger); + targetNode = new(new IPEndPoint(IPAddress.Parse(targetAddress), targetPort), new NetworkBufferSettings(1 << 22), logger: logger); this.timeout = (int)TimeSpan.FromSeconds(opts.Timeout).TotalMilliseconds; this.logger = logger; } diff --git a/playground/MigrateBench/MigrateSlotWalk.cs b/playground/MigrateBench/MigrateSlotWalk.cs index e0907736e4..bd02d7e3cf 100644 --- a/playground/MigrateBench/MigrateSlotWalk.cs +++ b/playground/MigrateBench/MigrateSlotWalk.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. +using System.Net; using Garnet.client; using Garnet.common; using Microsoft.Extensions.Logging; @@ -32,7 +33,7 @@ public MigrateSlotWalk(Options opts, ILogger logger = null) var endpoint = endpoints[i].Split(':'); nodes[i].address = endpoint[0]; nodes[i].port = endpoint[1]; - nodes[i].gc = new GarnetClientSession(nodes[i].address, int.Parse(nodes[i].port), new NetworkBufferSettings(1 << 22)); + nodes[i].gc = new GarnetClientSession(new IPEndPoint(IPAddress.Parse(nodes[i].address), int.Parse(nodes[i].port)), new NetworkBufferSettings(1 << 22)); } this.logger = logger; } diff --git a/samples/GarnetClientSample/GarnetClientSamples.cs b/samples/GarnetClientSample/GarnetClientSamples.cs index 19882557cb..75521c07e1 100644 --- a/samples/GarnetClientSample/GarnetClientSamples.cs +++ b/samples/GarnetClientSample/GarnetClientSamples.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. using System; +using System.Net; using System.Net.Security; using System.Security.Cryptography.X509Certificates; using System.Text; @@ -16,14 +17,12 @@ namespace GarnetClientSample /// public class GarnetClientSamples { - readonly string address; - readonly int port; + readonly EndPoint endpoint; readonly bool useTLS; - public GarnetClientSamples(string address, int port, bool useTLS) + public GarnetClientSamples(EndPoint endpoint, bool useTLS) { - this.address = address; - this.port = port; + this.endpoint = endpoint; this.useTLS = useTLS; } @@ -44,7 +43,7 @@ public async Task RunAll() async Task PingAsync() { - using var db = new GarnetClient(address, port, GetSslOpts()); + using var db = new GarnetClient(endpoint, GetSslOpts()); await db.ConnectAsync(); var pong = await db.PingAsync(); if (pong != "PONG") @@ -54,7 +53,7 @@ async Task PingAsync() async Task SetGetAsync() { - using var db = new GarnetClient(address, port, GetSslOpts()); + using var db = new GarnetClient(endpoint, GetSslOpts()); await db.ConnectAsync(); string origValue = "abcdefg"; @@ -69,7 +68,7 @@ async Task SetGetAsync() void SetGetSync() { - using var db = new GarnetClient(address, port, GetSslOpts()); + using var db = new GarnetClient(endpoint, GetSslOpts()); db.Connect(); string origValue = "abcdefg"; @@ -83,7 +82,7 @@ void SetGetSync() async Task IncrAsync() { - using var db = new GarnetClient(address, port, GetSslOpts()); + using var db = new GarnetClient(endpoint, GetSslOpts()); await db.ConnectAsync(); // Key storing integer @@ -107,7 +106,7 @@ async Task IncrAsync() async Task IncrByAsync(long nIncr) { - using var db = new GarnetClient(address, port, GetSslOpts()); + using var db = new GarnetClient(endpoint, GetSslOpts()); await db.ConnectAsync(); // Key storing integer @@ -132,7 +131,7 @@ async Task IncrByAsync(long nIncr) async Task DecrByAsync(long nDecr) { - using var db = new GarnetClient(address, port, GetSslOpts()); + using var db = new GarnetClient(endpoint, GetSslOpts()); await db.ConnectAsync(); // Key storing integer @@ -157,7 +156,7 @@ async Task DecrByAsync(long nDecr) async Task DecrAsync(string strKey, int nVal) { - using var db = new GarnetClient(address, port, GetSslOpts()); + using var db = new GarnetClient(endpoint, GetSslOpts()); await db.ConnectAsync(); await db.StringSetAsync(strKey, $"{nVal}"); @@ -178,7 +177,7 @@ async Task DecrAsync(string strKey, int nVal) async Task IncrNoKeyAsync() { - using var db = new GarnetClient(address, port, GetSslOpts()); + using var db = new GarnetClient(endpoint, GetSslOpts()); await db.ConnectAsync(); // Key storing integer @@ -198,7 +197,7 @@ async Task IncrNoKeyAsync() async Task ExistsAsync() { - using var db = new GarnetClient(address, port, GetSslOpts()); + using var db = new GarnetClient(endpoint, GetSslOpts()); await db.ConnectAsync(); // Key storing integer @@ -214,7 +213,7 @@ async Task ExistsAsync() async Task DeleteAsync() { - using var db = new GarnetClient(address, port, GetSslOpts()); + using var db = new GarnetClient(endpoint, GetSslOpts()); await db.ConnectAsync(); // Key storing integer @@ -232,7 +231,7 @@ async Task DeleteAsync() async Task SetGetMemoryAsync() { - using var db = new GarnetClient(address, port, GetSslOpts()); + using var db = new GarnetClient(endpoint, GetSslOpts()); await db.ConnectAsync(); var key = new Memory(new byte[17]); @@ -272,7 +271,7 @@ void ConnectionStressRunner(int thread_id) int i = 0; while (true) { - using var client = new GarnetClient(address, port, GetSslOpts()); + using var client = new GarnetClient(endpoint, GetSslOpts()); client.Connect(); Console.WriteLine($"{thread_id}:{i++}: {client.PingAsync().GetAwaiter().GetResult()}"); client.Dispose(); diff --git a/samples/GarnetClientSample/Program.cs b/samples/GarnetClientSample/Program.cs index f5beac657d..d1de53dc38 100644 --- a/samples/GarnetClientSample/Program.cs +++ b/samples/GarnetClientSample/Program.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. +using System.Net; using System.Threading.Tasks; namespace GarnetClientSample @@ -10,13 +11,13 @@ namespace GarnetClientSample /// class Program { - static readonly string address = "127.0.0.1"; - static readonly int port = 6379; + static readonly EndPoint endpoint = new IPEndPoint(IPAddress.Loopback, 6379); + static readonly bool useTLS = false; static async Task Main() { - await new GarnetClientSamples(address, port, useTLS).RunAll(); + await new GarnetClientSamples(endpoint, useTLS).RunAll(); // await new SERedisSamples(address, port).RunAll(); } diff --git a/test/Garnet.test.cluster/ClusterAuthCommsTests.cs b/test/Garnet.test.cluster/ClusterAuthCommsTests.cs index 2f26bcae7f..365ac30290 100644 --- a/test/Garnet.test.cluster/ClusterAuthCommsTests.cs +++ b/test/Garnet.test.cluster/ClusterAuthCommsTests.cs @@ -215,7 +215,7 @@ public void ClusterSimpleACLReload() // Restart node with new ACL file context.nodes[0].Dispose(false); - context.nodes[0] = context.CreateInstance(context.clusterTestUtils.GetEndPoint(0).Port, useAcl: true, cleanClusterConfig: false); + context.nodes[0] = context.CreateInstance(context.clusterTestUtils.GetEndPoint(0), useAcl: true, cleanClusterConfig: false); context.nodes[0].Start(); context.CreateConnection(clientCreds: cc[0]); diff --git a/test/Garnet.test.cluster/ClusterConfigTests.cs b/test/Garnet.test.cluster/ClusterConfigTests.cs index 55c7b705fb..ade91dc42e 100644 --- a/test/Garnet.test.cluster/ClusterConfigTests.cs +++ b/test/Garnet.test.cluster/ClusterConfigTests.cs @@ -71,7 +71,7 @@ public void ClusterForgetAfterNodeRestartTest() // Restart node with new ACL file context.nodes[0].Dispose(false); - context.nodes[0] = context.CreateInstance(context.clusterTestUtils.GetEndPoint(0).Port, useAcl: true, cleanClusterConfig: false); + context.nodes[0] = context.CreateInstance(context.clusterTestUtils.GetEndPoint(0), useAcl: true, cleanClusterConfig: false); context.nodes[0].Start(); context.CreateConnection(); diff --git a/test/Garnet.test.cluster/ClusterManagementTests.cs b/test/Garnet.test.cluster/ClusterManagementTests.cs index eb2d46404d..51ca6f950e 100644 --- a/test/Garnet.test.cluster/ClusterManagementTests.cs +++ b/test/Garnet.test.cluster/ClusterManagementTests.cs @@ -389,7 +389,7 @@ public void ClusterRestartNodeDropGossip() context.nodes[restartingNode].Dispose(deleteDir: true); context.nodes[restartingNode] = context.CreateInstance( - context.clusterTestUtils.GetEndPoint(restartingNode).Port, + context.clusterTestUtils.GetEndPoint(restartingNode), disableObjects: true, tryRecover: false, enableAOF: true, @@ -413,7 +413,7 @@ public void ClusterRestartNodeDropGossip() public void ClusterClientList() { const int NodeCount = 4; - context.CreateInstances(NodeCount, enableAOF: true, MainMemoryReplication: true, CommitFrequencyMs: -1); + context.CreateInstances(NodeCount, enableAOF: true, mainMemoryReplication: true, commitFrequencyMs: -1); context.CreateConnection(); _ = context.clusterTestUtils.SimpleSetupCluster(NodeCount / 2, 1, logger: context.logger); @@ -544,7 +544,7 @@ public void ClusterClientList() public void ClusterClientKill() { const int NodeCount = 4; - context.CreateInstances(NodeCount, enableAOF: true, MainMemoryReplication: true, CommitFrequencyMs: -1); + context.CreateInstances(NodeCount, enableAOF: true, mainMemoryReplication: true, commitFrequencyMs: -1); context.CreateConnection(); _ = context.clusterTestUtils.SimpleSetupCluster(NodeCount / 2, 1, logger: context.logger); @@ -561,7 +561,7 @@ public void ClusterClientKillSlave() // Test SLAVE separately - it's equivalent to REPLICA, but needed for compatibility const int NodeCount = 4; - context.CreateInstances(NodeCount, enableAOF: true, MainMemoryReplication: true, CommitFrequencyMs: -1); + context.CreateInstances(NodeCount, enableAOF: true, mainMemoryReplication: true, commitFrequencyMs: -1); context.CreateConnection(); _ = context.clusterTestUtils.SimpleSetupCluster(NodeCount / 2, 1, logger: context.logger); diff --git a/test/Garnet.test.cluster/ClusterMigrateTests.cs b/test/Garnet.test.cluster/ClusterMigrateTests.cs index 7ddc728d53..440cf64250 100644 --- a/test/Garnet.test.cluster/ClusterMigrateTests.cs +++ b/test/Garnet.test.cluster/ClusterMigrateTests.cs @@ -335,22 +335,22 @@ public void ClusterAddDelSlots() ClassicAssert.AreEqual(resp, "OK"); - var respState = context.clusterTestUtils.SetKey(0, key, val, out var _, out var _, out var _, logger: context.logger); + var respState = context.clusterTestUtils.SetKey(0, key, val, out var _, out var _, logger: context.logger); ClassicAssert.AreEqual(respState, ResponseState.OK); resp = context.clusterTestUtils.AddDelSlots(0, [slot], false); ClassicAssert.AreEqual(resp, "OK"); - respState = context.clusterTestUtils.SetKey(0, key, val, out var _, out var _, out var _, logger: context.logger); + respState = context.clusterTestUtils.SetKey(0, key, val, out var _, out var _, logger: context.logger); ClassicAssert.AreEqual(respState, ResponseState.CLUSTERDOWN); - resp = context.clusterTestUtils.GetKey(0, key, out var _, out var _, out var _, out var _, logger: context.logger); + resp = context.clusterTestUtils.GetKey(0, key, out var _, out var _, out var _, logger: context.logger); ClassicAssert.AreEqual(resp, "CLUSTERDOWN"); resp = context.clusterTestUtils.AddDelSlots(0, [slot], true); ClassicAssert.AreEqual(resp, "OK"); - resp = context.clusterTestUtils.GetKey(0, key, out var _, out var _, out var _, out var _, logger: context.logger); + resp = context.clusterTestUtils.GetKey(0, key, out var _, out var _, out var _, logger: context.logger); ClassicAssert.AreEqual(resp, val); #endregion @@ -372,7 +372,7 @@ public void ClusterSlotChangeStatus() var key = Encoding.ASCII.GetBytes("{abc}0"); var val = Encoding.ASCII.GetBytes("1234"); - var respState = context.clusterTestUtils.SetKey(sourcePortIndex, key, val, out _, out _, out _, logger: context.logger); + var respState = context.clusterTestUtils.SetKey(sourcePortIndex, key, val, out _, out _, logger: context.logger); ClassicAssert.AreEqual(respState, ResponseState.OK); var slot = (int)HashSlotUtils.HashSlot(key); var expectedSlot = 7638; @@ -441,47 +441,43 @@ public void ClusterSlotChangeStatus() #region TEST_REDIRECTION //0. other node alway redirect to source node - resp = context.clusterTestUtils.GetKey(otherNodeIndex, key, out slot, out var address, out var port, out var responseState, logger: context.logger); + resp = context.clusterTestUtils.GetKey(otherNodeIndex, key, out slot, out var endpoint, out var responseState, logger: context.logger); ClassicAssert.AreEqual(ResponseState.MOVED, responseState); ClassicAssert.AreEqual(resp, "MOVED"); ClassicAssert.AreEqual(expectedSlot, slot); - ClassicAssert.AreEqual(address, context.clusterTestUtils.GetEndPoint(sourcePortIndex).Address.ToString()); - ClassicAssert.AreEqual(port, context.clusterTestUtils.GetEndPoint(sourcePortIndex).Port); + ClassicAssert.AreEqual(endpoint, context.clusterTestUtils.GetEndPoint(sourcePortIndex)); //1. Can read source migrating - resp = context.clusterTestUtils.GetKey(sourcePortIndex, key, out _, out _, out _, out responseState, logger: context.logger); + resp = context.clusterTestUtils.GetKey(sourcePortIndex, key, out _, out _, out responseState, logger: context.logger); ClassicAssert.AreEqual(ResponseState.OK, responseState); ClassicAssert.AreEqual(resp, val); //2. Request on source node redirect with asking for new keys to target node - resp = context.clusterTestUtils.GetKey(sourcePortIndex, Encoding.ASCII.GetBytes("{abc}1"), out slot, out address, out port, out responseState, logger: context.logger); + resp = context.clusterTestUtils.GetKey(sourcePortIndex, Encoding.ASCII.GetBytes("{abc}1"), out slot, out endpoint, out responseState, logger: context.logger); ClassicAssert.AreEqual(ResponseState.ASK, responseState); ClassicAssert.AreEqual(resp, "ASK"); ClassicAssert.AreEqual(expectedSlot, slot); - ClassicAssert.AreEqual(address, context.clusterTestUtils.GetEndPoint(targetPortIndex).Address.ToString()); - ClassicAssert.AreEqual(port, context.clusterTestUtils.GetEndPoint(targetPortIndex).Port); + ClassicAssert.AreEqual(endpoint, context.clusterTestUtils.GetEndPoint(targetPortIndex)); //3. request on target node without asking redirect to source node. - resp = context.clusterTestUtils.GetKey(targetPortIndex, Encoding.ASCII.GetBytes("{abc}1"), out slot, out address, out port, out responseState, logger: context.logger); + resp = context.clusterTestUtils.GetKey(targetPortIndex, Encoding.ASCII.GetBytes("{abc}1"), out slot, out endpoint, out responseState, logger: context.logger); ClassicAssert.AreEqual(ResponseState.MOVED, responseState); ClassicAssert.AreEqual(resp, "MOVED"); ClassicAssert.AreEqual(expectedSlot, slot); - ClassicAssert.AreEqual(address, context.clusterTestUtils.GetEndPoint(sourcePortIndex).Address.ToString()); - ClassicAssert.AreEqual(port, context.clusterTestUtils.GetEndPoint(sourcePortIndex).Port); + ClassicAssert.AreEqual(endpoint, context.clusterTestUtils.GetEndPoint(sourcePortIndex)); //4. request write on source node to existing key try-again migrating - respState = context.clusterTestUtils.SetKey(sourcePortIndex, Encoding.ASCII.GetBytes("{abc}0"), Encoding.ASCII.GetBytes("5678"), out _, out _, out _, logger: context.logger); + respState = context.clusterTestUtils.SetKey(sourcePortIndex, Encoding.ASCII.GetBytes("{abc}0"), Encoding.ASCII.GetBytes("5678"), out _, out _, logger: context.logger); ClassicAssert.AreEqual(respState, ResponseState.OK); //5. request write on source node to new key redirect. - respState = context.clusterTestUtils.SetKey(sourcePortIndex, Encoding.ASCII.GetBytes("{abc}1"), Encoding.ASCII.GetBytes("5678"), out slot, out address, out port, logger: context.logger); + respState = context.clusterTestUtils.SetKey(sourcePortIndex, Encoding.ASCII.GetBytes("{abc}1"), Encoding.ASCII.GetBytes("5678"), out slot, out endpoint, logger: context.logger); ClassicAssert.AreEqual(respState, ResponseState.ASK); ClassicAssert.AreEqual(expectedSlot, slot); - ClassicAssert.AreEqual(address, context.clusterTestUtils.GetEndPoint(targetPortIndex).Address.ToString()); - ClassicAssert.AreEqual(port, context.clusterTestUtils.GetEndPoint(targetPortIndex).Port); + ClassicAssert.AreEqual(endpoint, context.clusterTestUtils.GetEndPoint(targetPortIndex)); //6. request on target after asking response empty for new key - resp = context.clusterTestUtils.GetKey(targetPortIndex, Encoding.ASCII.GetBytes("{abc}1"), out _, out _, out _, out responseState, true, logger: context.logger); + resp = context.clusterTestUtils.GetKey(targetPortIndex, Encoding.ASCII.GetBytes("{abc}1"), out _, out _, out responseState, true, logger: context.logger); ClassicAssert.AreEqual(ResponseState.OK, responseState); ClassicAssert.AreEqual(null, resp); @@ -490,16 +486,15 @@ public void ClusterSlotChangeStatus() #region RESET_SLOT_STATE resp = context.clusterTestUtils.SetSlot(targetPortIndex, expectedSlot, "STABLE", "", logger: context.logger); ClassicAssert.AreEqual(resp, "OK"); - resp = context.clusterTestUtils.GetKey(targetPortIndex, Encoding.ASCII.GetBytes("{abc}1"), out slot, out address, out port, out responseState, logger: context.logger); + resp = context.clusterTestUtils.GetKey(targetPortIndex, Encoding.ASCII.GetBytes("{abc}1"), out slot, out endpoint, out responseState, logger: context.logger); ClassicAssert.AreEqual(ResponseState.MOVED, responseState); ClassicAssert.AreEqual(resp, "MOVED"); ClassicAssert.AreEqual(expectedSlot, slot); - ClassicAssert.AreEqual(address, context.clusterTestUtils.GetEndPoint(sourcePortIndex).Address.ToString()); - ClassicAssert.AreEqual(port, context.clusterTestUtils.GetEndPoint(sourcePortIndex).Port); + ClassicAssert.AreEqual(endpoint, context.clusterTestUtils.GetEndPoint(sourcePortIndex)); resp = context.clusterTestUtils.SetSlot(sourcePortIndex, expectedSlot, "STABLE", "", logger: context.logger); ClassicAssert.AreEqual(resp, "OK"); - resp = context.clusterTestUtils.GetKey(sourcePortIndex, Encoding.ASCII.GetBytes("{abc}1"), out _, out _, out _, out responseState, logger: context.logger); + resp = context.clusterTestUtils.GetKey(sourcePortIndex, Encoding.ASCII.GetBytes("{abc}1"), out _, out _, out responseState, logger: context.logger); ClassicAssert.AreEqual(ResponseState.OK, responseState); #endregion @@ -568,7 +563,6 @@ public void ClusterRedirectMessage() public void ClusterSimpleMigrateSlots() { context.logger.LogDebug("0. ClusterSimpleMigrateSlotsTest started"); - var Port = TestUtils.Port; var Shards = defaultShards; context.CreateInstances(Shards, useTLS: UseTLS); context.CreateConnection(useTLS: UseTLS); @@ -596,10 +590,10 @@ public void ClusterSimpleMigrateSlots() // Check data are inserted correctly foreach (var entry in data) { - var value = context.clusterTestUtils.GetKey(context.clusterTestUtils.GetEndPointFromPort(sourcePort), entry.Key, out var _slot, out var _address, out var _port, out var responseState); + var value = context.clusterTestUtils.GetKey(context.clusterTestUtils.GetEndPointFromPort(sourcePort), entry.Key, out var _slot, out var endpoint, out var responseState); ClassicAssert.AreEqual(ResponseState.OK, responseState); ClassicAssert.AreEqual(Encoding.ASCII.GetString(entry.Value), value, $"data not inserted correctly => expected: {Encoding.ASCII.GetString(entry.Value)}, actual: {value}"); - ClassicAssert.AreEqual(sourcePort, _port); + ClassicAssert.AreEqual(sourcePort, endpoint.Port); ClassicAssert.AreEqual((ushort)slot, _slot); } @@ -612,15 +606,15 @@ public void ClusterSimpleMigrateSlots() var keysList = data.Keys.ToList(); for (var i = 0; i < keysList.Count; i++) { - var value = context.clusterTestUtils.GetKey(context.clusterTestUtils.GetEndPointFromPort(targetPort), keysList[i], out var _slot, out var _address, out var _port, out var responseState); + var value = context.clusterTestUtils.GetKey(context.clusterTestUtils.GetEndPointFromPort(targetPort), keysList[i], out var _slot, out var endPoint, out var responseState); while (responseState != ResponseState.OK) { _ = Thread.Yield(); - value = context.clusterTestUtils.GetKey(context.clusterTestUtils.GetEndPointFromPort(targetPort), keysList[i], out _slot, out _address, out _port, out responseState); + value = context.clusterTestUtils.GetKey(context.clusterTestUtils.GetEndPointFromPort(targetPort), keysList[i], out _slot, out endPoint, out responseState); } - ClassicAssert.AreEqual(targetPort, _port, $"[{sourcePort}] => [{targetPort}] == {_port} | expected: {targetPort}, actual: {_port}"); - ClassicAssert.AreEqual(data[keysList[i]], Encoding.ASCII.GetBytes(value), $"[{sourcePort}] => [{targetPort}] == {_port} | expected: {Encoding.ASCII.GetString(data[keysList[i]])}, actual: {value}"); + ClassicAssert.AreEqual(targetPort, endPoint.Port, $"[{sourcePort}] => [{targetPort}] == {endPoint.Port} | expected: {targetPort}, actual: {endPoint.Port}"); + ClassicAssert.AreEqual(data[keysList[i]], Encoding.ASCII.GetBytes(value), $"[{sourcePort}] => [{targetPort}] == {endPoint.Port} | expected: {Encoding.ASCII.GetString(data[keysList[i]])}, actual: {value}"); } context.logger.LogDebug("5. Checking keys done"); @@ -842,7 +836,6 @@ private string DoZCOUNT(int nodeIndex, byte[] key, out int count, out string add public void ClusterSimpleMigrateSlotsWithObjects() { context.logger.LogDebug("0. ClusterSimpleMigrateSlotsWithObjectsTest started"); - var Port = TestUtils.Port; var Shards = defaultShards; context.CreateInstances(defaultShards, useTLS: UseTLS); context.CreateConnection(useTLS: UseTLS); @@ -923,10 +916,9 @@ public void ClusterSimpleMigrateKeys() keys.Add(newKey); ClassicAssert.AreEqual(_workingSlot, ClusterTestUtils.HashSlot(newKey)); - var resp = context.clusterTestUtils.SetKey(sourceNodeIndex, newKey, newKey, out _, out var address, out var port, logger: context.logger); + var resp = context.clusterTestUtils.SetKey(sourceNodeIndex, newKey, newKey, out _, out var endpoint, logger: context.logger); ClassicAssert.AreEqual(resp, ResponseState.OK); - ClassicAssert.AreEqual(address, context.clusterTestUtils.GetEndPoint(sourceNodeIndex).Address.ToString()); - ClassicAssert.AreEqual(port, context.clusterTestUtils.GetEndPoint(sourceNodeIndex).Port); + ClassicAssert.AreEqual(endpoint, context.clusterTestUtils.GetEndPoint(sourceNodeIndex)); } context.logger.LogDebug("2. Test keys loaded"); @@ -981,17 +973,16 @@ public void ClusterSimpleMigrateKeys() context.logger.LogDebug("13. Checking migrate keys starting"); foreach (var _key in keys) { - var resp = context.clusterTestUtils.GetKey(otherNodeIndex, _key, out var slot, out var address, out var port, out var responseState, logger: context.logger); - while (port != context.clusterTestUtils.GetEndPoint(targetNodeIndex).Port && responseState != ResponseState.OK) + var resp = context.clusterTestUtils.GetKey(otherNodeIndex, _key, out var slot, out var endpoint, out var responseState, logger: context.logger); + while (endpoint.Port != context.clusterTestUtils.GetEndPoint(targetNodeIndex).Port && responseState != ResponseState.OK) { - resp = context.clusterTestUtils.GetKey(otherNodeIndex, _key, out slot, out address, out port, out responseState, logger: context.logger); + resp = context.clusterTestUtils.GetKey(otherNodeIndex, _key, out slot, out endpoint, out responseState, logger: context.logger); } ClassicAssert.AreEqual(resp, "MOVED"); ClassicAssert.AreEqual(_workingSlot, slot); - ClassicAssert.AreEqual(context.clusterTestUtils.GetEndPoint(targetNodeIndex).Address.ToString(), address); - ClassicAssert.AreEqual(context.clusterTestUtils.GetEndPoint(targetNodeIndex).Port, port); + ClassicAssert.AreEqual(context.clusterTestUtils.GetEndPoint(targetNodeIndex), endpoint); - resp = context.clusterTestUtils.GetKey(targetNodeIndex, _key, out _, out _, out _, out responseState, logger: context.logger); + resp = context.clusterTestUtils.GetKey(targetNodeIndex, _key, out _, out _, out responseState, logger: context.logger); ClassicAssert.AreEqual(responseState, ResponseState.OK); ClassicAssert.AreEqual(resp, _key); } @@ -1006,7 +997,6 @@ public void ClusterSimpleMigrateKeys() public void ClusterSimpleMigrateKeysWithObjects() { context.logger.LogDebug("0. ClusterSimpleMigrateKeysWithObjectsTest started"); - var Port = TestUtils.Port; var Shards = defaultShards; context.CreateInstances(Shards, useTLS: UseTLS); context.CreateConnection(useTLS: UseTLS); @@ -1131,9 +1121,12 @@ private void MigrateSlotsTask(int sourceNodePort, int targetNodePort, List private void OperateOnSlotsTask(Dictionary> data, int targetNodeIndex) { - var Port = TestUtils.Port; - var Shards = defaultShards; - var Ports = Enumerable.Range(Port, Shards).ToList(); + if (TestUtils.EndPoint is not IPEndPoint endpoint) + throw new NotSupportedException(); + + var port = endpoint.Port; + var shards = defaultShards; + var Ports = Enumerable.Range(port, shards).ToList(); var connections = ClusterTestUtils.CreateLightRequestConnections([.. Ports]); operatedOnData = []; @@ -1149,7 +1142,7 @@ private void OperateOnSlotsTask(Dictionary> data { if (iterCount++ > maxIter) break; var entryIndex = context.r.Next(0, operatedOnData.Count); - var nodeIndex = context.r.Next(0, Shards); + var nodeIndex = context.r.Next(0, shards); var get = context.r.Next(0, 1) == 0; var oldEntry = operatedOnData[entryIndex]; var key = oldEntry.Item2; @@ -1159,18 +1152,18 @@ private void OperateOnSlotsTask(Dictionary> data if (get) { - var getValue = context.clusterTestUtils.GetKey(nodeIndex, key, out var slot, out var redirectAddressA, out var redirectPortA, out var status, logger: context.logger); + var getValue = context.clusterTestUtils.GetKey(nodeIndex, key, out var slot, out var redirectEndpointA, out var status, logger: context.logger); switch (status) { case ResponseState.OK: ClassicAssert.AreEqual(Encoding.ASCII.GetString(value), getValue, $"{Encoding.ASCII.GetString(value)} => {getValue}"); break; case ResponseState.MOVED: // Everyone redirect to node that is current owner - var srcNodeIndex = context.clusterTestUtils.GetEndPointIndexFromPort(redirectPortA); + var srcNodeIndex = context.clusterTestUtils.GetEndPointIndexFromPort(redirectEndpointA.Port); ClassicAssert.AreNotEqual(srcNodeIndex, -1); - getValue = context.clusterTestUtils.GetKey(srcNodeIndex, key, out _, out var redirectAddressB, out var redirectPortB, out status, logger: context.logger); + getValue = context.clusterTestUtils.GetKey(srcNodeIndex, key, out _, out var redirectEndpointB, out status, logger: context.logger); if (status == ResponseState.OK) - ClassicAssert.AreEqual(value, getValue, $"{redirectPortA} => {redirectPortB}"); + ClassicAssert.AreEqual(value, getValue, $"{redirectEndpointA.Port} => {redirectEndpointB.Port}"); else if (status == ResponseState.MOVED)// can redirect again if source has made target the owner { //ClassicAssert.AreEqual(connections[targetNodeIndex].Port, redirectPortB, @@ -1179,8 +1172,7 @@ private void OperateOnSlotsTask(Dictionary> data } break; case ResponseState.ASK: - ClassicAssert.AreEqual(connections[targetNodeIndex].Port, redirectPortA); - ClassicAssert.AreEqual(connections[targetNodeIndex].Address, redirectAddressA); + ClassicAssert.AreEqual(connections[targetNodeIndex].EndPoint, redirectEndpointA); break; case ResponseState.CLUSTERDOWN: goto retryRequest; @@ -1193,16 +1185,16 @@ private void OperateOnSlotsTask(Dictionary> data { var newValue = new byte[value.Length]; RandomBytes(ref newValue); - var status = context.clusterTestUtils.SetKey(nodeIndex, key, newValue, out _, out var address, out var port, logger: context.logger); + var status = context.clusterTestUtils.SetKey(nodeIndex, key, newValue, out _, out endpoint, logger: context.logger); switch (status) { case ResponseState.OK: operatedOnData[entryIndex] = new(oldEntry.Item1, oldEntry.Item2, newValue); break; case ResponseState.MOVED: //everyone redirect to node that is current owner - var srcNodeIndex = context.clusterTestUtils.GetEndPointIndexFromPort(port); + var srcNodeIndex = context.clusterTestUtils.GetEndPointIndexFromPort(endpoint.Port); ClassicAssert.AreNotEqual(srcNodeIndex, -1); - status = context.clusterTestUtils.SetKey(srcNodeIndex, key, newValue, out _, out address, out port, logger: context.logger); + status = context.clusterTestUtils.SetKey(srcNodeIndex, key, newValue, out _, out endpoint, logger: context.logger); if (status == ResponseState.OK) operatedOnData[entryIndex] = new(oldEntry.Item1, oldEntry.Item2, newValue); else if (status == ResponseState.MOVED) @@ -1216,8 +1208,7 @@ private void OperateOnSlotsTask(Dictionary> data break; case ResponseState.ASK: - ClassicAssert.AreEqual(connections[targetNodeIndex].Port, port); - ClassicAssert.AreEqual(connections[targetNodeIndex].Address, address); + ClassicAssert.AreEqual(connections[targetNodeIndex], endpoint); break; case ResponseState.CLUSTERDOWN: goto retryRequest; @@ -1272,16 +1263,15 @@ public void ClusterSimpleMigrateWithReadWrite() var key = entry.Item2; var val = entry.Item3; - var resp = context.clusterTestUtils.GetKey(targetNodeIndex, key, out var slot, out var address, out var port, out var responseState, logger: context.logger); + var resp = context.clusterTestUtils.GetKey(targetNodeIndex, key, out var slot, out var endpoint, out var responseState, logger: context.logger); while (responseState != ResponseState.OK) { _ = Thread.Yield(); - resp = context.clusterTestUtils.GetKey(targetNodeIndex, key, out slot, out address, out port, out responseState, logger: context.logger); + resp = context.clusterTestUtils.GetKey(targetNodeIndex, key, out slot, out endpoint, out responseState, logger: context.logger); } ClassicAssert.AreEqual(ResponseState.OK, responseState); ClassicAssert.AreEqual(val, resp, $"{val} != {resp}"); - ClassicAssert.AreEqual(context.clusterTestUtils.GetEndPoint(targetNodeIndex).Port, port); - ClassicAssert.AreEqual(context.clusterTestUtils.GetEndPoint(targetNodeIndex).Address.ToString(), address); + ClassicAssert.AreEqual(context.clusterTestUtils.GetEndPoint(targetNodeIndex), endpoint); } context.logger.LogDebug("5. Checking keys after migration done"); @@ -1460,7 +1450,7 @@ public void ClusterAllowWritesDuringMigrateTest() ClassicAssert.AreEqual(srcNode.NodeId, _srcNode.NodeId); // Create key before migration - var status = context.clusterTestUtils.SetKey(srcNode.EndPoint.ToIPEndPoint(), keyExists, oldValue, out _, out _, out _, logger: context.logger); + var status = context.clusterTestUtils.SetKey(srcNode.EndPoint.ToIPEndPoint(), keyExists, oldValue, out _, out _, logger: context.logger); ClassicAssert.AreEqual(ResponseState.OK, status); // Get slot mapping @@ -1514,34 +1504,32 @@ public void ClusterAllowWritesDuringMigrateTest() void OperateOnNonExistentKey(EndPoint endPoint, byte[] key, byte[] value) { // Perform write => expected response ASK - status = context.clusterTestUtils.SetKey(endPoint.ToIPEndPoint(), key, value, out int _slot, out string address, out int port, logger: context.logger); + status = context.clusterTestUtils.SetKey(endPoint.ToIPEndPoint(), key, value, out var _slot, out var actualEndpoint, logger: context.logger); ClassicAssert.AreEqual(ResponseState.ASK, status); ClassicAssert.AreEqual(slot, _slot); - ClassicAssert.AreEqual(tgtNode.EndPoint.ToIPEndPoint().Address.ToString(), address); - ClassicAssert.AreEqual(tgtNode.EndPoint.ToIPEndPoint().Port, port); + ClassicAssert.AreEqual(tgtNode.EndPoint.ToIPEndPoint(), actualEndpoint); // Perform read => expected response ASK - _ = context.clusterTestUtils.GetKey(endPoint.ToIPEndPoint(), key, out _slot, out address, out port, out status, logger: context.logger); + _ = context.clusterTestUtils.GetKey(endPoint.ToIPEndPoint(), key, out _slot, out actualEndpoint, out status, logger: context.logger); ClassicAssert.AreEqual(ResponseState.ASK, status); ClassicAssert.AreEqual(slot, _slot); - ClassicAssert.AreEqual(tgtNode.EndPoint.ToIPEndPoint().Address.ToString(), address); - ClassicAssert.AreEqual(tgtNode.EndPoint.ToIPEndPoint().Port, port); + ClassicAssert.AreEqual(tgtNode.EndPoint.ToIPEndPoint(), actualEndpoint); } // Operate on existing key when slot is in MIGRATING state void OperateOnExistingKey(EndPoint endPoint, byte[] key, byte[] oldValue, byte[] newValue) { // Perform read => expected response OK - var _value = context.clusterTestUtils.GetKey(endPoint.ToIPEndPoint(), keyExists, out _, out _, out _, out status, logger: context.logger); + var _value = context.clusterTestUtils.GetKey(endPoint.ToIPEndPoint(), keyExists, out _, out _, out status, logger: context.logger); ClassicAssert.AreEqual(ResponseState.OK, status); ClassicAssert.AreEqual(oldValue, _value); // Perform write => expected response OK - status = context.clusterTestUtils.SetKey(endPoint.ToIPEndPoint(), key, newValue, out _, out _, out _, logger: context.logger); + status = context.clusterTestUtils.SetKey(endPoint.ToIPEndPoint(), key, newValue, out _, out _, logger: context.logger); ClassicAssert.AreEqual(ResponseState.OK, status); // Perform read => expected response OK - _value = context.clusterTestUtils.GetKey(endPoint.ToIPEndPoint(), keyExists, out _, out _, out _, out status, logger: context.logger); + _value = context.clusterTestUtils.GetKey(endPoint.ToIPEndPoint(), keyExists, out _, out _, out status, logger: context.logger); ClassicAssert.AreEqual(ResponseState.OK, status); ClassicAssert.AreEqual(newValue, _value); } @@ -1553,7 +1541,7 @@ Task WriteWorkload(IPEndPoint endPoint, byte[] key, int keyLen = 16) while (true) { context.clusterTestUtils.RandomBytes(ref value); - var status = context.clusterTestUtils.SetKey(endPoint, key, value, out int _slot, out string address, out int port, logger: context.logger); + var status = context.clusterTestUtils.SetKey(endPoint, key, value, out _, out _, logger: context.logger); if (status == ResponseState.OK) { @@ -1635,10 +1623,9 @@ public void ClusterMigrateDataSlotsRange() var key = new byte[keySize]; context.clusterTestUtils.RandomBytes(ref key); - var resp = context.clusterTestUtils.SetKey(srcNodeIndex, key, key, out _, out var address, out var port, logger: context.logger); + var resp = context.clusterTestUtils.SetKey(srcNodeIndex, key, key, out _, out var endpoint, logger: context.logger); ClassicAssert.AreEqual(resp, ResponseState.OK); - ClassicAssert.AreEqual(address, context.clusterTestUtils.GetEndPoint(srcNodeIndex).Address.ToString()); - ClassicAssert.AreEqual(port, context.clusterTestUtils.GetEndPoint(srcNodeIndex).Port); + ClassicAssert.AreEqual(endpoint, context.clusterTestUtils.GetEndPoint(srcNodeIndex)); keys.Add(key); } @@ -1664,7 +1651,7 @@ public void ClusterMigrateDataSlotsRange() foreach (var key in keys) { - var resp = context.clusterTestUtils.GetKey(dstNodeIndex, key, out _, out _, out _, out var responseState, logger: context.logger); + var resp = context.clusterTestUtils.GetKey(dstNodeIndex, key, out _, out _, out var responseState, logger: context.logger); ClassicAssert.AreEqual(ResponseState.OK, responseState); ClassicAssert.AreEqual(Encoding.ASCII.GetString(key), resp); } diff --git a/test/Garnet.test.cluster/ClusterRedirectTests.cs b/test/Garnet.test.cluster/ClusterRedirectTests.cs index cdf6607b01..11ce401f05 100644 --- a/test/Garnet.test.cluster/ClusterRedirectTests.cs +++ b/test/Garnet.test.cluster/ClusterRedirectTests.cs @@ -486,7 +486,6 @@ private static (ResponseState, string, string[]) SendToNodeFromSlot(ref LightCli result, out _, out _, - out _, out var value, out var values); if (checkAssert) @@ -501,14 +500,13 @@ private static (ResponseState, string, string[]) SendToNodeFromSlot(ref LightCli while (otherNodeIndex == nodeIndex) otherNodeIndex = context.r.Next(0, connections.Length); var result = connections[otherNodeIndex].SendCommand(cmd); - var status = ClusterTestUtils.ParseResponseState(result, out var _slot, out var _address, out var _port, out var _value, out var _values); + var status = ClusterTestUtils.ParseResponseState(result, out var _slot, out var endpoint, out var _value, out var _values); ClassicAssert.AreEqual(status, ResponseState.MOVED, cmd); ClassicAssert.AreEqual(_slot, slot, cmd); - ClassicAssert.AreEqual(_address, connections[nodeIndex].Address, cmd); - ClassicAssert.AreEqual(_port, connections[nodeIndex].Port, cmd); + ClassicAssert.AreEqual(endpoint, connections[nodeIndex].EndPoint, cmd); result = connections[nodeIndex].SendCommand(cmd); - status = ClusterTestUtils.ParseResponseState(result, out _, out _, out _, out _value, out _values); + status = ClusterTestUtils.ParseResponseState(result, out _, out _, out _value, out _values); ClassicAssert.AreEqual(status, ResponseState.OK, cmdTag); return (status, _value, _values); @@ -530,7 +528,7 @@ private void SendToImportingNode( for (var j = 0; j < setupCmd.Length; j++) { var resp = connections[targetNodeIndex].SendCommand(setupCmd[j]); - var respStatus = ClusterTestUtils.ParseResponseState(resp, out _, out _, out _, out _, out _); + var respStatus = ClusterTestUtils.ParseResponseState(resp, out _, out _, out _, out _); ClassicAssert.AreEqual(respStatus, ResponseState.OK); ClusterTestUtils.Asking(ref connections[targetNodeIndex]); } @@ -538,7 +536,7 @@ private void SendToImportingNode( } var result = connections[targetNodeIndex].SendCommand(testCmd); - var status = ClusterTestUtils.ParseResponseState(result, out var _slot, out var _address, out var _port, out var _value, out var _values); + var status = ClusterTestUtils.ParseResponseState(result, out var _slot, out var endpoint, out var _value, out var _values); if (CheckFlag(command.testFlags, TestFlags.ASKING)) { @@ -558,7 +556,7 @@ private void SendToImportingNode( { ClusterTestUtils.Asking(ref connections[targetNodeIndex]); var resp = connections[targetNodeIndex].SendCommand(cleanCmd[j]); - var respStatus = ClusterTestUtils.ParseResponseState(resp, out _, out _, out _, out _, out _); + var respStatus = ClusterTestUtils.ParseResponseState(resp, out _, out _, out _, out _); ClassicAssert.AreEqual(respStatus, ResponseState.OK); } } @@ -566,8 +564,7 @@ private void SendToImportingNode( else { ClassicAssert.AreEqual(status, ResponseState.MOVED); - ClassicAssert.AreEqual(_address, connections[sourceNodeIndex].Address); - ClassicAssert.AreEqual(_port, connections[sourceNodeIndex].Port); + ClassicAssert.AreEqual(endpoint, connections[sourceNodeIndex].EndPoint); ClassicAssert.AreEqual(_slot, slots[0]); } } @@ -592,7 +589,7 @@ private void SendToMigratingNode( for (var j = 0; j < setupCmd.Length; j++) { var resp = connections[sourceNodeIndex].SendCommand(setupCmd[j]); - var respStatus = ClusterTestUtils.ParseResponseState(resp, out _, out _, out _, out _, out _); + var respStatus = ClusterTestUtils.ParseResponseState(resp, out _, out _, out _, out _); ClassicAssert.AreEqual(respStatus, ResponseState.OK); } } @@ -602,7 +599,7 @@ private void SendToMigratingNode( ClassicAssert.AreEqual(respMigrating, "OK"); result = connections[sourceNodeIndex].SendCommand(testCmd); - status = ClusterTestUtils.ParseResponseState(result, out _, out var _address, out var _port, out _, out _); + status = ClusterTestUtils.ParseResponseState(result, out _, out var endpoint, out _, out _); var respMigratingStable = ClusterTestUtils.SetSlot(ref connections[sourceNodeIndex], migrateSlot, "STABLE", ""); ClassicAssert.AreEqual(respMigratingStable, "OK"); @@ -618,8 +615,7 @@ private void SendToMigratingNode( else { ClassicAssert.AreEqual(status, ResponseState.ASK, command.cmdTag); - ClassicAssert.AreEqual(_port, connections[targetNodeIndex].Port, command.cmdTag); - ClassicAssert.AreEqual(_address, connections[targetNodeIndex].Address, command.cmdTag); + ClassicAssert.AreEqual(endpoint, connections[targetNodeIndex].EndPoint, command.cmdTag); } if (CheckFlag(command.testFlags, TestFlags.KEY_EXISTS)) @@ -629,7 +625,7 @@ private void SendToMigratingNode( for (var j = 0; j < cleanCmd.Length; j++) { var resp = connections[sourceNodeIndex].SendCommand(cleanCmd[j]); - var respStatus = ClusterTestUtils.ParseResponseState(resp, out _, out _, out _, out _, out _); + var respStatus = ClusterTestUtils.ParseResponseState(resp, out _, out _, out _, out _); ClassicAssert.AreEqual(respStatus, ResponseState.OK); } } diff --git a/test/Garnet.test.cluster/ClusterTestContext.cs b/test/Garnet.test.cluster/ClusterTestContext.cs index 8bc92d8668..011b0a25ed 100644 --- a/test/Garnet.test.cluster/ClusterTestContext.cs +++ b/test/Garnet.test.cluster/ClusterTestContext.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Net; using System.Security.Cryptography.X509Certificates; using System.Text; using System.Threading; @@ -84,17 +85,17 @@ public void RegisterCustomTxn(string name, Func proc /// /// /// - /// - /// - /// + /// + /// + /// /// - /// - /// - /// - /// - /// - /// - /// + /// + /// + /// + /// + /// + /// + /// /// /// /// @@ -106,17 +107,17 @@ public void CreateInstances( bool tryRecover = false, bool disableObjects = false, bool lowMemory = false, - string MemorySize = default, - string PageSize = default, - string SegmentSize = "1g", + string memorySize = default, + string pageSize = default, + string segmentSize = "1g", bool enableAOF = false, - bool MainMemoryReplication = false, - bool OnDemandCheckpoint = false, - string AofMemorySize = "64m", - int CommitFrequencyMs = 0, - bool DisableStorageTier = false, - bool EnableIncrementalSnapshots = false, - bool FastCommit = true, + bool mainMemoryReplication = false, + bool onDemandCheckpoint = false, + string aofMemorySize = "64m", + int commitFrequencyMs = 0, + bool disableStorageTier = false, + bool enableIncrementalSnapshots = false, + bool fastCommit = true, int timeout = -1, bool useTLS = false, bool useAcl = false, @@ -130,7 +131,9 @@ public void CreateInstances( LuaMemoryManagementMode luaMemoryMode = LuaMemoryManagementMode.Native, string luaMemoryLimit = "") { - endpoints = TestUtils.GetEndPoints(shards, 7000); + TestUtils.EndPoint = new IPEndPoint(IPAddress.Loopback, 7000); + endpoints = TestUtils.GetShardEndPoints(shards, IPAddress.Loopback, 7000); + nodes = TestUtils.CreateGarnetCluster( TestFolder, disablePubSub: disablePubSub, @@ -143,16 +146,16 @@ public void CreateInstances( UseTLS: useTLS, cleanClusterConfig: cleanClusterConfig, lowMemory: lowMemory, - MemorySize: MemorySize, - PageSize: PageSize, - SegmentSize: SegmentSize, - MainMemoryReplication: MainMemoryReplication, - AofMemorySize: AofMemorySize, - CommitFrequencyMs: CommitFrequencyMs, - DisableStorageTier: DisableStorageTier, - OnDemandCheckpoint: OnDemandCheckpoint, - EnableIncrementalSnapshots: EnableIncrementalSnapshots, - FastCommit: FastCommit, + MemorySize: memorySize, + PageSize: pageSize, + SegmentSize: segmentSize, + MainMemoryReplication: mainMemoryReplication, + AofMemorySize: aofMemorySize, + CommitFrequencyMs: commitFrequencyMs, + DisableStorageTier: disableStorageTier, + OnDemandCheckpoint: onDemandCheckpoint, + EnableIncrementalSnapshots: enableIncrementalSnapshots, + FastCommit: fastCommit, useAcl: useAcl, aclFile: credManager.aclFilePath, authUsername: clusterCreds.user, @@ -172,7 +175,7 @@ public void CreateInstances( /// /// Create single cluster instance with corresponding options /// - /// + /// /// /// /// @@ -196,7 +199,7 @@ public void CreateInstances( /// /// public GarnetServer CreateInstance( - int Port, + EndPoint endpoint, bool cleanClusterConfig = true, bool disableEpochCollision = false, bool tryRecover = false, @@ -225,26 +228,26 @@ public GarnetServer CreateInstance( var opts = TestUtils.GetGarnetServerOptions( TestFolder, TestFolder, - Port, + endpoint, disablePubSub: true, disableObjects: disableObjects, enableAOF: enableAOF, timeout: timeout, gossipDelay: gossipDelay, tryRecover: tryRecover, - UseTLS: useTLS, + useTLS: useTLS, cleanClusterConfig: cleanClusterConfig, lowMemory: lowMemory, - MemorySize: MemorySize, - PageSize: PageSize, - SegmentSize: SegmentSize, - MainMemoryReplication: MainMemoryReplication, - AofMemorySize: AofMemorySize, - CommitFrequencyMs: CommitFrequencyMs, - DisableStorageTier: DisableStorageTier, - OnDemandCheckpoint: OnDemandCheckpoint, - EnableIncrementalSnapshots: EnableIncrementalSnapshots, - FastCommit: FastCommit, + memorySize: MemorySize, + pageSize: PageSize, + segmentSize: SegmentSize, + mainMemoryReplication: MainMemoryReplication, + aofMemorySize: AofMemorySize, + commitFrequencyMs: CommitFrequencyMs, + disableStorageTier: DisableStorageTier, + onDemandCheckpoint: OnDemandCheckpoint, + enableIncrementalSnapshots: EnableIncrementalSnapshots, + fastCommit: FastCommit, useAcl: useAcl, asyncReplay: asyncReplay, aclFile: credManager.aclFilePath, @@ -337,10 +340,10 @@ public void PopulatePrimary( primaryIndex = slotMap[slot]; } - var resp = clusterTestUtils.SetKey(primaryIndex, keyBytes, Encoding.ASCII.GetBytes(value.ToString()), out int _, out string _, out int _, logger: logger); + var resp = clusterTestUtils.SetKey(primaryIndex, keyBytes, Encoding.ASCII.GetBytes(value.ToString()), out int _, out _, logger: logger); ClassicAssert.AreEqual(ResponseState.OK, resp); - var retVal = clusterTestUtils.GetKey(primaryIndex, keyBytes, out int _, out string _, out int _, out ResponseState responseState, logger: logger); + var retVal = clusterTestUtils.GetKey(primaryIndex, keyBytes, out int _, out _, out ResponseState responseState, logger: logger); ClassicAssert.AreEqual(ResponseState.OK, responseState); ClassicAssert.AreEqual(value, int.Parse(retVal)); @@ -447,10 +450,10 @@ public void ValidateKVCollectionAgainstReplica( replicaIndex = slotMap[slot]; } - var retVal = clusterTestUtils.GetKey(replicaIndex, keyBytes, out var _, out var _, out var _, out var responseState, logger: logger); + var retVal = clusterTestUtils.GetKey(replicaIndex, keyBytes, out _, out _, out var responseState, logger: logger); while (responseState != ResponseState.OK || retVal == null || (value != int.Parse(retVal))) { - retVal = clusterTestUtils.GetKey(replicaIndex, keyBytes, out var _, out var _, out var _, out responseState, logger: logger); + retVal = clusterTestUtils.GetKey(replicaIndex, keyBytes, out _, out _, out responseState, logger: logger); ClusterTestUtils.BackOff(cancellationToken: cts.Token); } ClassicAssert.AreEqual(ResponseState.OK, responseState); @@ -491,15 +494,15 @@ public void SendAndValidateKeys(int primaryIndex, int replicaIndex, int keyLengt var key = orderedKeys ? (keyOffset++).ToString() : clusterTestUtils.RandomStr(keyLength); var keyBytes = Encoding.ASCII.GetBytes(key); var value = r.Next(); - var resp = clusterTestUtils.SetKey(primaryIndex, keyBytes, Encoding.ASCII.GetBytes(value.ToString()), out int _, out string _, out int _, logger: logger); + var resp = clusterTestUtils.SetKey(primaryIndex, keyBytes, Encoding.ASCII.GetBytes(value.ToString()), out int _, out _, logger: logger); ClassicAssert.AreEqual(ResponseState.OK, resp); clusterTestUtils.WaitForReplicaAofSync(primaryIndex, replicaIndex); - var retVal = clusterTestUtils.GetKey(replicaIndex, keyBytes, out int _, out string _, out int _, out ResponseState responseState, logger: logger); + var retVal = clusterTestUtils.GetKey(replicaIndex, keyBytes, out _, out _, out ResponseState responseState, logger: logger); while (responseState != ResponseState.OK || retVal == null || (value != int.Parse(retVal))) { - retVal = clusterTestUtils.GetKey(replicaIndex, keyBytes, out int _, out string _, out int _, out responseState, logger: logger); + retVal = clusterTestUtils.GetKey(replicaIndex, keyBytes, out _, out _, out responseState, logger: logger); ClusterTestUtils.BackOff(cancellationToken: cts.Token); } ClassicAssert.AreEqual(ResponseState.OK, responseState); diff --git a/test/Garnet.test.cluster/ClusterTestUtils.cs b/test/Garnet.test.cluster/ClusterTestUtils.cs index 367cc52f67..bad107c5e4 100644 --- a/test/Garnet.test.cluster/ClusterTestUtils.cs +++ b/test/Garnet.test.cluster/ClusterTestUtils.cs @@ -679,8 +679,7 @@ public GarnetClientSession GetGarnetClientSession(int nodeIndex) if (gcsConnections[nodeIndex] == null) { - var endpoint = GetEndPoint(nodeIndex).ToIPEndPoint(); - gcsConnections[nodeIndex] = new GarnetClientSession(endpoint.Address.ToString(), endpoint.Port, new()); + gcsConnections[nodeIndex] = new GarnetClientSession(GetEndPoint(nodeIndex), new()); gcsConnections[nodeIndex].Connect(); } return gcsConnections[nodeIndex]; @@ -866,16 +865,14 @@ public static string ParseRespToString(byte[] data, out string[] resultArray) public static ResponseState ParseResponseState( byte[] result, out int slot, - out string address, - out int port, + out IPEndPoint endpoint, out string returnValue, out string[] returnValueArray) { returnValue = null; returnValueArray = null; slot = default; - address = default; - port = default; + endpoint = null; if (result[0] == (byte)'+' || result[0] == (byte)':' || result[0] == '*' || result[0] == '$') { @@ -884,12 +881,12 @@ public static ResponseState ParseResponseState( } else if (result.AsSpan().StartsWith(MOVED)) { - GetEndPointFromResponse(result, out slot, out address, out port); + GetEndPointFromResponse(result, out slot, out endpoint); return ResponseState.MOVED; } else if (result.AsSpan().StartsWith(ASK)) { - GetEndPointFromResponse(result, out slot, out address, out port); + GetEndPointFromResponse(result, out slot, out endpoint); return ResponseState.ASK; } else if (result.AsSpan().StartsWith(MIGRATING)) @@ -914,20 +911,17 @@ public static ResponseState ParseResponseState( byte[] key, byte[] result, out int slot, - out string address, - out int port, + out IPEndPoint endpoint, out byte[] value, out string[] values) { value = null; values = null; slot = -1; - address = null; - port = -1; + endpoint = null; if (result[0] == (byte)'+' || result[0] == (byte)':' || result[0] == '*' || result[0] == '$') { - port = node.Port; - address = node.Address; + endpoint = node.EndPoint as IPEndPoint; slot = HashSlot(key); var strValue = ParseRespToString(result, out values); if (strValue != null) @@ -936,12 +930,12 @@ public static ResponseState ParseResponseState( } else if (result.AsSpan()[..MOVED.Length].SequenceEqual(MOVED)) { - GetEndPointFromResponse(result, out slot, out address, out port); + GetEndPointFromResponse(result, out slot, out endpoint); return ResponseState.MOVED; } else if (result.AsSpan()[..ASK.Length].SequenceEqual(ASK)) { - GetEndPointFromResponse(result, out slot, out address, out port); + GetEndPointFromResponse(result, out slot, out endpoint); return ResponseState.ASK; } else if (result.AsSpan()[..MIGRATING.Length].SequenceEqual(MIGRATING)) @@ -966,7 +960,7 @@ public static LightClientRequest[] CreateLightRequestConnections(int[] Ports) LightClientRequest[] lightClientRequests = new LightClientRequest[Ports.Length]; for (int i = 0; i < Ports.Length; i++) { - lightClientRequests[i] = new LightClientRequest("127.0.0.1", Ports[i], 0, LightReceive); + lightClientRequests[i] = new LightClientRequest(new IPEndPoint(IPAddress.Loopback, Ports[i]), 0, LightReceive); } return lightClientRequests; } @@ -1553,13 +1547,16 @@ public int GetSourceNodeIndexFromSlot(ushort slot, ILogger logger) return -1; } - public static void GetEndPointFromResponse(byte[] resp, out int slot, out string address, out int port) + public static void GetEndPointFromResponse(byte[] resp, out int slot, out IPEndPoint endpoint) { var strResp = Encoding.ASCII.GetString(resp); var data = strResp.Split(' '); slot = int.Parse(data[1]); - address = data[2].Split(':')[0]; - port = int.Parse(data[2].Split(':')[1].Split('\r')[0]); + + var endpointSplit = data[2].Split(':'); + endpoint = new IPEndPoint( + IPAddress.Parse(endpointSplit[0]), + int.Parse(endpointSplit[1].Split('\r')[0])); } public string AddDelSlots(int nodeIndex, List slots, bool addslot, ILogger logger = null) @@ -2127,18 +2124,17 @@ public List ClusterShards(IPEndPoint endPoint, ILogger logger = null) } } - public ResponseState SetKey(int nodeIndex, byte[] key, byte[] value, out int slot, out string address, out int port, bool asking = false, int expiry = -1, ILogger logger = null) + public ResponseState SetKey(int nodeIndex, byte[] key, byte[] value, out int slot, out IPEndPoint actualEndpoint, bool asking = false, int expiry = -1, ILogger logger = null) { var endPoint = GetEndPoint(nodeIndex); - return SetKey(endPoint, key, value, out slot, out address, out port, asking, expiry, logger); + return SetKey(endPoint, key, value, out slot, out actualEndpoint, asking, expiry, logger); } - public ResponseState SetKey(IPEndPoint endPoint, byte[] key, byte[] value, out int slot, out string address, out int port, bool asking = false, int expiry = -1, ILogger logger = null) + public ResponseState SetKey(IPEndPoint endPoint, byte[] key, byte[] value, out int slot, out IPEndPoint actualEndpoint, bool asking = false, int expiry = -1, ILogger logger = null) { var server = GetServer(endPoint); slot = -1; - address = endPoint.Address.ToString(); - port = endPoint.Port; + actualEndpoint = endPoint; if (asking) { @@ -2158,14 +2154,14 @@ public ResponseState SetKey(IPEndPoint endPoint, byte[] key, byte[] value, out i { if (expiry == -1) { - ICollection args = new List() { (object)key, (object)value }; + ICollection args = [key, value]; var resp = (string)server.Execute("set", args, CommandFlags.NoRedirect); ClassicAssert.AreEqual("OK", resp); return ResponseState.OK; } else { - ICollection args = new List() { (object)key, (object)expiry, (object)value }; + ICollection args = [key, expiry, value]; var resp = (string)server.Execute("setex", args, CommandFlags.NoRedirect); ClassicAssert.AreEqual("OK", resp); return ResponseState.OK; @@ -2176,15 +2172,15 @@ public ResponseState SetKey(IPEndPoint endPoint, byte[] key, byte[] value, out i var tokens = e.Message.Split(' '); if (tokens.Length > 10 && tokens[2].Equals("MOVED")) { - address = tokens[5].Split(':')[0]; - port = int.Parse(tokens[5].Split(':')[1]); + var endpointSplit = tokens[5].Split(':'); + actualEndpoint = new IPEndPoint(IPAddress.Parse(endpointSplit[0]), int.Parse(endpointSplit[1])); slot = int.Parse(tokens[8]); return ResponseState.MOVED; } else if (tokens.Length > 10 && tokens[0].Equals("Endpoint")) { - address = tokens[1].Split(':')[0]; - port = int.Parse(tokens[1].Split(':')[1]); + var endpointSplit = tokens[1].Split(':'); + actualEndpoint = new IPEndPoint(IPAddress.Parse(endpointSplit[0]), int.Parse(endpointSplit[1])); slot = int.Parse(tokens[4]); return ResponseState.ASK; } @@ -2206,17 +2202,16 @@ public ResponseState SetKey(IPEndPoint endPoint, byte[] key, byte[] value, out i } } - public string GetKey(int nodeIndex, byte[] key, out int slot, out string address, out int port, out ResponseState responseState, bool asking = false, ILogger logger = null) + public string GetKey(int nodeIndex, byte[] key, out int slot, out IPEndPoint actualEndpoint, out ResponseState responseState, bool asking = false, ILogger logger = null) { var endPoint = GetEndPoint(nodeIndex); - return GetKey(endPoint, key, out slot, out address, out port, out responseState, asking, logger); + return GetKey(endPoint, key, out slot, out actualEndpoint, out responseState, asking, logger); } - public string GetKey(IPEndPoint endPoint, byte[] key, out int slot, out string address, out int port, out ResponseState responseState, bool asking = false, ILogger logger = null) + public string GetKey(IPEndPoint endPoint, byte[] key, out int slot, out IPEndPoint actualEndpoint, out ResponseState responseState, bool asking = false, ILogger logger = null) { slot = -1; - address = endPoint.Address.ToString(); - port = endPoint.Port; + actualEndpoint = endPoint; responseState = ResponseState.NONE; var server = GetServer(endPoint); string result; @@ -2247,24 +2242,26 @@ public string GetKey(IPEndPoint endPoint, byte[] key, out int slot, out string a var tokens = e.Message.Split(' '); if (tokens.Length > 10 && tokens[2].Equals("MOVED")) { - address = tokens[5].Split(':')[0]; - port = int.Parse(tokens[5].Split(':')[1]); + var endpointSplit = tokens[5].Split(':'); + actualEndpoint = new IPEndPoint(IPAddress.Parse(endpointSplit[0]), int.Parse(endpointSplit[1])); slot = int.Parse(tokens[8]); responseState = ResponseState.MOVED; return "MOVED"; } else if (tokens.Length > 10 && tokens[0].Equals("Endpoint")) { - address = tokens[1].Split(':')[0]; - port = int.Parse(tokens[1].Split(':')[1]); + var endpointSplit = tokens[1].Split(':'); + actualEndpoint = new IPEndPoint(IPAddress.Parse(endpointSplit[0]), int.Parse(endpointSplit[1])); + slot = int.Parse(tokens[4]); responseState = ResponseState.ASK; return "ASK"; } else if (tokens[0].Equals("ASK")) { - address = tokens[2].Split(':')[0]; - port = int.Parse(tokens[2].Split(':')[1]); + var endpointSplit = tokens[2].Split(':'); + actualEndpoint = new IPEndPoint(IPAddress.Parse(endpointSplit[0]), int.Parse(endpointSplit[1])); + slot = int.Parse(tokens[1]); responseState = ResponseState.ASK; return "ASK"; diff --git a/test/Garnet.test.cluster/ReplicationTests/ClusterReplicationBaseTests.cs b/test/Garnet.test.cluster/ReplicationTests/ClusterReplicationBaseTests.cs index 14d6f93ed3..4f7749a8a9 100644 --- a/test/Garnet.test.cluster/ReplicationTests/ClusterReplicationBaseTests.cs +++ b/test/Garnet.test.cluster/ReplicationTests/ClusterReplicationBaseTests.cs @@ -192,7 +192,7 @@ public void ClusterSRNoCheckpointRestartSecondary([Values] bool performRMW, [Val // Restart secondary context.nodes[1] = context.CreateInstance( - context.clusterTestUtils.GetEndPoint(1).Port, + context.clusterTestUtils.GetEndPoint(1), disableObjects: disableObjects, tryRecover: true, enableAOF: true, @@ -265,7 +265,7 @@ public void ClusterSRPrimaryCheckpoint([Values] bool performRMW, [Values] bool d // Restart secondary context.nodes[1] = context.CreateInstance( - context.clusterTestUtils.GetEndPoint(1).Port, + context.clusterTestUtils.GetEndPoint(1), disableObjects: disableObjects, tryRecover: true, enableAOF: true, @@ -314,7 +314,7 @@ void ClusterSRPrimaryCheckpointRetrieve(bool performRMW, bool disableObjects, bo var primary_count = 1; var nodes_count = primary_count + primary_count * replica_count; ClassicAssert.IsTrue(primary_count > 0); - context.CreateInstances(nodes_count, disableObjects: disableObjects, lowMemory: lowMemory, SegmentSize: manySegments ? "4k" : "1g", DisableStorageTier: disableStorageTier, EnableIncrementalSnapshots: incrementalSnapshots, enableAOF: true, useTLS: useTLS, asyncReplay: asyncReplay); + context.CreateInstances(nodes_count, disableObjects: disableObjects, lowMemory: lowMemory, segmentSize: manySegments ? "4k" : "1g", disableStorageTier: disableStorageTier, enableIncrementalSnapshots: incrementalSnapshots, enableAOF: true, useTLS: useTLS, asyncReplay: asyncReplay); context.CreateConnection(useTLS: useTLS); var (shards, _) = context.clusterTestUtils.SimpleSetupCluster(primary_count, replica_count, logger: context.logger); @@ -361,7 +361,7 @@ void ClusterSRPrimaryCheckpointRetrieve(bool performRMW, bool disableObjects, bo // Restart secondary context.nodes[replicaIndex] = context.CreateInstance( - context.clusterTestUtils.GetEndPoint(replicaIndex).Port, + context.clusterTestUtils.GetEndPoint(replicaIndex), disableObjects: disableObjects, tryRecover: true, enableAOF: true, @@ -487,7 +487,7 @@ public void ClusterSRPrimaryRestart([Values] bool performRMW, [Values] bool disa // Restart Primary context.nodes[0] = context.CreateInstance( - context.clusterTestUtils.GetEndPoint(0).Port, + context.clusterTestUtils.GetEndPoint(0), disableObjects: disableObjects, tryRecover: true, enableAOF: true, @@ -532,7 +532,7 @@ public void ClusterSRRedirectWrites() ClassicAssert.AreEqual(0, shards[0].slotRanges[0].Item1); ClassicAssert.AreEqual(16383, shards[0].slotRanges[0].Item2); - var resp = context.clusterTestUtils.SetKey(1, Encoding.ASCII.GetBytes("testKey"), Encoding.ASCII.GetBytes("testValue"), out _, out _, out _, logger: context.logger); + var resp = context.clusterTestUtils.SetKey(1, Encoding.ASCII.GetBytes("testKey"), Encoding.ASCII.GetBytes("testValue"), out _, out _, logger: context.logger); ClassicAssert.AreEqual(ResponseState.MOVED, resp); } @@ -650,7 +650,7 @@ public void ClusterFailoverAttachReplicas([Values] bool performRMW, [Values] boo var primary_count = 1; var nodes_count = primary_count + (primary_count * replica_count); ClassicAssert.IsTrue(primary_count > 0); - context.CreateInstances(nodes_count, disableObjects: true, EnableIncrementalSnapshots: enableIncrementalSnapshots, enableAOF: true, useTLS: useTLS, asyncReplay: asyncReplay); + context.CreateInstances(nodes_count, disableObjects: true, enableIncrementalSnapshots: enableIncrementalSnapshots, enableAOF: true, useTLS: useTLS, asyncReplay: asyncReplay); context.CreateConnection(useTLS: useTLS); var (shards, _) = context.clusterTestUtils.SimpleSetupCluster(primary_count, replica_count, logger: context.logger); @@ -731,7 +731,7 @@ public void ClusterReplicationCheckpointCleanupTest([Values] bool performRMW, [V var primary_count = 1; var nodes_count = primary_count + (primary_count * replica_count); ClassicAssert.IsTrue(primary_count > 0); - context.CreateInstances(nodes_count, tryRecover: true, disableObjects: disableObjects, lowMemory: true, SegmentSize: "4k", EnableIncrementalSnapshots: enableIncrementalSnapshots, enableAOF: true, useTLS: useTLS, asyncReplay: asyncReplay); + context.CreateInstances(nodes_count, tryRecover: true, disableObjects: disableObjects, lowMemory: true, segmentSize: "4k", enableIncrementalSnapshots: enableIncrementalSnapshots, enableAOF: true, useTLS: useTLS, asyncReplay: asyncReplay); context.CreateConnection(useTLS: useTLS); ClassicAssert.AreEqual("OK", context.clusterTestUtils.AddDelSlotsRange(0, [(0, 16383)], true, context.logger)); context.clusterTestUtils.BumpEpoch(0, logger: context.logger); @@ -764,7 +764,7 @@ public void ClusterMainMemoryReplicationAttachReplicas() var primary_count = 1; var nodes_count = primary_count + (primary_count * replica_count); ClassicAssert.IsTrue(primary_count > 0); - context.CreateInstances(nodes_count, disableObjects: true, MainMemoryReplication: true, OnDemandCheckpoint: true, CommitFrequencyMs: -1, enableAOF: true, useTLS: useTLS, asyncReplay: asyncReplay); + context.CreateInstances(nodes_count, disableObjects: true, mainMemoryReplication: true, onDemandCheckpoint: true, commitFrequencyMs: -1, enableAOF: true, useTLS: useTLS, asyncReplay: asyncReplay); context.CreateConnection(useTLS: useTLS); ClassicAssert.AreEqual("OK", context.clusterTestUtils.AddDelSlotsRange(0, new List<(int, int)>() { (0, 16383) }, true)); @@ -808,7 +808,7 @@ public void ClusterDontKnowReplicaFailTest([Values] bool performRMW, [Values] bo var primary_count = 1; var nodes_count = primary_count + (primary_count * replica_count); ClassicAssert.IsTrue(primary_count > 0); - context.CreateInstances(nodes_count, disableObjects: true, MainMemoryReplication: MainMemoryReplication, OnDemandCheckpoint: onDemandCheckpoint, CommitFrequencyMs: -1, enableAOF: true, useTLS: useTLS, asyncReplay: asyncReplay); + context.CreateInstances(nodes_count, disableObjects: true, mainMemoryReplication: MainMemoryReplication, onDemandCheckpoint: onDemandCheckpoint, commitFrequencyMs: -1, enableAOF: true, useTLS: useTLS, asyncReplay: asyncReplay); context.CreateConnection(useTLS: useTLS); var primaryNodeIndex = 0; @@ -909,7 +909,7 @@ void ClusterDivergentReplicasTest(bool performRMW, bool disableObjects, bool ckp var primary_count = 1; var nodes_count = primary_count + (primary_count * replica_count); ClassicAssert.IsTrue(primary_count > 0); - context.CreateInstances(nodes_count, disableObjects: disableObjects, MainMemoryReplication: mainMemoryReplication, CommitFrequencyMs: mainMemoryReplication ? -1 : 0, OnDemandCheckpoint: mainMemoryReplication, FastCommit: fastCommit, enableAOF: true, useTLS: useTLS, asyncReplay: asyncReplay); + context.CreateInstances(nodes_count, disableObjects: disableObjects, mainMemoryReplication: mainMemoryReplication, commitFrequencyMs: mainMemoryReplication ? -1 : 0, onDemandCheckpoint: mainMemoryReplication, fastCommit: fastCommit, enableAOF: true, useTLS: useTLS, asyncReplay: asyncReplay); context.CreateConnection(useTLS: useTLS); _ = context.clusterTestUtils.SimpleSetupCluster(primary_count, replica_count, logger: context.logger); @@ -1043,7 +1043,7 @@ public void ClusterReplicateFails() ServerCredential userCreds = new(UserName, Password, IsAdmin: true, UsedForClusterAuth: false, IsClearText: true); context.GenerateCredentials([userCreds, clusterCreds]); - context.CreateInstances(2, disableObjects: true, disablePubSub: true, enableAOF: true, clusterCreds: clusterCreds, useAcl: true, MainMemoryReplication: true, CommitFrequencyMs: -1, asyncReplay: asyncReplay); + context.CreateInstances(2, disableObjects: true, disablePubSub: true, enableAOF: true, clusterCreds: clusterCreds, useAcl: true, mainMemoryReplication: true, commitFrequencyMs: -1, asyncReplay: asyncReplay); var primaryEndpoint = (IPEndPoint)context.endpoints.First(); var replicaEndpoint = (IPEndPoint)context.endpoints.Last(); @@ -1072,7 +1072,7 @@ public void ClusterReplicationCheckpointAlignmentTest([Values] bool performRMW) var primaryNodeIndex = 0; var replicaNodeIndex = 1; ClassicAssert.IsTrue(primary_count > 0); - context.CreateInstances(nodes_count, disableObjects: false, MainMemoryReplication: true, CommitFrequencyMs: -1, OnDemandCheckpoint: true, enableAOF: true, useTLS: useTLS, asyncReplay: asyncReplay); + context.CreateInstances(nodes_count, disableObjects: false, mainMemoryReplication: true, commitFrequencyMs: -1, onDemandCheckpoint: true, enableAOF: true, useTLS: useTLS, asyncReplay: asyncReplay); context.CreateConnection(useTLS: useTLS); _ = context.clusterTestUtils.SimpleSetupCluster(primary_count, replica_count, logger: context.logger); @@ -1112,7 +1112,7 @@ public void ClusterReplicationCheckpointAlignmentTest([Values] bool performRMW) // Restart primary and do not recover context.nodes[primaryNodeIndex] = context.CreateInstance( - context.clusterTestUtils.GetEndPoint(primaryNodeIndex).Port, + context.clusterTestUtils.GetEndPoint(primaryNodeIndex), disableObjects: true, tryRecover: false, enableAOF: true, @@ -1127,7 +1127,7 @@ public void ClusterReplicationCheckpointAlignmentTest([Values] bool performRMW) // Restart secondary and recover context.nodes[replicaNodeIndex] = context.CreateInstance( - context.clusterTestUtils.GetEndPoint(replicaNodeIndex).Port, + context.clusterTestUtils.GetEndPoint(replicaNodeIndex), disableObjects: true, tryRecover: true, enableAOF: true, @@ -1167,7 +1167,7 @@ public void ClusterReplicationCheckpointAlignmentTest([Values] bool performRMW) // At this point attached replica should be empty because primary did not have any data because it did not recover foreach (var pair in context.kvPairs) { - var resp = context.clusterTestUtils.GetKey(replicaNodeIndex, Encoding.ASCII.GetBytes(pair.Key), out _, out _, out _, out var state, logger: context.logger); + var resp = context.clusterTestUtils.GetKey(replicaNodeIndex, Encoding.ASCII.GetBytes(pair.Key), out _, out _, out var state, logger: context.logger); ClassicAssert.IsNull(resp); } } diff --git a/test/Garnet.test/CacheSizeTrackerTests.cs b/test/Garnet.test/CacheSizeTrackerTests.cs index 41dfabddf5..93fe4430c5 100644 --- a/test/Garnet.test/CacheSizeTrackerTests.cs +++ b/test/Garnet.test/CacheSizeTrackerTests.cs @@ -122,7 +122,7 @@ public void ReadCacheIncreaseEmptyPageCountTest() } ClassicAssert.AreEqual(6200, cacheSizeTracker.readCacheTracker.LogHeapSizeBytes); // 25 * 248 for each hashset object - var info = TestUtils.GetStoreAddressInfo(redis.GetServer(TestUtils.Address, TestUtils.Port), includeReadCache: true, isObjectStore: true); + var info = TestUtils.GetStoreAddressInfo(redis.GetServer(TestUtils.EndPoint), includeReadCache: true, isObjectStore: true); ClassicAssert.AreEqual(632, info.ReadCacheTailAddress); // 25 (records) * 24 (rec size) + 24 (initial) + 8 (page boundary) if (!readCacheEpcEvent.Wait(TimeSpan.FromSeconds(3 * 3 * LogSizeTracker.resizeTaskDelaySeconds))) diff --git a/test/Garnet.test/GarnetClientTests.cs b/test/Garnet.test/GarnetClientTests.cs index 56498b2fcc..1ec6d773e9 100644 --- a/test/Garnet.test/GarnetClientTests.cs +++ b/test/Garnet.test/GarnetClientTests.cs @@ -2,12 +2,15 @@ // Licensed under the MIT license. using System; +using System.IO; using System.Linq; +using System.Net.Sockets; using System.Text; using System.Threading; using System.Threading.Tasks; using Garnet.common; using NUnit.Framework; +using NUnit.Framework.Internal; using NUnit.Framework.Legacy; namespace Garnet.test @@ -119,10 +122,10 @@ static void WaitAndReset(ManualResetEventSlim e) [Test] public void SetGetWithCallback([Values] bool useTLS) { - using var server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, EnableTLS: useTLS); + using var server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, enableTLS: useTLS); server.Start(); - using var db = TestUtils.GetGarnetClient(useTLS); + using var db = TestUtils.GetGarnetClient(useTLS: useTLS); db.Connect(); string origValue = "abcdefg"; @@ -150,7 +153,7 @@ public void SimpleMetricsTest([Values] bool recordLatency) using var server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir); server.Start(); - var db = TestUtils.GetGarnetClient(recordLatency: recordLatency); + using var db = TestUtils.GetGarnetClient(recordLatency: recordLatency); db.Connect(); var metrics = db.GetLatencyMetrics(); @@ -182,7 +185,7 @@ public async Task SimpleStringArrayTest([Values] bool stringParams) using var server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir); server.Start(); - var db = TestUtils.GetGarnetClient(); + using var db = TestUtils.GetGarnetClient(); db.Connect(); long n = stringParams ? @@ -198,7 +201,7 @@ public async Task SimpleNoArgsTest() using var server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir); server.Start(); - var db = TestUtils.GetGarnetClient(); + using var db = TestUtils.GetGarnetClient(); db.Connect(); var result = await db.ExecuteForStringResultAsync("PING"); @@ -216,7 +219,7 @@ public async Task SimpleIncrTest([Values] bool stringParams) server.Start(); var key = "mykey"; - var db = TestUtils.GetGarnetClient(); + using var db = TestUtils.GetGarnetClient(); db.Connect(); long expectedV = 1; @@ -278,7 +281,7 @@ public async Task SimpleDecrTest([Values] bool stringParams) server.Start(); var key = "mykey"; - var db = TestUtils.GetGarnetClient(); + using var db = TestUtils.GetGarnetClient(); db.Connect(); long expectedV = -1; @@ -338,7 +341,7 @@ public async Task CanUseSetNxStringResultAsync() using var server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir); server.Start(); - var db = TestUtils.GetGarnetClient(); + using var db = TestUtils.GetGarnetClient(); db.Connect(); var result = await db.ExecuteForStringResultAsync("SET", ["mykey", "Hello", "NX"]); @@ -354,10 +357,10 @@ public async Task CanUseSetNxStringResultAsync() [Test] public async Task CanUseMGetTests([Values] bool disableObjectStore) { - using var server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, DisableObjects: disableObjectStore); + using var server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, disableObjects: disableObjectStore); server.Start(); - var db = TestUtils.GetGarnetClient(); + using var db = TestUtils.GetGarnetClient(); db.Connect(); var nKeys = worldcities.GetLength(0); var keys = new string[nKeys]; @@ -420,7 +423,7 @@ public async Task CanUseMGetTests([Values] bool disableObjectStore) private async Task ReadValuesMGet(string[] keys, CancellationToken t) { - var db = TestUtils.GetGarnetClient(); + using var db = TestUtils.GetGarnetClient(); db.Connect(); var vals = new string[keys.Length]; vals = await db.StringGetAsync(keys, t); @@ -464,7 +467,7 @@ public async Task CanDoBulkDeleteTests([Values] bool useStringType) using var server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir); server.Start(); - var db = TestUtils.GetGarnetClient(); + using var db = TestUtils.GetGarnetClient(); db.Connect(); var nKeys = worldcities.GetLength(0); @@ -556,7 +559,7 @@ public async Task CanDoBulkDeleteTests([Values] bool useStringType) private static async Task DeleteKeysWithCT(string[] keys, Memory[] keysMB, ManualResetEventSlim mreObj, CancellationToken t, bool useMemoryType = false) { - var db = TestUtils.GetGarnetClient(); + using var db = TestUtils.GetGarnetClient(); db.Connect(); //wait until is signaled to proceed @@ -575,5 +578,21 @@ private static async Task DeleteKeysWithCT(string[] keys, Memory[] k return 0; } + + [Test] + public async Task UnixSocket_Ping([Values] bool useTls) + { + var unixSocketPath = "./unix-socket-ping-test.sock"; + var unixSocketEndpoint = new UnixDomainSocketEndPoint(unixSocketPath); + + using var server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, unixSocketEndpoint, enableTLS: useTls, unixSocketPath: unixSocketPath); + server.Start(); + + using var db = TestUtils.GetGarnetClient(unixSocketEndpoint, useTLS: useTls); + await db.ConnectAsync(); + + var result = await db.ExecuteForStringResultAsync("PING"); + ClassicAssert.AreEqual("PONG", result); + } } } \ No newline at end of file diff --git a/test/Garnet.test/GarnetJSON/JsonCommandsTest.cs b/test/Garnet.test/GarnetJSON/JsonCommandsTest.cs index 7ed66c9d36..b0d7024ffe 100644 --- a/test/Garnet.test/GarnetJSON/JsonCommandsTest.cs +++ b/test/Garnet.test/GarnetJSON/JsonCommandsTest.cs @@ -97,7 +97,7 @@ public void SaveRecoverTest() ClassicAssert.AreEqual("[{\"a\":1}]", (string)retValue); // Issue and wait for DB save - var server = redis.GetServer($"{TestUtils.Address}:{TestUtils.Port}"); + var server = redis.GetServer(TestUtils.EndPoint); server.Save(SaveType.BackgroundSave); while (server.LastSave().Ticks == DateTimeOffset.FromUnixTimeSeconds(0).Ticks) Thread.Sleep(10); } diff --git a/test/Garnet.test/GarnetServerConfigTests.cs b/test/Garnet.test/GarnetServerConfigTests.cs index 2890af40b2..c57d3dfabd 100644 --- a/test/Garnet.test/GarnetServerConfigTests.cs +++ b/test/Garnet.test/GarnetServerConfigTests.cs @@ -13,25 +13,16 @@ using Garnet.server; using Microsoft.Extensions.Logging; using NUnit.Framework; +using NUnit.Framework.Internal; using NUnit.Framework.Legacy; using Tsavorite.core; using Tsavorite.devices; namespace Garnet.test { - [TestFixture, NonParallelizable] - public class GarnetServerConfigTests { - [SetUp] - public void Setup() - { } - - [TearDown] - public void TearDown() - { } - [Test] public void DefaultConfigurationOptionsCoverage() { @@ -422,5 +413,45 @@ static bool TryParseGarnetConfOptions(string json, out Options options, out List } } } + + [Test] + public void UnixSocketPath_CanParseValidPath() + { + string[] args = ["--unixsocket", "./config-parse-test.sock"]; + var parseSuccessful = ServerSettingsManager.TryParseCommandLineArguments(args, out _, out _, out _); + ClassicAssert.IsTrue(parseSuccessful); + } + + [Test] + public void UnixSocketPath_InvalidPathFails() + { + // Socket path directory does not exists + string[] args = ["--unixsocket", "./does-not-exists/config-parse-test.sock"]; + var parseSuccessful = ServerSettingsManager.TryParseCommandLineArguments(args, out _, out _, out _); + ClassicAssert.IsFalse(parseSuccessful); + } + + [Test] + public void UnixSocketPermission_CanParseValidPermission() + { + if (OperatingSystem.IsWindows()) + return; + + string[] args = ["--unixsocketperm", "777"]; + var parseSuccessful = ServerSettingsManager.TryParseCommandLineArguments(args, out var options, out _, out _); + ClassicAssert.IsTrue(parseSuccessful); + ClassicAssert.AreEqual(777, options.UnixSocketPermission); + } + + [Test] + public void UnixSocketPermission_InvalidPermissionFails() + { + if (OperatingSystem.IsWindows()) + return; + + string[] args = ["--unixsocketperm", "888"]; + var parseSuccessful = ServerSettingsManager.TryParseCommandLineArguments(args, out _, out _, out _); + ClassicAssert.IsFalse(parseSuccessful); + } } } \ No newline at end of file diff --git a/test/Garnet.test/IndexGrowthTests.cs b/test/Garnet.test/IndexGrowthTests.cs index c08d2f90cb..785866f85f 100644 --- a/test/Garnet.test/IndexGrowthTests.cs +++ b/test/Garnet.test/IndexGrowthTests.cs @@ -161,7 +161,7 @@ public void IndexGrowthTestWithDiskReadAndCheckpoint() ClassicAssert.AreEqual(values[0], db.StringGet(keys[0]).ToString()); // Issue and wait for DB save - var server = redis.GetServer($"{TestUtils.Address}:{TestUtils.Port}"); + var server = redis.GetServer(TestUtils.EndPoint); server.Save(SaveType.BackgroundSave); while (server.LastSave().Ticks == DateTimeOffset.FromUnixTimeSeconds(0).Ticks) Thread.Sleep(10); } @@ -225,7 +225,7 @@ public void ObjectStoreIndexGrowthTestWithDiskReadAndCheckpoint() VerifyObjectStoreSetMembers(db, keys, values); // Issue and wait for DB save - var server = redis.GetServer($"{TestUtils.Address}:{TestUtils.Port}"); + var server = redis.GetServer(TestUtils.EndPoint); server.Save(SaveType.BackgroundSave); while (server.LastSave().Ticks == DateTimeOffset.FromUnixTimeSeconds(0).Ticks) Thread.Sleep(10); } diff --git a/test/Garnet.test/LuaScriptTests.cs b/test/Garnet.test/LuaScriptTests.cs index 4ed1be723b..4949a15ceb 100644 --- a/test/Garnet.test/LuaScriptTests.cs +++ b/test/Garnet.test/LuaScriptTests.cs @@ -277,7 +277,7 @@ public void CanDoScriptFlush() { using var redis = ConnectionMultiplexer.Connect(TestUtils.GetConfig(allowAdmin: true)); var db = redis.GetDatabase(0); - var server = redis.GetServer($"{TestUtils.Address}:{TestUtils.Port}"); + var server = redis.GetServer(TestUtils.EndPoint); // Load a script in the memory var script = "return 1;"; @@ -298,7 +298,7 @@ public void CannotScriptUseExtendedEncoding() using var redis = ConnectionMultiplexer.Connect(TestUtils.GetConfig()); var extendedCharsScript = "return '僕'"; - var server = redis.GetServer($"{TestUtils.Address}:{TestUtils.Port}"); + var server = redis.GetServer(TestUtils.EndPoint); var scriptSha = server.ScriptLoad(extendedCharsScript); var result = redis.GetDatabase(0).ScriptEvaluate(scriptSha); ClassicAssert.AreNotEqual("僕", result); diff --git a/test/Garnet.test/ReadCacheTests.cs b/test/Garnet.test/ReadCacheTests.cs index 80aa968484..4e6015bc54 100644 --- a/test/Garnet.test/ReadCacheTests.cs +++ b/test/Garnet.test/ReadCacheTests.cs @@ -36,7 +36,7 @@ public void MainStoreReadCacheTest() { using var redis = ConnectionMultiplexer.Connect(TestUtils.GetConfig(allowAdmin: true)); var db = redis.GetDatabase(0); - var server = redis.GetServer(TestUtils.Address, TestUtils.Port); + var server = redis.GetServer(TestUtils.EndPoint); var info = TestUtils.GetStoreAddressInfo(server, includeReadCache: true); // Start at tail address of 64 @@ -98,7 +98,7 @@ public void ObjectStoreReadCacheTest() { using var redis = ConnectionMultiplexer.Connect(TestUtils.GetConfig(allowAdmin: true)); var db = redis.GetDatabase(0); - var server = redis.GetServer(TestUtils.Address, TestUtils.Port); + var server = redis.GetServer(TestUtils.EndPoint); var info = TestUtils.GetStoreAddressInfo(server, includeReadCache: true, isObjectStore: true); // Start at tail address of 24 diff --git a/test/Garnet.test/Resp/ACL/RespCommandTests.cs b/test/Garnet.test/Resp/ACL/RespCommandTests.cs index 3bb8fe5149..f5f693e29b 100644 --- a/test/Garnet.test/Resp/ACL/RespCommandTests.cs +++ b/test/Garnet.test/Resp/ACL/RespCommandTests.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Numerics; using System.Reflection; @@ -3606,7 +3607,7 @@ async Task DoHIncrByFloatAsync(GarnetClient client) { string val = await client.ExecuteForStringResultAsync("HINCRBYFLOAT", ["foo", "bar", "1.0"]); cur += 1.0; - ClassicAssert.AreEqual(cur, double.Parse(val)); + ClassicAssert.AreEqual(cur, double.Parse(val, CultureInfo.InvariantCulture)); } } diff --git a/test/Garnet.test/RespAdminCommandsTests.cs b/test/Garnet.test/RespAdminCommandsTests.cs index 7bf4f07f06..3e74e3c6a4 100644 --- a/test/Garnet.test/RespAdminCommandsTests.cs +++ b/test/Garnet.test/RespAdminCommandsTests.cs @@ -142,7 +142,7 @@ public void TimeWithReturnErrorTest() public void SeSaveTest() { using var redis = ConnectionMultiplexer.Connect(TestUtils.GetConfig(allowAdmin: true)); - IServer server = redis.GetServer($"{TestUtils.Address}:{TestUtils.Port}"); + IServer server = redis.GetServer(TestUtils.EndPoint); var lastSave = server.LastSave(); @@ -162,7 +162,7 @@ public void SeSaveRecoverTest([Values] bool disableObj, [Values] bool useAzure) if (useAzure) TestUtils.IgnoreIfNotRunningAzureTests(); server.Dispose(); - server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, DisableObjects: disableObj, UseAzureStorage: useAzure); + server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, disableObjects: disableObj, useAzureStorage: useAzure); server.Start(); using (var redis = ConnectionMultiplexer.Connect(TestUtils.GetConfig(allowAdmin: true))) @@ -171,13 +171,13 @@ public void SeSaveRecoverTest([Values] bool disableObj, [Values] bool useAzure) db.StringSet("SeSaveRecoverTestKey", "SeSaveRecoverTestValue"); // Issue and wait for DB save - var server = redis.GetServer($"{TestUtils.Address}:{TestUtils.Port}"); + var server = redis.GetServer(TestUtils.EndPoint); server.Save(SaveType.BackgroundSave); while (server.LastSave().Ticks == DateTimeOffset.FromUnixTimeSeconds(0).Ticks) Thread.Sleep(10); } server.Dispose(false); - server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, tryRecover: true, UseAzureStorage: useAzure); + server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, tryRecover: true, useAzureStorage: useAzure); server.Start(); using (var redis = ConnectionMultiplexer.Connect(TestUtils.GetConfig(allowAdmin: true))) @@ -203,7 +203,7 @@ public void SeSaveRecoverObjectTest() ClassicAssert.AreEqual(ldata, returned_data_before_recovery); // Issue and wait for DB save - var server = redis.GetServer($"{TestUtils.Address}:{TestUtils.Port}"); + var server = redis.GetServer(TestUtils.EndPoint); server.Save(SaveType.BackgroundSave); while (server.LastSave().Ticks == DateTimeOffset.FromUnixTimeSeconds(0).Ticks) Thread.Sleep(10); } @@ -242,7 +242,7 @@ public void SeSaveRecoverCustomObjectTest() ClassicAssert.AreEqual(value, (string)retValue); // Issue and wait for DB save - var server = redis.GetServer($"{TestUtils.Address}:{TestUtils.Port}"); + var server = redis.GetServer(TestUtils.EndPoint); server.Save(SaveType.BackgroundSave); while (server.LastSave().Ticks == DateTimeOffset.FromUnixTimeSeconds(0).Ticks) Thread.Sleep(10); } @@ -288,7 +288,7 @@ static void ValidateServerData(IDatabase db, string strKey, string strValue, str ValidateServerData(db, strKey, strValue, listKey, listValue); // Issue and wait for DB save - var server = redis.GetServer($"{TestUtils.Address}:{TestUtils.Port}"); + var server = redis.GetServer(TestUtils.EndPoint); server.Save(SaveType.BackgroundSave); while (server.LastSave().Ticks == DateTimeOffset.FromUnixTimeSeconds(0).Ticks) Thread.Sleep(10); } @@ -329,7 +329,7 @@ public void SeSaveRecoverMultipleObjectsTest(int memorySize, int recoveryMemoryS ClassicAssert.AreEqual(ldataArr, db.ListRange($"SeSaveRecoverTestKey{i:0000}"), $"key {i:0000}"); // Issue and wait for DB save - var server = redis.GetServer($"{TestUtils.Address}:{TestUtils.Port}"); + var server = redis.GetServer(TestUtils.EndPoint); server.Save(SaveType.BackgroundSave); while (server.LastSave().Ticks == DateTimeOffset.FromUnixTimeSeconds(0).Ticks) Thread.Sleep(10); } @@ -363,7 +363,7 @@ public void SeSaveRecoverMultipleKeysTest(string memorySize, string recoveryMemo bool disableObj = true; server.Dispose(); - server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, DisableObjects: disableObj, lowMemory: true, MemorySize: memorySize, PageSize: "512", enableAOF: true); + server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, disableObjects: disableObj, lowMemory: true, MemorySize: memorySize, PageSize: "512", enableAOF: true); server.Start(); using (var redis = ConnectionMultiplexer.Connect(TestUtils.GetConfig(allowAdmin: true))) @@ -383,7 +383,7 @@ public void SeSaveRecoverMultipleKeysTest(string memorySize, string recoveryMemo var inforesult = db.Execute("INFO"); // Issue and wait for DB save - var server = redis.GetServer($"{TestUtils.Address}:{TestUtils.Port}"); + var server = redis.GetServer(TestUtils.EndPoint); server.Save(SaveType.BackgroundSave); while (server.LastSave().Ticks == DateTimeOffset.FromUnixTimeSeconds(0).Ticks) Thread.Sleep(10); @@ -402,7 +402,7 @@ public void SeSaveRecoverMultipleKeysTest(string memorySize, string recoveryMemo } server.Dispose(false); - server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, DisableObjects: disableObj, tryRecover: true, lowMemory: true, MemorySize: recoveryMemorySize, PageSize: "512", enableAOF: true); + server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, disableObjects: disableObj, tryRecover: true, lowMemory: true, MemorySize: recoveryMemorySize, PageSize: "512", enableAOF: true); server.Start(); using (var redis = ConnectionMultiplexer.Connect(TestUtils.GetConfig(allowAdmin: true))) @@ -447,7 +447,7 @@ public void SeAofRecoverTest() public void SeFlushDbAndFlushAllTest([Values(RespCommand.FLUSHALL, RespCommand.FLUSHDB)] RespCommand cmd) { using var redis = ConnectionMultiplexer.Connect(TestUtils.GetConfig(allowAdmin: true)); - IServer server = redis.GetServer($"{TestUtils.Address}:{TestUtils.Port}"); + IServer server = redis.GetServer(TestUtils.EndPoint); var db = redis.GetDatabase(0); diff --git a/test/Garnet.test/RespAofAzureTests.cs b/test/Garnet.test/RespAofAzureTests.cs index 9787ba1e74..3759cd59dc 100644 --- a/test/Garnet.test/RespAofAzureTests.cs +++ b/test/Garnet.test/RespAofAzureTests.cs @@ -30,7 +30,7 @@ public class RespAofAzureTests [SetUp] public void Setup() { - server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, enableAOF: true, lowMemory: true, UseAzureStorage: true); + server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, enableAOF: true, lowMemory: true, useAzureStorage: true); server.Start(); } @@ -52,7 +52,7 @@ public void AofUpsertStoreRecoverTest() server.Store.CommitAOF(true); server.Dispose(false); - server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, tryRecover: true, enableAOF: true, UseAzureStorage: true); + server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, tryRecover: true, enableAOF: true, useAzureStorage: true); server.Start(); using (var redis = ConnectionMultiplexer.Connect(TestUtils.GetConfig())) @@ -77,7 +77,7 @@ public void AofUpsertStoreAutoCommitRecoverTest() server.Store.WaitForCommit(); server.Dispose(false); - server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, tryRecover: true, enableAOF: true, UseAzureStorage: true); + server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, tryRecover: true, enableAOF: true, useAzureStorage: true); server.Start(); using (var redis = ConnectionMultiplexer.Connect(TestUtils.GetConfig())) @@ -94,7 +94,7 @@ public void AofUpsertStoreAutoCommitRecoverTest() public void AofUpsertStoreAutoCommitCommitWaitRecoverTest() { server.Dispose(false); - server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, tryRecover: false, enableAOF: true, commitWait: true, UseAzureStorage: true); + server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, tryRecover: false, enableAOF: true, commitWait: true, useAzureStorage: true); server.Start(); using (var redis = ConnectionMultiplexer.Connect(TestUtils.GetConfig())) @@ -104,7 +104,7 @@ public void AofUpsertStoreAutoCommitCommitWaitRecoverTest() db.StringSet("SeAofUpsertRecoverTestKey2", "SeAofUpsertRecoverTestValue2"); } server.Dispose(false); - server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, tryRecover: true, enableAOF: true, UseAzureStorage: true); + server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, tryRecover: true, enableAOF: true, useAzureStorage: true); server.Start(); using (var redis = ConnectionMultiplexer.Connect(TestUtils.GetConfig())) @@ -127,7 +127,7 @@ public void AofUpsertStoreCkptRecoverTest() db.StringSet("SeAofUpsertRecoverTestKey2", "SeAofUpsertRecoverTestValue2"); // Issue and wait for DB save - var server = redis.GetServer($"{TestUtils.Address}:{TestUtils.Port}"); + var server = redis.GetServer(TestUtils.EndPoint); server.Save(SaveType.BackgroundSave); while (server.LastSave().Ticks == DateTimeOffset.FromUnixTimeSeconds(0).Ticks) Thread.Sleep(10); @@ -137,7 +137,7 @@ public void AofUpsertStoreCkptRecoverTest() server.Store.CommitAOF(true); server.Dispose(false); - server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, tryRecover: true, enableAOF: true, UseAzureStorage: true); + server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, tryRecover: true, enableAOF: true, useAzureStorage: true); server.Start(); using (var redis = ConnectionMultiplexer.Connect(TestUtils.GetConfig())) @@ -164,7 +164,7 @@ public void AofRMWStoreRecoverTest() server.Store.CommitAOF(true); server.Dispose(false); - server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, tryRecover: true, enableAOF: true, UseAzureStorage: true); + server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, tryRecover: true, enableAOF: true, useAzureStorage: true); server.Start(); using (var redis = ConnectionMultiplexer.Connect(TestUtils.GetConfig())) @@ -199,7 +199,7 @@ public void AofDeleteStoreRecoverTest() server.Store.CommitAOF(true); server.Dispose(false); - server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, tryRecover: true, enableAOF: true, UseAzureStorage: true); + server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, tryRecover: true, enableAOF: true, useAzureStorage: true); server.Start(); using (var redis = ConnectionMultiplexer.Connect(TestUtils.GetConfig())) @@ -234,7 +234,7 @@ public void AofExpiryRMWStoreRecoverTest() server.Store.CommitAOF(true); server.Dispose(false); - server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, tryRecover: true, enableAOF: true, UseAzureStorage: true); + server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, tryRecover: true, enableAOF: true, useAzureStorage: true); server.Start(); using (var redis = ConnectionMultiplexer.Connect(TestUtils.GetConfig())) @@ -268,7 +268,7 @@ public void AofRMWObjectStoreRecoverTest() server.Store.CommitAOF(true); server.Dispose(false); - server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, tryRecover: true, enableAOF: true, UseAzureStorage: true); + server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, tryRecover: true, enableAOF: true, useAzureStorage: true); server.Start(); using (var redis = ConnectionMultiplexer.Connect(TestUtils.GetConfig())) @@ -314,7 +314,7 @@ public void AofDeleteObjectStoreRecoverTest() server.Store.CommitAOF(true); server.Dispose(false); - server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, tryRecover: true, enableAOF: true, UseAzureStorage: true); + server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, tryRecover: true, enableAOF: true, useAzureStorage: true); server.Start(); using (var redis = ConnectionMultiplexer.Connect(TestUtils.GetConfig())) @@ -350,7 +350,7 @@ public void AofRMWObjectStoreCopyUpdateRecoverTest() } server.Store.CommitAOF(true); server.Dispose(false); - server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, tryRecover: true, enableAOF: true, UseAzureStorage: true); + server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, tryRecover: true, enableAOF: true, useAzureStorage: true); server.Start(); using (var redis = ConnectionMultiplexer.Connect(TestUtils.GetConfig())) @@ -373,7 +373,7 @@ public void AofMultiRMWStoreCkptRecoverTest() using (var redis = ConnectionMultiplexer.Connect(TestUtils.GetConfig(allowAdmin: true))) { var db = redis.GetDatabase(0); - var server = redis.GetServer($"{TestUtils.Address}:{TestUtils.Port}"); + var server = redis.GetServer(TestUtils.EndPoint); ret = db.StringIncrement("key1", 2); ClassicAssert.AreEqual(2, ret); @@ -405,7 +405,7 @@ public void AofMultiRMWStoreCkptRecoverTest() server.Store.CommitAOF(true); server.Dispose(false); - server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, tryRecover: true, enableAOF: true, UseAzureStorage: true); + server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, tryRecover: true, enableAOF: true, useAzureStorage: true); server.Start(); using (var redis = ConnectionMultiplexer.Connect(TestUtils.GetConfig(allowAdmin: true))) diff --git a/test/Garnet.test/RespAofTests.cs b/test/Garnet.test/RespAofTests.cs index 90e8bb61e4..4479630dc8 100644 --- a/test/Garnet.test/RespAofTests.cs +++ b/test/Garnet.test/RespAofTests.cs @@ -196,7 +196,7 @@ public void AofUpsertStoreCkptRecoverTest() db.StringSet("SeAofUpsertRecoverTestKey2", "SeAofUpsertRecoverTestValue2"); // Issue and wait for DB save - var server = redis.GetServer($"{TestUtils.Address}:{TestUtils.Port}"); + var server = redis.GetServer(TestUtils.EndPoint); server.Save(SaveType.BackgroundSave); while (server.LastSave().Ticks == DateTimeOffset.FromUnixTimeSeconds(0).Ticks) Thread.Sleep(10); @@ -580,7 +580,7 @@ public void AofMultiRMWStoreCkptRecoverTest() using (var redis = ConnectionMultiplexer.Connect(TestUtils.GetConfig(allowAdmin: true))) { var db = redis.GetDatabase(0); - var server = redis.GetServer($"{TestUtils.Address}:{TestUtils.Port}"); + var server = redis.GetServer(TestUtils.EndPoint); ret = db.StringIncrement("key1", 2); ClassicAssert.AreEqual(2, ret); diff --git a/test/Garnet.test/RespEtagTests.cs b/test/Garnet.test/RespEtagTests.cs index 3bd15c489e..460ab47203 100644 --- a/test/Garnet.test/RespEtagTests.cs +++ b/test/Garnet.test/RespEtagTests.cs @@ -981,7 +981,7 @@ public void SingleDeleteWithObjectStoreDisabledForEtagSetData() TearDown(); TestUtils.DeleteDirectory(TestUtils.MethodTestDir, wait: true); - server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, DisableObjects: true); + server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, disableObjects: true); server.Start(); var key = "delKey"; @@ -1014,7 +1014,7 @@ public void SingleDeleteWithObjectStoreDisable_LTMForEtagSetData() TearDown(); TestUtils.DeleteDirectory(TestUtils.MethodTestDir, wait: true); - server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, lowMemory: true, DisableObjects: true); + server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, lowMemory: true, disableObjects: true); server.Start(); using var redis = ConnectionMultiplexer.Connect(TestUtils.GetConfig()); var db = redis.GetDatabase(0); @@ -1055,7 +1055,7 @@ public void MultiKeyDeleteForEtagSetData([Values] bool withoutObjectStore) { TearDown(); TestUtils.DeleteDirectory(TestUtils.MethodTestDir, wait: true); - server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, DisableObjects: true); + server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, disableObjects: true); server.Start(); } @@ -1090,7 +1090,7 @@ public void MultiKeyUnlinkForEtagSetData([Values] bool withoutObjectStore) { TearDown(); TestUtils.DeleteDirectory(TestUtils.MethodTestDir, wait: true); - server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, DisableObjects: true); + server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, disableObjects: true); server.Start(); } @@ -1124,7 +1124,7 @@ public void SingleExistsForEtagSetData([Values] bool withoutObjectStore) { TearDown(); TestUtils.DeleteDirectory(TestUtils.MethodTestDir, wait: true); - server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, DisableObjects: true); + server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, disableObjects: true); server.Start(); } using var redis = ConnectionMultiplexer.Connect(TestUtils.GetConfig()); diff --git a/test/Garnet.test/RespHashTests.cs b/test/Garnet.test/RespHashTests.cs index d162db0f0c..cacd3ed60d 100644 --- a/test/Garnet.test/RespHashTests.cs +++ b/test/Garnet.test/RespHashTests.cs @@ -1113,7 +1113,7 @@ public async Task CanDoHashExpireLTM() { using var redis = ConnectionMultiplexer.Connect(TestUtils.GetConfig(allowAdmin: true)); var db = redis.GetDatabase(0); - var server = redis.GetServer(TestUtils.Address, TestUtils.Port); + var server = redis.GetServer(TestUtils.EndPoint); string[] smallExpireKeys = ["user:user0", "user:user1"]; string[] largeExpireKeys = ["user:user2", "user:user3"]; diff --git a/test/Garnet.test/RespInfoTests.cs b/test/Garnet.test/RespInfoTests.cs index c6c5e077f9..14af2ff239 100644 --- a/test/Garnet.test/RespInfoTests.cs +++ b/test/Garnet.test/RespInfoTests.cs @@ -21,7 +21,7 @@ public void Setup() { r = new Random(674386); TestUtils.DeleteDirectory(TestUtils.MethodTestDir, wait: true); - server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, disablePubSub: true, latencyMonitor: true, metricsSamplingFreq: 1, DisableObjects: true); + server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, disablePubSub: true, latencyMonitor: true, metricsSamplingFreq: 1, disableObjects: true); server.Start(); } diff --git a/test/Garnet.test/RespListGarnetClientTests.cs b/test/Garnet.test/RespListGarnetClientTests.cs index 44137a37e3..e3642d28e2 100644 --- a/test/Garnet.test/RespListGarnetClientTests.cs +++ b/test/Garnet.test/RespListGarnetClientTests.cs @@ -55,7 +55,7 @@ public void Setup() public async Task AddElementsToTheListHeadInBulk_WithCallback(string key, string[] elements, string[] expectedList) { // Arrange - using var db = new GarnetClient(TestUtils.Address, TestUtils.Port); + using var db = new GarnetClient(TestUtils.EndPoint); await db.ConnectAsync(); using ManualResetEventSlim e = new(); @@ -85,7 +85,7 @@ public async Task AddElementsToTheListHeadInBulk_WithCallback(string key, string public async Task AddElementsToTheListHead_WithCallback(string key, string[] elements, string[] expectedList) { // Arrange - using var db = new GarnetClient(TestUtils.Address, TestUtils.Port); + using var db = new GarnetClient(TestUtils.EndPoint); await db.ConnectAsync(); using ManualResetEventSlim e = new(); @@ -120,7 +120,7 @@ public async Task AddElementsToTheListHead_WithCallback(string key, string[] ele public async Task AddElementsToTheListHead_WithAsync(string key, string[] elements, string[] expectedList) { // Arrange - using var db = new GarnetClient(TestUtils.Address, TestUtils.Port); + using var db = new GarnetClient(TestUtils.EndPoint); await db.ConnectAsync(); // Act & Assert @@ -137,7 +137,7 @@ public async Task AddElementsToTheListHead_WithAsync(string key, string[] elemen public async Task AddElementsToListTailInBulk_WithCallback(string key, string[] elements, string[] expectedList) { // Arrange - using var db = new GarnetClient(TestUtils.Address, TestUtils.Port); + using var db = new GarnetClient(TestUtils.EndPoint); await db.ConnectAsync(); using ManualResetEventSlim e = new(); @@ -167,7 +167,7 @@ public async Task AddElementsToListTailInBulk_WithCallback(string key, string[] public async Task AddElementsToListTail_WithCallback(string key, string[] elements, string[] expectedList) { // Arrange - using var db = new GarnetClient(TestUtils.Address, TestUtils.Port); + using var db = new GarnetClient(TestUtils.EndPoint); await db.ConnectAsync(); using ManualResetEventSlim e = new(); @@ -202,7 +202,7 @@ public async Task AddElementsToListTail_WithCallback(string key, string[] elemen public async Task AddElementsToTheListTail_WithAsync(string key, string[] elements, string[] expectedList) { // Arrange - using var db = new GarnetClient(TestUtils.Address, TestUtils.Port); + using var db = new GarnetClient(TestUtils.EndPoint); await db.ConnectAsync(); // Act & Assert @@ -220,7 +220,7 @@ public async Task GetListElements(int start, int stop, string[] expectedValues) { // Arrange var testKey = GetTestKey("list1"); - using var db = new GarnetClient(TestUtils.Address, TestUtils.Port); + using var db = new GarnetClient(TestUtils.EndPoint); await db.ConnectAsync(); await db.KeyDeleteAsync([testKey]); @@ -239,7 +239,7 @@ public async Task GetListLength() { // Arrange var testKey = GetTestKey("list1"); - using var db = new GarnetClient(TestUtils.Address, TestUtils.Port); + using var db = new GarnetClient(TestUtils.EndPoint); await db.ConnectAsync(); await db.KeyDeleteAsync([testKey]); diff --git a/test/Garnet.test/RespLowMemoryTests.cs b/test/Garnet.test/RespLowMemoryTests.cs index 091d100637..f6406f4001 100644 --- a/test/Garnet.test/RespLowMemoryTests.cs +++ b/test/Garnet.test/RespLowMemoryTests.cs @@ -48,7 +48,7 @@ public void PersistCopyUpdateTest() { using var redis = ConnectionMultiplexer.Connect(TestUtils.GetConfig(allowAdmin: true)); var db = redis.GetDatabase(0); - var server = redis.GetServer(TestUtils.Address, TestUtils.Port); + var server = redis.GetServer(TestUtils.EndPoint); var info = TestUtils.GetStoreAddressInfo(server); // Start at tail address of 64 diff --git a/test/Garnet.test/RespSortedSetGarnetClientTests.cs b/test/Garnet.test/RespSortedSetGarnetClientTests.cs index ef7d456381..230d5fa66e 100644 --- a/test/Garnet.test/RespSortedSetGarnetClientTests.cs +++ b/test/Garnet.test/RespSortedSetGarnetClientTests.cs @@ -63,7 +63,7 @@ public void TearDown() [Test] public async Task CanDoZAddGarnetAsync() { - using var db = new GarnetClient(TestUtils.Address, TestUtils.Port); + using var db = new GarnetClient(TestUtils.EndPoint); db.Connect(); List parameters = @@ -93,7 +93,7 @@ public async Task CanDoZAddGarnetAsync() [Test] public void CanDoZAddGarnetCallback() { - using var db = new GarnetClient(TestUtils.Address, TestUtils.Port); + using var db = new GarnetClient(TestUtils.EndPoint); db.Connect(); List parameters = ["myzset1", "1", "KEY1", "2", "KEY2"]; db.ExecuteForMemoryResult(SimpleMemoryResultCallback, 1, "ZADD", parameters); @@ -104,7 +104,7 @@ public void CanDoZAddGarnetCallback() [Test] public void CanDoExecuteForStringResultCallback() { - using var db = new GarnetClient(TestUtils.Address, TestUtils.Port); + using var db = new GarnetClient(TestUtils.EndPoint); db.Connect(); var expectedResult = "2"; List parameters = ["myzset1", "1", "KEY1", "2", "KEY2"]; @@ -130,7 +130,7 @@ public void CanDoExecuteForStringResultCallback() [Test] public async Task CanDoZCardGarnetUsingMemoryResultAsync() { - using var db = new GarnetClient(TestUtils.Address, TestUtils.Port); + using var db = new GarnetClient(TestUtils.EndPoint); db.Connect(); List parameters = ["myzset1", "1", "KEY1", "2", "KEY2"]; db.ExecuteForMemoryResult(SimpleMemoryResultCallback, 1, "ZADD", parameters); @@ -156,7 +156,7 @@ private void SimpleMemoryResultCallback(long context, MemoryResult result) [Test] public async Task CanDoZAddGarnetMultithread() { - using var db = new GarnetClient(TestUtils.Address, TestUtils.Port); + using var db = new GarnetClient(TestUtils.EndPoint); db.Connect(); var numThreads = 2; Task[] tasks = new Task[numThreads]; @@ -232,7 +232,7 @@ public async Task CanDoZAddGarnetMultithread() [Test] public async Task CanUseExecuteForStringArrayResult() { - using var db = new GarnetClient(TestUtils.Address, TestUtils.Port); + using var db = new GarnetClient(TestUtils.EndPoint); db.Connect(); List parameters = @@ -262,7 +262,7 @@ public async Task CanUseExecuteForStringArrayResult() [Test] public async Task CanUseExecuteForStringResultAsync() { - using var db = new GarnetClient(TestUtils.Address, TestUtils.Port); + using var db = new GarnetClient(TestUtils.EndPoint); db.Connect(); List parameters = []; parameters = ["myzset1", "1", "KEY1", "2", "KEY2"]; @@ -283,7 +283,7 @@ public async Task CanUseExecuteForStringResultAsync() [Test] public async Task CanUseExecuteForMemoryResultArrayWithCancellationTokenAsync() { - using var db = new GarnetClient(TestUtils.Address, TestUtils.Port); + using var db = new GarnetClient(TestUtils.EndPoint); db.Connect(); List parameters = @@ -323,7 +323,7 @@ public async Task CanUseExecuteForMemoryResultArrayWithCancellationTokenAsync() [Test] public async Task CanUseExecuteForMemoryResultWithCancellationTokenAsync() { - using var db = new GarnetClient(TestUtils.Address, TestUtils.Port); + using var db = new GarnetClient(TestUtils.EndPoint); db.Connect(); List parameters = @@ -368,7 +368,7 @@ public async Task CanUseExecuteForMemoryResultWithCancellationTokenAsync() private static async Task DoZRangeAsync(int taskId, CancellationToken ct) { var rnd = new Random(); - using var db = new GarnetClient(TestUtils.Address, TestUtils.Port); + using var db = new GarnetClient(TestUtils.EndPoint); db.Connect(); var parameters = new List(); @@ -412,7 +412,7 @@ private static string GetUniqueName(ConcurrentDictionary names, int [Test] public async Task CanGarnetClientUseZaddZrem() { - using var db = new GarnetClient(TestUtils.Address, TestUtils.Port); + using var db = new GarnetClient(TestUtils.EndPoint); db.Connect(); // add a new Sorted Set @@ -442,7 +442,7 @@ public async Task CanGarnetClientUseZaddZrem() [Test] public void CanGarnetClientUseZaddZremWithCallback() { - using var db = new GarnetClient(TestUtils.Address, TestUtils.Port); + using var db = new GarnetClient(TestUtils.EndPoint); db.Connect(); ManualResetEventSlim e = new(); @@ -490,7 +490,7 @@ public void CanGarnetClientUseZaddZremWithCallback() [Test] public async Task CanGarnetClientUseZCard() { - using var db = new GarnetClient(TestUtils.Address, TestUtils.Port); + using var db = new GarnetClient(TestUtils.EndPoint); db.Connect(); var expectedValue = 0; diff --git a/test/Garnet.test/RespSortedSetTests.cs b/test/Garnet.test/RespSortedSetTests.cs index 3abfaa67c2..250e1fbed4 100644 --- a/test/Garnet.test/RespSortedSetTests.cs +++ b/test/Garnet.test/RespSortedSetTests.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -268,7 +269,7 @@ public void AddWithOptions() testArgs = [key, "INCR", "3.5", "a"]; resp = db.Execute("ZADD", testArgs); - ClassicAssert.IsTrue(double.TryParse(resp.ToString(), out var newVal)); + ClassicAssert.IsTrue(double.TryParse(resp.ToString(), CultureInfo.InvariantCulture, out var newVal)); ClassicAssert.AreEqual(4.5, newVal); } diff --git a/test/Garnet.test/RespTests.cs b/test/Garnet.test/RespTests.cs index 17a411c9b5..01ed796c13 100644 --- a/test/Garnet.test/RespTests.cs +++ b/test/Garnet.test/RespTests.cs @@ -1387,7 +1387,7 @@ public void SingleDeleteWithObjectStoreDisabled() TearDown(); TestUtils.DeleteDirectory(TestUtils.MethodTestDir, wait: true); - server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, DisableObjects: true); + server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, disableObjects: true); server.Start(); var key = "delKey"; @@ -1419,7 +1419,7 @@ public void SingleDeleteWithObjectStoreDisable_LTM() TearDown(); TestUtils.DeleteDirectory(TestUtils.MethodTestDir, wait: true); - server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, lowMemory: true, DisableObjects: true); + server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, lowMemory: true, disableObjects: true); server.Start(); using var redis = ConnectionMultiplexer.Connect(TestUtils.GetConfig()); var db = redis.GetDatabase(0); @@ -1460,7 +1460,7 @@ public void MultiKeyDelete([Values] bool withoutObjectStore) { TearDown(); TestUtils.DeleteDirectory(TestUtils.MethodTestDir, wait: true); - server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, DisableObjects: true); + server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, disableObjects: true); server.Start(); } @@ -1528,7 +1528,7 @@ public void MultiKeyUnlink([Values] bool withoutObjectStore) { TearDown(); TestUtils.DeleteDirectory(TestUtils.MethodTestDir, wait: true); - server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, DisableObjects: true); + server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, disableObjects: true); server.Start(); } @@ -1594,7 +1594,7 @@ public void SingleExists([Values] bool withoutObjectStore) { TearDown(); TestUtils.DeleteDirectory(TestUtils.MethodTestDir, wait: true); - server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, DisableObjects: true); + server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, disableObjects: true); server.Start(); } using var redis = ConnectionMultiplexer.Connect(TestUtils.GetConfig()); @@ -1859,7 +1859,7 @@ public void SingleRenameKeyEdgeCase([Values] bool withoutObjectStore) { TearDown(); TestUtils.DeleteDirectory(TestUtils.MethodTestDir, wait: true); - server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, DisableObjects: true); + server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, disableObjects: true); server.Start(); } using var redis = ConnectionMultiplexer.Connect(TestUtils.GetConfig()); @@ -3769,7 +3769,7 @@ public void AsyncTest1() // Set up low-memory database TearDown(); TestUtils.DeleteDirectory(TestUtils.MethodTestDir, wait: true); - server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, lowMemory: true, DisableObjects: true); + server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, lowMemory: true, disableObjects: true); server.Start(); string firstKey = null, firstValue = null, lastKey = null, lastValue = null; diff --git a/test/Garnet.test/RespTlsTests.cs b/test/Garnet.test/RespTlsTests.cs index 7e61240516..bfe58dd7a7 100644 --- a/test/Garnet.test/RespTlsTests.cs +++ b/test/Garnet.test/RespTlsTests.cs @@ -23,7 +23,7 @@ public class RespTlsTests public void Setup() { TestUtils.DeleteDirectory(TestUtils.MethodTestDir, wait: true); - server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, EnableTLS: true); + server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, enableTLS: true); server.Start(); } diff --git a/test/Garnet.test/TestUtils.cs b/test/Garnet.test/TestUtils.cs index fbca671df8..662f6dcaaa 100644 --- a/test/Garnet.test/TestUtils.cs +++ b/test/Garnet.test/TestUtils.cs @@ -9,6 +9,7 @@ using System.Net; using System.Net.NetworkInformation; using System.Net.Security; +using System.Net.Sockets; using System.Security.Cryptography.X509Certificates; using System.Text; using System.Threading; @@ -43,14 +44,9 @@ public struct StoreAddressInfo internal static class TestUtils { /// - /// Address + /// Test server end point /// - public static string Address = "127.0.0.1"; - - /// - /// Port - /// - public static int Port = 33278; + public static EndPoint EndPoint = new IPEndPoint(IPAddress.Loopback, 33278); /// /// Whether to use a test progress logger @@ -159,14 +155,14 @@ private static bool TryGetRespCommandData(string resourcePath, ILogger lo static bool IsAzuriteRunning() { // If Azurite is running, it will run on localhost and listen on port 10000 and/or 10001. - IPAddress expectedIp = new([127, 0, 0, 1]); + var expectedIp = IPAddress.Loopback; var expectedPorts = new[] { 10000, 10001 }; var activeTcpListeners = IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners(); var relevantListeners = activeTcpListeners.Where(t => expectedPorts.Contains(t.Port) && - t.Address.Equals(expectedIp)) + t.Address == expectedIp) .ToList(); return relevantListeners.Any(); @@ -184,19 +180,20 @@ internal static void IgnoreIfNotRunningAzureTests() /// public static GarnetServer CreateGarnetServer( string logCheckpointDir, + EndPoint endpoint = null, bool disablePubSub = false, bool tryRecover = false, bool lowMemory = false, string MemorySize = default, string PageSize = default, bool enableAOF = false, - bool EnableTLS = false, - bool DisableObjects = false, + bool enableTLS = false, + bool disableObjects = false, int metricsSamplingFreq = -1, bool latencyMonitor = false, int commitFrequencyMs = 0, bool commitWait = false, - bool UseAzureStorage = false, + bool useAzureStorage = false, string defaultPassword = null, bool useAcl = false, // NOTE: Temporary until ACL is enforced as default string aclFile = null, @@ -219,21 +216,23 @@ public static GarnetServer CreateGarnetServer( string pubSubPageSize = null, bool asyncReplay = false, LuaMemoryManagementMode luaMemoryMode = LuaMemoryManagementMode.Native, - string luaMemoryLimit = "") + string luaMemoryLimit = "", + string unixSocketPath = null, + UnixFileMode unixSocketPermission = default) { - if (UseAzureStorage) + if (useAzureStorage) IgnoreIfNotRunningAzureTests(); - var _LogDir = logCheckpointDir; - if (UseAzureStorage) - _LogDir = $"{AzureTestContainer}/{AzureTestDirectory}"; + var logDir = logCheckpointDir; + if (useAzureStorage) + logDir = $"{AzureTestContainer}/{AzureTestDirectory}"; - if (logCheckpointDir != null && !UseAzureStorage) _LogDir = new DirectoryInfo(string.IsNullOrEmpty(_LogDir) ? "." : _LogDir).FullName; + if (logCheckpointDir != null && !useAzureStorage) logDir = new DirectoryInfo(string.IsNullOrEmpty(logDir) ? "." : logDir).FullName; - var _CheckpointDir = logCheckpointDir; - if (UseAzureStorage) - _CheckpointDir = $"{AzureTestContainer}/{AzureTestDirectory}"; + var checkpointDir = logCheckpointDir; + if (useAzureStorage) + checkpointDir = $"{AzureTestContainer}/{AzureTestDirectory}"; - if (logCheckpointDir != null && !UseAzureStorage) _CheckpointDir = new DirectoryInfo(string.IsNullOrEmpty(_CheckpointDir) ? "." : _CheckpointDir).FullName; + if (logCheckpointDir != null && !useAzureStorage) checkpointDir = new DirectoryInfo(string.IsNullOrEmpty(checkpointDir) ? "." : checkpointDir).FullName; if (useAcl) { @@ -262,10 +261,9 @@ public static GarnetServer CreateGarnetServer( GarnetServerOptions opts = new(logger) { EnableStorageTier = logCheckpointDir != null, - LogDir = _LogDir, - CheckpointDir = _CheckpointDir, - Address = Address, - Port = Port, + LogDir = logDir, + CheckpointDir = checkpointDir, + EndPoint = endpoint ?? EndPoint, DisablePubSub = disablePubSub, Recover = tryRecover, IndexSize = indexSize, @@ -274,7 +272,7 @@ public static GarnetServer CreateGarnetServer( EnableLua = enableLua, CommitFrequencyMs = commitFrequencyMs, WaitForCommit = commitWait, - TlsOptions = EnableTLS ? new GarnetTlsOptions( + TlsOptions = enableTLS ? new GarnetTlsOptions( certFileName: certFile, certPassword: certPassword, clientCertificateRequired: true, @@ -282,11 +280,11 @@ public static GarnetServer CreateGarnetServer( issuerCertificatePath: null, null, 0, false, null, logger: logger) : null, - DisableObjects = DisableObjects, + DisableObjects = disableObjects, QuietMode = true, MetricsSamplingFrequency = metricsSamplingFreq, LatencyMonitor = latencyMonitor, - DeviceFactoryCreator = UseAzureStorage ? + DeviceFactoryCreator = useAzureStorage ? () => new AzureStorageNamedDeviceFactory(AzureEmulatedStorageString, logger) : () => new LocalStorageNamedDeviceFactory(logger: logger), AuthSettings = authenticationSettings, @@ -300,6 +298,8 @@ public static GarnetServer CreateGarnetServer( EnableObjectStoreReadCache = enableObjectStoreReadCache, ReplicationOffsetMaxLag = asyncReplay ? -1 : 0, LuaOptions = enableLua ? new LuaOptions(luaMemoryMode, luaMemoryLimit, logger) : null, + UnixSocketPath = unixSocketPath, + UnixSocketPermission = unixSocketPermission }; if (!string.IsNullOrEmpty(pubSubPageSize)) @@ -413,7 +413,7 @@ public static GarnetServer[] CreateGarnetCluster( var opts = GetGarnetServerOptions( checkpointDir, checkpointDir, - endpoint.Port, + endpoint, disablePubSub, disableObjects, tryRecover, @@ -421,19 +421,19 @@ public static GarnetServer[] CreateGarnetCluster( timeout, gossipDelay, UseAzureStorage, - UseTLS: UseTLS, + useTLS: UseTLS, cleanClusterConfig: cleanClusterConfig, lowMemory: lowMemory, - MemorySize: MemorySize, - PageSize: PageSize, - SegmentSize: SegmentSize, - MainMemoryReplication: MainMemoryReplication, - AofMemorySize: AofMemorySize, - OnDemandCheckpoint: OnDemandCheckpoint, - CommitFrequencyMs: CommitFrequencyMs, - DisableStorageTier: DisableStorageTier, - EnableIncrementalSnapshots: EnableIncrementalSnapshots, - FastCommit: FastCommit, + memorySize: MemorySize, + pageSize: PageSize, + segmentSize: SegmentSize, + mainMemoryReplication: MainMemoryReplication, + aofMemorySize: AofMemorySize, + onDemandCheckpoint: OnDemandCheckpoint, + commitFrequencyMs: CommitFrequencyMs, + disableStorageTier: DisableStorageTier, + enableIncrementalSnapshots: EnableIncrementalSnapshots, + fastCommit: FastCommit, authUsername: authUsername, authPassword: authPassword, useAcl: useAcl, @@ -448,13 +448,18 @@ public static GarnetServer[] CreateGarnetCluster( luaMemoryLimit: luaMemoryLimit); ClassicAssert.IsNotNull(opts); - int iter = 0; - while (!IsPortAvailable(opts.Port)) + + if (opts.EndPoint is IPEndPoint ipEndpoint) { - ClassicAssert.Less(30, iter, "Failed to connect within 30 seconds"); - TestContext.Progress.WriteLine($"Waiting for Port {opts.Port} to become available for {TestContext.CurrentContext.WorkerId}:{iter++}"); - Thread.Sleep(1000); + int iter = 0; + while (!IsPortAvailable(ipEndpoint.Port)) + { + ClassicAssert.Less(30, iter, "Failed to connect within 30 seconds"); + TestContext.Progress.WriteLine($"Waiting for Port {ipEndpoint.Port} to become available for {TestContext.CurrentContext.WorkerId}:{iter++}"); + Thread.Sleep(1000); + } } + nodes[i] = new GarnetServer(opts, loggerFactory); } return nodes; @@ -463,27 +468,27 @@ public static GarnetServer[] CreateGarnetCluster( public static GarnetServerOptions GetGarnetServerOptions( string checkpointDir, string logDir, - int Port, + EndPoint endpoint, bool disablePubSub = false, bool disableObjects = false, bool tryRecover = false, bool enableAOF = false, int timeout = -1, int gossipDelay = 5, - bool UseAzureStorage = false, - bool UseTLS = false, + bool useAzureStorage = false, + bool useTLS = false, bool cleanClusterConfig = false, bool lowMemory = false, - string MemorySize = default, - string PageSize = default, - string SegmentSize = "1g", - bool MainMemoryReplication = false, - string AofMemorySize = "64m", - bool OnDemandCheckpoint = false, - int CommitFrequencyMs = 0, - bool DisableStorageTier = false, - bool EnableIncrementalSnapshots = false, - bool FastCommit = true, + string memorySize = default, + string pageSize = default, + string segmentSize = "1g", + bool mainMemoryReplication = false, + string aofMemorySize = "64m", + bool onDemandCheckpoint = false, + int commitFrequencyMs = 0, + bool disableStorageTier = false, + bool enableIncrementalSnapshots = false, + bool fastCommit = true, string authUsername = null, string authPassword = null, bool useAcl = false, // NOTE: Temporary until ACL is enforced as default @@ -495,19 +500,37 @@ public static GarnetServerOptions GetGarnetServerOptions( bool asyncReplay = false, ILogger logger = null, LuaMemoryManagementMode luaMemoryMode = LuaMemoryManagementMode.Native, - string luaMemoryLimit = "") + string luaMemoryLimit = "", + string unixSocketPath = null) { - if (UseAzureStorage) + if (useAzureStorage) IgnoreIfNotRunningAzureTests(); - var _LogDir = logDir + $"/{Port}"; - if (UseAzureStorage) - _LogDir = $"{AzureTestContainer}/{AzureTestDirectory}/{Port}"; - if (logDir != null && !UseAzureStorage) _LogDir = new DirectoryInfo(string.IsNullOrEmpty(_LogDir) ? "." : _LogDir).FullName; - var _CheckpointDir = checkpointDir + $"/{Port}"; - if (UseAzureStorage) - _CheckpointDir = $"{AzureTestContainer}/{AzureTestDirectory}/{Port}"; - if (!UseAzureStorage) _CheckpointDir = new DirectoryInfo(string.IsNullOrEmpty(_CheckpointDir) ? "." : _CheckpointDir).FullName; + if (useAzureStorage) + { + logDir = Path.Join(AzureTestContainer, AzureTestDirectory); + checkpointDir = Path.Join(AzureTestContainer, AzureTestDirectory); + } + + if (endpoint is IPEndPoint ipEndpoint) + { + logDir = Path.Join(logDir, ipEndpoint.Port.ToString()); + checkpointDir = Path.Join(checkpointDir, ipEndpoint.Port.ToString()); + } + else if (endpoint is UnixDomainSocketEndPoint && !string.IsNullOrEmpty(unixSocketPath)) + { + var socketFileName = Path.GetFileName(unixSocketPath); + + logDir = Path.Join(logDir, socketFileName); + checkpointDir = Path.Join(checkpointDir, socketFileName); + } + else throw new NotSupportedException("Unsupported endpoint type."); + + if (!useAzureStorage) + { + logDir = Path.GetFullPath(logDir); + checkpointDir = Path.GetFullPath(checkpointDir); + } IAuthenticationSettings authenticationSettings = null; if (useAcl && aadAuthenticationSettings != null) @@ -526,13 +549,12 @@ public static GarnetServerOptions GetGarnetServerOptions( GarnetServerOptions opts = new(logger) { ThreadPoolMinThreads = 100, - SegmentSize = SegmentSize, - ObjectStoreSegmentSize = SegmentSize, - EnableStorageTier = UseAzureStorage ? true : DisableStorageTier ? false : logDir != null, - LogDir = DisableStorageTier ? null : _LogDir, - CheckpointDir = _CheckpointDir, - Address = Address, - Port = Port, + SegmentSize = segmentSize, + ObjectStoreSegmentSize = segmentSize, + EnableStorageTier = useAzureStorage || (!disableStorageTier && logDir != null), + LogDir = disableStorageTier ? null : logDir, + CheckpointDir = checkpointDir, + EndPoint = endpoint, DisablePubSub = disablePubSub, DisableObjects = disableObjects, Recover = tryRecover, @@ -545,9 +567,9 @@ public static GarnetServerOptions GetGarnetServerOptions( EnableAOF = enableAOF, MemorySize = "1g", GossipDelay = gossipDelay, - EnableFastCommit = FastCommit, + EnableFastCommit = fastCommit, MetricsSamplingFrequency = metricsSamplingFrequency, - TlsOptions = UseTLS ? new GarnetTlsOptions( + TlsOptions = useTLS ? new GarnetTlsOptions( certFileName: certFile, certPassword: certPassword, clientCertificateRequired: true, @@ -568,26 +590,27 @@ public static GarnetServerOptions GetGarnetServerOptions( }, logger: logger) : null, - DeviceFactoryCreator = UseAzureStorage ? + DeviceFactoryCreator = useAzureStorage ? () => new AzureStorageNamedDeviceFactory(AzureEmulatedStorageString, logger) : () => new LocalStorageNamedDeviceFactory(logger: logger), - MainMemoryReplication = MainMemoryReplication, - AofMemorySize = AofMemorySize, - OnDemandCheckpoint = OnDemandCheckpoint, - CommitFrequencyMs = CommitFrequencyMs, - EnableIncrementalSnapshots = EnableIncrementalSnapshots, + MainMemoryReplication = mainMemoryReplication, + AofMemorySize = aofMemorySize, + OnDemandCheckpoint = onDemandCheckpoint, + CommitFrequencyMs = commitFrequencyMs, + EnableIncrementalSnapshots = enableIncrementalSnapshots, AuthSettings = useAcl ? authenticationSettings : (authPassword != null ? authenticationSettings : null), ClusterUsername = authUsername, ClusterPassword = authPassword, EnableLua = enableLua, ReplicationOffsetMaxLag = asyncReplay ? -1 : 0, LuaOptions = enableLua ? new LuaOptions(luaMemoryMode, luaMemoryLimit) : null, + UnixSocketPath = unixSocketPath }; if (lowMemory) { - opts.MemorySize = opts.ObjectStoreLogMemorySize = MemorySize == default ? "1024" : MemorySize; - opts.PageSize = opts.ObjectStorePageSize = PageSize == default ? "512" : PageSize; + opts.MemorySize = opts.ObjectStoreLogMemorySize = memorySize == default ? "1024" : memorySize; + opts.PageSize = opts.ObjectStorePageSize = pageSize == default ? "512" : pageSize; } return opts; @@ -617,7 +640,6 @@ public static bool IsPortAvailable(int port) /// public static ConfigurationOptions GetConfig( EndPointCollection endpoints = default, - int port = default, bool allowAdmin = false, bool disablePubSub = false, bool useTLS = false, @@ -636,7 +658,7 @@ public static ConfigurationOptions GetConfig( cmds.Remove("PUBLISH"); } - EndPointCollection defaultEndPoints = endpoints == default ? new() { { Address, port == default ? Port : port }, } : endpoints; + var defaultEndPoints = endpoints == default ? [EndPoint] : endpoints; var configOptions = new ConfigurationOptions { EndPoints = defaultEndPoints, @@ -679,7 +701,7 @@ public static ConfigurationOptions GetConfig( return configOptions; } - public static GarnetClient GetGarnetClient(bool useTLS = false, bool recordLatency = false) + public static GarnetClient GetGarnetClient(EndPoint endpoint = null, bool useTLS = false, bool recordLatency = false) { SslClientAuthenticationOptions sslOptions = null; if (useTLS) @@ -692,7 +714,7 @@ public static GarnetClient GetGarnetClient(bool useTLS = false, bool recordLaten RemoteCertificateValidationCallback = ValidateServerCertificate, }; } - return new GarnetClient(Address, Port, sslOptions, recordLatency: recordLatency); + return new GarnetClient(endpoint ?? EndPoint, sslOptions, recordLatency: recordLatency); } public static GarnetClientSession GetGarnetClientSession(bool useTLS = false, bool recordLatency = false) @@ -708,7 +730,7 @@ public static GarnetClientSession GetGarnetClientSession(bool useTLS = false, bo RemoteCertificateValidationCallback = ValidateServerCertificate, }; } - return new GarnetClientSession(Address, Port, new(), tlsOptions: sslOptions); + return new GarnetClientSession(EndPoint, new(), tlsOptions: sslOptions); } public static LightClientRequest CreateRequest(LightClient.OnResponseDelegateUnsafe onReceive = null, bool useTLS = false, CountResponseType countResponseType = CountResponseType.Tokens) @@ -724,15 +746,14 @@ public static LightClientRequest CreateRequest(LightClient.OnResponseDelegateUns RemoteCertificateValidationCallback = ValidateServerCertificate, }; } - return new LightClientRequest(Address, Port, 0, onReceive, sslOptions, countResponseType); + return new LightClientRequest(EndPoint, 0, onReceive, sslOptions, countResponseType); } - public static EndPointCollection GetEndPoints(int shards, int port = default) + public static EndPointCollection GetShardEndPoints(int shards, IPAddress address, int port) { - Port = port == default ? Port : port; EndPointCollection endPoints = []; for (int i = 0; i < shards; i++) - endPoints.Add(IPAddress.Parse("127.0.0.1"), Port + i); + endPoints.Add(address, port + i); return endPoints; } diff --git a/test/Garnet.test/UnixSocketTests.cs b/test/Garnet.test/UnixSocketTests.cs new file mode 100644 index 0000000000..cb0dabb09e --- /dev/null +++ b/test/Garnet.test/UnixSocketTests.cs @@ -0,0 +1,94 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +using System; +using System.Buffers; +using System.IO; +using System.Linq; +using System.Net.Sockets; +using System.Threading.Tasks; +using NUnit.Framework; +using NUnit.Framework.Legacy; +using StackExchange.Redis; + +namespace Garnet.test +{ + [TestFixture] + public class UnixSocketTests + { + [SetUp] + public void Setup() + { + TestUtils.DeleteDirectory(TestUtils.MethodTestDir, wait: true); + } + + [TearDown] + public void TearDown() + { + TestUtils.DeleteDirectory(TestUtils.MethodTestDir); + } + + [Test] + public async Task Permission_SetPermissionMatches([Values] bool useTls) + { + if (OperatingSystem.IsWindows()) + return; + + var unixSocketPath = "./unix-socket-permission-test.sock"; + var unixSocketEndpoint = new UnixDomainSocketEndPoint(unixSocketPath); + var unixSocketPermission = + UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute | + UnixFileMode.GroupRead | UnixFileMode.GroupWrite | UnixFileMode.GroupExecute; // 770 + + using var server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, unixSocketEndpoint, enableTLS: useTls, unixSocketPath: unixSocketPath, unixSocketPermission: unixSocketPermission); + server.Start(); + + using var client = await ConnectionMultiplexer.ConnectAsync(TestUtils.GetConfig([unixSocketEndpoint], useTLS: useTls)); + var db = client.GetDatabase(0); + + ClassicAssert.IsTrue(client.IsConnected); + ClassicAssert.AreEqual(unixSocketPermission, File.GetUnixFileMode(unixSocketPath)); + } + + [Test] + public async Task Ping_DoesNotThrow([Values] bool useTls) + { + var unixSocketPath = "./unix-socket-ping-test.sock"; + var unixSocketEndpoint = new UnixDomainSocketEndPoint(unixSocketPath); + + using var server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, unixSocketEndpoint, enableTLS: useTls, unixSocketPath: unixSocketPath); + server.Start(); + + using var client = await ConnectionMultiplexer.ConnectAsync(TestUtils.GetConfig([unixSocketEndpoint], useTLS: useTls)); + var db = client.GetDatabase(0); + + ClassicAssert.IsTrue(client.IsConnected); + Assert.DoesNotThrowAsync(() => db.PingAsync()); + } + + [Test] + public async Task SetGet_Equals([Values] bool useTls, [Values(256, 256 * 2048)] int valueBufferLength) + { + var unixSocketPath = "./unix-socket-set-get-test.sock"; + var unixSocketEndpoint = new UnixDomainSocketEndPoint(unixSocketPath); + + using var server = TestUtils.CreateGarnetServer(TestUtils.MethodTestDir, unixSocketEndpoint, enableTLS: useTls, unixSocketPath: unixSocketPath); + server.Start(); + + using var client = await ConnectionMultiplexer.ConnectAsync(TestUtils.GetConfig([unixSocketEndpoint], useTLS: useTls)); + var db = client.GetDatabase(0); + + var buffer = ArrayPool.Shared.Rent(valueBufferLength); + buffer.AsSpan().Fill(0x42); + + var result = await db.StringSetAsync("mykey", buffer); + ClassicAssert.IsTrue(result); + + var actualValue = (byte[])await db.StringGetAsync("mykey"); + + ClassicAssert.IsTrue(buffer.AsSpan().SequenceEqual(actualValue)); + + ArrayPool.Shared.Return(buffer); + } + } +} \ No newline at end of file