From 32f562effd41e49086b2fb21545c9abdadc2fd1b Mon Sep 17 00:00:00 2001
From: Guriy Samarin <guriy@amazon.com>
Date: Mon, 5 Feb 2024 17:52:57 +0000
Subject: [PATCH 01/10] up c# syntax, make cli defaults in one style

---
 benchmarks/csharp/Program.cs   |  30 +++----
 csharp/lib/AsyncClient.cs      | 160 ++++++++++++++++-----------------
 csharp/lib/Logger.cs           | 129 +++++++++++++-------------
 csharp/lib/Message.cs          |  24 +----
 csharp/lib/MessageContainer.cs |  82 ++++++++---------
 5 files changed, 196 insertions(+), 229 deletions(-)

diff --git a/benchmarks/csharp/Program.cs b/benchmarks/csharp/Program.cs
index 541e3100cd..f1aaabd34e 100644
--- a/benchmarks/csharp/Program.cs
+++ b/benchmarks/csharp/Program.cs
@@ -16,30 +16,30 @@ private enum ChosenAction { GET_NON_EXISTING, GET_EXISTING, SET };
 
     public class CommandLineOptions
     {
-        [Option('r', "resultsFile", Required = false, HelpText = "Set the file to which the JSON results are written.")]
-        public string resultsFile { get; set; } = "../results/csharp-results.json";
+        [Option('r', "resultsFile", Required = false, HelpText = "Set the file to which the JSON results are written.", Default = "../results/csharp-results.json")]
+        public string resultsFile { get; set; }
 
-        [Option('d', "dataSize", Required = false, HelpText = "The size of the sent data in bytes.")]
-        public int dataSize { get; set; } = 100;
+        [Option('d', "dataSize", Required = false, HelpText = "The size of the sent data in bytes.", Default = 100)]
+        public int dataSize { get; set; }
 
         [Option('c', "concurrentTasks", Required = false, HelpText = "The number of concurrent operations to perform.", Default = new[] { 1, 10, 100, 1000 })]
         public IEnumerable<int> concurrentTasks { get; set; }
 
-        [Option('l', "clients", Required = false, HelpText = "Which clients should run")]
-        public string clientsToRun { get; set; } = "all";
+        [Option('l', "clients", Required = false, HelpText = "Which clients should run", Default = "all")]
+        public string clientsToRun { get; set; }
 
-        [Option('h', "host", Required = false, HelpText = "What host to target")]
-        public string host { get; set; } = "localhost";
+        [Option('h', "host", Required = false, HelpText = "What host to target", Default = "localhost")]
+        public string host { get; set; }
 
         [Option('C', "clientCount", Required = false, HelpText = "Number of clients to run concurrently", Default = new[] { 1 })]
         public IEnumerable<int> clientCount { get; set; }
 
-        [Option('t', "tls", HelpText = "Should benchmark a TLS server")]
-        public bool tls { get; set; } = false;
+        [Option('t', "tls", HelpText = "Should benchmark a TLS server", Default = false)]
+        public bool tls { get; set; }
 
 
-        [Option('m', "minimal", HelpText = "Should use a minimal number of actions")]
-        public bool minimal { get; set; } = false;
+        [Option('m', "minimal", HelpText = "Should use a minimal number of actions", Default = false)]
+        public bool minimal { get; set; }
     }
 
     private const int PORT = 6379;
@@ -118,10 +118,8 @@ private static double calculate_latency(IEnumerable<double> latency_list, double
 
     private static void print_results(string resultsFile)
     {
-        using (FileStream createStream = File.Create(resultsFile))
-        {
-            JsonSerializer.Serialize(createStream, bench_json_results);
-        }
+        using FileStream createStream = File.Create(resultsFile);
+        JsonSerializer.Serialize(createStream, bench_json_results);
     }
 
     private static async Task redis_benchmark(
diff --git a/csharp/lib/AsyncClient.cs b/csharp/lib/AsyncClient.cs
index b50fe77bf4..459407704a 100644
--- a/csharp/lib/AsyncClient.cs
+++ b/csharp/lib/AsyncClient.cs
@@ -4,114 +4,110 @@
 
 using System.Runtime.InteropServices;
 
-namespace Glide
+namespace Glide;
+
+public class AsyncClient : IDisposable
 {
-    public class AsyncClient : IDisposable
+    #region public methods
+    public AsyncClient(string host, UInt32 port, bool useTLS)
     {
-        #region public methods
-        public AsyncClient(string host, UInt32 port, bool useTLS)
+        successCallbackDelegate = SuccessCallback;
+        var successCallbackPointer = Marshal.GetFunctionPointerForDelegate(successCallbackDelegate);
+        failureCallbackDelegate = FailureCallback;
+        var failureCallbackPointer = Marshal.GetFunctionPointerForDelegate(failureCallbackDelegate);
+        clientPointer = CreateClientFfi(host, port, useTLS, successCallbackPointer, failureCallbackPointer);
+        if (clientPointer == IntPtr.Zero)
         {
-            successCallbackDelegate = SuccessCallback;
-            var successCallbackPointer = Marshal.GetFunctionPointerForDelegate(successCallbackDelegate);
-            failureCallbackDelegate = FailureCallback;
-            var failureCallbackPointer = Marshal.GetFunctionPointerForDelegate(failureCallbackDelegate);
-            clientPointer = CreateClientFfi(host, port, useTLS, successCallbackPointer, failureCallbackPointer);
-            if (clientPointer == IntPtr.Zero)
-            {
-                throw new Exception("Failed creating a client");
-            }
+            throw new Exception("Failed creating a client");
         }
+    }
 
-        public async Task SetAsync(string key, string value)
-        {
-            var message = messageContainer.GetMessageForCall(key, value);
-            SetFfi(clientPointer, (ulong)message.Index, message.KeyPtr, message.ValuePtr);
-            await message;
-        }
+    public async Task SetAsync(string key, string value)
+    {
+        var message = messageContainer.GetMessageForCall(key, value);
+        SetFfi(clientPointer, (ulong)message.Index, message.KeyPtr, message.ValuePtr);
+        await message;
+    }
 
-        public async Task<string?> GetAsync(string key)
-        {
-            var message = messageContainer.GetMessageForCall(key, null);
-            GetFfi(clientPointer, (ulong)message.Index, message.KeyPtr);
-            return await message;
-        }
+    public async Task<string?> GetAsync(string key)
+    {
+        var message = messageContainer.GetMessageForCall(key, null);
+        GetFfi(clientPointer, (ulong)message.Index, message.KeyPtr);
+        return await message;
+    }
 
-        public void Dispose()
+    public void Dispose()
+    {
+        if (clientPointer == IntPtr.Zero)
         {
-            if (clientPointer == IntPtr.Zero)
-            {
-                return;
-            }
-            messageContainer.DisposeWithError(null);
-            CloseClientFfi(clientPointer);
-            clientPointer = IntPtr.Zero;
+            return;
         }
+        messageContainer.DisposeWithError(null);
+        CloseClientFfi(clientPointer);
+        clientPointer = IntPtr.Zero;
+    }
 
-        #endregion public methods
+    #endregion public methods
 
-        #region private methods
+    #region private methods
 
-        private void SuccessCallback(ulong index, IntPtr str)
+    private void SuccessCallback(ulong index, IntPtr str)
+    {
+        var result = str == IntPtr.Zero ? null : Marshal.PtrToStringAnsi(str);
+        // Work needs to be offloaded from the calling thread, because otherwise we might starve the client's thread pool.
+        Task.Run(() =>
         {
-            var result = str == IntPtr.Zero ? null : Marshal.PtrToStringAnsi(str);
-            // Work needs to be offloaded from the calling thread, because otherwise we might starve the client's thread pool.
-            Task.Run(() =>
-            {
-                var message = messageContainer.GetMessage((int)index);
-                message.SetResult(result);
-            });
-        }
+            var message = messageContainer.GetMessage((int)index);
+            message.SetResult(result);
+        });
+    }
 
-        private void FailureCallback(ulong index)
+    private void FailureCallback(ulong index)
+    {
+        // Work needs to be offloaded from the calling thread, because otherwise we might starve the client's thread pool.
+        Task.Run(() =>
         {
-            // Work needs to be offloaded from the calling thread, because otherwise we might starve the client's thread pool.
-            Task.Run(() =>
-            {
-                var message = messageContainer.GetMessage((int)index);
-                message.SetException(new Exception("Operation failed"));
-            });
-        }
+            var message = messageContainer.GetMessage((int)index);
+            message.SetException(new Exception("Operation failed"));
+        });
+    }
 
-        ~AsyncClient()
-        {
-            Dispose();
-        }
-        #endregion private methods
+    ~AsyncClient() => Dispose();
+    #endregion private methods
 
-        #region private fields
+    #region private fields
 
-        /// Held as a measure to prevent the delegate being garbage collected. These are delegated once
-        /// and held in order to prevent the cost of marshalling on each function call.
-        private FailureAction failureCallbackDelegate;
+    /// Held as a measure to prevent the delegate being garbage collected. These are delegated once
+    /// and held in order to prevent the cost of marshalling on each function call.
+    private FailureAction failureCallbackDelegate;
 
-        /// Held as a measure to prevent the delegate being garbage collected. These are delegated once
-        /// and held in order to prevent the cost of marshalling on each function call.
-        private StringAction successCallbackDelegate;
+    /// Held as a measure to prevent the delegate being garbage collected. These are delegated once
+    /// and held in order to prevent the cost of marshalling on each function call.
+    private StringAction successCallbackDelegate;
 
-        /// Raw pointer to the underlying native client.
-        private IntPtr clientPointer;
+    /// Raw pointer to the underlying native client.
+    private IntPtr clientPointer;
 
-        private readonly MessageContainer<string> messageContainer = new();
+    private readonly MessageContainer<string> messageContainer = new();
 
-        #endregion private fields
+    #endregion private fields
 
-        #region FFI function declarations
+    #region FFI function declarations
 
-        private delegate void StringAction(ulong index, IntPtr str);
-        private delegate void FailureAction(ulong index);
-        [DllImport("libglide_rs", CallingConvention = CallingConvention.Cdecl, EntryPoint = "get")]
-        private static extern void GetFfi(IntPtr client, ulong index, IntPtr key);
+    private delegate void StringAction(ulong index, IntPtr str);
+    private delegate void FailureAction(ulong index);
+    [DllImport("libglide_rs", CallingConvention = CallingConvention.Cdecl, EntryPoint = "get")]
+    private static extern void GetFfi(IntPtr client, ulong index, IntPtr key);
 
-        [DllImport("libglide_rs", CallingConvention = CallingConvention.Cdecl, EntryPoint = "set")]
-        private static extern void SetFfi(IntPtr client, ulong index, IntPtr key, IntPtr value);
+    [DllImport("libglide_rs", CallingConvention = CallingConvention.Cdecl, EntryPoint = "set")]
+    private static extern void SetFfi(IntPtr client, ulong index, IntPtr key, IntPtr value);
 
-        private delegate void IntAction(IntPtr arg);
-        [DllImport("libglide_rs", CallingConvention = CallingConvention.Cdecl, EntryPoint = "create_client")]
-        private static extern IntPtr CreateClientFfi(String host, UInt32 port, bool useTLS, IntPtr successCallback, IntPtr failureCallback);
+    private delegate void IntAction(IntPtr arg);
+    [DllImport("libglide_rs", CallingConvention = CallingConvention.Cdecl, EntryPoint = "create_client")]
+    private static extern IntPtr CreateClientFfi(String host, UInt32 port, bool useTLS, IntPtr successCallback, IntPtr failureCallback);
 
-        [DllImport("libglide_rs", CallingConvention = CallingConvention.Cdecl, EntryPoint = "close_client")]
-        private static extern void CloseClientFfi(IntPtr client);
+    [DllImport("libglide_rs", CallingConvention = CallingConvention.Cdecl, EntryPoint = "close_client")]
+    private static extern void CloseClientFfi(IntPtr client);
 
-        #endregion
-    }
+    #endregion
 }
diff --git a/csharp/lib/Logger.cs b/csharp/lib/Logger.cs
index da9e172090..7edc16f16c 100644
--- a/csharp/lib/Logger.cs
+++ b/csharp/lib/Logger.cs
@@ -6,83 +6,80 @@
 using System.Text;
 
 
-namespace Glide
-{
-    // TODO - use a bindings generator to create this enum.
-    public enum Level
-    {
-        Error = 0,
-        Warn = 1,
-        Info = 2,
-        Debug = 3,
-        Trace = 4
-    }
+namespace Glide;
 
-    /*
-    A class that allows logging which is consistent with logs from the internal rust core.
-    Only one instance of this class can exist at any given time. The logger can be set up in 2 ways -
-        1. By calling init, which creates and modifies a new logger only if one doesn't exist.
-        2. By calling setConfig, which replaces the existing logger, and means that new logs will not be saved with the logs that were sent before the call.
-    If no call to any of these function is received, the first log attempt will initialize a new logger with default level decided by rust core (normally - console, error).
-    */
-    public class Logger
-    {
-        #region private fields
+// TODO - use a bindings generator to create this enum.
+public enum Level
+{
+    Error = 0,
+    Warn = 1,
+    Info = 2,
+    Debug = 3,
+    Trace = 4
+}
 
-        private static Level? loggerLevel = null;
-        #endregion private fields
+/*
+A class that allows logging which is consistent with logs from the internal rust core.
+Only one instance of this class can exist at any given time. The logger can be set up in 2 ways -
+    1. By calling init, which creates and modifies a new logger only if one doesn't exist.
+    2. By calling setConfig, which replaces the existing logger, and means that new logs will not be saved with the logs that were sent before the call.
+If no call to any of these function is received, the first log attempt will initialize a new logger with default level decided by rust core (normally - console, error).
+*/
+public class Logger
+{
+    #region private fields
 
-        #region internal methods
-        // Initialize a logger instance if none were initialized before - this method is meant to be used when there is no intention to replace an existing logger.
-        // The logger will filter all logs with a level lower than the given level,
-        // If given a fileName argument, will write the logs to files postfixed with fileName. If fileName isn't provided, the logs will be written to the console.
-        internal static void Init(Level? level, string? filename = null)
-        {
-            if (Logger.loggerLevel is null)
-            {
-                SetLoggerConfig(level, filename);
-            }
-        }
+    private static Level? loggerLevel = null;
+    #endregion private fields
 
-        // take the arguments from the user and provide to the core-logger (see ../logger-core)
-        // if the level is higher then the logger level (error is 0, warn 1, etc.) simply return without operation
-        // if a logger instance doesn't exist, create new one with default mode (decided by rust core, normally - level: error, target: console)
-        // logIdentifier arg is a string contain data that suppose to give the log a context and make it easier to find certain type of logs.
-        // when the log is connect to certain task the identifier should be the task id, when the log is not part of specific task the identifier should give a context to the log - for example, "create client".
-        internal static void Log(Level logLevel, string logIdentifier, string message)
+    #region internal methods
+    // Initialize a logger instance if none were initialized before - this method is meant to be used when there is no intention to replace an existing logger.
+    // The logger will filter all logs with a level lower than the given level,
+    // If given a fileName argument, will write the logs to files postfixed with fileName. If fileName isn't provided, the logs will be written to the console.
+    internal static void Init(Level? level, string? filename = null)
+    {
+        if (Logger.loggerLevel is null)
         {
-            if (Logger.loggerLevel is null)
-            {
-                SetLoggerConfig(logLevel);
-            }
-            if (!(logLevel <= Logger.loggerLevel)) return;
-            log(Convert.ToInt32(logLevel), Encoding.UTF8.GetBytes(logIdentifier), Encoding.UTF8.GetBytes(message));
+            SetLoggerConfig(level, filename);
         }
-        #endregion internal methods
+    }
 
-        #region public methods
-        // config the logger instance - in fact - create new logger instance with the new args
-        // exist in addition to init for two main reason's:
-        // 1. if GLIDE dev want intentionally to change the logger instance configuration
-        // 2. external user want to set the logger and we don't want to return to him the logger itself, just config it
-        // the level argument is the level of the logs you want the system to provide (error logs, warn logs, etc.)
-        // the filename argument is optional - if provided the target of the logs will be the file mentioned, else will be the console
-        public static void SetLoggerConfig(Level? level, string? filename = null)
+    // take the arguments from the user and provide to the core-logger (see ../logger-core)
+    // if the level is higher then the logger level (error is 0, warn 1, etc.) simply return without operation
+    // if a logger instance doesn't exist, create new one with default mode (decided by rust core, normally - level: error, target: console)
+    // logIdentifier arg is a string contain data that suppose to give the log a context and make it easier to find certain type of logs.
+    // when the log is connect to certain task the identifier should be the task id, when the log is not part of specific task the identifier should give a context to the log - for example, "create client".
+    internal static void Log(Level logLevel, string logIdentifier, string message)
+    {
+        if (Logger.loggerLevel is null)
         {
-            var buffer = filename is null ? null : Encoding.UTF8.GetBytes(filename);
-            Logger.loggerLevel = InitInternalLogger(Convert.ToInt32(level), buffer);
+            SetLoggerConfig(logLevel);
         }
-        #endregion public methods
-
-        #region FFI function declaration
-        [DllImport("libglide_rs", CallingConvention = CallingConvention.Cdecl, EntryPoint = "log")]
-        private static extern void log(Int32 logLevel, byte[] logIdentifier, byte[] message);
-
-        [DllImport("libglide_rs", CallingConvention = CallingConvention.Cdecl, EntryPoint = "init")]
-        private static extern Level InitInternalLogger(Int32 level, byte[]? filename);
+        if (!(logLevel <= Logger.loggerLevel)) return;
+        log(Convert.ToInt32(logLevel), Encoding.UTF8.GetBytes(logIdentifier), Encoding.UTF8.GetBytes(message));
+    }
+    #endregion internal methods
 
-        #endregion
+    #region public methods
+    // config the logger instance - in fact - create new logger instance with the new args
+    // exist in addition to init for two main reason's:
+    // 1. if GLIDE dev want intentionally to change the logger instance configuration
+    // 2. external user want to set the logger and we don't want to return to him the logger itself, just config it
+    // the level argument is the level of the logs you want the system to provide (error logs, warn logs, etc.)
+    // the filename argument is optional - if provided the target of the logs will be the file mentioned, else will be the console
+    public static void SetLoggerConfig(Level? level, string? filename = null)
+    {
+        var buffer = filename is null ? null : Encoding.UTF8.GetBytes(filename);
+        Logger.loggerLevel = InitInternalLogger(Convert.ToInt32(level), buffer);
     }
+    #endregion public methods
+
+    #region FFI function declaration
+    [DllImport("libglide_rs", CallingConvention = CallingConvention.Cdecl, EntryPoint = "log")]
+    private static extern void log(Int32 logLevel, byte[] logIdentifier, byte[] message);
 
+    [DllImport("libglide_rs", CallingConvention = CallingConvention.Cdecl, EntryPoint = "init")]
+    private static extern Level InitInternalLogger(Int32 level, byte[]? filename);
 
+    #endregion
 }
diff --git a/csharp/lib/Message.cs b/csharp/lib/Message.cs
index 0263df7cd1..daa11602c6 100644
--- a/csharp/lib/Message.cs
+++ b/csharp/lib/Message.cs
@@ -38,7 +38,7 @@ public Message(int index, MessageContainer<T> container)
     private T? result;
     private Exception? exception;
 
-    /// Triggers a succesful completion of the task returned from the latest call 
+    /// Triggers a succesful completion of the task returned from the latest call
     /// to CreateTask.
     public void SetResult(T? result)
     {
@@ -78,10 +78,7 @@ private void CheckRaceAndCallContinuation()
         }
     }
 
-    public Message<T> GetAwaiter()
-    {
-        return this;
-    }
+    public Message<T> GetAwaiter() => this;
 
     /// This returns a task that will complete once SetException / SetResult are called,
     /// and ensures that the internal state of the message is set-up before the task is created,
@@ -124,20 +121,7 @@ public void OnCompleted(Action continuation)
         CheckRaceAndCallContinuation();
     }
 
-    public bool IsCompleted
-    {
-        get
-        {
-            return completionState == COMPLETION_STAGE_CONTINUATION_EXECUTED;
-        }
-    }
+    public bool IsCompleted => completionState == COMPLETION_STAGE_CONTINUATION_EXECUTED;
 
-    public T? GetResult()
-    {
-        if (this.exception != null)
-        {
-            throw this.exception;
-        }
-        return this.result;
-    }
+    public T? GetResult() => this.exception is null ? this.result : throw this.exception;
 }
diff --git a/csharp/lib/MessageContainer.cs b/csharp/lib/MessageContainer.cs
index ddc19e3323..5999702363 100644
--- a/csharp/lib/MessageContainer.cs
+++ b/csharp/lib/MessageContainer.cs
@@ -4,67 +4,59 @@
 
 using System.Collections.Concurrent;
 
-namespace Glide
+namespace Glide;
+
+
+internal class MessageContainer<T>
 {
+    internal Message<T> GetMessage(int index) => messages[index];
 
-    internal class MessageContainer<T>
+    internal Message<T> GetMessageForCall(string? key, string? value)
     {
-        internal Message<T> GetMessage(int index)
-        {
-            return messages[index];
-        }
-
-        internal Message<T> GetMessageForCall(string? key, string? value)
-        {
-            var message = GetFreeMessage();
-            message.StartTask(key, value, this);
-            return message;
-        }
+        var message = GetFreeMessage();
+        message.StartTask(key, value, this);
+        return message;
+    }
 
-        private Message<T> GetFreeMessage()
+    private Message<T> GetFreeMessage()
+    {
+        if (!availableMessages.TryDequeue(out var message))
         {
-            if (!availableMessages.TryDequeue(out var message))
+            lock (messages)
             {
-                lock (messages)
-                {
-                    var index = messages.Count;
-                    message = new Message<T>(index, this);
-                    messages.Add(message);
-                }
+                var index = messages.Count;
+                message = new Message<T>(index, this);
+                messages.Add(message);
             }
-            return message;
         }
+        return message;
+    }
 
-        public void ReturnFreeMessage(Message<T> message)
-        {
-            availableMessages.Enqueue(message);
-        }
+    public void ReturnFreeMessage(Message<T> message) => availableMessages.Enqueue(message);
 
-        internal void DisposeWithError(Exception? error)
+    internal void DisposeWithError(Exception? error)
+    {
+        lock (messages)
         {
-            lock (messages)
+            foreach (var message in messages.Where(message => !message.IsCompleted))
             {
-                foreach (var message in messages.Where(message => !message.IsCompleted))
+                try
                 {
-                    try
-                    {
-                        message.SetException(new TaskCanceledException("Client closed", error));
-                    }
-                    catch (Exception) { }
+                    message.SetException(new TaskCanceledException("Client closed", error));
                 }
-                messages.Clear();
+                catch (Exception) { }
             }
-            availableMessages.Clear();
+            messages.Clear();
         }
-
-        /// This list allows us random-access to the message in each index,
-        /// which means that once we receive a callback with an index, we can
-        /// find the message to resolve in constant time.
-        private List<Message<T>> messages = new();
-
-        /// This queue contains the messages that were created and are currently unused by any task,
-        /// so they can be reused y new tasks instead of allocating new messages.
-        private ConcurrentQueue<Message<T>> availableMessages = new();
+        availableMessages.Clear();
     }
 
+    /// This list allows us random-access to the message in each index,
+    /// which means that once we receive a callback with an index, we can
+    /// find the message to resolve in constant time.
+    private List<Message<T>> messages = new();
+
+    /// This queue contains the messages that were created and are currently unused by any task,
+    /// so they can be reused y new tasks instead of allocating new messages.
+    private ConcurrentQueue<Message<T>> availableMessages = new();
 }

From 9d1c26410374cb335227c4200a706c56d13a6507 Mon Sep 17 00:00:00 2001
From: Guriy Samarin <guriy@amazon.com>
Date: Tue, 6 Feb 2024 00:46:27 +0000
Subject: [PATCH 02/10] add .editorconfig

---
 .editorconfig | 372 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 372 insertions(+)
 create mode 100644 .editorconfig

diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000000..0002f363fa
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,372 @@
+root = true
+
+# All files
+[*]
+trim_trailing_whitespace = true
+insert_final_newline = true
+indent_style = space
+
+# Xml files
+[*.xml]
+indent_size = 2
+
+# C# files
+[*.cs]
+
+#### Core EditorConfig Options ####
+
+# Indentation and spacing
+indent_size = 4
+tab_width = 4
+
+# New line preferences
+end_of_line = crlf
+insert_final_newline = false
+
+#### .NET Coding Conventions ####
+[*.{cs,vb}]
+
+# Organize usings
+dotnet_separate_import_directive_groups = true
+dotnet_sort_system_directives_first = true
+file_header_template = unset
+
+# this. and Me. preferences
+dotnet_style_qualification_for_event = false:silent
+dotnet_style_qualification_for_field = false:silent
+dotnet_style_qualification_for_method = false:silent
+dotnet_style_qualification_for_property = false:silent
+
+# Language keywords vs BCL types preferences
+dotnet_style_predefined_type_for_locals_parameters_members = true:silent
+dotnet_style_predefined_type_for_member_access = true:silent
+
+# Parentheses preferences
+dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent
+dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent
+dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
+dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent
+
+# Modifier preferences
+dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion
+
+# Expression-level preferences
+dotnet_style_coalesce_expression = true:suggestion
+dotnet_style_collection_initializer = true:suggestion
+dotnet_style_explicit_tuple_names = true:suggestion
+dotnet_style_null_propagation = true:suggestion
+dotnet_style_object_initializer = true:suggestion
+dotnet_style_operator_placement_when_wrapping = beginning_of_line
+dotnet_style_prefer_auto_properties = true:suggestion
+dotnet_style_prefer_compound_assignment = true:suggestion
+dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion
+dotnet_style_prefer_conditional_expression_over_return = true:suggestion
+dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
+dotnet_style_prefer_inferred_tuple_names = true:suggestion
+dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
+dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
+dotnet_style_prefer_simplified_interpolation = true:suggestion
+
+# Field preferences
+dotnet_style_readonly_field = true:warning
+
+# Parameter preferences
+dotnet_code_quality_unused_parameters = all:suggestion
+
+# Suppression preferences
+dotnet_remove_unnecessary_suppression_exclusions = none
+
+#### C# Coding Conventions ####
+[*.cs]
+
+# var preferences
+csharp_style_var_elsewhere = false:suggestion
+csharp_style_var_for_built_in_types = false:suggestion
+csharp_style_var_when_type_is_apparent = false:suggestion
+csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion
+
+# Expression-bodied members
+csharp_style_expression_bodied_accessors = true:silent
+csharp_style_expression_bodied_constructors = false:silent
+csharp_style_expression_bodied_indexers = true:suggestion
+csharp_style_expression_bodied_lambdas = true:suggestion
+csharp_style_expression_bodied_local_functions = false:silent
+csharp_style_expression_bodied_methods = true:suggestion
+csharp_style_expression_bodied_operators = true:suggestion
+csharp_style_expression_bodied_properties = true:suggestion
+
+# Pattern matching preferences
+csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
+csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
+csharp_style_prefer_not_pattern = true:suggestion
+csharp_style_prefer_pattern_matching = true:silent
+csharp_style_prefer_switch_expression = true:suggestion
+csharp_style_prefer_tuple_swap = true:suggestion
+csharp_style_prefer_extended_property_pattern = true:suggestion
+csharp_style_prefer_method_group_conversion = true:silent
+
+# Null-checking preferences
+csharp_style_conditional_delegate_call = true:suggestion
+
+# Modifier preferences
+csharp_prefer_static_local_function = true:warning
+csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion
+
+# Code-block preferences
+csharp_prefer_braces = true:silent
+csharp_prefer_simple_using_statement = true:suggestion
+csharp_style_namespace_declarations = file_scoped:suggestion
+dotnet_style_namespace_match_folder = true:suggestion
+csharp_style_prefer_top_level_statements = true:silent
+
+# Expression-level preferences
+csharp_prefer_simple_default_expression = true:suggestion
+csharp_style_deconstructed_variable_declaration = true:suggestion
+csharp_style_inlined_variable_declaration = true:suggestion
+csharp_style_pattern_local_over_anonymous_function = true:suggestion
+csharp_style_prefer_index_operator = true:suggestion
+csharp_style_prefer_range_operator = true:suggestion
+csharp_style_throw_expression = true:suggestion
+csharp_style_unused_value_assignment_preference = discard_variable:suggestion
+csharp_style_unused_value_expression_statement_preference = discard_variable:silent
+
+# 'using' directive preferences
+csharp_using_directive_placement = outside_namespace:silent
+
+#### C# Formatting Rules ####
+
+# New line preferences
+csharp_new_line_before_catch = true
+csharp_new_line_before_else = true
+csharp_new_line_before_finally = true
+csharp_new_line_before_members_in_anonymous_types = true
+csharp_new_line_before_members_in_object_initializers = true
+csharp_new_line_before_open_brace = all
+csharp_new_line_between_query_expression_clauses = true
+
+# Indentation preferences
+csharp_indent_block_contents = true
+csharp_indent_braces = false
+csharp_indent_case_contents = true
+csharp_indent_case_contents_when_block = true
+csharp_indent_labels = one_less_than_current
+csharp_indent_switch_labels = true
+
+# Space preferences
+csharp_space_after_cast = false
+csharp_space_after_colon_in_inheritance_clause = true
+csharp_space_after_comma = true
+csharp_space_after_dot = false
+csharp_space_after_keywords_in_control_flow_statements = true
+csharp_space_after_semicolon_in_for_statement = true
+csharp_space_around_binary_operators = before_and_after
+csharp_space_around_declaration_statements = false
+csharp_space_before_colon_in_inheritance_clause = true
+csharp_space_before_comma = false
+csharp_space_before_dot = false
+csharp_space_before_open_square_brackets = false
+csharp_space_before_semicolon_in_for_statement = false
+csharp_space_between_empty_square_brackets = false
+csharp_space_between_method_call_empty_parameter_list_parentheses = false
+csharp_space_between_method_call_name_and_opening_parenthesis = false
+csharp_space_between_method_call_parameter_list_parentheses = false
+csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
+csharp_space_between_method_declaration_name_and_open_parenthesis = false
+csharp_space_between_method_declaration_parameter_list_parentheses = false
+csharp_space_between_parentheses = false
+csharp_space_between_square_brackets = false
+
+# Wrapping preferences
+csharp_preserve_single_line_blocks = true
+csharp_preserve_single_line_statements = true
+
+#### Naming styles ####
+[*.{cs,vb}]
+
+# Naming rules
+
+dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.symbols = types_and_namespaces
+dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.style = pascalcase
+
+dotnet_naming_rule.interfaces_should_be_ipascalcase.severity = suggestion
+dotnet_naming_rule.interfaces_should_be_ipascalcase.symbols = interfaces
+dotnet_naming_rule.interfaces_should_be_ipascalcase.style = ipascalcase
+
+dotnet_naming_rule.type_parameters_should_be_tpascalcase.severity = suggestion
+dotnet_naming_rule.type_parameters_should_be_tpascalcase.symbols = type_parameters
+dotnet_naming_rule.type_parameters_should_be_tpascalcase.style = tpascalcase
+
+dotnet_naming_rule.methods_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.methods_should_be_pascalcase.symbols = methods
+dotnet_naming_rule.methods_should_be_pascalcase.style = pascalcase
+
+dotnet_naming_rule.properties_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.properties_should_be_pascalcase.symbols = properties
+dotnet_naming_rule.properties_should_be_pascalcase.style = pascalcase
+
+dotnet_naming_rule.events_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.events_should_be_pascalcase.symbols = events
+dotnet_naming_rule.events_should_be_pascalcase.style = pascalcase
+
+dotnet_naming_rule.local_variables_should_be_camelcase.severity = suggestion
+dotnet_naming_rule.local_variables_should_be_camelcase.symbols = local_variables
+dotnet_naming_rule.local_variables_should_be_camelcase.style = camelcase
+
+dotnet_naming_rule.local_constants_should_be_camelcase.severity = suggestion
+dotnet_naming_rule.local_constants_should_be_camelcase.symbols = local_constants
+dotnet_naming_rule.local_constants_should_be_camelcase.style = camelcase
+
+dotnet_naming_rule.parameters_should_be_camelcase.severity = suggestion
+dotnet_naming_rule.parameters_should_be_camelcase.symbols = parameters
+dotnet_naming_rule.parameters_should_be_camelcase.style = camelcase
+
+dotnet_naming_rule.public_fields_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.public_fields_should_be_pascalcase.symbols = public_fields
+dotnet_naming_rule.public_fields_should_be_pascalcase.style = pascalcase
+
+dotnet_naming_rule.private_fields_should_be__camelcase.severity = suggestion
+dotnet_naming_rule.private_fields_should_be__camelcase.symbols = private_fields
+dotnet_naming_rule.private_fields_should_be__camelcase.style = _camelcase
+
+dotnet_naming_rule.private_static_fields_should_be_s_camelcase.severity = suggestion
+dotnet_naming_rule.private_static_fields_should_be_s_camelcase.symbols = private_static_fields
+dotnet_naming_rule.private_static_fields_should_be_s_camelcase.style = s_camelcase
+
+dotnet_naming_rule.public_constant_fields_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.public_constant_fields_should_be_pascalcase.symbols = public_constant_fields
+dotnet_naming_rule.public_constant_fields_should_be_pascalcase.style = pascalcase
+
+dotnet_naming_rule.private_constant_fields_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.private_constant_fields_should_be_pascalcase.symbols = private_constant_fields
+dotnet_naming_rule.private_constant_fields_should_be_pascalcase.style = pascalcase
+
+dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.symbols = public_static_readonly_fields
+dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.style = pascalcase
+
+dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.symbols = private_static_readonly_fields
+dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.style = pascalcase
+
+dotnet_naming_rule.enums_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.enums_should_be_pascalcase.symbols = enums
+dotnet_naming_rule.enums_should_be_pascalcase.style = pascalcase
+
+dotnet_naming_rule.local_functions_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.local_functions_should_be_pascalcase.symbols = local_functions
+dotnet_naming_rule.local_functions_should_be_pascalcase.style = pascalcase
+
+dotnet_naming_rule.non_field_members_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.non_field_members_should_be_pascalcase.symbols = non_field_members
+dotnet_naming_rule.non_field_members_should_be_pascalcase.style = pascalcase
+
+# Symbol specifications
+
+dotnet_naming_symbols.interfaces.applicable_kinds = interface
+dotnet_naming_symbols.interfaces.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.interfaces.required_modifiers =
+
+dotnet_naming_symbols.enums.applicable_kinds = enum
+dotnet_naming_symbols.enums.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.enums.required_modifiers =
+
+dotnet_naming_symbols.events.applicable_kinds = event
+dotnet_naming_symbols.events.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.events.required_modifiers =
+
+dotnet_naming_symbols.methods.applicable_kinds = method
+dotnet_naming_symbols.methods.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.methods.required_modifiers =
+
+dotnet_naming_symbols.properties.applicable_kinds = property
+dotnet_naming_symbols.properties.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.properties.required_modifiers =
+
+dotnet_naming_symbols.public_fields.applicable_kinds = field
+dotnet_naming_symbols.public_fields.applicable_accessibilities = public, internal
+dotnet_naming_symbols.public_fields.required_modifiers =
+
+dotnet_naming_symbols.private_fields.applicable_kinds = field
+dotnet_naming_symbols.private_fields.applicable_accessibilities = private, protected, protected_internal, private_protected
+dotnet_naming_symbols.private_fields.required_modifiers =
+
+dotnet_naming_symbols.private_static_fields.applicable_kinds = field
+dotnet_naming_symbols.private_static_fields.applicable_accessibilities = private, protected, protected_internal, private_protected
+dotnet_naming_symbols.private_static_fields.required_modifiers = static
+
+dotnet_naming_symbols.types_and_namespaces.applicable_kinds = namespace, class, struct, interface, enum
+dotnet_naming_symbols.types_and_namespaces.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.types_and_namespaces.required_modifiers =
+
+dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
+dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.non_field_members.required_modifiers =
+
+dotnet_naming_symbols.type_parameters.applicable_kinds = namespace
+dotnet_naming_symbols.type_parameters.applicable_accessibilities = *
+dotnet_naming_symbols.type_parameters.required_modifiers =
+
+dotnet_naming_symbols.private_constant_fields.applicable_kinds = field
+dotnet_naming_symbols.private_constant_fields.applicable_accessibilities = private, protected, protected_internal, private_protected
+dotnet_naming_symbols.private_constant_fields.required_modifiers = const
+
+dotnet_naming_symbols.local_variables.applicable_kinds = local
+dotnet_naming_symbols.local_variables.applicable_accessibilities = local
+dotnet_naming_symbols.local_variables.required_modifiers =
+
+dotnet_naming_symbols.local_constants.applicable_kinds = local
+dotnet_naming_symbols.local_constants.applicable_accessibilities = local
+dotnet_naming_symbols.local_constants.required_modifiers = const
+
+dotnet_naming_symbols.parameters.applicable_kinds = parameter
+dotnet_naming_symbols.parameters.applicable_accessibilities = *
+dotnet_naming_symbols.parameters.required_modifiers =
+
+dotnet_naming_symbols.public_constant_fields.applicable_kinds = field
+dotnet_naming_symbols.public_constant_fields.applicable_accessibilities = public, internal
+dotnet_naming_symbols.public_constant_fields.required_modifiers = const
+
+dotnet_naming_symbols.public_static_readonly_fields.applicable_kinds = field
+dotnet_naming_symbols.public_static_readonly_fields.applicable_accessibilities = public, internal
+dotnet_naming_symbols.public_static_readonly_fields.required_modifiers = readonly, static
+
+dotnet_naming_symbols.private_static_readonly_fields.applicable_kinds = field
+dotnet_naming_symbols.private_static_readonly_fields.applicable_accessibilities = private, protected, protected_internal, private_protected
+dotnet_naming_symbols.private_static_readonly_fields.required_modifiers = readonly, static
+
+dotnet_naming_symbols.local_functions.applicable_kinds = local_function
+dotnet_naming_symbols.local_functions.applicable_accessibilities = *
+dotnet_naming_symbols.local_functions.required_modifiers =
+
+# Naming styles
+
+dotnet_naming_style.pascalcase.required_prefix =
+dotnet_naming_style.pascalcase.required_suffix =
+dotnet_naming_style.pascalcase.word_separator =
+dotnet_naming_style.pascalcase.capitalization = pascal_case
+
+dotnet_naming_style.ipascalcase.required_prefix = I
+dotnet_naming_style.ipascalcase.required_suffix =
+dotnet_naming_style.ipascalcase.word_separator =
+dotnet_naming_style.ipascalcase.capitalization = pascal_case
+
+dotnet_naming_style.tpascalcase.required_prefix = T
+dotnet_naming_style.tpascalcase.required_suffix =
+dotnet_naming_style.tpascalcase.word_separator =
+dotnet_naming_style.tpascalcase.capitalization = pascal_case
+
+dotnet_naming_style._camelcase.required_prefix = _
+dotnet_naming_style._camelcase.required_suffix =
+dotnet_naming_style._camelcase.word_separator =
+dotnet_naming_style._camelcase.capitalization = camel_case
+
+dotnet_naming_style.camelcase.required_prefix =
+dotnet_naming_style.camelcase.required_suffix =
+dotnet_naming_style.camelcase.word_separator =
+dotnet_naming_style.camelcase.capitalization = camel_case
+
+dotnet_naming_style.s_camelcase.required_prefix = s_
+dotnet_naming_style.s_camelcase.required_suffix =
+dotnet_naming_style.s_camelcase.word_separator =
+dotnet_naming_style.s_camelcase.capitalization = camel_case

From 6f4799c919a094e60862cbf411b43f2a3aeccdc2 Mon Sep 17 00:00:00 2001
From: Shachar Langbeheim <nihohit@gmail.com>
Date: Tue, 6 Feb 2024 18:30:02 +0200
Subject: [PATCH 03/10] C#: Fail CI on warnings. (#904)

* C#: Fail CI on warnings.

* Fix found warnings.
---
 .github/workflows/csharp.yml   | 2 +-
 benchmarks/csharp/Program.cs   | 6 +++---
 benchmarks/install_and_test.sh | 2 +-
 3 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/.github/workflows/csharp.yml b/.github/workflows/csharp.yml
index f12d8eda2c..af017c542c 100644
--- a/.github/workflows/csharp.yml
+++ b/.github/workflows/csharp.yml
@@ -54,7 +54,7 @@ jobs:
 
             - name: Test
               working-directory: ./csharp
-              run: dotnet test --framework net6.0
+              run: dotnet test --framework net6.0 /warnaserror
 
             - uses: ./.github/workflows/test-benchmark
               with:
diff --git a/benchmarks/csharp/Program.cs b/benchmarks/csharp/Program.cs
index f1aaabd34e..839d3099cb 100644
--- a/benchmarks/csharp/Program.cs
+++ b/benchmarks/csharp/Program.cs
@@ -23,7 +23,7 @@ public class CommandLineOptions
         public int dataSize { get; set; }
 
         [Option('c', "concurrentTasks", Required = false, HelpText = "The number of concurrent operations to perform.", Default = new[] { 1, 10, 100, 1000 })]
-        public IEnumerable<int> concurrentTasks { get; set; }
+        public IEnumerable<int> concurrentTasks { get; set; } = Enumerable.Empty<int>();
 
         [Option('l', "clients", Required = false, HelpText = "Which clients should run", Default = "all")]
         public string clientsToRun { get; set; }
@@ -32,7 +32,7 @@ public class CommandLineOptions
         public string host { get; set; }
 
         [Option('C', "clientCount", Required = false, HelpText = "Number of clients to run concurrently", Default = new[] { 1 })]
-        public IEnumerable<int> clientCount { get; set; }
+        public IEnumerable<int> clientCount { get; set; } = Enumerable.Empty<int>();
 
         [Option('t', "tls", HelpText = "Should benchmark a TLS server", Default = false)]
         public bool tls { get; set; }
@@ -335,7 +335,7 @@ private static int number_of_iterations(int num_of_concurrent_tasks)
 
     public static async Task Main(string[] args)
     {
-        CommandLineOptions options = new ();
+        CommandLineOptions options = new();
         Parser.Default
             .ParseArguments<CommandLineOptions>(args).WithParsed<CommandLineOptions>(parsed => { options = parsed; });
 
diff --git a/benchmarks/install_and_test.sh b/benchmarks/install_and_test.sh
index 7d3d8429bf..2e0a80957e 100755
--- a/benchmarks/install_and_test.sh
+++ b/benchmarks/install_and_test.sh
@@ -67,7 +67,7 @@ function runNodeBenchmark(){
 function runCSharpBenchmark(){
   cd ${BENCH_FOLDER}/csharp
   dotnet clean
-  dotnet build --configuration Release
+  dotnet build --configuration Release /warnaserror
   dotnet run --framework net6.0 --configuration Release --resultsFile=../$1 --dataSize $2 --concurrentTasks $concurrentTasks --clients $chosenClients --host $host --clientCount $clientCount $tlsFlag $portFlag $minimalFlag
 }
 

From 69a310826f87d81b52bd0308b87245da07554316 Mon Sep 17 00:00:00 2001
From: Guriy Samarin <guriy@amazon.com>
Date: Tue, 6 Feb 2024 23:09:12 +0000
Subject: [PATCH 04/10] enable cicd linter and fix formatting

---
 .github/workflows/csharp.yml          |   3 +
 benchmarks/csharp/Program.cs          |  10 +-
 csharp/lib/AsyncClient.cs             |   6 +-
 csharp/lib/Logger.cs                  | 170 ++++++++---------
 csharp/lib/Message.cs                 | 255 +++++++++++++-------------
 csharp/lib/MessageContainer.cs        | 124 ++++++-------
 csharp/lib/Properties/AssemblyInfo.cs |  14 +-
 csharp/tests/AsyncClientTests.cs      |  10 +-
 csharp/tests/Usings.cs                |  10 +-
 9 files changed, 305 insertions(+), 297 deletions(-)

diff --git a/.github/workflows/csharp.yml b/.github/workflows/csharp.yml
index af017c542c..62f4a34809 100644
--- a/.github/workflows/csharp.yml
+++ b/.github/workflows/csharp.yml
@@ -52,6 +52,9 @@ jobs:
             - name: Start redis server
               run: redis-server &
 
+            - name: Format
+              run: dotnet format --verify-no-changes --verbosity diagnostic
+
             - name: Test
               working-directory: ./csharp
               run: dotnet test --framework net6.0 /warnaserror
diff --git a/benchmarks/csharp/Program.cs b/benchmarks/csharp/Program.cs
index 839d3099cb..eea19e2006 100644
--- a/benchmarks/csharp/Program.cs
+++ b/benchmarks/csharp/Program.cs
@@ -5,9 +5,13 @@
 using System.Collections.Concurrent;
 using System.Diagnostics;
 using System.Text.Json;
-using Glide;
+
 using CommandLine;
+
+using Glide;
+
 using LinqStatistics;
+
 using StackExchange.Redis;
 
 public static class MainClass
@@ -258,7 +262,7 @@ public void Dispose()
         internal Func<string, Task<string?>> get;
         internal Func<string, string, Task> set;
 
-        private Action disposalFunction;
+        private readonly Action disposalFunction;
     }
 
     private async static Task<ClientWrapper[]> createClients(int clientCount,
@@ -350,4 +354,4 @@ public static async Task Main(string[] args)
 
         print_results(options.resultsFile);
     }
-}
+}
\ No newline at end of file
diff --git a/csharp/lib/AsyncClient.cs b/csharp/lib/AsyncClient.cs
index 459407704a..9657907b64 100644
--- a/csharp/lib/AsyncClient.cs
+++ b/csharp/lib/AsyncClient.cs
@@ -79,11 +79,11 @@ private void FailureCallback(ulong index)
 
     /// Held as a measure to prevent the delegate being garbage collected. These are delegated once
     /// and held in order to prevent the cost of marshalling on each function call.
-    private FailureAction failureCallbackDelegate;
+    private readonly FailureAction failureCallbackDelegate;
 
     /// Held as a measure to prevent the delegate being garbage collected. These are delegated once
     /// and held in order to prevent the cost of marshalling on each function call.
-    private StringAction successCallbackDelegate;
+    private readonly StringAction successCallbackDelegate;
 
     /// Raw pointer to the underlying native client.
     private IntPtr clientPointer;
@@ -110,4 +110,4 @@ private void FailureCallback(ulong index)
     private static extern void CloseClientFfi(IntPtr client);
 
     #endregion
-}
+}
\ No newline at end of file
diff --git a/csharp/lib/Logger.cs b/csharp/lib/Logger.cs
index 7edc16f16c..03fb47707d 100644
--- a/csharp/lib/Logger.cs
+++ b/csharp/lib/Logger.cs
@@ -1,85 +1,85 @@
-/**
- * Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0
- */
-
-using System.Runtime.InteropServices;
-using System.Text;
-
-
-namespace Glide;
-
-// TODO - use a bindings generator to create this enum.
-public enum Level
-{
-    Error = 0,
-    Warn = 1,
-    Info = 2,
-    Debug = 3,
-    Trace = 4
-}
-
-/*
-A class that allows logging which is consistent with logs from the internal rust core.
-Only one instance of this class can exist at any given time. The logger can be set up in 2 ways -
-    1. By calling init, which creates and modifies a new logger only if one doesn't exist.
-    2. By calling setConfig, which replaces the existing logger, and means that new logs will not be saved with the logs that were sent before the call.
-If no call to any of these function is received, the first log attempt will initialize a new logger with default level decided by rust core (normally - console, error).
-*/
-public class Logger
-{
-    #region private fields
-
-    private static Level? loggerLevel = null;
-    #endregion private fields
-
-    #region internal methods
-    // Initialize a logger instance if none were initialized before - this method is meant to be used when there is no intention to replace an existing logger.
-    // The logger will filter all logs with a level lower than the given level,
-    // If given a fileName argument, will write the logs to files postfixed with fileName. If fileName isn't provided, the logs will be written to the console.
-    internal static void Init(Level? level, string? filename = null)
-    {
-        if (Logger.loggerLevel is null)
-        {
-            SetLoggerConfig(level, filename);
-        }
-    }
-
-    // take the arguments from the user and provide to the core-logger (see ../logger-core)
-    // if the level is higher then the logger level (error is 0, warn 1, etc.) simply return without operation
-    // if a logger instance doesn't exist, create new one with default mode (decided by rust core, normally - level: error, target: console)
-    // logIdentifier arg is a string contain data that suppose to give the log a context and make it easier to find certain type of logs.
-    // when the log is connect to certain task the identifier should be the task id, when the log is not part of specific task the identifier should give a context to the log - for example, "create client".
-    internal static void Log(Level logLevel, string logIdentifier, string message)
-    {
-        if (Logger.loggerLevel is null)
-        {
-            SetLoggerConfig(logLevel);
-        }
-        if (!(logLevel <= Logger.loggerLevel)) return;
-        log(Convert.ToInt32(logLevel), Encoding.UTF8.GetBytes(logIdentifier), Encoding.UTF8.GetBytes(message));
-    }
-    #endregion internal methods
-
-    #region public methods
-    // config the logger instance - in fact - create new logger instance with the new args
-    // exist in addition to init for two main reason's:
-    // 1. if GLIDE dev want intentionally to change the logger instance configuration
-    // 2. external user want to set the logger and we don't want to return to him the logger itself, just config it
-    // the level argument is the level of the logs you want the system to provide (error logs, warn logs, etc.)
-    // the filename argument is optional - if provided the target of the logs will be the file mentioned, else will be the console
-    public static void SetLoggerConfig(Level? level, string? filename = null)
-    {
-        var buffer = filename is null ? null : Encoding.UTF8.GetBytes(filename);
-        Logger.loggerLevel = InitInternalLogger(Convert.ToInt32(level), buffer);
-    }
-    #endregion public methods
-
-    #region FFI function declaration
-    [DllImport("libglide_rs", CallingConvention = CallingConvention.Cdecl, EntryPoint = "log")]
-    private static extern void log(Int32 logLevel, byte[] logIdentifier, byte[] message);
-
-    [DllImport("libglide_rs", CallingConvention = CallingConvention.Cdecl, EntryPoint = "init")]
-    private static extern Level InitInternalLogger(Int32 level, byte[]? filename);
-
-    #endregion
-}
+/**
+ * Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0
+ */
+
+using System.Runtime.InteropServices;
+using System.Text;
+
+
+namespace Glide;
+
+// TODO - use a bindings generator to create this enum.
+public enum Level
+{
+    Error = 0,
+    Warn = 1,
+    Info = 2,
+    Debug = 3,
+    Trace = 4
+}
+
+/*
+A class that allows logging which is consistent with logs from the internal rust core.
+Only one instance of this class can exist at any given time. The logger can be set up in 2 ways -
+    1. By calling init, which creates and modifies a new logger only if one doesn't exist.
+    2. By calling setConfig, which replaces the existing logger, and means that new logs will not be saved with the logs that were sent before the call.
+If no call to any of these function is received, the first log attempt will initialize a new logger with default level decided by rust core (normally - console, error).
+*/
+public class Logger
+{
+    #region private fields
+
+    private static Level? loggerLevel = null;
+    #endregion private fields
+
+    #region internal methods
+    // Initialize a logger instance if none were initialized before - this method is meant to be used when there is no intention to replace an existing logger.
+    // The logger will filter all logs with a level lower than the given level,
+    // If given a fileName argument, will write the logs to files postfixed with fileName. If fileName isn't provided, the logs will be written to the console.
+    internal static void Init(Level? level, string? filename = null)
+    {
+        if (Logger.loggerLevel is null)
+        {
+            SetLoggerConfig(level, filename);
+        }
+    }
+
+    // take the arguments from the user and provide to the core-logger (see ../logger-core)
+    // if the level is higher then the logger level (error is 0, warn 1, etc.) simply return without operation
+    // if a logger instance doesn't exist, create new one with default mode (decided by rust core, normally - level: error, target: console)
+    // logIdentifier arg is a string contain data that suppose to give the log a context and make it easier to find certain type of logs.
+    // when the log is connect to certain task the identifier should be the task id, when the log is not part of specific task the identifier should give a context to the log - for example, "create client".
+    internal static void Log(Level logLevel, string logIdentifier, string message)
+    {
+        if (Logger.loggerLevel is null)
+        {
+            SetLoggerConfig(logLevel);
+        }
+        if (!(logLevel <= Logger.loggerLevel)) return;
+        log(Convert.ToInt32(logLevel), Encoding.UTF8.GetBytes(logIdentifier), Encoding.UTF8.GetBytes(message));
+    }
+    #endregion internal methods
+
+    #region public methods
+    // config the logger instance - in fact - create new logger instance with the new args
+    // exist in addition to init for two main reason's:
+    // 1. if GLIDE dev want intentionally to change the logger instance configuration
+    // 2. external user want to set the logger and we don't want to return to him the logger itself, just config it
+    // the level argument is the level of the logs you want the system to provide (error logs, warn logs, etc.)
+    // the filename argument is optional - if provided the target of the logs will be the file mentioned, else will be the console
+    public static void SetLoggerConfig(Level? level, string? filename = null)
+    {
+        var buffer = filename is null ? null : Encoding.UTF8.GetBytes(filename);
+        Logger.loggerLevel = InitInternalLogger(Convert.ToInt32(level), buffer);
+    }
+    #endregion public methods
+
+    #region FFI function declaration
+    [DllImport("libglide_rs", CallingConvention = CallingConvention.Cdecl, EntryPoint = "log")]
+    private static extern void log(Int32 logLevel, byte[] logIdentifier, byte[] message);
+
+    [DllImport("libglide_rs", CallingConvention = CallingConvention.Cdecl, EntryPoint = "init")]
+    private static extern Level InitInternalLogger(Int32 level, byte[]? filename);
+
+    #endregion
+}
\ No newline at end of file
diff --git a/csharp/lib/Message.cs b/csharp/lib/Message.cs
index daa11602c6..6c5f4203f3 100644
--- a/csharp/lib/Message.cs
+++ b/csharp/lib/Message.cs
@@ -1,127 +1,128 @@
-/**
- * Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0
- */
-
-using System.Diagnostics;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-using Glide;
-
-/// Reusable source of ValueTask. This object can be allocated once and then reused
-/// to create multiple asynchronous operations, as long as each call to CreateTask
-/// is awaited to completion before the next call begins.
-internal class Message<T> : INotifyCompletion
-{
-    /// This is the index of the message in an external array, that allows the user to
-    /// know how to find the message and set its result.
-    public int Index { get; }
-
-    /// The pointer to the unmanaged memory that contains the operation's key.
-    public IntPtr KeyPtr { get; private set; }
-
-    /// The pointer to the unmanaged memory that contains the operation's key.
-    public IntPtr ValuePtr { get; private set; }
-    private MessageContainer<T> container;
-
-    public Message(int index, MessageContainer<T> container)
-    {
-        Index = index;
-        continuation = () => { };
-        this.container = container;
-    }
-
-    private Action? continuation;
-    const int COMPLETION_STAGE_STARTED = 0;
-    const int COMPLETION_STAGE_NEXT_SHOULD_EXECUTE_CONTINUATION = 1;
-    const int COMPLETION_STAGE_CONTINUATION_EXECUTED = 2;
-    private int completionState;
-    private T? result;
-    private Exception? exception;
-
-    /// Triggers a succesful completion of the task returned from the latest call
-    /// to CreateTask.
-    public void SetResult(T? result)
-    {
-        this.result = result;
-        FinishSet();
-    }
-
-    /// Triggers a failure completion of the task returned from the latest call to
-    /// CreateTask.
-    public void SetException(Exception exc)
-    {
-        this.exception = exc;
-        FinishSet();
-    }
-
-    private void FinishSet()
-    {
-        FreePointers();
-
-        CheckRaceAndCallContinuation();
-    }
-
-    private void CheckRaceAndCallContinuation()
-    {
-        if (Interlocked.CompareExchange(ref this.completionState, COMPLETION_STAGE_NEXT_SHOULD_EXECUTE_CONTINUATION, COMPLETION_STAGE_STARTED) == COMPLETION_STAGE_NEXT_SHOULD_EXECUTE_CONTINUATION)
-        {
-            Debug.Assert(this.continuation != null);
-            this.completionState = COMPLETION_STAGE_CONTINUATION_EXECUTED;
-            try
-            {
-                continuation();
-            }
-            finally
-            {
-                this.container.ReturnFreeMessage(this);
-            }
-        }
-    }
-
-    public Message<T> GetAwaiter() => this;
-
-    /// This returns a task that will complete once SetException / SetResult are called,
-    /// and ensures that the internal state of the message is set-up before the task is created,
-    /// and cleaned once it is complete.
-    public void StartTask(string? key, string? value, object client)
-    {
-        continuation = null;
-        this.completionState = COMPLETION_STAGE_STARTED;
-        this.result = default(T);
-        this.exception = null;
-        this.client = client;
-        this.KeyPtr = key is null ? IntPtr.Zero : Marshal.StringToHGlobalAnsi(key);
-        this.ValuePtr = value is null ? IntPtr.Zero : Marshal.StringToHGlobalAnsi(value);
-    }
-
-    // This function isn't thread-safe. Access to it should be from a single thread, and only once per operation.
-    // For the sake of performance, this responsibility is on the caller, and the function doesn't contain any safety measures.
-    private void FreePointers()
-    {
-        if (KeyPtr != IntPtr.Zero)
-        {
-            Marshal.FreeHGlobal(KeyPtr);
-            KeyPtr = IntPtr.Zero;
-        }
-        if (ValuePtr != IntPtr.Zero)
-        {
-            Marshal.FreeHGlobal(ValuePtr);
-            ValuePtr = IntPtr.Zero;
-        }
-        client = null;
-    }
-
-    // Holding the client prevents it from being CG'd until all operations complete.
-    private object? client;
-
-
-    public void OnCompleted(Action continuation)
-    {
-        this.continuation = continuation;
-        CheckRaceAndCallContinuation();
-    }
-
-    public bool IsCompleted => completionState == COMPLETION_STAGE_CONTINUATION_EXECUTED;
-
-    public T? GetResult() => this.exception is null ? this.result : throw this.exception;
-}
+/**
+ * Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0
+ */
+
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+using Glide;
+
+/// Reusable source of ValueTask. This object can be allocated once and then reused
+/// to create multiple asynchronous operations, as long as each call to CreateTask
+/// is awaited to completion before the next call begins.
+internal class Message<T> : INotifyCompletion
+{
+    /// This is the index of the message in an external array, that allows the user to
+    /// know how to find the message and set its result.
+    public int Index { get; }
+
+    /// The pointer to the unmanaged memory that contains the operation's key.
+    public IntPtr KeyPtr { get; private set; }
+
+    /// The pointer to the unmanaged memory that contains the operation's key.
+    public IntPtr ValuePtr { get; private set; }
+    private readonly MessageContainer<T> container;
+
+    public Message(int index, MessageContainer<T> container)
+    {
+        Index = index;
+        continuation = () => { };
+        this.container = container;
+    }
+
+    private Action? continuation;
+    const int COMPLETION_STAGE_STARTED = 0;
+    const int COMPLETION_STAGE_NEXT_SHOULD_EXECUTE_CONTINUATION = 1;
+    const int COMPLETION_STAGE_CONTINUATION_EXECUTED = 2;
+    private int completionState;
+    private T? result;
+    private Exception? exception;
+
+    /// Triggers a succesful completion of the task returned from the latest call
+    /// to CreateTask.
+    public void SetResult(T? result)
+    {
+        this.result = result;
+        FinishSet();
+    }
+
+    /// Triggers a failure completion of the task returned from the latest call to
+    /// CreateTask.
+    public void SetException(Exception exc)
+    {
+        this.exception = exc;
+        FinishSet();
+    }
+
+    private void FinishSet()
+    {
+        FreePointers();
+
+        CheckRaceAndCallContinuation();
+    }
+
+    private void CheckRaceAndCallContinuation()
+    {
+        if (Interlocked.CompareExchange(ref this.completionState, COMPLETION_STAGE_NEXT_SHOULD_EXECUTE_CONTINUATION, COMPLETION_STAGE_STARTED) == COMPLETION_STAGE_NEXT_SHOULD_EXECUTE_CONTINUATION)
+        {
+            Debug.Assert(this.continuation != null);
+            this.completionState = COMPLETION_STAGE_CONTINUATION_EXECUTED;
+            try
+            {
+                continuation();
+            }
+            finally
+            {
+                this.container.ReturnFreeMessage(this);
+            }
+        }
+    }
+
+    public Message<T> GetAwaiter() => this;
+
+    /// This returns a task that will complete once SetException / SetResult are called,
+    /// and ensures that the internal state of the message is set-up before the task is created,
+    /// and cleaned once it is complete.
+    public void StartTask(string? key, string? value, object client)
+    {
+        continuation = null;
+        this.completionState = COMPLETION_STAGE_STARTED;
+        this.result = default(T);
+        this.exception = null;
+        this.client = client;
+        this.KeyPtr = key is null ? IntPtr.Zero : Marshal.StringToHGlobalAnsi(key);
+        this.ValuePtr = value is null ? IntPtr.Zero : Marshal.StringToHGlobalAnsi(value);
+    }
+
+    // This function isn't thread-safe. Access to it should be from a single thread, and only once per operation.
+    // For the sake of performance, this responsibility is on the caller, and the function doesn't contain any safety measures.
+    private void FreePointers()
+    {
+        if (KeyPtr != IntPtr.Zero)
+        {
+            Marshal.FreeHGlobal(KeyPtr);
+            KeyPtr = IntPtr.Zero;
+        }
+        if (ValuePtr != IntPtr.Zero)
+        {
+            Marshal.FreeHGlobal(ValuePtr);
+            ValuePtr = IntPtr.Zero;
+        }
+        client = null;
+    }
+
+    // Holding the client prevents it from being CG'd until all operations complete.
+    private object? client;
+
+
+    public void OnCompleted(Action continuation)
+    {
+        this.continuation = continuation;
+        CheckRaceAndCallContinuation();
+    }
+
+    public bool IsCompleted => completionState == COMPLETION_STAGE_CONTINUATION_EXECUTED;
+
+    public T? GetResult() => this.exception is null ? this.result : throw this.exception;
+}
\ No newline at end of file
diff --git a/csharp/lib/MessageContainer.cs b/csharp/lib/MessageContainer.cs
index 5999702363..9c05ccb556 100644
--- a/csharp/lib/MessageContainer.cs
+++ b/csharp/lib/MessageContainer.cs
@@ -1,62 +1,62 @@
-/**
- * Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0
- */
-
-using System.Collections.Concurrent;
-
-namespace Glide;
-
-
-internal class MessageContainer<T>
-{
-    internal Message<T> GetMessage(int index) => messages[index];
-
-    internal Message<T> GetMessageForCall(string? key, string? value)
-    {
-        var message = GetFreeMessage();
-        message.StartTask(key, value, this);
-        return message;
-    }
-
-    private Message<T> GetFreeMessage()
-    {
-        if (!availableMessages.TryDequeue(out var message))
-        {
-            lock (messages)
-            {
-                var index = messages.Count;
-                message = new Message<T>(index, this);
-                messages.Add(message);
-            }
-        }
-        return message;
-    }
-
-    public void ReturnFreeMessage(Message<T> message) => availableMessages.Enqueue(message);
-
-    internal void DisposeWithError(Exception? error)
-    {
-        lock (messages)
-        {
-            foreach (var message in messages.Where(message => !message.IsCompleted))
-            {
-                try
-                {
-                    message.SetException(new TaskCanceledException("Client closed", error));
-                }
-                catch (Exception) { }
-            }
-            messages.Clear();
-        }
-        availableMessages.Clear();
-    }
-
-    /// This list allows us random-access to the message in each index,
-    /// which means that once we receive a callback with an index, we can
-    /// find the message to resolve in constant time.
-    private List<Message<T>> messages = new();
-
-    /// This queue contains the messages that were created and are currently unused by any task,
-    /// so they can be reused y new tasks instead of allocating new messages.
-    private ConcurrentQueue<Message<T>> availableMessages = new();
-}
+/**
+ * Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0
+ */
+
+using System.Collections.Concurrent;
+
+namespace Glide;
+
+
+internal class MessageContainer<T>
+{
+    internal Message<T> GetMessage(int index) => messages[index];
+
+    internal Message<T> GetMessageForCall(string? key, string? value)
+    {
+        var message = GetFreeMessage();
+        message.StartTask(key, value, this);
+        return message;
+    }
+
+    private Message<T> GetFreeMessage()
+    {
+        if (!availableMessages.TryDequeue(out var message))
+        {
+            lock (messages)
+            {
+                var index = messages.Count;
+                message = new Message<T>(index, this);
+                messages.Add(message);
+            }
+        }
+        return message;
+    }
+
+    public void ReturnFreeMessage(Message<T> message) => availableMessages.Enqueue(message);
+
+    internal void DisposeWithError(Exception? error)
+    {
+        lock (messages)
+        {
+            foreach (var message in messages.Where(message => !message.IsCompleted))
+            {
+                try
+                {
+                    message.SetException(new TaskCanceledException("Client closed", error));
+                }
+                catch (Exception) { }
+            }
+            messages.Clear();
+        }
+        availableMessages.Clear();
+    }
+
+    /// This list allows us random-access to the message in each index,
+    /// which means that once we receive a callback with an index, we can
+    /// find the message to resolve in constant time.
+    private readonly List<Message<T>> messages = new();
+
+    /// This queue contains the messages that were created and are currently unused by any task,
+    /// so they can be reused y new tasks instead of allocating new messages.
+    private readonly ConcurrentQueue<Message<T>> availableMessages = new();
+}
diff --git a/csharp/lib/Properties/AssemblyInfo.cs b/csharp/lib/Properties/AssemblyInfo.cs
index a6412c5b2e..876c013e0a 100644
--- a/csharp/lib/Properties/AssemblyInfo.cs
+++ b/csharp/lib/Properties/AssemblyInfo.cs
@@ -1,7 +1,7 @@
-/**
- * Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0
- */
-
-using System.Runtime.CompilerServices;
-
-[assembly: InternalsVisibleTo("tests")]
+/**
+ * Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0
+ */
+
+using System.Runtime.CompilerServices;
+
+[assembly: InternalsVisibleTo("tests")]
\ No newline at end of file
diff --git a/csharp/tests/AsyncClientTests.cs b/csharp/tests/AsyncClientTests.cs
index dad14c953f..a322e37514 100644
--- a/csharp/tests/AsyncClientTests.cs
+++ b/csharp/tests/AsyncClientTests.cs
@@ -1,7 +1,7 @@
-/**
- * Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0
- */
-
+/**
+ * Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0
+ */
+
 namespace tests;
 
 using Glide;
@@ -119,4 +119,4 @@ public void ConcurrentOperationsWork()
             Task.WaitAll(operations.ToArray());
         }
     }
-}
+}
\ No newline at end of file
diff --git a/csharp/tests/Usings.cs b/csharp/tests/Usings.cs
index 6638c1219c..08ffe13f66 100644
--- a/csharp/tests/Usings.cs
+++ b/csharp/tests/Usings.cs
@@ -1,5 +1,5 @@
-/**
- * Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0
- */
-
-global using NUnit.Framework;
+/**
+ * Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0
+ */
+
+global using NUnit.Framework;
\ No newline at end of file

From 927f3519c4ee6702e063c94ad0093097e23bb99e Mon Sep 17 00:00:00 2001
From: Guriy Samarin <guriy@amazon.com>
Date: Tue, 6 Feb 2024 23:12:52 +0000
Subject: [PATCH 05/10] fix working dir

---
 .github/workflows/csharp.yml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.github/workflows/csharp.yml b/.github/workflows/csharp.yml
index 62f4a34809..3c838c38c5 100644
--- a/.github/workflows/csharp.yml
+++ b/.github/workflows/csharp.yml
@@ -53,6 +53,7 @@ jobs:
               run: redis-server &
 
             - name: Format
+              working-directory: ./csharp
               run: dotnet format --verify-no-changes --verbosity diagnostic
 
             - name: Test

From 7f27c36c70501da4c2897cab2aee33969ddeba09 Mon Sep 17 00:00:00 2001
From: Guriy Samarin <guriy@amazon.com>
Date: Tue, 6 Feb 2024 23:19:06 +0000
Subject: [PATCH 06/10] fix MessageContainer.cs

---
 csharp/lib/MessageContainer.cs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/csharp/lib/MessageContainer.cs b/csharp/lib/MessageContainer.cs
index 9c05ccb556..7de0947650 100644
--- a/csharp/lib/MessageContainer.cs
+++ b/csharp/lib/MessageContainer.cs
@@ -59,4 +59,4 @@ internal void DisposeWithError(Exception? error)
     /// This queue contains the messages that were created and are currently unused by any task,
     /// so they can be reused y new tasks instead of allocating new messages.
     private readonly ConcurrentQueue<Message<T>> availableMessages = new();
-}
+}
\ No newline at end of file

From 822b15f1acc7117f78387eb7bf98925242a02d59 Mon Sep 17 00:00:00 2001
From: Guriy Samarin <guriy@amazon.com>
Date: Tue, 6 Feb 2024 23:27:50 +0000
Subject: [PATCH 07/10] Non-nullable property must contain a non-null value
 when exiting constructor

---
 benchmarks/csharp/Program.cs | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/benchmarks/csharp/Program.cs b/benchmarks/csharp/Program.cs
index eea19e2006..519e1ea887 100644
--- a/benchmarks/csharp/Program.cs
+++ b/benchmarks/csharp/Program.cs
@@ -21,7 +21,7 @@ private enum ChosenAction { GET_NON_EXISTING, GET_EXISTING, SET };
     public class CommandLineOptions
     {
         [Option('r', "resultsFile", Required = false, HelpText = "Set the file to which the JSON results are written.", Default = "../results/csharp-results.json")]
-        public string resultsFile { get; set; }
+        public string resultsFile { get; set; } = string.Empty;
 
         [Option('d', "dataSize", Required = false, HelpText = "The size of the sent data in bytes.", Default = 100)]
         public int dataSize { get; set; }
@@ -30,10 +30,10 @@ public class CommandLineOptions
         public IEnumerable<int> concurrentTasks { get; set; } = Enumerable.Empty<int>();
 
         [Option('l', "clients", Required = false, HelpText = "Which clients should run", Default = "all")]
-        public string clientsToRun { get; set; }
+        public string clientsToRun { get; set; } = string.Empty;
 
         [Option('h', "host", Required = false, HelpText = "What host to target", Default = "localhost")]
-        public string host { get; set; }
+        public string host { get; set; } = string.Empty;
 
         [Option('C', "clientCount", Required = false, HelpText = "Number of clients to run concurrently", Default = new[] { 1 })]
         public IEnumerable<int> clientCount { get; set; } = Enumerable.Empty<int>();
@@ -354,4 +354,4 @@ public static async Task Main(string[] args)
 
         print_results(options.resultsFile);
     }
-}
\ No newline at end of file
+}

From bfd90c9e5a800e7d77a0d75ab0ca7a45b731f025 Mon Sep 17 00:00:00 2001
From: Guriy Samarin <guriy@amazon.com>
Date: Tue, 6 Feb 2024 23:30:22 +0000
Subject: [PATCH 08/10] fix format

---
 benchmarks/csharp/Program.cs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/benchmarks/csharp/Program.cs b/benchmarks/csharp/Program.cs
index 519e1ea887..d753d09357 100644
--- a/benchmarks/csharp/Program.cs
+++ b/benchmarks/csharp/Program.cs
@@ -354,4 +354,4 @@ public static async Task Main(string[] args)
 
         print_results(options.resultsFile);
     }
-}
+}
\ No newline at end of file

From f082274b608629f934fa37543f66083b7ecebecf Mon Sep 17 00:00:00 2001
From: Guriy Samarin <guriy@amazon.com>
Date: Tue, 6 Feb 2024 23:39:30 +0000
Subject: [PATCH 09/10] correct formatting

---
 .editorconfig                         |   4 +-
 benchmarks/csharp/Program.cs          | 714 +++++++++++++-------------
 csharp/lib/AsyncClient.cs             | 226 ++++----
 csharp/lib/Logger.cs                  | 170 +++---
 csharp/lib/Message.cs                 | 256 ++++-----
 csharp/lib/MessageContainer.cs        | 124 ++---
 csharp/lib/Properties/AssemblyInfo.cs |  14 +-
 csharp/tests/AsyncClientTests.cs      | 244 ++++-----
 csharp/tests/Usings.cs                |  10 +-
 9 files changed, 881 insertions(+), 881 deletions(-)

diff --git a/.editorconfig b/.editorconfig
index 0002f363fa..824238f9c1 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -20,8 +20,8 @@ indent_size = 4
 tab_width = 4
 
 # New line preferences
-end_of_line = crlf
-insert_final_newline = false
+end_of_line = lf
+insert_final_newline = true
 
 #### .NET Coding Conventions ####
 [*.{cs,vb}]
diff --git a/benchmarks/csharp/Program.cs b/benchmarks/csharp/Program.cs
index d753d09357..04d426a156 100644
--- a/benchmarks/csharp/Program.cs
+++ b/benchmarks/csharp/Program.cs
@@ -1,357 +1,357 @@
-/**
- * Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0
- */
-
-using System.Collections.Concurrent;
-using System.Diagnostics;
-using System.Text.Json;
-
-using CommandLine;
-
-using Glide;
-
-using LinqStatistics;
-
-using StackExchange.Redis;
-
-public static class MainClass
-{
-    private enum ChosenAction { GET_NON_EXISTING, GET_EXISTING, SET };
-
-    public class CommandLineOptions
-    {
-        [Option('r', "resultsFile", Required = false, HelpText = "Set the file to which the JSON results are written.", Default = "../results/csharp-results.json")]
-        public string resultsFile { get; set; } = string.Empty;
-
-        [Option('d', "dataSize", Required = false, HelpText = "The size of the sent data in bytes.", Default = 100)]
-        public int dataSize { get; set; }
-
-        [Option('c', "concurrentTasks", Required = false, HelpText = "The number of concurrent operations to perform.", Default = new[] { 1, 10, 100, 1000 })]
-        public IEnumerable<int> concurrentTasks { get; set; } = Enumerable.Empty<int>();
-
-        [Option('l', "clients", Required = false, HelpText = "Which clients should run", Default = "all")]
-        public string clientsToRun { get; set; } = string.Empty;
-
-        [Option('h', "host", Required = false, HelpText = "What host to target", Default = "localhost")]
-        public string host { get; set; } = string.Empty;
-
-        [Option('C', "clientCount", Required = false, HelpText = "Number of clients to run concurrently", Default = new[] { 1 })]
-        public IEnumerable<int> clientCount { get; set; } = Enumerable.Empty<int>();
-
-        [Option('t', "tls", HelpText = "Should benchmark a TLS server", Default = false)]
-        public bool tls { get; set; }
-
-
-        [Option('m', "minimal", HelpText = "Should use a minimal number of actions", Default = false)]
-        public bool minimal { get; set; }
-    }
-
-    private const int PORT = 6379;
-    private static string getAddress(string host)
-    {
-        return $"{host}:{PORT}";
-    }
-
-    private static string getAddressForStackExchangeRedis(string host, bool useTLS)
-    {
-        return $"{getAddress(host)},ssl={useTLS}";
-    }
-
-    private static string getAddressWithRedisPrefix(string host, bool useTLS)
-    {
-        var protocol = useTLS ? "rediss" : "redis";
-        return $"{protocol}://{getAddress(host)}";
-    }
-    private const double PROB_GET = 0.8;
-
-    private const double PROB_GET_EXISTING_KEY = 0.8;
-    private const int SIZE_GET_KEYSPACE = 3750000; // 3.75 million
-    private const int SIZE_SET_KEYSPACE = 3000000; // 3 million
-
-    private static readonly Random randomizer = new();
-    private static long started_tasks_counter = 0;
-    private static readonly List<Dictionary<string, object>> bench_json_results = new();
-
-    private static string generate_value(int size)
-    {
-        return new string('0', size);
-    }
-
-    private static string generate_key_set()
-    {
-        return (randomizer.Next(SIZE_SET_KEYSPACE) + 1).ToString();
-    }
-    private static string generate_key_get()
-    {
-        return (randomizer.Next(SIZE_SET_KEYSPACE, SIZE_GET_KEYSPACE) + 1).ToString();
-    }
-
-    private static ChosenAction choose_action()
-    {
-        if (randomizer.NextDouble() > PROB_GET)
-        {
-            return ChosenAction.SET;
-        }
-        if (randomizer.NextDouble() > PROB_GET_EXISTING_KEY)
-        {
-            return ChosenAction.GET_NON_EXISTING;
-        }
-        return ChosenAction.GET_EXISTING;
-    }
-
-    /// copied from https://stackoverflow.com/questions/8137391/percentile-calculation
-    private static double Percentile(double[] sequence, double excelPercentile)
-    {
-        Array.Sort(sequence);
-        int N = sequence.Length;
-        double n = (N - 1) * excelPercentile + 1;
-        if (n == 1d) return sequence[0];
-        else if (n == N) return sequence[N - 1];
-        else
-        {
-            int k = (int)n;
-            double d = n - k;
-            return sequence[k - 1] + d * (sequence[k] - sequence[k - 1]);
-        }
-    }
-
-    private static double calculate_latency(IEnumerable<double> latency_list, double percentile_point)
-    {
-        return Math.Round(Percentile(latency_list.ToArray(), percentile_point), 2);
-    }
-
-    private static void print_results(string resultsFile)
-    {
-        using FileStream createStream = File.Create(resultsFile);
-        JsonSerializer.Serialize(createStream, bench_json_results);
-    }
-
-    private static async Task redis_benchmark(
-        ClientWrapper[] clients,
-        long total_commands,
-        string data,
-        Dictionary<ChosenAction, ConcurrentBag<double>> action_latencies)
-    {
-        var stopwatch = new Stopwatch();
-        do
-        {
-            Interlocked.Increment(ref started_tasks_counter);
-            var index = (int)(started_tasks_counter % clients.Length);
-            var client = clients[index];
-            var action = choose_action();
-            stopwatch.Start();
-            switch (action)
-            {
-                case ChosenAction.GET_EXISTING:
-                    await client.get(generate_key_set());
-                    break;
-                case ChosenAction.GET_NON_EXISTING:
-                    await client.get(generate_key_get());
-                    break;
-                case ChosenAction.SET:
-                    await client.set(generate_key_set(), data);
-                    break;
-            }
-            stopwatch.Stop();
-            var latency_list = action_latencies[action];
-            latency_list.Add(((double)stopwatch.ElapsedMilliseconds) / 1000);
-        } while (started_tasks_counter < total_commands);
-    }
-
-    private static async Task<long> create_bench_tasks(
-        ClientWrapper[] clients,
-        int total_commands,
-        string data,
-        int num_of_concurrent_tasks,
-        Dictionary<ChosenAction, ConcurrentBag<double>> action_latencies
-    )
-    {
-        started_tasks_counter = 0;
-        var stopwatch = Stopwatch.StartNew();
-        var running_tasks = new List<Task>();
-        for (var i = 0; i < num_of_concurrent_tasks; i++)
-        {
-            running_tasks.Add(
-                redis_benchmark(clients, total_commands, data, action_latencies)
-            );
-        }
-        await Task.WhenAll(running_tasks);
-        stopwatch.Stop();
-        return stopwatch.ElapsedMilliseconds;
-    }
-
-    private static Dictionary<string, object> latency_results(
-        string prefix,
-        ConcurrentBag<double> latencies
-    )
-    {
-        return new Dictionary<string, object>
-        {
-            {prefix + "_p50_latency", calculate_latency(latencies, 0.5)},
-            {prefix + "_p90_latency", calculate_latency(latencies, 0.9)},
-            {prefix + "_p99_latency", calculate_latency(latencies, 0.99)},
-            {prefix + "_average_latency", Math.Round(latencies.Average(), 3)},
-            {prefix + "_std_dev", latencies.StandardDeviation()},
-        };
-    }
-
-    private static async Task run_clients(
-        ClientWrapper[] clients,
-        string client_name,
-        int total_commands,
-        int data_size,
-        int num_of_concurrent_tasks
-    )
-    {
-        Console.WriteLine($"Starting {client_name} data size: {data_size} concurrency: {num_of_concurrent_tasks} client count: {clients.Length} {DateTime.UtcNow.ToString("HH:mm:ss")}");
-        var action_latencies = new Dictionary<ChosenAction, ConcurrentBag<double>>() {
-            {ChosenAction.GET_NON_EXISTING, new()},
-            {ChosenAction.GET_EXISTING, new()},
-            {ChosenAction.SET, new()},
-        };
-        var data = generate_value(data_size);
-        var elapsed_milliseconds = await create_bench_tasks(
-            clients,
-            total_commands,
-            data,
-            num_of_concurrent_tasks,
-            action_latencies
-        );
-        var tps = Math.Round((double)started_tasks_counter / ((double)elapsed_milliseconds / 1000));
-
-        var get_non_existing_latencies = action_latencies[ChosenAction.GET_NON_EXISTING];
-        var get_non_existing_latency_results = latency_results("get_non_existing", get_non_existing_latencies);
-
-        var get_existing_latencies = action_latencies[ChosenAction.GET_EXISTING];
-        var get_existing_latency_results = latency_results("get_existing", get_existing_latencies);
-
-        var set_latencies = action_latencies[ChosenAction.SET];
-        var set_latency_results = latency_results("set", set_latencies);
-
-        var result = new Dictionary<string, object>
-        {
-            {"client", client_name},
-            {"num_of_tasks", num_of_concurrent_tasks},
-            {"data_size", data_size},
-            {"tps", tps},
-            {"client_count", clients.Length},
-            {"is_cluster", "false"}
-        };
-        result = result
-            .Concat(get_existing_latency_results)
-            .Concat(get_non_existing_latency_results)
-            .Concat(set_latency_results)
-            .ToDictionary(pair => pair.Key, pair => pair.Value);
-        bench_json_results.Add(result);
-    }
-
-    private class ClientWrapper : IDisposable
-    {
-        internal ClientWrapper(Func<string, Task<string?>> get, Func<string, string, Task> set, Action disposalFunction)
-        {
-            this.get = get;
-            this.set = set;
-            this.disposalFunction = disposalFunction;
-        }
-
-        public void Dispose()
-        {
-            this.disposalFunction();
-        }
-
-        internal Func<string, Task<string?>> get;
-        internal Func<string, string, Task> set;
-
-        private readonly Action disposalFunction;
-    }
-
-    private async static Task<ClientWrapper[]> createClients(int clientCount,
-        Func<Task<(Func<string, Task<string?>>,
-                   Func<string, string, Task>,
-                   Action)>> clientCreation)
-    {
-        var tasks = Enumerable.Range(0, clientCount).Select(async (_) =>
-        {
-            var tuple = await clientCreation();
-            return new ClientWrapper(tuple.Item1, tuple.Item2, tuple.Item3);
-        });
-        return await Task.WhenAll(tasks);
-    }
-
-    private static async Task run_with_parameters(int total_commands,
-        int data_size,
-        int num_of_concurrent_tasks,
-        string clientsToRun,
-        string host,
-        int clientCount,
-        bool useTLS)
-    {
-        if (clientsToRun == "all" || clientsToRun == "glide")
-        {
-            var clients = await createClients(clientCount, () =>
-            {
-                var glide_client = new AsyncClient(host, PORT, useTLS);
-                return Task.FromResult<(Func<string, Task<string?>>, Func<string, string, Task>, Action)>(
-                    (async (key) => await glide_client.GetAsync(key),
-                     async (key, value) => await glide_client.SetAsync(key, value),
-                     () => glide_client.Dispose()));
-            });
-
-            await run_clients(
-                clients,
-                "glide",
-                total_commands,
-                data_size,
-                num_of_concurrent_tasks
-            );
-        }
-
-        if (clientsToRun == "all")
-        {
-            var clients = await createClients(clientCount, () =>
-                {
-                    var connection = ConnectionMultiplexer.Connect(getAddressForStackExchangeRedis(host, useTLS));
-                    var db = connection.GetDatabase();
-                    return Task.FromResult<(Func<string, Task<string?>>, Func<string, string, Task>, Action)>(
-                        (async (key) => await db.StringGetAsync(key),
-                         async (key, value) => await db.StringSetAsync(key, value),
-                         () => connection.Dispose()));
-                });
-            await run_clients(
-                clients,
-                "StackExchange.Redis",
-                total_commands,
-                data_size,
-                num_of_concurrent_tasks
-            );
-
-            foreach (var client in clients)
-            {
-                client.Dispose();
-            }
-        }
-    }
-
-    private static int number_of_iterations(int num_of_concurrent_tasks)
-    {
-        return Math.Min(Math.Max(100000, num_of_concurrent_tasks * 10000), 10000000);
-    }
-
-    public static async Task Main(string[] args)
-    {
-        CommandLineOptions options = new();
-        Parser.Default
-            .ParseArguments<CommandLineOptions>(args).WithParsed<CommandLineOptions>(parsed => { options = parsed; });
-
-        Logger.SetLoggerConfig(Level.Info, Path.GetFileNameWithoutExtension(options.resultsFile));
-        var product = options.concurrentTasks.SelectMany(concurrentTasks =>
-            options.clientCount.Select(clientCount => (concurrentTasks: concurrentTasks, dataSize: options.dataSize, clientCount: clientCount))).Where(tuple => tuple.concurrentTasks >= tuple.clientCount);
-        foreach (var (concurrentTasks, dataSize, clientCount) in product)
-        {
-            var iterations = options.minimal ? 1000 : number_of_iterations(concurrentTasks);
-            await run_with_parameters(iterations, dataSize, concurrentTasks, options.clientsToRun, options.host, clientCount, options.tls);
-        }
-
-        print_results(options.resultsFile);
-    }
-}
\ No newline at end of file
+/**
+ * Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0
+ */
+
+using System.Collections.Concurrent;
+using System.Diagnostics;
+using System.Text.Json;
+
+using CommandLine;
+
+using Glide;
+
+using LinqStatistics;
+
+using StackExchange.Redis;
+
+public static class MainClass
+{
+    private enum ChosenAction { GET_NON_EXISTING, GET_EXISTING, SET };
+
+    public class CommandLineOptions
+    {
+        [Option('r', "resultsFile", Required = false, HelpText = "Set the file to which the JSON results are written.", Default = "../results/csharp-results.json")]
+        public string resultsFile { get; set; } = string.Empty;
+
+        [Option('d', "dataSize", Required = false, HelpText = "The size of the sent data in bytes.", Default = 100)]
+        public int dataSize { get; set; }
+
+        [Option('c', "concurrentTasks", Required = false, HelpText = "The number of concurrent operations to perform.", Default = new[] { 1, 10, 100, 1000 })]
+        public IEnumerable<int> concurrentTasks { get; set; } = Enumerable.Empty<int>();
+
+        [Option('l', "clients", Required = false, HelpText = "Which clients should run", Default = "all")]
+        public string clientsToRun { get; set; } = string.Empty;
+
+        [Option('h', "host", Required = false, HelpText = "What host to target", Default = "localhost")]
+        public string host { get; set; } = string.Empty;
+
+        [Option('C', "clientCount", Required = false, HelpText = "Number of clients to run concurrently", Default = new[] { 1 })]
+        public IEnumerable<int> clientCount { get; set; } = Enumerable.Empty<int>();
+
+        [Option('t', "tls", HelpText = "Should benchmark a TLS server", Default = false)]
+        public bool tls { get; set; }
+
+
+        [Option('m', "minimal", HelpText = "Should use a minimal number of actions", Default = false)]
+        public bool minimal { get; set; }
+    }
+
+    private const int PORT = 6379;
+    private static string getAddress(string host)
+    {
+        return $"{host}:{PORT}";
+    }
+
+    private static string getAddressForStackExchangeRedis(string host, bool useTLS)
+    {
+        return $"{getAddress(host)},ssl={useTLS}";
+    }
+
+    private static string getAddressWithRedisPrefix(string host, bool useTLS)
+    {
+        var protocol = useTLS ? "rediss" : "redis";
+        return $"{protocol}://{getAddress(host)}";
+    }
+    private const double PROB_GET = 0.8;
+
+    private const double PROB_GET_EXISTING_KEY = 0.8;
+    private const int SIZE_GET_KEYSPACE = 3750000; // 3.75 million
+    private const int SIZE_SET_KEYSPACE = 3000000; // 3 million
+
+    private static readonly Random randomizer = new();
+    private static long started_tasks_counter = 0;
+    private static readonly List<Dictionary<string, object>> bench_json_results = new();
+
+    private static string generate_value(int size)
+    {
+        return new string('0', size);
+    }
+
+    private static string generate_key_set()
+    {
+        return (randomizer.Next(SIZE_SET_KEYSPACE) + 1).ToString();
+    }
+    private static string generate_key_get()
+    {
+        return (randomizer.Next(SIZE_SET_KEYSPACE, SIZE_GET_KEYSPACE) + 1).ToString();
+    }
+
+    private static ChosenAction choose_action()
+    {
+        if (randomizer.NextDouble() > PROB_GET)
+        {
+            return ChosenAction.SET;
+        }
+        if (randomizer.NextDouble() > PROB_GET_EXISTING_KEY)
+        {
+            return ChosenAction.GET_NON_EXISTING;
+        }
+        return ChosenAction.GET_EXISTING;
+    }
+
+    /// copied from https://stackoverflow.com/questions/8137391/percentile-calculation
+    private static double Percentile(double[] sequence, double excelPercentile)
+    {
+        Array.Sort(sequence);
+        int N = sequence.Length;
+        double n = (N - 1) * excelPercentile + 1;
+        if (n == 1d) return sequence[0];
+        else if (n == N) return sequence[N - 1];
+        else
+        {
+            int k = (int)n;
+            double d = n - k;
+            return sequence[k - 1] + d * (sequence[k] - sequence[k - 1]);
+        }
+    }
+
+    private static double calculate_latency(IEnumerable<double> latency_list, double percentile_point)
+    {
+        return Math.Round(Percentile(latency_list.ToArray(), percentile_point), 2);
+    }
+
+    private static void print_results(string resultsFile)
+    {
+        using FileStream createStream = File.Create(resultsFile);
+        JsonSerializer.Serialize(createStream, bench_json_results);
+    }
+
+    private static async Task redis_benchmark(
+        ClientWrapper[] clients,
+        long total_commands,
+        string data,
+        Dictionary<ChosenAction, ConcurrentBag<double>> action_latencies)
+    {
+        var stopwatch = new Stopwatch();
+        do
+        {
+            Interlocked.Increment(ref started_tasks_counter);
+            var index = (int)(started_tasks_counter % clients.Length);
+            var client = clients[index];
+            var action = choose_action();
+            stopwatch.Start();
+            switch (action)
+            {
+                case ChosenAction.GET_EXISTING:
+                    await client.get(generate_key_set());
+                    break;
+                case ChosenAction.GET_NON_EXISTING:
+                    await client.get(generate_key_get());
+                    break;
+                case ChosenAction.SET:
+                    await client.set(generate_key_set(), data);
+                    break;
+            }
+            stopwatch.Stop();
+            var latency_list = action_latencies[action];
+            latency_list.Add(((double)stopwatch.ElapsedMilliseconds) / 1000);
+        } while (started_tasks_counter < total_commands);
+    }
+
+    private static async Task<long> create_bench_tasks(
+        ClientWrapper[] clients,
+        int total_commands,
+        string data,
+        int num_of_concurrent_tasks,
+        Dictionary<ChosenAction, ConcurrentBag<double>> action_latencies
+    )
+    {
+        started_tasks_counter = 0;
+        var stopwatch = Stopwatch.StartNew();
+        var running_tasks = new List<Task>();
+        for (var i = 0; i < num_of_concurrent_tasks; i++)
+        {
+            running_tasks.Add(
+                redis_benchmark(clients, total_commands, data, action_latencies)
+            );
+        }
+        await Task.WhenAll(running_tasks);
+        stopwatch.Stop();
+        return stopwatch.ElapsedMilliseconds;
+    }
+
+    private static Dictionary<string, object> latency_results(
+        string prefix,
+        ConcurrentBag<double> latencies
+    )
+    {
+        return new Dictionary<string, object>
+        {
+            {prefix + "_p50_latency", calculate_latency(latencies, 0.5)},
+            {prefix + "_p90_latency", calculate_latency(latencies, 0.9)},
+            {prefix + "_p99_latency", calculate_latency(latencies, 0.99)},
+            {prefix + "_average_latency", Math.Round(latencies.Average(), 3)},
+            {prefix + "_std_dev", latencies.StandardDeviation()},
+        };
+    }
+
+    private static async Task run_clients(
+        ClientWrapper[] clients,
+        string client_name,
+        int total_commands,
+        int data_size,
+        int num_of_concurrent_tasks
+    )
+    {
+        Console.WriteLine($"Starting {client_name} data size: {data_size} concurrency: {num_of_concurrent_tasks} client count: {clients.Length} {DateTime.UtcNow.ToString("HH:mm:ss")}");
+        var action_latencies = new Dictionary<ChosenAction, ConcurrentBag<double>>() {
+            {ChosenAction.GET_NON_EXISTING, new()},
+            {ChosenAction.GET_EXISTING, new()},
+            {ChosenAction.SET, new()},
+        };
+        var data = generate_value(data_size);
+        var elapsed_milliseconds = await create_bench_tasks(
+            clients,
+            total_commands,
+            data,
+            num_of_concurrent_tasks,
+            action_latencies
+        );
+        var tps = Math.Round((double)started_tasks_counter / ((double)elapsed_milliseconds / 1000));
+
+        var get_non_existing_latencies = action_latencies[ChosenAction.GET_NON_EXISTING];
+        var get_non_existing_latency_results = latency_results("get_non_existing", get_non_existing_latencies);
+
+        var get_existing_latencies = action_latencies[ChosenAction.GET_EXISTING];
+        var get_existing_latency_results = latency_results("get_existing", get_existing_latencies);
+
+        var set_latencies = action_latencies[ChosenAction.SET];
+        var set_latency_results = latency_results("set", set_latencies);
+
+        var result = new Dictionary<string, object>
+        {
+            {"client", client_name},
+            {"num_of_tasks", num_of_concurrent_tasks},
+            {"data_size", data_size},
+            {"tps", tps},
+            {"client_count", clients.Length},
+            {"is_cluster", "false"}
+        };
+        result = result
+            .Concat(get_existing_latency_results)
+            .Concat(get_non_existing_latency_results)
+            .Concat(set_latency_results)
+            .ToDictionary(pair => pair.Key, pair => pair.Value);
+        bench_json_results.Add(result);
+    }
+
+    private class ClientWrapper : IDisposable
+    {
+        internal ClientWrapper(Func<string, Task<string?>> get, Func<string, string, Task> set, Action disposalFunction)
+        {
+            this.get = get;
+            this.set = set;
+            this.disposalFunction = disposalFunction;
+        }
+
+        public void Dispose()
+        {
+            this.disposalFunction();
+        }
+
+        internal Func<string, Task<string?>> get;
+        internal Func<string, string, Task> set;
+
+        private readonly Action disposalFunction;
+    }
+
+    private async static Task<ClientWrapper[]> createClients(int clientCount,
+        Func<Task<(Func<string, Task<string?>>,
+                   Func<string, string, Task>,
+                   Action)>> clientCreation)
+    {
+        var tasks = Enumerable.Range(0, clientCount).Select(async (_) =>
+        {
+            var tuple = await clientCreation();
+            return new ClientWrapper(tuple.Item1, tuple.Item2, tuple.Item3);
+        });
+        return await Task.WhenAll(tasks);
+    }
+
+    private static async Task run_with_parameters(int total_commands,
+        int data_size,
+        int num_of_concurrent_tasks,
+        string clientsToRun,
+        string host,
+        int clientCount,
+        bool useTLS)
+    {
+        if (clientsToRun == "all" || clientsToRun == "glide")
+        {
+            var clients = await createClients(clientCount, () =>
+            {
+                var glide_client = new AsyncClient(host, PORT, useTLS);
+                return Task.FromResult<(Func<string, Task<string?>>, Func<string, string, Task>, Action)>(
+                    (async (key) => await glide_client.GetAsync(key),
+                     async (key, value) => await glide_client.SetAsync(key, value),
+                     () => glide_client.Dispose()));
+            });
+
+            await run_clients(
+                clients,
+                "glide",
+                total_commands,
+                data_size,
+                num_of_concurrent_tasks
+            );
+        }
+
+        if (clientsToRun == "all")
+        {
+            var clients = await createClients(clientCount, () =>
+                {
+                    var connection = ConnectionMultiplexer.Connect(getAddressForStackExchangeRedis(host, useTLS));
+                    var db = connection.GetDatabase();
+                    return Task.FromResult<(Func<string, Task<string?>>, Func<string, string, Task>, Action)>(
+                        (async (key) => await db.StringGetAsync(key),
+                         async (key, value) => await db.StringSetAsync(key, value),
+                         () => connection.Dispose()));
+                });
+            await run_clients(
+                clients,
+                "StackExchange.Redis",
+                total_commands,
+                data_size,
+                num_of_concurrent_tasks
+            );
+
+            foreach (var client in clients)
+            {
+                client.Dispose();
+            }
+        }
+    }
+
+    private static int number_of_iterations(int num_of_concurrent_tasks)
+    {
+        return Math.Min(Math.Max(100000, num_of_concurrent_tasks * 10000), 10000000);
+    }
+
+    public static async Task Main(string[] args)
+    {
+        CommandLineOptions options = new();
+        Parser.Default
+            .ParseArguments<CommandLineOptions>(args).WithParsed<CommandLineOptions>(parsed => { options = parsed; });
+
+        Logger.SetLoggerConfig(Level.Info, Path.GetFileNameWithoutExtension(options.resultsFile));
+        var product = options.concurrentTasks.SelectMany(concurrentTasks =>
+            options.clientCount.Select(clientCount => (concurrentTasks: concurrentTasks, dataSize: options.dataSize, clientCount: clientCount))).Where(tuple => tuple.concurrentTasks >= tuple.clientCount);
+        foreach (var (concurrentTasks, dataSize, clientCount) in product)
+        {
+            var iterations = options.minimal ? 1000 : number_of_iterations(concurrentTasks);
+            await run_with_parameters(iterations, dataSize, concurrentTasks, options.clientsToRun, options.host, clientCount, options.tls);
+        }
+
+        print_results(options.resultsFile);
+    }
+}
diff --git a/csharp/lib/AsyncClient.cs b/csharp/lib/AsyncClient.cs
index 9657907b64..83e3d4c39b 100644
--- a/csharp/lib/AsyncClient.cs
+++ b/csharp/lib/AsyncClient.cs
@@ -1,113 +1,113 @@
-/**
- * Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0
- */
-
-using System.Runtime.InteropServices;
-
-namespace Glide;
-
-public class AsyncClient : IDisposable
-{
-    #region public methods
-    public AsyncClient(string host, UInt32 port, bool useTLS)
-    {
-        successCallbackDelegate = SuccessCallback;
-        var successCallbackPointer = Marshal.GetFunctionPointerForDelegate(successCallbackDelegate);
-        failureCallbackDelegate = FailureCallback;
-        var failureCallbackPointer = Marshal.GetFunctionPointerForDelegate(failureCallbackDelegate);
-        clientPointer = CreateClientFfi(host, port, useTLS, successCallbackPointer, failureCallbackPointer);
-        if (clientPointer == IntPtr.Zero)
-        {
-            throw new Exception("Failed creating a client");
-        }
-    }
-
-    public async Task SetAsync(string key, string value)
-    {
-        var message = messageContainer.GetMessageForCall(key, value);
-        SetFfi(clientPointer, (ulong)message.Index, message.KeyPtr, message.ValuePtr);
-        await message;
-    }
-
-    public async Task<string?> GetAsync(string key)
-    {
-        var message = messageContainer.GetMessageForCall(key, null);
-        GetFfi(clientPointer, (ulong)message.Index, message.KeyPtr);
-        return await message;
-    }
-
-    public void Dispose()
-    {
-        if (clientPointer == IntPtr.Zero)
-        {
-            return;
-        }
-        messageContainer.DisposeWithError(null);
-        CloseClientFfi(clientPointer);
-        clientPointer = IntPtr.Zero;
-    }
-
-    #endregion public methods
-
-    #region private methods
-
-    private void SuccessCallback(ulong index, IntPtr str)
-    {
-        var result = str == IntPtr.Zero ? null : Marshal.PtrToStringAnsi(str);
-        // Work needs to be offloaded from the calling thread, because otherwise we might starve the client's thread pool.
-        Task.Run(() =>
-        {
-            var message = messageContainer.GetMessage((int)index);
-            message.SetResult(result);
-        });
-    }
-
-    private void FailureCallback(ulong index)
-    {
-        // Work needs to be offloaded from the calling thread, because otherwise we might starve the client's thread pool.
-        Task.Run(() =>
-        {
-            var message = messageContainer.GetMessage((int)index);
-            message.SetException(new Exception("Operation failed"));
-        });
-    }
-
-    ~AsyncClient() => Dispose();
-    #endregion private methods
-
-    #region private fields
-
-    /// Held as a measure to prevent the delegate being garbage collected. These are delegated once
-    /// and held in order to prevent the cost of marshalling on each function call.
-    private readonly FailureAction failureCallbackDelegate;
-
-    /// Held as a measure to prevent the delegate being garbage collected. These are delegated once
-    /// and held in order to prevent the cost of marshalling on each function call.
-    private readonly StringAction successCallbackDelegate;
-
-    /// Raw pointer to the underlying native client.
-    private IntPtr clientPointer;
-
-    private readonly MessageContainer<string> messageContainer = new();
-
-    #endregion private fields
-
-    #region FFI function declarations
-
-    private delegate void StringAction(ulong index, IntPtr str);
-    private delegate void FailureAction(ulong index);
-    [DllImport("libglide_rs", CallingConvention = CallingConvention.Cdecl, EntryPoint = "get")]
-    private static extern void GetFfi(IntPtr client, ulong index, IntPtr key);
-
-    [DllImport("libglide_rs", CallingConvention = CallingConvention.Cdecl, EntryPoint = "set")]
-    private static extern void SetFfi(IntPtr client, ulong index, IntPtr key, IntPtr value);
-
-    private delegate void IntAction(IntPtr arg);
-    [DllImport("libglide_rs", CallingConvention = CallingConvention.Cdecl, EntryPoint = "create_client")]
-    private static extern IntPtr CreateClientFfi(String host, UInt32 port, bool useTLS, IntPtr successCallback, IntPtr failureCallback);
-
-    [DllImport("libglide_rs", CallingConvention = CallingConvention.Cdecl, EntryPoint = "close_client")]
-    private static extern void CloseClientFfi(IntPtr client);
-
-    #endregion
-}
\ No newline at end of file
+/**
+ * Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0
+ */
+
+using System.Runtime.InteropServices;
+
+namespace Glide;
+
+public class AsyncClient : IDisposable
+{
+    #region public methods
+    public AsyncClient(string host, UInt32 port, bool useTLS)
+    {
+        successCallbackDelegate = SuccessCallback;
+        var successCallbackPointer = Marshal.GetFunctionPointerForDelegate(successCallbackDelegate);
+        failureCallbackDelegate = FailureCallback;
+        var failureCallbackPointer = Marshal.GetFunctionPointerForDelegate(failureCallbackDelegate);
+        clientPointer = CreateClientFfi(host, port, useTLS, successCallbackPointer, failureCallbackPointer);
+        if (clientPointer == IntPtr.Zero)
+        {
+            throw new Exception("Failed creating a client");
+        }
+    }
+
+    public async Task SetAsync(string key, string value)
+    {
+        var message = messageContainer.GetMessageForCall(key, value);
+        SetFfi(clientPointer, (ulong)message.Index, message.KeyPtr, message.ValuePtr);
+        await message;
+    }
+
+    public async Task<string?> GetAsync(string key)
+    {
+        var message = messageContainer.GetMessageForCall(key, null);
+        GetFfi(clientPointer, (ulong)message.Index, message.KeyPtr);
+        return await message;
+    }
+
+    public void Dispose()
+    {
+        if (clientPointer == IntPtr.Zero)
+        {
+            return;
+        }
+        messageContainer.DisposeWithError(null);
+        CloseClientFfi(clientPointer);
+        clientPointer = IntPtr.Zero;
+    }
+
+    #endregion public methods
+
+    #region private methods
+
+    private void SuccessCallback(ulong index, IntPtr str)
+    {
+        var result = str == IntPtr.Zero ? null : Marshal.PtrToStringAnsi(str);
+        // Work needs to be offloaded from the calling thread, because otherwise we might starve the client's thread pool.
+        Task.Run(() =>
+        {
+            var message = messageContainer.GetMessage((int)index);
+            message.SetResult(result);
+        });
+    }
+
+    private void FailureCallback(ulong index)
+    {
+        // Work needs to be offloaded from the calling thread, because otherwise we might starve the client's thread pool.
+        Task.Run(() =>
+        {
+            var message = messageContainer.GetMessage((int)index);
+            message.SetException(new Exception("Operation failed"));
+        });
+    }
+
+    ~AsyncClient() => Dispose();
+    #endregion private methods
+
+    #region private fields
+
+    /// Held as a measure to prevent the delegate being garbage collected. These are delegated once
+    /// and held in order to prevent the cost of marshalling on each function call.
+    private readonly FailureAction failureCallbackDelegate;
+
+    /// Held as a measure to prevent the delegate being garbage collected. These are delegated once
+    /// and held in order to prevent the cost of marshalling on each function call.
+    private readonly StringAction successCallbackDelegate;
+
+    /// Raw pointer to the underlying native client.
+    private IntPtr clientPointer;
+
+    private readonly MessageContainer<string> messageContainer = new();
+
+    #endregion private fields
+
+    #region FFI function declarations
+
+    private delegate void StringAction(ulong index, IntPtr str);
+    private delegate void FailureAction(ulong index);
+    [DllImport("libglide_rs", CallingConvention = CallingConvention.Cdecl, EntryPoint = "get")]
+    private static extern void GetFfi(IntPtr client, ulong index, IntPtr key);
+
+    [DllImport("libglide_rs", CallingConvention = CallingConvention.Cdecl, EntryPoint = "set")]
+    private static extern void SetFfi(IntPtr client, ulong index, IntPtr key, IntPtr value);
+
+    private delegate void IntAction(IntPtr arg);
+    [DllImport("libglide_rs", CallingConvention = CallingConvention.Cdecl, EntryPoint = "create_client")]
+    private static extern IntPtr CreateClientFfi(String host, UInt32 port, bool useTLS, IntPtr successCallback, IntPtr failureCallback);
+
+    [DllImport("libglide_rs", CallingConvention = CallingConvention.Cdecl, EntryPoint = "close_client")]
+    private static extern void CloseClientFfi(IntPtr client);
+
+    #endregion
+}
diff --git a/csharp/lib/Logger.cs b/csharp/lib/Logger.cs
index 03fb47707d..7edc16f16c 100644
--- a/csharp/lib/Logger.cs
+++ b/csharp/lib/Logger.cs
@@ -1,85 +1,85 @@
-/**
- * Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0
- */
-
-using System.Runtime.InteropServices;
-using System.Text;
-
-
-namespace Glide;
-
-// TODO - use a bindings generator to create this enum.
-public enum Level
-{
-    Error = 0,
-    Warn = 1,
-    Info = 2,
-    Debug = 3,
-    Trace = 4
-}
-
-/*
-A class that allows logging which is consistent with logs from the internal rust core.
-Only one instance of this class can exist at any given time. The logger can be set up in 2 ways -
-    1. By calling init, which creates and modifies a new logger only if one doesn't exist.
-    2. By calling setConfig, which replaces the existing logger, and means that new logs will not be saved with the logs that were sent before the call.
-If no call to any of these function is received, the first log attempt will initialize a new logger with default level decided by rust core (normally - console, error).
-*/
-public class Logger
-{
-    #region private fields
-
-    private static Level? loggerLevel = null;
-    #endregion private fields
-
-    #region internal methods
-    // Initialize a logger instance if none were initialized before - this method is meant to be used when there is no intention to replace an existing logger.
-    // The logger will filter all logs with a level lower than the given level,
-    // If given a fileName argument, will write the logs to files postfixed with fileName. If fileName isn't provided, the logs will be written to the console.
-    internal static void Init(Level? level, string? filename = null)
-    {
-        if (Logger.loggerLevel is null)
-        {
-            SetLoggerConfig(level, filename);
-        }
-    }
-
-    // take the arguments from the user and provide to the core-logger (see ../logger-core)
-    // if the level is higher then the logger level (error is 0, warn 1, etc.) simply return without operation
-    // if a logger instance doesn't exist, create new one with default mode (decided by rust core, normally - level: error, target: console)
-    // logIdentifier arg is a string contain data that suppose to give the log a context and make it easier to find certain type of logs.
-    // when the log is connect to certain task the identifier should be the task id, when the log is not part of specific task the identifier should give a context to the log - for example, "create client".
-    internal static void Log(Level logLevel, string logIdentifier, string message)
-    {
-        if (Logger.loggerLevel is null)
-        {
-            SetLoggerConfig(logLevel);
-        }
-        if (!(logLevel <= Logger.loggerLevel)) return;
-        log(Convert.ToInt32(logLevel), Encoding.UTF8.GetBytes(logIdentifier), Encoding.UTF8.GetBytes(message));
-    }
-    #endregion internal methods
-
-    #region public methods
-    // config the logger instance - in fact - create new logger instance with the new args
-    // exist in addition to init for two main reason's:
-    // 1. if GLIDE dev want intentionally to change the logger instance configuration
-    // 2. external user want to set the logger and we don't want to return to him the logger itself, just config it
-    // the level argument is the level of the logs you want the system to provide (error logs, warn logs, etc.)
-    // the filename argument is optional - if provided the target of the logs will be the file mentioned, else will be the console
-    public static void SetLoggerConfig(Level? level, string? filename = null)
-    {
-        var buffer = filename is null ? null : Encoding.UTF8.GetBytes(filename);
-        Logger.loggerLevel = InitInternalLogger(Convert.ToInt32(level), buffer);
-    }
-    #endregion public methods
-
-    #region FFI function declaration
-    [DllImport("libglide_rs", CallingConvention = CallingConvention.Cdecl, EntryPoint = "log")]
-    private static extern void log(Int32 logLevel, byte[] logIdentifier, byte[] message);
-
-    [DllImport("libglide_rs", CallingConvention = CallingConvention.Cdecl, EntryPoint = "init")]
-    private static extern Level InitInternalLogger(Int32 level, byte[]? filename);
-
-    #endregion
-}
\ No newline at end of file
+/**
+ * Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0
+ */
+
+using System.Runtime.InteropServices;
+using System.Text;
+
+
+namespace Glide;
+
+// TODO - use a bindings generator to create this enum.
+public enum Level
+{
+    Error = 0,
+    Warn = 1,
+    Info = 2,
+    Debug = 3,
+    Trace = 4
+}
+
+/*
+A class that allows logging which is consistent with logs from the internal rust core.
+Only one instance of this class can exist at any given time. The logger can be set up in 2 ways -
+    1. By calling init, which creates and modifies a new logger only if one doesn't exist.
+    2. By calling setConfig, which replaces the existing logger, and means that new logs will not be saved with the logs that were sent before the call.
+If no call to any of these function is received, the first log attempt will initialize a new logger with default level decided by rust core (normally - console, error).
+*/
+public class Logger
+{
+    #region private fields
+
+    private static Level? loggerLevel = null;
+    #endregion private fields
+
+    #region internal methods
+    // Initialize a logger instance if none were initialized before - this method is meant to be used when there is no intention to replace an existing logger.
+    // The logger will filter all logs with a level lower than the given level,
+    // If given a fileName argument, will write the logs to files postfixed with fileName. If fileName isn't provided, the logs will be written to the console.
+    internal static void Init(Level? level, string? filename = null)
+    {
+        if (Logger.loggerLevel is null)
+        {
+            SetLoggerConfig(level, filename);
+        }
+    }
+
+    // take the arguments from the user and provide to the core-logger (see ../logger-core)
+    // if the level is higher then the logger level (error is 0, warn 1, etc.) simply return without operation
+    // if a logger instance doesn't exist, create new one with default mode (decided by rust core, normally - level: error, target: console)
+    // logIdentifier arg is a string contain data that suppose to give the log a context and make it easier to find certain type of logs.
+    // when the log is connect to certain task the identifier should be the task id, when the log is not part of specific task the identifier should give a context to the log - for example, "create client".
+    internal static void Log(Level logLevel, string logIdentifier, string message)
+    {
+        if (Logger.loggerLevel is null)
+        {
+            SetLoggerConfig(logLevel);
+        }
+        if (!(logLevel <= Logger.loggerLevel)) return;
+        log(Convert.ToInt32(logLevel), Encoding.UTF8.GetBytes(logIdentifier), Encoding.UTF8.GetBytes(message));
+    }
+    #endregion internal methods
+
+    #region public methods
+    // config the logger instance - in fact - create new logger instance with the new args
+    // exist in addition to init for two main reason's:
+    // 1. if GLIDE dev want intentionally to change the logger instance configuration
+    // 2. external user want to set the logger and we don't want to return to him the logger itself, just config it
+    // the level argument is the level of the logs you want the system to provide (error logs, warn logs, etc.)
+    // the filename argument is optional - if provided the target of the logs will be the file mentioned, else will be the console
+    public static void SetLoggerConfig(Level? level, string? filename = null)
+    {
+        var buffer = filename is null ? null : Encoding.UTF8.GetBytes(filename);
+        Logger.loggerLevel = InitInternalLogger(Convert.ToInt32(level), buffer);
+    }
+    #endregion public methods
+
+    #region FFI function declaration
+    [DllImport("libglide_rs", CallingConvention = CallingConvention.Cdecl, EntryPoint = "log")]
+    private static extern void log(Int32 logLevel, byte[] logIdentifier, byte[] message);
+
+    [DllImport("libglide_rs", CallingConvention = CallingConvention.Cdecl, EntryPoint = "init")]
+    private static extern Level InitInternalLogger(Int32 level, byte[]? filename);
+
+    #endregion
+}
diff --git a/csharp/lib/Message.cs b/csharp/lib/Message.cs
index 6c5f4203f3..c0d4c7f07b 100644
--- a/csharp/lib/Message.cs
+++ b/csharp/lib/Message.cs
@@ -1,128 +1,128 @@
-/**
- * Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0
- */
-
-using System.Diagnostics;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-
-using Glide;
-
-/// Reusable source of ValueTask. This object can be allocated once and then reused
-/// to create multiple asynchronous operations, as long as each call to CreateTask
-/// is awaited to completion before the next call begins.
-internal class Message<T> : INotifyCompletion
-{
-    /// This is the index of the message in an external array, that allows the user to
-    /// know how to find the message and set its result.
-    public int Index { get; }
-
-    /// The pointer to the unmanaged memory that contains the operation's key.
-    public IntPtr KeyPtr { get; private set; }
-
-    /// The pointer to the unmanaged memory that contains the operation's key.
-    public IntPtr ValuePtr { get; private set; }
-    private readonly MessageContainer<T> container;
-
-    public Message(int index, MessageContainer<T> container)
-    {
-        Index = index;
-        continuation = () => { };
-        this.container = container;
-    }
-
-    private Action? continuation;
-    const int COMPLETION_STAGE_STARTED = 0;
-    const int COMPLETION_STAGE_NEXT_SHOULD_EXECUTE_CONTINUATION = 1;
-    const int COMPLETION_STAGE_CONTINUATION_EXECUTED = 2;
-    private int completionState;
-    private T? result;
-    private Exception? exception;
-
-    /// Triggers a succesful completion of the task returned from the latest call
-    /// to CreateTask.
-    public void SetResult(T? result)
-    {
-        this.result = result;
-        FinishSet();
-    }
-
-    /// Triggers a failure completion of the task returned from the latest call to
-    /// CreateTask.
-    public void SetException(Exception exc)
-    {
-        this.exception = exc;
-        FinishSet();
-    }
-
-    private void FinishSet()
-    {
-        FreePointers();
-
-        CheckRaceAndCallContinuation();
-    }
-
-    private void CheckRaceAndCallContinuation()
-    {
-        if (Interlocked.CompareExchange(ref this.completionState, COMPLETION_STAGE_NEXT_SHOULD_EXECUTE_CONTINUATION, COMPLETION_STAGE_STARTED) == COMPLETION_STAGE_NEXT_SHOULD_EXECUTE_CONTINUATION)
-        {
-            Debug.Assert(this.continuation != null);
-            this.completionState = COMPLETION_STAGE_CONTINUATION_EXECUTED;
-            try
-            {
-                continuation();
-            }
-            finally
-            {
-                this.container.ReturnFreeMessage(this);
-            }
-        }
-    }
-
-    public Message<T> GetAwaiter() => this;
-
-    /// This returns a task that will complete once SetException / SetResult are called,
-    /// and ensures that the internal state of the message is set-up before the task is created,
-    /// and cleaned once it is complete.
-    public void StartTask(string? key, string? value, object client)
-    {
-        continuation = null;
-        this.completionState = COMPLETION_STAGE_STARTED;
-        this.result = default(T);
-        this.exception = null;
-        this.client = client;
-        this.KeyPtr = key is null ? IntPtr.Zero : Marshal.StringToHGlobalAnsi(key);
-        this.ValuePtr = value is null ? IntPtr.Zero : Marshal.StringToHGlobalAnsi(value);
-    }
-
-    // This function isn't thread-safe. Access to it should be from a single thread, and only once per operation.
-    // For the sake of performance, this responsibility is on the caller, and the function doesn't contain any safety measures.
-    private void FreePointers()
-    {
-        if (KeyPtr != IntPtr.Zero)
-        {
-            Marshal.FreeHGlobal(KeyPtr);
-            KeyPtr = IntPtr.Zero;
-        }
-        if (ValuePtr != IntPtr.Zero)
-        {
-            Marshal.FreeHGlobal(ValuePtr);
-            ValuePtr = IntPtr.Zero;
-        }
-        client = null;
-    }
-
-    // Holding the client prevents it from being CG'd until all operations complete.
-    private object? client;
-
-
-    public void OnCompleted(Action continuation)
-    {
-        this.continuation = continuation;
-        CheckRaceAndCallContinuation();
-    }
-
-    public bool IsCompleted => completionState == COMPLETION_STAGE_CONTINUATION_EXECUTED;
-
-    public T? GetResult() => this.exception is null ? this.result : throw this.exception;
-}
\ No newline at end of file
+/**
+ * Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0
+ */
+
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+using Glide;
+
+/// Reusable source of ValueTask. This object can be allocated once and then reused
+/// to create multiple asynchronous operations, as long as each call to CreateTask
+/// is awaited to completion before the next call begins.
+internal class Message<T> : INotifyCompletion
+{
+    /// This is the index of the message in an external array, that allows the user to
+    /// know how to find the message and set its result.
+    public int Index { get; }
+
+    /// The pointer to the unmanaged memory that contains the operation's key.
+    public IntPtr KeyPtr { get; private set; }
+
+    /// The pointer to the unmanaged memory that contains the operation's key.
+    public IntPtr ValuePtr { get; private set; }
+    private readonly MessageContainer<T> container;
+
+    public Message(int index, MessageContainer<T> container)
+    {
+        Index = index;
+        continuation = () => { };
+        this.container = container;
+    }
+
+    private Action? continuation;
+    const int COMPLETION_STAGE_STARTED = 0;
+    const int COMPLETION_STAGE_NEXT_SHOULD_EXECUTE_CONTINUATION = 1;
+    const int COMPLETION_STAGE_CONTINUATION_EXECUTED = 2;
+    private int completionState;
+    private T? result;
+    private Exception? exception;
+
+    /// Triggers a succesful completion of the task returned from the latest call
+    /// to CreateTask.
+    public void SetResult(T? result)
+    {
+        this.result = result;
+        FinishSet();
+    }
+
+    /// Triggers a failure completion of the task returned from the latest call to
+    /// CreateTask.
+    public void SetException(Exception exc)
+    {
+        this.exception = exc;
+        FinishSet();
+    }
+
+    private void FinishSet()
+    {
+        FreePointers();
+
+        CheckRaceAndCallContinuation();
+    }
+
+    private void CheckRaceAndCallContinuation()
+    {
+        if (Interlocked.CompareExchange(ref this.completionState, COMPLETION_STAGE_NEXT_SHOULD_EXECUTE_CONTINUATION, COMPLETION_STAGE_STARTED) == COMPLETION_STAGE_NEXT_SHOULD_EXECUTE_CONTINUATION)
+        {
+            Debug.Assert(this.continuation != null);
+            this.completionState = COMPLETION_STAGE_CONTINUATION_EXECUTED;
+            try
+            {
+                continuation();
+            }
+            finally
+            {
+                this.container.ReturnFreeMessage(this);
+            }
+        }
+    }
+
+    public Message<T> GetAwaiter() => this;
+
+    /// This returns a task that will complete once SetException / SetResult are called,
+    /// and ensures that the internal state of the message is set-up before the task is created,
+    /// and cleaned once it is complete.
+    public void StartTask(string? key, string? value, object client)
+    {
+        continuation = null;
+        this.completionState = COMPLETION_STAGE_STARTED;
+        this.result = default(T);
+        this.exception = null;
+        this.client = client;
+        this.KeyPtr = key is null ? IntPtr.Zero : Marshal.StringToHGlobalAnsi(key);
+        this.ValuePtr = value is null ? IntPtr.Zero : Marshal.StringToHGlobalAnsi(value);
+    }
+
+    // This function isn't thread-safe. Access to it should be from a single thread, and only once per operation.
+    // For the sake of performance, this responsibility is on the caller, and the function doesn't contain any safety measures.
+    private void FreePointers()
+    {
+        if (KeyPtr != IntPtr.Zero)
+        {
+            Marshal.FreeHGlobal(KeyPtr);
+            KeyPtr = IntPtr.Zero;
+        }
+        if (ValuePtr != IntPtr.Zero)
+        {
+            Marshal.FreeHGlobal(ValuePtr);
+            ValuePtr = IntPtr.Zero;
+        }
+        client = null;
+    }
+
+    // Holding the client prevents it from being CG'd until all operations complete.
+    private object? client;
+
+
+    public void OnCompleted(Action continuation)
+    {
+        this.continuation = continuation;
+        CheckRaceAndCallContinuation();
+    }
+
+    public bool IsCompleted => completionState == COMPLETION_STAGE_CONTINUATION_EXECUTED;
+
+    public T? GetResult() => this.exception is null ? this.result : throw this.exception;
+}
diff --git a/csharp/lib/MessageContainer.cs b/csharp/lib/MessageContainer.cs
index 7de0947650..faa1b5a277 100644
--- a/csharp/lib/MessageContainer.cs
+++ b/csharp/lib/MessageContainer.cs
@@ -1,62 +1,62 @@
-/**
- * Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0
- */
-
-using System.Collections.Concurrent;
-
-namespace Glide;
-
-
-internal class MessageContainer<T>
-{
-    internal Message<T> GetMessage(int index) => messages[index];
-
-    internal Message<T> GetMessageForCall(string? key, string? value)
-    {
-        var message = GetFreeMessage();
-        message.StartTask(key, value, this);
-        return message;
-    }
-
-    private Message<T> GetFreeMessage()
-    {
-        if (!availableMessages.TryDequeue(out var message))
-        {
-            lock (messages)
-            {
-                var index = messages.Count;
-                message = new Message<T>(index, this);
-                messages.Add(message);
-            }
-        }
-        return message;
-    }
-
-    public void ReturnFreeMessage(Message<T> message) => availableMessages.Enqueue(message);
-
-    internal void DisposeWithError(Exception? error)
-    {
-        lock (messages)
-        {
-            foreach (var message in messages.Where(message => !message.IsCompleted))
-            {
-                try
-                {
-                    message.SetException(new TaskCanceledException("Client closed", error));
-                }
-                catch (Exception) { }
-            }
-            messages.Clear();
-        }
-        availableMessages.Clear();
-    }
-
-    /// This list allows us random-access to the message in each index,
-    /// which means that once we receive a callback with an index, we can
-    /// find the message to resolve in constant time.
-    private readonly List<Message<T>> messages = new();
-
-    /// This queue contains the messages that were created and are currently unused by any task,
-    /// so they can be reused y new tasks instead of allocating new messages.
-    private readonly ConcurrentQueue<Message<T>> availableMessages = new();
-}
\ No newline at end of file
+/**
+ * Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0
+ */
+
+using System.Collections.Concurrent;
+
+namespace Glide;
+
+
+internal class MessageContainer<T>
+{
+    internal Message<T> GetMessage(int index) => messages[index];
+
+    internal Message<T> GetMessageForCall(string? key, string? value)
+    {
+        var message = GetFreeMessage();
+        message.StartTask(key, value, this);
+        return message;
+    }
+
+    private Message<T> GetFreeMessage()
+    {
+        if (!availableMessages.TryDequeue(out var message))
+        {
+            lock (messages)
+            {
+                var index = messages.Count;
+                message = new Message<T>(index, this);
+                messages.Add(message);
+            }
+        }
+        return message;
+    }
+
+    public void ReturnFreeMessage(Message<T> message) => availableMessages.Enqueue(message);
+
+    internal void DisposeWithError(Exception? error)
+    {
+        lock (messages)
+        {
+            foreach (var message in messages.Where(message => !message.IsCompleted))
+            {
+                try
+                {
+                    message.SetException(new TaskCanceledException("Client closed", error));
+                }
+                catch (Exception) { }
+            }
+            messages.Clear();
+        }
+        availableMessages.Clear();
+    }
+
+    /// This list allows us random-access to the message in each index,
+    /// which means that once we receive a callback with an index, we can
+    /// find the message to resolve in constant time.
+    private readonly List<Message<T>> messages = new();
+
+    /// This queue contains the messages that were created and are currently unused by any task,
+    /// so they can be reused y new tasks instead of allocating new messages.
+    private readonly ConcurrentQueue<Message<T>> availableMessages = new();
+}
diff --git a/csharp/lib/Properties/AssemblyInfo.cs b/csharp/lib/Properties/AssemblyInfo.cs
index 876c013e0a..a6412c5b2e 100644
--- a/csharp/lib/Properties/AssemblyInfo.cs
+++ b/csharp/lib/Properties/AssemblyInfo.cs
@@ -1,7 +1,7 @@
-/**
- * Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0
- */
-
-using System.Runtime.CompilerServices;
-
-[assembly: InternalsVisibleTo("tests")]
\ No newline at end of file
+/**
+ * Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0
+ */
+
+using System.Runtime.CompilerServices;
+
+[assembly: InternalsVisibleTo("tests")]
diff --git a/csharp/tests/AsyncClientTests.cs b/csharp/tests/AsyncClientTests.cs
index a322e37514..e9adfdf97b 100644
--- a/csharp/tests/AsyncClientTests.cs
+++ b/csharp/tests/AsyncClientTests.cs
@@ -1,122 +1,122 @@
-/**
- * Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0
- */
-
-namespace tests;
-
-using Glide;
-
-// TODO - need to start a new redis server for each test?
-public class AsyncClientTests
-{
-    [OneTimeSetUp]
-    public void Setup()
-    {
-        Glide.Logger.SetLoggerConfig(Glide.Level.Info);
-    }
-
-    private async Task GetAndSetRandomValues(AsyncClient client)
-    {
-        var key = Guid.NewGuid().ToString();
-        var value = Guid.NewGuid().ToString();
-        await client.SetAsync(key, value);
-        var result = await client.GetAsync(key);
-        Assert.That(result, Is.EqualTo(value));
-    }
-
-    [Test]
-    public async Task GetReturnsLastSet()
-    {
-        using (var client = new AsyncClient("localhost", 6379, false))
-        {
-            await GetAndSetRandomValues(client);
-        }
-    }
-
-    [Test]
-    public async Task GetAndSetCanHandleNonASCIIUnicode()
-    {
-        using (var client = new AsyncClient("localhost", 6379, false))
-        {
-            var key = Guid.NewGuid().ToString();
-            var value = "שלום hello 汉字";
-            await client.SetAsync(key, value);
-            var result = await client.GetAsync(key);
-            Assert.That(result, Is.EqualTo(value));
-        }
-    }
-
-    [Test]
-    public async Task GetReturnsNull()
-    {
-        using (var client = new AsyncClient("localhost", 6379, false))
-        {
-            var result = await client.GetAsync(Guid.NewGuid().ToString());
-            Assert.That(result, Is.EqualTo(null));
-        }
-    }
-
-    [Test]
-    public async Task GetReturnsEmptyString()
-    {
-        using (var client = new AsyncClient("localhost", 6379, false))
-        {
-            var key = Guid.NewGuid().ToString();
-            var value = "";
-            await client.SetAsync(key, value);
-            var result = await client.GetAsync(key);
-            Assert.That(result, Is.EqualTo(value));
-        }
-    }
-
-    [Test]
-    public async Task HandleVeryLargeInput()
-    {
-        using (var client = new AsyncClient("localhost", 6379, false))
-        {
-            var key = Guid.NewGuid().ToString();
-            var value = Guid.NewGuid().ToString();
-            const int EXPECTED_SIZE = 2 << 23;
-            while (value.Length < EXPECTED_SIZE)
-            {
-                value += value;
-            }
-            await client.SetAsync(key, value);
-            var result = await client.GetAsync(key);
-            Assert.That(result, Is.EqualTo(value));
-        }
-    }
-
-    // This test is slow and hardly a unit test, but it caught timing and releasing issues in the past,
-    // so it's being kept.
-    [Test]
-    public void ConcurrentOperationsWork()
-    {
-        using (var client = new AsyncClient("localhost", 6379, false))
-        {
-            var operations = new List<Task>();
-
-            for (int i = 0; i < 1000; ++i)
-            {
-                var index = i;
-                operations.Add(Task.Run(async () =>
-                {
-                    for (int i = 0; i < 1000; ++i)
-                    {
-                        if ((i + index) % 2 == 0)
-                        {
-                            await GetAndSetRandomValues(client);
-                        }
-                        else
-                        {
-                            var result = await client.GetAsync(Guid.NewGuid().ToString());
-                            Assert.That(result, Is.EqualTo(null));
-                        }
-                    }
-                }));
-            }
-
-            Task.WaitAll(operations.ToArray());
-        }
-    }
-}
\ No newline at end of file
+/**
+ * Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0
+ */
+
+namespace tests;
+
+using Glide;
+
+// TODO - need to start a new redis server for each test?
+public class AsyncClientTests
+{
+    [OneTimeSetUp]
+    public void Setup()
+    {
+        Glide.Logger.SetLoggerConfig(Glide.Level.Info);
+    }
+
+    private async Task GetAndSetRandomValues(AsyncClient client)
+    {
+        var key = Guid.NewGuid().ToString();
+        var value = Guid.NewGuid().ToString();
+        await client.SetAsync(key, value);
+        var result = await client.GetAsync(key);
+        Assert.That(result, Is.EqualTo(value));
+    }
+
+    [Test]
+    public async Task GetReturnsLastSet()
+    {
+        using (var client = new AsyncClient("localhost", 6379, false))
+        {
+            await GetAndSetRandomValues(client);
+        }
+    }
+
+    [Test]
+    public async Task GetAndSetCanHandleNonASCIIUnicode()
+    {
+        using (var client = new AsyncClient("localhost", 6379, false))
+        {
+            var key = Guid.NewGuid().ToString();
+            var value = "שלום hello 汉字";
+            await client.SetAsync(key, value);
+            var result = await client.GetAsync(key);
+            Assert.That(result, Is.EqualTo(value));
+        }
+    }
+
+    [Test]
+    public async Task GetReturnsNull()
+    {
+        using (var client = new AsyncClient("localhost", 6379, false))
+        {
+            var result = await client.GetAsync(Guid.NewGuid().ToString());
+            Assert.That(result, Is.EqualTo(null));
+        }
+    }
+
+    [Test]
+    public async Task GetReturnsEmptyString()
+    {
+        using (var client = new AsyncClient("localhost", 6379, false))
+        {
+            var key = Guid.NewGuid().ToString();
+            var value = "";
+            await client.SetAsync(key, value);
+            var result = await client.GetAsync(key);
+            Assert.That(result, Is.EqualTo(value));
+        }
+    }
+
+    [Test]
+    public async Task HandleVeryLargeInput()
+    {
+        using (var client = new AsyncClient("localhost", 6379, false))
+        {
+            var key = Guid.NewGuid().ToString();
+            var value = Guid.NewGuid().ToString();
+            const int EXPECTED_SIZE = 2 << 23;
+            while (value.Length < EXPECTED_SIZE)
+            {
+                value += value;
+            }
+            await client.SetAsync(key, value);
+            var result = await client.GetAsync(key);
+            Assert.That(result, Is.EqualTo(value));
+        }
+    }
+
+    // This test is slow and hardly a unit test, but it caught timing and releasing issues in the past,
+    // so it's being kept.
+    [Test]
+    public void ConcurrentOperationsWork()
+    {
+        using (var client = new AsyncClient("localhost", 6379, false))
+        {
+            var operations = new List<Task>();
+
+            for (int i = 0; i < 1000; ++i)
+            {
+                var index = i;
+                operations.Add(Task.Run(async () =>
+                {
+                    for (int i = 0; i < 1000; ++i)
+                    {
+                        if ((i + index) % 2 == 0)
+                        {
+                            await GetAndSetRandomValues(client);
+                        }
+                        else
+                        {
+                            var result = await client.GetAsync(Guid.NewGuid().ToString());
+                            Assert.That(result, Is.EqualTo(null));
+                        }
+                    }
+                }));
+            }
+
+            Task.WaitAll(operations.ToArray());
+        }
+    }
+}
diff --git a/csharp/tests/Usings.cs b/csharp/tests/Usings.cs
index 08ffe13f66..6638c1219c 100644
--- a/csharp/tests/Usings.cs
+++ b/csharp/tests/Usings.cs
@@ -1,5 +1,5 @@
-/**
- * Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0
- */
-
-global using NUnit.Framework;
\ No newline at end of file
+/**
+ * Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0
+ */
+
+global using NUnit.Framework;

From 80bb57c2360969b7c1bdb099809188e6695c2a45 Mon Sep 17 00:00:00 2001
From: Guriy Samarin <guriy@amazon.com>
Date: Tue, 6 Feb 2024 23:56:09 +0000
Subject: [PATCH 10/10] fix merge

---
 benchmarks/csharp/Program.cs | 357 +----------------------------------
 1 file changed, 1 insertion(+), 356 deletions(-)

diff --git a/benchmarks/csharp/Program.cs b/benchmarks/csharp/Program.cs
index 4a175b4b09..11df0e36be 100644
--- a/benchmarks/csharp/Program.cs
+++ b/benchmarks/csharp/Program.cs
@@ -14,360 +14,6 @@
 
 using StackExchange.Redis;
 
-public static class MainClass
-{
-    private enum ChosenAction { GET_NON_EXISTING, GET_EXISTING, SET };
-
-    public class CommandLineOptions
-    {
-        [Option('r', "resultsFile", Required = false, HelpText = "Set the file to which the JSON results are written.", Default = "../results/csharp-results.json")]
-        public string resultsFile { get; set; } = string.Empty;
-
-        [Option('d', "dataSize", Required = false, HelpText = "The size of the sent data in bytes.", Default = 100)]
-        public int dataSize { get; set; }
-
-        [Option('c', "concurrentTasks", Required = false, HelpText = "The number of concurrent operations to perform.", Default = new[] { 1, 10, 100, 1000 })]
-        public IEnumerable<int> concurrentTasks { get; set; } = Enumerable.Empty<int>();
-
-        [Option('l', "clients", Required = false, HelpText = "Which clients should run", Default = "all")]
-        public string clientsToRun { get; set; } = string.Empty;
-
-        [Option('h', "host", Required = false, HelpText = "What host to target", Default = "localhost")]
-        public string host { get; set; } = string.Empty;
-
-        [Option('C', "clientCount", Required = false, HelpText = "Number of clients to run concurrently", Default = new[] { 1 })]
-        public IEnumerable<int> clientCount { get; set; } = Enumerable.Empty<int>();
-
-        [Option('t', "tls", HelpText = "Should benchmark a TLS server", Default = false)]
-        public bool tls { get; set; }
-
-
-        [Option('m', "minimal", HelpText = "Should use a minimal number of actions", Default = false)]
-        public bool minimal { get; set; }
-    }
-
-    private const int PORT = 6379;
-    private static string getAddress(string host)
-    {
-        return $"{host}:{PORT}";
-    }
-
-    private static string getAddressForStackExchangeRedis(string host, bool useTLS)
-    {
-        return $"{getAddress(host)},ssl={useTLS}";
-    }
-
-    private static string getAddressWithRedisPrefix(string host, bool useTLS)
-    {
-        var protocol = useTLS ? "rediss" : "redis";
-        return $"{protocol}://{getAddress(host)}";
-    }
-    private const double PROB_GET = 0.8;
-
-    private const double PROB_GET_EXISTING_KEY = 0.8;
-    private const int SIZE_GET_KEYSPACE = 3750000; // 3.75 million
-    private const int SIZE_SET_KEYSPACE = 3000000; // 3 million
-
-    private static readonly Random randomizer = new();
-    private static long started_tasks_counter = 0;
-    private static readonly List<Dictionary<string, object>> bench_json_results = new();
-
-    private static string generate_value(int size)
-    {
-        return new string('0', size);
-    }
-
-    private static string generate_key_set()
-    {
-        return (randomizer.Next(SIZE_SET_KEYSPACE) + 1).ToString();
-    }
-    private static string generate_key_get()
-    {
-        return (randomizer.Next(SIZE_SET_KEYSPACE, SIZE_GET_KEYSPACE) + 1).ToString();
-    }
-
-    private static ChosenAction choose_action()
-    {
-        if (randomizer.NextDouble() > PROB_GET)
-        {
-            return ChosenAction.SET;
-        }
-        if (randomizer.NextDouble() > PROB_GET_EXISTING_KEY)
-        {
-            return ChosenAction.GET_NON_EXISTING;
-        }
-        return ChosenAction.GET_EXISTING;
-    }
-
-    /// copied from https://stackoverflow.com/questions/8137391/percentile-calculation
-    private static double Percentile(double[] sequence, double excelPercentile)
-    {
-        Array.Sort(sequence);
-        int N = sequence.Length;
-        double n = (N - 1) * excelPercentile + 1;
-        if (n == 1d) return sequence[0];
-        else if (n == N) return sequence[N - 1];
-        else
-        {
-            int k = (int)n;
-            double d = n - k;
-            return sequence[k - 1] + d * (sequence[k] - sequence[k - 1]);
-        }
-    }
-
-    private static double calculate_latency(IEnumerable<double> latency_list, double percentile_point)
-    {
-        return Math.Round(Percentile(latency_list.ToArray(), percentile_point), 2);
-    }
-
-    private static void print_results(string resultsFile)
-    {
-        using FileStream createStream = File.Create(resultsFile);
-        JsonSerializer.Serialize(createStream, bench_json_results);
-    }
-
-    private static async Task redis_benchmark(
-        ClientWrapper[] clients,
-        long total_commands,
-        string data,
-        Dictionary<ChosenAction, ConcurrentBag<double>> action_latencies)
-    {
-        var stopwatch = new Stopwatch();
-        do
-        {
-            Interlocked.Increment(ref started_tasks_counter);
-            var index = (int)(started_tasks_counter % clients.Length);
-            var client = clients[index];
-            var action = choose_action();
-            stopwatch.Start();
-            switch (action)
-            {
-                case ChosenAction.GET_EXISTING:
-                    await client.get(generate_key_set());
-                    break;
-                case ChosenAction.GET_NON_EXISTING:
-                    await client.get(generate_key_get());
-                    break;
-                case ChosenAction.SET:
-                    await client.set(generate_key_set(), data);
-                    break;
-            }
-            stopwatch.Stop();
-            var latency_list = action_latencies[action];
-            latency_list.Add(((double)stopwatch.ElapsedMilliseconds) / 1000);
-        } while (started_tasks_counter < total_commands);
-    }
-
-    private static async Task<long> create_bench_tasks(
-        ClientWrapper[] clients,
-        int total_commands,
-        string data,
-        int num_of_concurrent_tasks,
-        Dictionary<ChosenAction, ConcurrentBag<double>> action_latencies
-    )
-    {
-        started_tasks_counter = 0;
-        var stopwatch = Stopwatch.StartNew();
-        var running_tasks = new List<Task>();
-        for (var i = 0; i < num_of_concurrent_tasks; i++)
-        {
-            running_tasks.Add(
-                redis_benchmark(clients, total_commands, data, action_latencies)
-            );
-        }
-        await Task.WhenAll(running_tasks);
-        stopwatch.Stop();
-        return stopwatch.ElapsedMilliseconds;
-    }
-
-    private static Dictionary<string, object> latency_results(
-        string prefix,
-        ConcurrentBag<double> latencies
-    )
-    {
-        return new Dictionary<string, object>
-        {
-            {prefix + "_p50_latency", calculate_latency(latencies, 0.5)},
-            {prefix + "_p90_latency", calculate_latency(latencies, 0.9)},
-            {prefix + "_p99_latency", calculate_latency(latencies, 0.99)},
-            {prefix + "_average_latency", Math.Round(latencies.Average(), 3)},
-            {prefix + "_std_dev", latencies.StandardDeviation()},
-        };
-    }
-
-    private static async Task run_clients(
-        ClientWrapper[] clients,
-        string client_name,
-        int total_commands,
-        int data_size,
-        int num_of_concurrent_tasks
-    )
-    {
-        Console.WriteLine($"Starting {client_name} data size: {data_size} concurrency: {num_of_concurrent_tasks} client count: {clients.Length} {DateTime.UtcNow.ToString("HH:mm:ss")}");
-        var action_latencies = new Dictionary<ChosenAction, ConcurrentBag<double>>() {
-            {ChosenAction.GET_NON_EXISTING, new()},
-            {ChosenAction.GET_EXISTING, new()},
-            {ChosenAction.SET, new()},
-        };
-        var data = generate_value(data_size);
-        var elapsed_milliseconds = await create_bench_tasks(
-            clients,
-            total_commands,
-            data,
-            num_of_concurrent_tasks,
-            action_latencies
-        );
-        var tps = Math.Round((double)started_tasks_counter / ((double)elapsed_milliseconds / 1000));
-
-        var get_non_existing_latencies = action_latencies[ChosenAction.GET_NON_EXISTING];
-        var get_non_existing_latency_results = latency_results("get_non_existing", get_non_existing_latencies);
-
-        var get_existing_latencies = action_latencies[ChosenAction.GET_EXISTING];
-        var get_existing_latency_results = latency_results("get_existing", get_existing_latencies);
-
-        var set_latencies = action_latencies[ChosenAction.SET];
-        var set_latency_results = latency_results("set", set_latencies);
-
-        var result = new Dictionary<string, object>
-        {
-            {"client", client_name},
-            {"num_of_tasks", num_of_concurrent_tasks},
-            {"data_size", data_size},
-            {"tps", tps},
-            {"client_count", clients.Length},
-            {"is_cluster", "false"}
-        };
-        result = result
-            .Concat(get_existing_latency_results)
-            .Concat(get_non_existing_latency_results)
-            .Concat(set_latency_results)
-            .ToDictionary(pair => pair.Key, pair => pair.Value);
-        bench_json_results.Add(result);
-    }
-
-    private class ClientWrapper : IDisposable
-    {
-        internal ClientWrapper(Func<string, Task<string?>> get, Func<string, string, Task> set, Action disposalFunction)
-        {
-            this.get = get;
-            this.set = set;
-            this.disposalFunction = disposalFunction;
-        }
-
-        public void Dispose()
-        {
-            this.disposalFunction();
-        }
-
-        internal Func<string, Task<string?>> get;
-        internal Func<string, string, Task> set;
-
-        private readonly Action disposalFunction;
-    }
-
-    private async static Task<ClientWrapper[]> createClients(int clientCount,
-        Func<Task<(Func<string, Task<string?>>,
-                   Func<string, string, Task>,
-                   Action)>> clientCreation)
-    {
-        var tasks = Enumerable.Range(0, clientCount).Select(async (_) =>
-        {
-            var tuple = await clientCreation();
-            return new ClientWrapper(tuple.Item1, tuple.Item2, tuple.Item3);
-        });
-        return await Task.WhenAll(tasks);
-    }
-
-    private static async Task run_with_parameters(int total_commands,
-        int data_size,
-        int num_of_concurrent_tasks,
-        string clientsToRun,
-        string host,
-        int clientCount,
-        bool useTLS)
-    {
-        if (clientsToRun == "all" || clientsToRun == "glide")
-        {
-            var clients = await createClients(clientCount, () =>
-            {
-                var glide_client = new AsyncClient(host, PORT, useTLS);
-                return Task.FromResult<(Func<string, Task<string?>>, Func<string, string, Task>, Action)>(
-                    (async (key) => await glide_client.GetAsync(key),
-                     async (key, value) => await glide_client.SetAsync(key, value),
-                     () => glide_client.Dispose()));
-            });
-
-            await run_clients(
-                clients,
-                "glide",
-                total_commands,
-                data_size,
-                num_of_concurrent_tasks
-            );
-        }
-
-        if (clientsToRun == "all")
-        {
-            var clients = await createClients(clientCount, () =>
-                {
-                    var connection = ConnectionMultiplexer.Connect(getAddressForStackExchangeRedis(host, useTLS));
-                    var db = connection.GetDatabase();
-                    return Task.FromResult<(Func<string, Task<string?>>, Func<string, string, Task>, Action)>(
-                        (async (key) => await db.StringGetAsync(key),
-                         async (key, value) => await db.StringSetAsync(key, value),
-                         () => connection.Dispose()));
-                });
-            await run_clients(
-                clients,
-                "StackExchange.Redis",
-                total_commands,
-                data_size,
-                num_of_concurrent_tasks
-            );
-
-            foreach (var client in clients)
-            {
-                client.Dispose();
-            }
-        }
-    }
-
-    private static int number_of_iterations(int num_of_concurrent_tasks)
-    {
-        return Math.Min(Math.Max(100000, num_of_concurrent_tasks * 10000), 10000000);
-    }
-
-    public static async Task Main(string[] args)
-    {
-        CommandLineOptions options = new();
-        Parser.Default
-            .ParseArguments<CommandLineOptions>(args).WithParsed<CommandLineOptions>(parsed => { options = parsed; });
-
-        Logger.SetLoggerConfig(Level.Info, Path.GetFileNameWithoutExtension(options.resultsFile));
-        var product = options.concurrentTasks.SelectMany(concurrentTasks =>
-            options.clientCount.Select(clientCount => (concurrentTasks: concurrentTasks, dataSize: options.dataSize, clientCount: clientCount))).Where(tuple => tuple.concurrentTasks >= tuple.clientCount);
-        foreach (var (concurrentTasks, dataSize, clientCount) in product)
-        {
-            var iterations = options.minimal ? 1000 : number_of_iterations(concurrentTasks);
-            await run_with_parameters(iterations, dataSize, concurrentTasks, options.clientsToRun, options.host, clientCount, options.tls);
-        }
-
-        print_results(options.resultsFile);
-    }
-}
-=======
-/**
- * Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0
- */
-
-using System.Collections.Concurrent;
-using System.Diagnostics;
-using System.Text.Json;
-using Glide;
-using CommandLine;
-using LinqStatistics;
-using StackExchange.Redis;
-
 public static class MainClass
 {
     private enum ChosenAction { GET_NON_EXISTING, GET_EXISTING, SET };
@@ -618,7 +264,7 @@ public void Dispose()
         internal Func<string, Task<string?>> get;
         internal Func<string, string, Task> set;
 
-        private Action disposalFunction;
+        private readonly Action disposalFunction;
     }
 
     private async static Task<ClientWrapper[]> createClients(int clientCount,
@@ -711,4 +357,3 @@ public static async Task Main(string[] args)
         print_results(options.resultsFile);
     }
 }
-