From 1962358251337f8fc1936a8b21fdcb69ce0e078f Mon Sep 17 00:00:00 2001 From: Kevin Montrose Date: Wed, 6 May 2026 20:20:24 -0400 Subject: [PATCH 01/16] consolidate various while(TryWriteXXX) loops into the existing helpers --- libs/server/Custom/CustomRespCommands.cs | 18 +-- libs/server/Lua/LuaCommands.cs | 12 +- libs/server/Metrics/Info/InfoCommand.cs | 12 +- .../Metrics/Latency/RespLatencyCommands.cs | 12 +- .../Metrics/Slowlog/RespSlowlogCommands.cs | 52 ++---- libs/server/Resp/ACLCommands.cs | 48 ++---- libs/server/Resp/AdminCommands.cs | 84 ++++------ libs/server/Resp/ArrayCommands.cs | 54 +++---- libs/server/Resp/AsyncProcessor.cs | 6 +- libs/server/Resp/BasicCommands.cs | 108 +++++-------- libs/server/Resp/BasicEtagCommands.cs | 3 +- libs/server/Resp/Bitmap/BitmapCommands.cs | 21 +-- libs/server/Resp/ClientCommands.cs | 27 ++-- .../Resp/HyperLogLog/HyperLogLogCommands.cs | 12 +- libs/server/Resp/KeyAdminCommands.cs | 30 ++-- libs/server/Resp/Objects/HashCommands.cs | 60 +++---- libs/server/Resp/Objects/ListCommands.cs | 72 +++------ libs/server/Resp/Objects/SetCommands.cs | 63 +++----- .../Resp/Objects/SharedObjectCommands.cs | 9 +- libs/server/Resp/Objects/SortedSetCommands.cs | 148 ++++++------------ .../Resp/Objects/SortedSetGeoCommands.cs | 6 +- libs/server/Resp/PubSubCommands.cs | 117 +++++--------- libs/server/Resp/PurgeBPCommand.cs | 3 +- libs/server/Resp/RespServerSession.cs | 3 +- .../Resp/Vector/RespServerSessionVectors.cs | 45 ++---- libs/server/ServerConfig.cs | 12 +- libs/server/Transaction/TxnRespCommands.cs | 18 +-- 27 files changed, 351 insertions(+), 704 deletions(-) diff --git a/libs/server/Custom/CustomRespCommands.cs b/libs/server/Custom/CustomRespCommands.cs index 3b97d3c5e5a..30216e23ee0 100644 --- a/libs/server/Custom/CustomRespCommands.cs +++ b/libs/server/Custom/CustomRespCommands.cs @@ -35,8 +35,7 @@ private bool TryTransactionProc(byte id, CustomTransactionProcedure proc, int st if (output.MemoryOwner != null) SendAndReset(output.MemoryOwner, output.Length); else - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); } else { @@ -72,8 +71,7 @@ private void TryCustomProcedure(CustomProcedure proc, int startIdx = 0) if (output.MemoryOwner != null) SendAndReset(output.MemoryOwner, output.Length); else - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); } else { @@ -106,8 +104,7 @@ private bool TryCustomRawStringCommand(RespCommand cmd, long expirat if (output.Memory != null) SendAndReset(output.Memory, output.Length); else - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); } else { @@ -119,8 +116,7 @@ private bool TryCustomRawStringCommand(RespCommand cmd, long expirat if (output.Memory != null) SendAndReset(output.Memory, output.Length); else - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); } else { @@ -164,8 +160,7 @@ private bool TryCustomObjectCommand(GarnetObjectType objType, byte s if (output.SpanByteAndMemory.Memory != null) SendAndReset(output.SpanByteAndMemory.Memory, output.SpanByteAndMemory.Length); else - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); break; } } @@ -180,8 +175,7 @@ private bool TryCustomObjectCommand(GarnetObjectType objType, byte s if (output.SpanByteAndMemory.Memory != null) SendAndReset(output.SpanByteAndMemory.Memory, output.SpanByteAndMemory.Length); else - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); break; case GarnetStatus.NOTFOUND: Debug.Assert(output.SpanByteAndMemory.Memory == null); diff --git a/libs/server/Lua/LuaCommands.cs b/libs/server/Lua/LuaCommands.cs index 07d6dcd41ab..ad03c94ea1a 100644 --- a/libs/server/Lua/LuaCommands.cs +++ b/libs/server/Lua/LuaCommands.cs @@ -199,8 +199,7 @@ private bool NetworkScriptExists() // Returns an array where each element is a 0 if the script does not exist, and a 1 if it does - while (!RespWriteUtils.TryWriteArrayLength(parseState.Count, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(parseState.Count); for (var shaIx = 0; shaIx < parseState.Count; shaIx++) { @@ -217,8 +216,7 @@ private bool NetworkScriptExists() exists = storeWrapper.storeScriptCache.ContainsKey(sha1Arg) ? 1 : 0; } - while (!RespWriteUtils.TryWriteInt32(exists, ref dcurr, dend)) - SendAndReset(); + WriteInt32(exists); } return true; @@ -265,8 +263,7 @@ private bool NetworkScriptFlush() } } - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); return true; } @@ -324,8 +321,7 @@ private bool NetworkScriptLoad() } } - while (!RespWriteUtils.TryWriteBulkString(digest, ref dcurr, dend)) - SendAndReset(); + WriteBulkString(digest); } return true; diff --git a/libs/server/Metrics/Info/InfoCommand.cs b/libs/server/Metrics/Info/InfoCommand.cs index 0722fdd3b9e..09abee868e3 100644 --- a/libs/server/Metrics/Info/InfoCommand.cs +++ b/libs/server/Metrics/Info/InfoCommand.cs @@ -61,8 +61,7 @@ private bool NetworkINFO() { if (storeWrapper.monitor != null) storeWrapper.monitor.resetEventFlags[InfoMetricsType.STATS] = true; - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); } else { @@ -75,8 +74,7 @@ private bool NetworkINFO() } else { - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_EMPTY, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_EMPTY); } } return true; @@ -86,12 +84,10 @@ private bool NetworkINFO() private void GetHelpMessage() { List sectionsHelp = InfoHelp.GetInfoTypeHelpMessage(); - while (!RespWriteUtils.TryWriteArrayLength(sectionsHelp.Count, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(sectionsHelp.Count); foreach (var sectionInfo in sectionsHelp) { - while (!RespWriteUtils.TryWriteAsciiBulkString(sectionInfo, ref dcurr, dend)) - SendAndReset(); + WriteAsciiBulkString(sectionInfo); } } } diff --git a/libs/server/Metrics/Latency/RespLatencyCommands.cs b/libs/server/Metrics/Latency/RespLatencyCommands.cs index d786fd8e083..7d9f1184f51 100644 --- a/libs/server/Metrics/Latency/RespLatencyCommands.cs +++ b/libs/server/Metrics/Latency/RespLatencyCommands.cs @@ -21,13 +21,11 @@ private bool NetworkLatencyHelp() } List latencyCommands = RespLatencyHelp.GetLatencyCommands(); - while (!RespWriteUtils.TryWriteArrayLength(latencyCommands.Count, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(latencyCommands.Count); foreach (string command in latencyCommands) { - while (!RespWriteUtils.TryWriteSimpleString(command, ref dcurr, dend)) - SendAndReset(); + WriteSimpleString(command); } return true; @@ -72,8 +70,7 @@ private bool NetworkLatencyHistogram() { var garnetLatencyMetrics = storeWrapper.monitor?.GlobalMetrics.globalLatencyMetrics; string response = garnetLatencyMetrics != null ? garnetLatencyMetrics.GetRespHistograms(events) : "*0\r\n"; - while (!RespWriteUtils.TryWriteAsciiDirect(response, ref dcurr, dend)) - SendAndReset(); + WriteAsciiDirect(response); } return true; @@ -122,8 +119,7 @@ private bool NetworkLatencyReset() storeWrapper.monitor.resetLatencyMetrics[e] = true; } - while (!RespWriteUtils.TryWriteInt32(events.Count, ref dcurr, dend)) - SendAndReset(); + WriteInt32(events.Count); } return true; diff --git a/libs/server/Metrics/Slowlog/RespSlowlogCommands.cs b/libs/server/Metrics/Slowlog/RespSlowlogCommands.cs index 8dc86db2ee7..f916d42b1f2 100644 --- a/libs/server/Metrics/Slowlog/RespSlowlogCommands.cs +++ b/libs/server/Metrics/Slowlog/RespSlowlogCommands.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.Runtime.CompilerServices; -using Garnet.common; using HdrHistogram; namespace Garnet.server @@ -24,13 +23,11 @@ private bool NetworkSlowLogHelp() } List slowLogCommands = RespSlowLogHelp.GetSlowLogCommands(); - while (!RespWriteUtils.TryWriteArrayLength(slowLogCommands.Count, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(slowLogCommands.Count); foreach (string command in slowLogCommands) { - while (!RespWriteUtils.TryWriteSimpleString(command, ref dcurr, dend)) - SendAndReset(); + WriteSimpleString(command); } return true; @@ -58,31 +55,23 @@ private bool NetworkSlowLogGet() if (storeWrapper.slowLogContainer == null) { - while (!RespWriteUtils.TryWriteArrayLength(0, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(0); return true; } var entries = storeWrapper.slowLogContainer.GetEntries(count); - while (!RespWriteUtils.TryWriteArrayLength(entries.Count, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(entries.Count); SessionParseState sps = default; foreach (var entry in entries) { - while (!RespWriteUtils.TryWriteArrayLength(6, ref dcurr, dend)) - SendAndReset(); - while (!RespWriteUtils.TryWriteInt32(entry.Id, ref dcurr, dend)) - SendAndReset(); - while (!RespWriteUtils.TryWriteInt32(entry.Timestamp, ref dcurr, dend)) - SendAndReset(); - while (!RespWriteUtils.TryWriteInt32(entry.Duration, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(6); + WriteInt32(entry.Id); + WriteInt32(entry.Timestamp); + WriteInt32(entry.Duration); if (entry.Arguments == null) { - while (!RespWriteUtils.TryWriteArrayLength(1, ref dcurr, dend)) - SendAndReset(); - while (!RespWriteUtils.TryWriteAsciiBulkString(entry.Command.ToString(), ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(1); + WriteAsciiBulkString(entry.Command.ToString()); } else { @@ -91,20 +80,15 @@ private bool NetworkSlowLogGet() { sps.DeserializeFrom(ptr); } - while (!RespWriteUtils.TryWriteArrayLength(sps.Count + 1, ref dcurr, dend)) - SendAndReset(); - while (!RespWriteUtils.TryWriteAsciiBulkString(entry.Command.ToString(), ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(sps.Count + 1); + WriteAsciiBulkString(entry.Command.ToString()); for (int i = 0; i < sps.Count; i++) { - while (!RespWriteUtils.TryWriteAsciiBulkString(sps.GetString(i), ref dcurr, dend)) - SendAndReset(); + WriteAsciiBulkString(sps.GetString(i)); } } - while (!RespWriteUtils.TryWriteAsciiBulkString(entry.ClientIpPort, ref dcurr, dend)) - SendAndReset(); - while (!RespWriteUtils.TryWriteAsciiBulkString(entry.ClientName, ref dcurr, dend)) - SendAndReset(); + WriteAsciiBulkString(entry.ClientIpPort); + WriteAsciiBulkString(entry.ClientName); } return true; } @@ -119,8 +103,7 @@ private bool NetworkSlowLogLen() { return AbortWithWrongNumberOfArguments(nameof(RespCommand.SLOWLOG_LEN)); } - while (!RespWriteUtils.TryWriteInt32(storeWrapper.slowLogContainer?.Count ?? 0, ref dcurr, dend)) - SendAndReset(); + WriteInt32(storeWrapper.slowLogContainer?.Count ?? 0); return true; } @@ -135,8 +118,7 @@ private bool NetworkSlowLogReset() return AbortWithWrongNumberOfArguments(nameof(RespCommand.SLOWLOG_RESET)); } storeWrapper.slowLogContainer?.Clear(); - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); return true; } diff --git a/libs/server/Resp/ACLCommands.cs b/libs/server/Resp/ACLCommands.cs index 216363d5b98..e3a6dc52146 100644 --- a/libs/server/Resp/ACLCommands.cs +++ b/libs/server/Resp/ACLCommands.cs @@ -64,13 +64,11 @@ private bool NetworkAclList() var aclAuthenticator = (GarnetACLAuthenticator)_authenticator; var userHandles = aclAuthenticator.GetAccessControlList().GetUserHandles(); - while (!RespWriteUtils.TryWriteArrayLength(userHandles.Count, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(userHandles.Count); foreach (var userHandle in userHandles) { - while (!RespWriteUtils.TryWriteAsciiBulkString(userHandle.Value.User.DescribeUser(), ref dcurr, dend)) - SendAndReset(); + WriteAsciiBulkString(userHandle.Value.User.DescribeUser()); } return true; @@ -93,13 +91,11 @@ private bool NetworkAclUsers() var aclAuthenticator = (GarnetACLAuthenticator)_authenticator; var users = aclAuthenticator.GetAccessControlList().GetUserHandles(); - while (!RespWriteUtils.TryWriteArrayLength(users.Count, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(users.Count); foreach (var user in users) { - while (!RespWriteUtils.TryWriteAsciiBulkString(user.Key, ref dcurr, dend)) - SendAndReset(); + WriteAsciiBulkString(user.Key); } return true; @@ -125,8 +121,7 @@ private bool NetworkAclCat() foreach (var category in categories) { - while (!RespWriteUtils.TryWriteAsciiBulkString(category, ref dcurr, dend)) - SendAndReset(); + WriteAsciiBulkString(category); } return true; @@ -204,8 +199,7 @@ private bool NetworkAclSetUser() return true; } - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); return true; } @@ -253,8 +247,7 @@ private bool NetworkAclDelUser() } // Return the number of successful deletes - while (!RespWriteUtils.TryWriteInt32(successfulDeletes, ref dcurr, dend)) - SendAndReset(); + WriteInt32(successfulDeletes); return true; } @@ -313,8 +306,7 @@ private bool NetworkAclLoad() logger?.LogInformation("Reading updated ACL configuration file '{filepath}'", aclAuthenticationSettings.AclConfigurationFile); storeWrapper.accessControlList.Load(aclAuthenticationSettings.DefaultPassword, aclAuthenticationSettings.AclConfigurationFile); - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); } catch (ACLException exception) { @@ -361,8 +353,7 @@ private bool NetworkAclSave() return true; } - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); return true; } @@ -446,32 +437,25 @@ private bool NetworkAclGetUser() { WriteMapLength(3); - while (!RespWriteUtils.TryWriteAsciiBulkString("flags", ref dcurr, dend)) - SendAndReset(); + WriteAsciiBulkString("flags"); WriteSetLength(1); - while (!RespWriteUtils.TryWriteAsciiBulkString(user.IsEnabled ? "on" : "off", ref dcurr, dend)) - SendAndReset(); + WriteAsciiBulkString(user.IsEnabled ? "on" : "off"); var passwords = user.Passwords; - while (!RespWriteUtils.TryWriteAsciiBulkString("passwords", ref dcurr, dend)) - SendAndReset(); + WriteAsciiBulkString("passwords"); - while (!RespWriteUtils.TryWriteArrayLength(passwords.Count, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(passwords.Count); foreach (var password in passwords) { - while (!RespWriteUtils.TryWriteAsciiBulkString($"#{password.ToString()}", ref dcurr, dend)) - SendAndReset(); + WriteAsciiBulkString($"#{password.ToString()}"); } - while (!RespWriteUtils.TryWriteAsciiBulkString("commands", ref dcurr, dend)) - SendAndReset(); + WriteAsciiBulkString("commands"); - while (!RespWriteUtils.TryWriteAsciiBulkString(user.GetEnabledCommandsDescription(), ref dcurr, dend)) - SendAndReset(); + WriteAsciiBulkString(user.GetEnabledCommandsDescription()); } return true; diff --git a/libs/server/Resp/AdminCommands.cs b/libs/server/Resp/AdminCommands.cs index f2e9b0f47a7..556934bde0d 100644 --- a/libs/server/Resp/AdminCommands.cs +++ b/libs/server/Resp/AdminCommands.cs @@ -510,8 +510,7 @@ private bool NetworkRegisterCs(CustomCommandManager customCommandManager) if (errorMsg.IsEmpty && TryRegisterCustomCommands(binaryPaths, cmdInfoPath, cmdDocsPath, classNameToRegisterArgs, customCommandManager, out errorMsg)) { - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); } else { @@ -571,8 +570,7 @@ private bool NetworkModuleLoad(CustomCommandManager customCommandManager) if (ModuleRegistrar.Instance.LoadModule(customCommandManager, assembliesList[0], moduleArgs, logger, out errorMsg)) { - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); } } @@ -608,8 +606,7 @@ private bool NetworkCOMMITAOF() // Have to block as we're on network thread _ = AsyncUtils.BlockingWait(CommitAofAsync(dbId)); - while (!RespWriteUtils.TryWriteSimpleString("AOF file committed"u8, ref dcurr, dend)) - SendAndReset(); + WriteSimpleString("AOF file committed"u8); return true; } @@ -631,8 +628,7 @@ private bool NetworkFORCEGC() } GC.Collect(generation, GCCollectionMode.Forced, true); - while (!RespWriteUtils.TryWriteSimpleString("GC completed"u8, ref dcurr, dend)) - SendAndReset(); + WriteSimpleString("GC completed"u8); return true; } @@ -655,8 +651,7 @@ private bool NetworkHCOLLECT(ref TGarnetApi storageApi) switch (status) { case GarnetStatus.OK: - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); break; default: while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_HCOLLECT_ALREADY_IN_PROGRESS, ref dcurr, dend)) @@ -685,8 +680,7 @@ private bool NetworkZCOLLECT(ref TGarnetApi storageApi) switch (status) { case GarnetStatus.OK: - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); break; default: while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_ZCOLLECT_ALREADY_IN_PROGRESS, ref dcurr, dend)) @@ -789,17 +783,13 @@ private bool NetworkROLE() if (!storeWrapper.serverOptions.EnableCluster) { - while (!RespWriteUtils.TryWriteArrayLength(3, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(3); - while (!RespWriteUtils.TryWriteAsciiBulkString("master", ref dcurr, dend)) - SendAndReset(); + WriteAsciiBulkString("master"); - while (!RespWriteUtils.TryWriteInt32(0, ref dcurr, dend)) - SendAndReset(); + WriteInt32(0); - while (!RespWriteUtils.TryWriteEmptyArray(ref dcurr, dend)) - SendAndReset(); + WriteEmptyArray(); } else { @@ -807,51 +797,37 @@ private bool NetworkROLE() { var (replication_offset, replicaInfo) = storeWrapper.clusterProvider.GetPrimaryInfo(); - while (!RespWriteUtils.TryWriteArrayLength(3, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(3); - while (!RespWriteUtils.TryWriteAsciiBulkString("master", ref dcurr, dend)) - SendAndReset(); + WriteAsciiBulkString("master"); - while (!RespWriteUtils.TryWriteInt64(replication_offset, ref dcurr, dend)) - SendAndReset(); + WriteInt64(replication_offset); - while (!RespWriteUtils.TryWriteArrayLength(replicaInfo.Count, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(replicaInfo.Count); foreach (var replice in replicaInfo) { - while (!RespWriteUtils.TryWriteArrayLength(3, ref dcurr, dend)) - SendAndReset(); - while (!RespWriteUtils.TryWriteAsciiBulkString(replice.address, ref dcurr, dend)) - SendAndReset(); - while (!RespWriteUtils.TryWriteInt32(replice.port, ref dcurr, dend)) - SendAndReset(); - while (!RespWriteUtils.TryWriteInt64(replice.replication_offset, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(3); + WriteAsciiBulkString(replice.address); + WriteInt32(replice.port); + WriteInt64(replice.replication_offset); } } else { var role = storeWrapper.clusterProvider.GetReplicaInfo(); - while (!RespWriteUtils.TryWriteArrayLength(5, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(5); - while (!RespWriteUtils.TryWriteAsciiBulkString("slave", ref dcurr, dend)) - SendAndReset(); + WriteAsciiBulkString("slave"); - while (!RespWriteUtils.TryWriteAsciiBulkString(role.address, ref dcurr, dend)) - SendAndReset(); + WriteAsciiBulkString(role.address); - while (!RespWriteUtils.TryWriteInt32(role.port, ref dcurr, dend)) - SendAndReset(); + WriteInt32(role.port); - while (!RespWriteUtils.TryWriteAsciiBulkString(role.replication_state, ref dcurr, dend)) - SendAndReset(); + WriteAsciiBulkString(role.replication_state); - while (!RespWriteUtils.TryWriteInt64(role.replication_offset, ref dcurr, dend)) - SendAndReset(); + WriteInt64(role.replication_offset); } } @@ -891,8 +867,7 @@ private bool NetworkSAVE() } else { - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); } return true; @@ -929,8 +904,7 @@ private bool NetworkEXPDELSCAN() // Resp Response Format => *2\r\n$NUM1\r\n$NUM2\r\n int requiredSpace = 5 + NumUtils.CountDigits(recordsExpired) + 3 + NumUtils.CountDigits(recordsScanned) + 2; - while (!RespWriteUtils.TryWriteArrayLength(2, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(2); while (!RespWriteUtils.TryWriteArrayItem(recordsExpired, ref dcurr, dend)) SendAndReset(); @@ -966,8 +940,7 @@ private bool NetworkLASTSAVE() Debug.Assert(dbFound); var seconds = db.LastSaveTime.ToUnixTimeSeconds(); - while (!RespWriteUtils.TryWriteInt64(seconds, ref dcurr, dend)) - SendAndReset(); + WriteInt64(seconds); return true; } @@ -1008,8 +981,7 @@ private bool NetworkBGSAVE() if (success) { - while (!RespWriteUtils.TryWriteSimpleString("Background saving started"u8, ref dcurr, dend)) - SendAndReset(); + WriteSimpleString("Background saving started"u8); } else { diff --git a/libs/server/Resp/ArrayCommands.cs b/libs/server/Resp/ArrayCommands.cs index 7ba2d976413..63216591903 100644 --- a/libs/server/Resp/ArrayCommands.cs +++ b/libs/server/Resp/ArrayCommands.cs @@ -21,8 +21,7 @@ private bool NetworkMGET(ref TGarnetApi storageApi) where TGarnetApi : IGarnetApi { // Write array header - while (!RespWriteUtils.TryWriteArrayLength(parseState.Count, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(parseState.Count); if (storeWrapper.serverOptions.EnableScatterGatherGet) { @@ -57,8 +56,7 @@ private bool NetworkMSET(ref TGarnetApi storageApi) var val = parseState.GetArgSliceByRef(c + 1); _ = storageApi.SET(key, val); } - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); return true; } @@ -77,8 +75,7 @@ private bool NetworkMSETNX(ref TGarnetApi storageApi) var status = storageApi.MSET_Conditional(ref input); // For a "set if not exists", NOTFOUND means that the operation succeeded - while (!RespWriteUtils.TryWriteInt32(status == GarnetStatus.NOTFOUND ? 1 : 0, ref dcurr, dend)) - SendAndReset(); + WriteInt32(status == GarnetStatus.NOTFOUND ? 1 : 0); return true; } @@ -96,8 +93,7 @@ private bool NetworkDEL(ref TGarnetApi storageApi) keysDeleted++; } - while (!RespWriteUtils.TryWriteInt32(keysDeleted, ref dcurr, dend)) - SendAndReset(); + WriteInt32(keysDeleted); return true; } @@ -131,8 +127,7 @@ private bool NetworkSELECT() if (index == this.activeDbId || this.TrySwitchActiveDatabaseSession(index)) { - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); } else { @@ -183,8 +178,7 @@ private bool NetworkSWAPDB() if (storeWrapper.TrySwapDatabases(index1, index2)) { - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); } else { @@ -203,8 +197,7 @@ private bool NetworkDBSIZE(ref TGarnetApi storageApi) return AbortWithWrongNumberOfArguments(nameof(RespCommand.DBSIZE)); } - while (!RespWriteUtils.TryWriteInt32(storageApi.GetDbSize(), ref dcurr, dend)) - SendAndReset(); + WriteInt32(storageApi.GetDbSize()); return true; } @@ -225,20 +218,17 @@ private bool NetworkKEYS(ref TGarnetApi storageApi) if (keys.Count > 0) { // Write size of the array - while (!RespWriteUtils.TryWriteArrayLength(keys.Count, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(keys.Count); // Write the keys matching the pattern foreach (var item in keys) { - while (!RespWriteUtils.TryWriteBulkString(item, ref dcurr, dend)) - SendAndReset(); + WriteBulkString(item); } } else { - while (!RespWriteUtils.TryWriteEmptyArray(ref dcurr, dend)) - SendAndReset(); + WriteEmptyArray(); } return true; @@ -297,22 +287,18 @@ private bool NetworkSCAN(ref TGarnetApi storageApi) // Prepare values for output if (keys.Count == 0) { - while (!RespWriteUtils.TryWriteArrayLength(2, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(2); // Number of keys "0" - while (!RespWriteUtils.TryWriteInt32AsBulkString(0, ref dcurr, dend)) - SendAndReset(); + WriteInt32AsBulkString(0); // Empty array - while (!RespWriteUtils.TryWriteEmptyArray(ref dcurr, dend)) - SendAndReset(); + WriteEmptyArray(); } else { // The response is two elements: the value of the cursor and the array of keys found matching the pattern - while (!RespWriteUtils.TryWriteArrayLength(2, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(2); if (keys.Count > 0) WriteOutputForScan(cursor, keys); @@ -334,13 +320,11 @@ private bool NetworkTYPE(ref TGarnetApi storageApi) if (status == GarnetStatus.OK) { - while (!RespWriteUtils.TryWriteSimpleString(typeName, ref dcurr, dend)) - SendAndReset(); + WriteSimpleString(typeName); } else { - while (!RespWriteUtils.TryWriteSimpleString("none"u8, ref dcurr, dend)) - SendAndReset(); + WriteSimpleString("none"u8); } return true; @@ -355,12 +339,10 @@ private void WriteOutputForScan(long cursorValue, List keys) { // The output is an array of two elements: cursor value and an array of keys // Note the cursor value should be formatted as bulk string ('$') - while (!RespWriteUtils.TryWriteInt64AsBulkString(cursorValue, ref dcurr, dend, out _)) - SendAndReset(); + WriteInt64AsBulkString(cursorValue); // Write size of the array - while (!RespWriteUtils.TryWriteArrayLength(keys.Count, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(keys.Count); // Write the keys matching the pattern for (int i = 0; i < keys.Count; i++) diff --git a/libs/server/Resp/AsyncProcessor.cs b/libs/server/Resp/AsyncProcessor.cs index a88fe0a534d..62e9c6512d1 100644 --- a/libs/server/Resp/AsyncProcessor.cs +++ b/libs/server/Resp/AsyncProcessor.cs @@ -106,10 +106,8 @@ async Task AsyncGetProcessorAsync(TGarnetApi storageApi) // We write async push response as an array: [ "async", "", "" ] while (!RespWriteUtils.TryWritePushLength(3, ref dcurr, dend)) SendAndReset(); - while (!RespWriteUtils.TryWriteBulkString(CmdStrings.async, ref dcurr, dend)) - SendAndReset(); - while (!RespWriteUtils.TryWriteInt32AsBulkString((int)completedOutputs.Current.Context, ref dcurr, dend)) - SendAndReset(); + WriteBulkString(CmdStrings.async); + WriteInt32AsBulkString((int)completedOutputs.Current.Context); if (completedOutputs.Current.Status.Found) { Debug.Assert(!o.IsSpanByte); diff --git a/libs/server/Resp/BasicCommands.cs b/libs/server/Resp/BasicCommands.cs index 9d7778e8dcf..cdabfde436d 100644 --- a/libs/server/Resp/BasicCommands.cs +++ b/libs/server/Resp/BasicCommands.cs @@ -288,8 +288,7 @@ private bool NetworkSET(ref TGarnetApi storageApi) storageApi.SET(key, value); - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); return true; } @@ -333,8 +332,7 @@ private bool NetworkSetRange(ref TGarnetApi storageApi) storageApi.SETRANGE(key, ref input, ref output); - while (!RespWriteUtils.TryWriteIntegerFromBytes(outputBuffer.Slice(0, output.Length), ref dcurr, dend)) - SendAndReset(); + WriteIntegerFromBytes(outputBuffer.Slice(0, output.Length)); return true; } @@ -369,8 +367,7 @@ private bool NetworkGetRange(ref TGarnetApi storageApi) { sessionMetrics?.incr_total_notfound(); Debug.Assert(o.IsSpanByte); - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_EMPTY, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_EMPTY); } return true; @@ -405,8 +402,7 @@ private bool NetworkSETEX(bool highPrecision, ref TGarnetApi storage var input = new RawStringInput(RespCommand.SETEX, 0, valMetadata); _ = storageApi.SET(key, ref input, ref sbVal); - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); return true; } @@ -429,8 +425,7 @@ private bool NetworkSETNX(bool highPrecision, ref TGarnetApi storage // The status returned for SETNX as NOTFOUND is the expected status in the happy path var retVal = status == GarnetStatus.NOTFOUND ? 1 : 0; - while (!RespWriteUtils.TryWriteInt32(retVal, ref dcurr, dend)) - SendAndReset(); + WriteInt32(retVal); return true; } @@ -627,8 +622,7 @@ private unsafe bool NetworkSET_EX(RespCommand cmd, ExpirationOption storageApi.SET(key, ref input, ref val); - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); return true; } @@ -654,8 +648,7 @@ private bool NetworkSET_Conditional(RespCommand cmd, int expiry, Arg // KEEPTTL without flags doesn't care whether it was found or not. if (cmd == RespCommand.SETKEEPTTL) { - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); } else { @@ -667,8 +660,7 @@ private bool NetworkSET_Conditional(RespCommand cmd, int expiry, Arg if (ok) { - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); } else { @@ -743,8 +735,7 @@ private bool NetworkIncrement(RespCommand cmd, ref TGarnetApi storag switch (errorFlag) { case OperationError.SUCCESS: - while (!RespWriteUtils.TryWriteIntegerFromBytes(outputBuffer.Slice(0, output.Length), ref dcurr, dend)) - SendAndReset(); + WriteIntegerFromBytes(outputBuffer.Slice(0, output.Length)); break; case OperationError.NAN_OR_INFINITY: case OperationError.INVALID_TYPE: @@ -782,8 +773,7 @@ private bool NetworkIncrementByFloat(ref TGarnetApi storageApi) switch (status) { case GarnetStatus.OK: - while (!RespWriteUtils.TryWriteBulkString(output.ReadOnlySpan, ref dcurr, dend)) - SendAndReset(); + WriteBulkString(output.ReadOnlySpan); break; case GarnetStatus.WRONGTYPE: default: @@ -812,8 +802,7 @@ private bool NetworkAppend(ref TGarnetApi storageApi) storageApi.APPEND(ref sbKey, ref input, ref output); - while (!RespWriteUtils.TryWriteIntegerFromBytes(outputBuffer.Slice(0, output.Length), ref dcurr, dend)) - SendAndReset(); + WriteIntegerFromBytes(outputBuffer.Slice(0, output.Length)); return true; } @@ -825,13 +814,11 @@ private bool NetworkPING() { if (isSubscriptionSession && respProtocolVersion == 2) { - while (!RespWriteUtils.TryWriteDirect(CmdStrings.SUSCRIBE_PONG, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.SUSCRIBE_PONG); } else { - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_PONG, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_PONG); } return true; } @@ -844,8 +831,7 @@ private bool NetworkASKING() //*1\r\n$6\r\n ASKING\r\n = 16 if (storeWrapper.serverOptions.EnableCluster) SessionAsking = 2; - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); return true; } @@ -854,8 +840,7 @@ private bool NetworkASKING() /// private bool NetworkQUIT() { - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); toDispose = true; return true; } @@ -912,8 +897,7 @@ private bool NetworkREADONLY() { //*1\r\n$8\r\nREADONLY\r\n clusterSession?.SetReadOnlySession(); - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); return true; } @@ -925,8 +909,7 @@ private bool NetworkREADWRITE() { //*1\r\n$9\r\nREADWRITE\r\n clusterSession?.SetReadWriteSession(); - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); return true; } @@ -951,12 +934,10 @@ private bool NetworkSTRLEN(ref TGarnetApi storageApi) switch (status) { case GarnetStatus.OK: - while (!RespWriteUtils.TryWriteInt32(value.Length, ref dcurr, dend)) - SendAndReset(); + WriteInt32(value.Length); break; case GarnetStatus.NOTFOUND: - while (!RespWriteUtils.TryWriteInt32(0, ref dcurr, dend)) - SendAndReset(); + WriteInt32(0); break; } @@ -1047,8 +1028,7 @@ private bool NetworkCOMMAND_COUNT() var commandCount = customCommandManagerSession.GetCustomCommandInfoCount() + respCommandCount; - while (!RespWriteUtils.TryWriteInt32(commandCount, ref dcurr, dend)) - SendAndReset(); + WriteInt32(commandCount); return true; } @@ -1193,13 +1173,11 @@ private bool NetworkCOMMAND_GETKEYS() var slicedParseState = parseState.Slice(simpleCmdInfo.IsSubCommand ? 2 : 1); var keys = slicedParseState.ExtractCommandKeys(simpleCmdInfo); - while (!RespWriteUtils.TryWriteArrayLength(keys.Length, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(keys.Length); foreach (var key in keys) { - while (!RespWriteUtils.TryWriteBulkString(key.Span, ref dcurr, dend)) - SendAndReset(); + WriteBulkString(key.Span); } return true; @@ -1230,24 +1208,20 @@ private bool NetworkCOMMAND_GETKEYSANDFLAGS() var slicedParseState = parseState.Slice(simpleCmdInfo.IsSubCommand ? 2 : 1); var keysAndFlags = slicedParseState.ExtractCommandKeysAndFlags(simpleCmdInfo); - while (!RespWriteUtils.TryWriteArrayLength(keysAndFlags.Length, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(keysAndFlags.Length); for (var i = 0; i < keysAndFlags.Length; i++) { - while (!RespWriteUtils.TryWriteArrayLength(2, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(2); - while (!RespWriteUtils.TryWriteBulkString(keysAndFlags[i].Item1.Span, ref dcurr, dend)) - SendAndReset(); + WriteBulkString(keysAndFlags[i].Item1.Span); var flags = EnumUtils.GetEnumDescriptions(keysAndFlags[i].Item2); WriteSetLength(flags.Length); foreach (var flag in flags) { - while (!RespWriteUtils.TryWriteBulkString(Encoding.ASCII.GetBytes(flag), ref dcurr, dend)) - SendAndReset(); + WriteBulkString(Encoding.ASCII.GetBytes(flag)); } } @@ -1359,8 +1333,7 @@ private bool NetworkTIME() var uSeconds = utcTime.ToString("ffffff"); var response = $"*2\r\n${seconds.ToString().Length}\r\n{seconds}\r\n${uSeconds.Length}\r\n{uSeconds}\r\n"; - while (!RespWriteUtils.TryWriteAsciiDirect(response, ref dcurr, dend)) - SendAndReset(); + WriteAsciiDirect(response); return true; } @@ -1398,8 +1371,7 @@ private bool NetworkAUTH() // XXX: There should be high-level AuthenticatorException if (this.AuthenticateUser(username, password)) { - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); } else { @@ -1454,8 +1426,7 @@ private bool NetworkMemoryUsage(ref TGarnetApi storageApi) if (status == GarnetStatus.OK) { - while (!RespWriteUtils.TryWriteInt32((int)memoryUsage, ref dcurr, dend)) - SendAndReset(); + WriteInt32((int)memoryUsage); } else { @@ -1517,8 +1488,7 @@ private bool NetworkASYNC() return true; } - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); return true; } @@ -1584,23 +1554,18 @@ void ProcessHelloCommand(byte? respProtocolVersion, ReadOnlySpan username, WriteMapLength(helloResult.Length + 1); for (var i = 0; i < helloResult.Length; i++) { - while (!RespWriteUtils.TryWriteAsciiBulkString(helloResult[i].Item1, ref dcurr, dend)) - SendAndReset(); + WriteAsciiBulkString(helloResult[i].Item1); if (helloResult[i].Item2 is long value) { - while (!RespWriteUtils.TryWriteInt64(value, ref dcurr, dend)) - SendAndReset(); + WriteInt64(value); } else { - while (!RespWriteUtils.TryWriteAsciiBulkString(helloResult[i].Item2.ToString(), ref dcurr, dend)) - SendAndReset(); + WriteAsciiBulkString(helloResult[i].Item2.ToString()); } } - while (!RespWriteUtils.TryWriteAsciiBulkString("modules", ref dcurr, dend)) - SendAndReset(); - while (!RespWriteUtils.TryWriteEmptyArray(ref dcurr, dend)) - SendAndReset(); + WriteAsciiBulkString("modules"); + WriteEmptyArray(); } /// @@ -1670,8 +1635,7 @@ void FlushDb(RespCommand cmd) ExecuteFlushDb(cmd, unsafeTruncateLog); logger?.LogInformation($"Running {nameof(cmd)} {{async}} {{mode}}", async ? "async" : "sync", unsafeTruncateLog ? " with unsafetruncatelog." : string.Empty); - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); } void ExecuteFlushDb(RespCommand cmd, bool unsafeTruncateLog) diff --git a/libs/server/Resp/BasicEtagCommands.cs b/libs/server/Resp/BasicEtagCommands.cs index 2fee440918d..06d69b99170 100644 --- a/libs/server/Resp/BasicEtagCommands.cs +++ b/libs/server/Resp/BasicEtagCommands.cs @@ -103,8 +103,7 @@ private bool NetworkDELIFGREATER(ref TGarnetApi storageApi) int keysDeleted = status == GarnetStatus.OK ? 1 : 0; - while (!RespWriteUtils.TryWriteInt32(keysDeleted, ref dcurr, dend)) - SendAndReset(); + WriteInt32(keysDeleted); return true; } diff --git a/libs/server/Resp/Bitmap/BitmapCommands.cs b/libs/server/Resp/Bitmap/BitmapCommands.cs index 20600939332..fd3a70444e1 100644 --- a/libs/server/Resp/Bitmap/BitmapCommands.cs +++ b/libs/server/Resp/Bitmap/BitmapCommands.cs @@ -189,8 +189,7 @@ private bool NetworkStringGetBit(ref TGarnetApi storageApi) var status = storageApi.StringGetBit(ref sbKey, ref input, ref o); if (status == GarnetStatus.NOTFOUND) - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_RETURN_VAL_0, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_RETURN_VAL_0); else dcurr += o.Length; @@ -251,8 +250,7 @@ private bool NetworkStringBitCount(ref TGarnetApi storageApi) } else if (status == GarnetStatus.NOTFOUND) { - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_RETURN_VAL_0, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_RETURN_VAL_0); } return true; @@ -324,8 +322,7 @@ private bool NetworkStringBitPosition(ref TGarnetApi storageApi) if (BitmapManager.TryValidateBitPosOffsets(startOffset, endOffset, offsetType, hasStartOffset, hasEndOffset)) { - while (!RespWriteUtils.TryWriteInt64(-1, ref dcurr, dend)) - SendAndReset(); + WriteInt64(-1); return true; } @@ -343,8 +340,7 @@ private bool NetworkStringBitPosition(ref TGarnetApi storageApi) else if (status == GarnetStatus.NOTFOUND) { var resp = bSetValSlice[0] == '0' ? CmdStrings.RESP_RETURN_VAL_0 : CmdStrings.RESP_RETURN_VAL_N1; - while (!RespWriteUtils.TryWriteDirect(resp, ref dcurr, dend)) - SendAndReset(); + WriteDirect(resp); } return true; @@ -375,8 +371,7 @@ private bool NetworkStringBitOperation(BitmapOperation bitOp, ref TG var input = new RawStringInput(RespCommand.BITOP, ref parseState); _ = storageApi.StringBitOperation(ref input, bitOp, out var result); - while (!RespWriteUtils.TryWriteInt64(result, ref dcurr, dend)) - SendAndReset(); + WriteInt64(result); return true; } @@ -546,8 +541,7 @@ private bool StringBitFieldAction(ref TGarnetApi storageApi, ArgSlice overflowTypeSlice = default) where TGarnetApi : IGarnetApi { - while (!RespWriteUtils.TryWriteArrayLength(secondaryCommandArgs.Count, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(secondaryCommandArgs.Count); var input = new RawStringInput(cmd); @@ -575,8 +569,7 @@ private bool StringBitFieldAction(ref TGarnetApi storageApi, if (status == GarnetStatus.NOTFOUND && opCode == RespCommand.GET) { - while (!RespWriteUtils.TryWriteInt32(0, ref dcurr, dend)) - SendAndReset(); + WriteInt32(0); } else { diff --git a/libs/server/Resp/ClientCommands.cs b/libs/server/Resp/ClientCommands.cs index 2264677a146..576e974b1c6 100644 --- a/libs/server/Resp/ClientCommands.cs +++ b/libs/server/Resp/ClientCommands.cs @@ -226,8 +226,7 @@ private bool NetworkCLIENTKILL() { _ = session.TryKill(); - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); return true; } @@ -401,8 +400,7 @@ private bool NetworkCLIENTKILL() } // Hand back result, which is count of clients _actually_ killed - while (!RespWriteUtils.TryWriteInt32(killed, ref dcurr, dend)) - SendAndReset(); + WriteInt32(killed); return true; } @@ -505,8 +503,7 @@ private bool NetworkCLIENTGETNAME() } else { - while (!RespWriteUtils.TryWriteAsciiBulkString(this.clientName, ref dcurr, dend)) - SendAndReset(); + WriteAsciiBulkString(this.clientName); } return true; @@ -529,8 +526,7 @@ private bool NetworkCLIENTSETNAME() this.clientName = name; - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); return true; } @@ -560,8 +556,7 @@ private bool NetworkCLIENTSETINFO() return AbortWithErrorMessage(CmdStrings.RESP_SYNTAX_ERROR); } - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); return true; } @@ -605,8 +600,7 @@ private bool NetworkCLIENTUNBLOCK() if (session is null) { - while (!RespWriteUtils.TryWriteInt32(0, ref dcurr, dend)) - SendAndReset(); + WriteInt32(0); return true; } @@ -616,20 +610,17 @@ private bool NetworkCLIENTUNBLOCK() if (!isBlocked) { - while (!RespWriteUtils.TryWriteInt32(0, ref dcurr, dend)) - SendAndReset(); + WriteInt32(0); return true; } var result = observer.TryForceUnblock(toThrowError); - while (!RespWriteUtils.TryWriteInt32(result ? 1 : 0, ref dcurr, dend)) - SendAndReset(); + WriteInt32(result ? 1 : 0); } else { - while (!RespWriteUtils.TryWriteInt32(0, ref dcurr, dend)) - SendAndReset(); + WriteInt32(0); } } else diff --git a/libs/server/Resp/HyperLogLog/HyperLogLogCommands.cs b/libs/server/Resp/HyperLogLog/HyperLogLogCommands.cs index 3ccff292178..62b23dcc635 100644 --- a/libs/server/Resp/HyperLogLog/HyperLogLogCommands.cs +++ b/libs/server/Resp/HyperLogLog/HyperLogLogCommands.cs @@ -49,13 +49,11 @@ private bool HyperLogLogAdd(ref TGarnetApi storageApi) if (pfaddUpdated > 0) { - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_RETURN_VAL_1, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_RETURN_VAL_1); } else { - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_RETURN_VAL_0, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_RETURN_VAL_0); } return true; } @@ -86,8 +84,7 @@ private bool HyperLogLogLength(ref TGarnetApi storageApi) } else { - while (!RespWriteUtils.TryWriteInt64(cardinality, ref dcurr, dend)) - SendAndReset(); + WriteInt64(cardinality); } return true; @@ -119,8 +116,7 @@ private bool HyperLogLogMerge(ref TGarnetApi storageApi) if (status == GarnetStatus.OK) { - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); } return true; } diff --git a/libs/server/Resp/KeyAdminCommands.cs b/libs/server/Resp/KeyAdminCommands.cs index dde122b592c..11465570ff5 100644 --- a/libs/server/Resp/KeyAdminCommands.cs +++ b/libs/server/Resp/KeyAdminCommands.cs @@ -113,8 +113,7 @@ bool NetworkRESTORE(ref TGarnetApi storageApi) if (status is GarnetStatus.NOTFOUND) { - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); return true; } @@ -249,8 +248,7 @@ private bool NetworkRENAME(ref TGarnetApi storageApi) switch (status) { case GarnetStatus.OK: - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); break; case GarnetStatus.NOTFOUND: while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_GENERIC_NOSUCHKEY, ref dcurr, dend)) @@ -292,8 +290,7 @@ private bool NetworkRENAMENX(ref TGarnetApi storageApi) { // Integer reply: 1 if key was renamed to newkey. // Integer reply: 0 if newkey already exists. - while (!RespWriteUtils.TryWriteInt32(result, ref dcurr, dend)) - SendAndReset(); + WriteInt32(result); } else { @@ -363,8 +360,7 @@ private bool NetworkEXISTS(ref TGarnetApi storageApi) exists++; } - while (!RespWriteUtils.TryWriteInt32(exists, ref dcurr, dend)) - SendAndReset(); + WriteInt32(exists); return true; } @@ -456,13 +452,11 @@ private bool NetworkEXPIRE(RespCommand command, ref TGarnetApi stora if (status == GarnetStatus.OK && timeoutSet) { - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_RETURN_VAL_1, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_RETURN_VAL_1); } else { - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_RETURN_VAL_0, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_RETURN_VAL_0); } return true; @@ -487,13 +481,11 @@ private bool NetworkPERSIST(ref TGarnetApi storageApi) if (status == GarnetStatus.OK) { - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_RETURN_VAL_1, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_RETURN_VAL_1); } else { - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_RETURN_VAL_0, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_RETURN_VAL_0); } return true; } @@ -528,8 +520,7 @@ private bool NetworkTTL(RespCommand command, ref TGarnetApi storageA } else { - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_RETURN_VAL_N2, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_RETURN_VAL_N2); } return true; } @@ -564,8 +555,7 @@ private bool NetworkEXPIRETIME(RespCommand command, ref TGarnetApi s } else { - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_RETURN_VAL_N2, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_RETURN_VAL_N2); } return true; } diff --git a/libs/server/Resp/Objects/HashCommands.cs b/libs/server/Resp/Objects/HashCommands.cs index bfcc4986cf5..6fe9e82fbc2 100644 --- a/libs/server/Resp/Objects/HashCommands.cs +++ b/libs/server/Resp/Objects/HashCommands.cs @@ -59,13 +59,11 @@ private unsafe bool HashSet(RespCommand command, ref TGarnetApi stor default: if (command == RespCommand.HMSET) { - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); } else { - while (!RespWriteUtils.TryWriteInt32(output.result1, ref dcurr, dend)) - SendAndReset(); + WriteInt32(output.result1); } break; } @@ -147,8 +145,7 @@ private bool HashGetAll(RespCommand command, ref TGarnetApi storageA ProcessOutput(output.SpanByteAndMemory); break; case GarnetStatus.NOTFOUND: - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_EMPTYLIST, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_EMPTYLIST); break; case GarnetStatus.WRONGTYPE: while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) @@ -192,8 +189,7 @@ private bool HashGetMultiple(RespCommand command, ref TGarnetApi sto break; case GarnetStatus.NOTFOUND: // Write an empty array of count - 1 elements with null values. - while (!RespWriteUtils.TryWriteArrayLength(parseState.Count - 1, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(parseState.Count - 1); for (var i = 0; i < parseState.Count - 1; ++i) WriteNull(); @@ -280,8 +276,7 @@ private bool HashRandomField(RespCommand command, ref TGarnetApi sto case GarnetStatus.NOTFOUND: if (includedCount) { - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_EMPTYLIST, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_EMPTYLIST); } else { @@ -325,12 +320,10 @@ private unsafe bool HashLength(ref TGarnetApi storageApi) { case GarnetStatus.OK: // Process output - while (!RespWriteUtils.TryWriteInt32(output.result1, ref dcurr, dend)) - SendAndReset(); + WriteInt32(output.result1); break; case GarnetStatus.NOTFOUND: - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_RETURN_VAL_0, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_RETURN_VAL_0); break; case GarnetStatus.WRONGTYPE: while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) @@ -368,12 +361,10 @@ private unsafe bool HashStrLength(ref TGarnetApi storageApi) { case GarnetStatus.OK: // Process output - while (!RespWriteUtils.TryWriteInt32(output.result1, ref dcurr, dend)) - SendAndReset(); + WriteInt32(output.result1); break; case GarnetStatus.NOTFOUND: - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_RETURN_VAL_0, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_RETURN_VAL_0); break; case GarnetStatus.WRONGTYPE: while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) @@ -410,12 +401,10 @@ private unsafe bool HashDelete(ref TGarnetApi storageApi) switch (status) { case GarnetStatus.OK: - while (!RespWriteUtils.TryWriteInt32(output.result1, ref dcurr, dend)) - SendAndReset(); + WriteInt32(output.result1); break; case GarnetStatus.NOTFOUND: - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_RETURN_VAL_0, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_RETURN_VAL_0); break; case GarnetStatus.WRONGTYPE: while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) @@ -451,12 +440,10 @@ private unsafe bool HashExists(ref TGarnetApi storageApi) switch (status) { case GarnetStatus.OK: - while (!RespWriteUtils.TryWriteInt32(output.result1, ref dcurr, dend)) - SendAndReset(); + WriteInt32(output.result1); break; case GarnetStatus.NOTFOUND: - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_RETURN_VAL_0, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_RETURN_VAL_0); break; case GarnetStatus.WRONGTYPE: while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) @@ -511,8 +498,7 @@ private unsafe bool HashKeys(RespCommand command, ref TGarnetApi sto ProcessOutput(output.SpanByteAndMemory); break; case GarnetStatus.NOTFOUND: - while (!RespWriteUtils.TryWriteEmptyArray(ref dcurr, dend)) - SendAndReset(); + WriteEmptyArray(); break; case GarnetStatus.WRONGTYPE: while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) @@ -653,12 +639,10 @@ private unsafe bool HashExpire(RespCommand command, ref TGarnetApi s SendAndReset(); break; case GarnetStatus.NOTFOUND: - while (!RespWriteUtils.TryWriteArrayLength(numFields, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(numFields); for (var i = 0; i < numFields; i++) { - while (!RespWriteUtils.TryWriteInt32(-2, ref dcurr, dend)) - SendAndReset(); + WriteInt32(-2); } break; default: @@ -743,12 +727,10 @@ private unsafe bool HashTimeToLive(RespCommand command, ref TGarnetA SendAndReset(); break; case GarnetStatus.NOTFOUND: - while (!RespWriteUtils.TryWriteArrayLength(numFields, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(numFields); for (var i = 0; i < numFields; i++) { - while (!RespWriteUtils.TryWriteInt32(-2, ref dcurr, dend)) - SendAndReset(); + WriteInt32(-2); } break; default: @@ -805,12 +787,10 @@ private unsafe bool HashPersist(ref TGarnetApi storageApi) SendAndReset(); break; case GarnetStatus.NOTFOUND: - while (!RespWriteUtils.TryWriteArrayLength(numFields, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(numFields); for (var i = 0; i < numFields; i++) { - while (!RespWriteUtils.TryWriteInt32(-2, ref dcurr, dend)) - SendAndReset(); + WriteInt32(-2); } break; default: diff --git a/libs/server/Resp/Objects/ListCommands.cs b/libs/server/Resp/Objects/ListCommands.cs index 2fac6516ec2..8d2f27f0138 100644 --- a/libs/server/Resp/Objects/ListCommands.cs +++ b/libs/server/Resp/Objects/ListCommands.cs @@ -54,8 +54,7 @@ private unsafe bool ListPush(RespCommand command, ref TGarnetApi sto else { // Write result to output - while (!RespWriteUtils.TryWriteInt32(output.result1, ref dcurr, dend)) - SendAndReset(); + WriteInt32(output.result1); } return true; } @@ -173,8 +172,7 @@ private unsafe bool ListPosition(ref TGarnetApi storageApi) if (count) { - while (!RespWriteUtils.TryWriteEmptyArray(ref dcurr, dend)) - SendAndReset(); + WriteEmptyArray(); } else { @@ -258,19 +256,15 @@ private unsafe bool ListPopMultiple(ref TGarnetApi storageApi) switch (statusOp) { case GarnetStatus.OK: - while (!RespWriteUtils.TryWriteArrayLength(2, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(2); - while (!RespWriteUtils.TryWriteBulkString(key.Span, ref dcurr, dend)) - SendAndReset(); + WriteBulkString(key.Span); - while (!RespWriteUtils.TryWriteArrayLength(elements.Length, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(elements.Length); foreach (var element in elements) { - while (!RespWriteUtils.TryWriteBulkString(element.Span, ref dcurr, dend)) - SendAndReset(); + WriteBulkString(element.Span); } break; @@ -331,14 +325,11 @@ private bool ListBlockingPop(RespCommand command) } else { - while (!RespWriteUtils.TryWriteArrayLength(2, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(2); - while (!RespWriteUtils.TryWriteBulkString(new Span(result.Key), ref dcurr, dend)) - SendAndReset(); + WriteBulkString(new Span(result.Key)); - while (!RespWriteUtils.TryWriteBulkString(new Span(result.Item), ref dcurr, dend)) - SendAndReset(); + WriteBulkString(new Span(result.Item)); } return true; @@ -440,8 +431,7 @@ private bool ListBlockingMove(ArgSlice srcKey, ArgSlice dstKey, } else { - while (!RespWriteUtils.TryWriteBulkString(new Span(result.Item), ref dcurr, dend)) - SendAndReset(); + WriteBulkString(new Span(result.Item)); } return true; @@ -474,8 +464,7 @@ private bool ListLength(ref TGarnetApi storageApi) switch (status) { case GarnetStatus.NOTFOUND: - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_RETURN_VAL_0, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_RETURN_VAL_0); break; case GarnetStatus.WRONGTYPE: while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) @@ -483,8 +472,7 @@ private bool ListLength(ref TGarnetApi storageApi) break; default: // Process output - while (!RespWriteUtils.TryWriteInt32(output.result1, ref dcurr, dend)) - SendAndReset(); + WriteInt32(output.result1); break; } @@ -534,8 +522,7 @@ private bool ListTrim(ref TGarnetApi storageApi) default: //GarnetStatus.OK or NOTFOUND have same result // no need to process output, just send OK - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); break; } @@ -586,8 +573,7 @@ private bool ListRange(ref TGarnetApi storageApi) ProcessOutput(output.SpanByteAndMemory); break; case GarnetStatus.NOTFOUND: - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_EMPTYLIST, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_EMPTYLIST); break; case GarnetStatus.WRONGTYPE: while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) @@ -685,12 +671,10 @@ private bool ListInsert(ref TGarnetApi storageApi) if (output.result1 == int.MinValue) return false; //process output - while (!RespWriteUtils.TryWriteInt32(output.result1, ref dcurr, dend)) - SendAndReset(); + WriteInt32(output.result1); break; case GarnetStatus.NOTFOUND: - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_RETURN_VAL_0, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_RETURN_VAL_0); break; case GarnetStatus.WRONGTYPE: while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) @@ -741,12 +725,10 @@ private bool ListRemove(ref TGarnetApi storageApi) if (output.result1 == int.MinValue) return false; //process output - while (!RespWriteUtils.TryWriteInt32(output.result1, ref dcurr, dend)) - SendAndReset(); + WriteInt32(output.result1); break; case GarnetStatus.NOTFOUND: - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_RETURN_VAL_0, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_RETURN_VAL_0); break; case GarnetStatus.WRONGTYPE: while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) @@ -789,8 +771,7 @@ private bool ListMove(ref TGarnetApi storageApi) case GarnetStatus.OK: if (node != null) { - while (!RespWriteUtils.TryWriteBulkString(node, ref dcurr, dend)) - SendAndReset(); + WriteBulkString(node); } else { @@ -832,8 +813,7 @@ private bool ListRightPopLeftPush(ref TGarnetApi storageApi) case GarnetStatus.OK: if (node != null) { - while (!RespWriteUtils.TryWriteBulkString(node, ref dcurr, dend)) - SendAndReset(); + WriteBulkString(node); } else { @@ -1016,20 +996,16 @@ private unsafe bool ListBlockingPopMultiple() return true; } - while (!RespWriteUtils.TryWriteArrayLength(2, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(2); - while (!RespWriteUtils.TryWriteBulkString(result.Key, ref dcurr, dend)) - SendAndReset(); + WriteBulkString(result.Key); var elements = result.Items; - while (!RespWriteUtils.TryWriteArrayLength(elements.Length, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(elements.Length); foreach (var element in elements) { - while (!RespWriteUtils.TryWriteBulkString(element, ref dcurr, dend)) - SendAndReset(); + WriteBulkString(element); } return true; diff --git a/libs/server/Resp/Objects/SetCommands.cs b/libs/server/Resp/Objects/SetCommands.cs index 7d7438ec465..aec2d2778b2 100644 --- a/libs/server/Resp/Objects/SetCommands.cs +++ b/libs/server/Resp/Objects/SetCommands.cs @@ -47,8 +47,7 @@ private unsafe bool SetAdd(ref TGarnetApi storageApi) break; default: // Write result to output - while (!RespWriteUtils.TryWriteInt32(output.result1, ref dcurr, dend)) - SendAndReset(); + WriteInt32(output.result1); break; } @@ -89,14 +88,12 @@ private bool SetIntersect(ref TGarnetApi storageApi) foreach (var item in result) { - while (!RespWriteUtils.TryWriteBulkString(item, ref dcurr, dend)) - SendAndReset(); + WriteBulkString(item); } } else { - while (!RespWriteUtils.TryWriteArrayLength(resultCount, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(resultCount); } break; @@ -137,8 +134,7 @@ private bool SetIntersectStore(ref TGarnetApi storageApi) switch (status) { case GarnetStatus.OK: - while (!RespWriteUtils.TryWriteInt32(output, ref dcurr, dend)) - SendAndReset(); + WriteInt32(output); break; case GarnetStatus.WRONGTYPE: while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) @@ -203,8 +199,7 @@ private bool SetIntersectLength(ref TGarnetApi storageApi) switch (status) { case GarnetStatus.OK: - while (!RespWriteUtils.TryWriteInt32(result, ref dcurr, dend)) - SendAndReset(); + WriteInt32(result); break; case GarnetStatus.WRONGTYPE: while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) @@ -247,8 +242,7 @@ private bool SetUnion(ref TGarnetApi storageApi) foreach (var item in result) { - while (!RespWriteUtils.TryWriteBulkString(item, ref dcurr, dend)) - SendAndReset(); + WriteBulkString(item); } break; case GarnetStatus.WRONGTYPE: @@ -288,8 +282,7 @@ private bool SetUnionStore(ref TGarnetApi storageApi) switch (status) { case GarnetStatus.OK: - while (!RespWriteUtils.TryWriteInt32(output, ref dcurr, dend)) - SendAndReset(); + WriteInt32(output); break; case GarnetStatus.WRONGTYPE: while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) @@ -330,12 +323,10 @@ private unsafe bool SetRemove(ref TGarnetApi storageApi) { case GarnetStatus.OK: // Write result to output - while (!RespWriteUtils.TryWriteInt32(output.result1, ref dcurr, dend)) - SendAndReset(); + WriteInt32(output.result1); break; case GarnetStatus.NOTFOUND: - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_RETURN_VAL_0, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_RETURN_VAL_0); break; case GarnetStatus.WRONGTYPE: while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) @@ -374,12 +365,10 @@ private unsafe bool SetLength(ref TGarnetApi storageApi) { case GarnetStatus.OK: // Process output - while (!RespWriteUtils.TryWriteInt32(output.result1, ref dcurr, dend)) - SendAndReset(); + WriteInt32(output.result1); break; case GarnetStatus.NOTFOUND: - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_RETURN_VAL_0, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_RETURN_VAL_0); break; case GarnetStatus.WRONGTYPE: while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) @@ -478,19 +467,16 @@ private unsafe bool SetIsMember(RespCommand cmd, ref TGarnetApi stor case GarnetStatus.NOTFOUND: if (isSingle) { - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_RETURN_VAL_0, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_RETURN_VAL_0); } else { var count = parseState.Count - 1; // Remove key - while (!RespWriteUtils.TryWriteArrayLength(count, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(count); for (var i = 0; i < count; i++) { - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_RETURN_VAL_0, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_RETURN_VAL_0); } } @@ -533,8 +519,7 @@ private unsafe bool SetPop(ref TGarnetApi storageApi) if (countParameter == 0) { - while (!RespWriteUtils.TryWriteEmptyArray(ref dcurr, dend)) - SendAndReset(); + WriteEmptyArray(); return true; } @@ -596,12 +581,10 @@ private unsafe bool SetMove(ref TGarnetApi storageApi) switch (status) { case GarnetStatus.OK: - while (!RespWriteUtils.TryWriteInt32(output, ref dcurr, dend)) - SendAndReset(); + WriteInt32(output); break; case GarnetStatus.NOTFOUND: - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_RETURN_VAL_0, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_RETURN_VAL_0); break; case GarnetStatus.WRONGTYPE: while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) @@ -645,8 +628,7 @@ private unsafe bool SetRandomMember(ref TGarnetApi storageApi) if (countParameter == 0) { - while (!RespWriteUtils.TryWriteEmptyArray(ref dcurr, dend)) - SendAndReset(); + WriteEmptyArray(); return true; } @@ -673,8 +655,7 @@ private unsafe bool SetRandomMember(ref TGarnetApi storageApi) case GarnetStatus.NOTFOUND: if (parseState.Count == 2) { - while (!RespWriteUtils.TryWriteEmptyArray(ref dcurr, dend)) - SendAndReset(); + WriteEmptyArray(); break; } @@ -723,8 +704,7 @@ private bool SetDiff(ref TGarnetApi storageApi) foreach (var item in output) { - while (!RespWriteUtils.TryWriteBulkString(item, ref dcurr, dend)) - SendAndReset(); + WriteBulkString(item); } } break; @@ -758,8 +738,7 @@ private bool SetDiffStore(ref TGarnetApi storageApi) switch (status) { case GarnetStatus.OK: - while (!RespWriteUtils.TryWriteInt32(output, ref dcurr, dend)) - SendAndReset(); + WriteInt32(output); break; case GarnetStatus.WRONGTYPE: while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) diff --git a/libs/server/Resp/Objects/SharedObjectCommands.cs b/libs/server/Resp/Objects/SharedObjectCommands.cs index 965f8d3645c..9ef803c434d 100644 --- a/libs/server/Resp/Objects/SharedObjectCommands.cs +++ b/libs/server/Resp/Objects/SharedObjectCommands.cs @@ -77,12 +77,9 @@ private unsafe bool ObjectScan(GarnetObjectType objectType, ref TGar return false; break; case GarnetStatus.NOTFOUND: - while (!RespWriteUtils.TryWriteArrayLength(2, ref dcurr, dend)) - SendAndReset(); - while (!RespWriteUtils.TryWriteInt32AsBulkString(0, ref dcurr, dend)) - SendAndReset(); - while (!RespWriteUtils.TryWriteEmptyArray(ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(2); + WriteInt32AsBulkString(0); + WriteEmptyArray(); break; case GarnetStatus.WRONGTYPE: while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) diff --git a/libs/server/Resp/Objects/SortedSetCommands.cs b/libs/server/Resp/Objects/SortedSetCommands.cs index 37cc6715a99..29f47d021f1 100644 --- a/libs/server/Resp/Objects/SortedSetCommands.cs +++ b/libs/server/Resp/Objects/SortedSetCommands.cs @@ -79,12 +79,10 @@ private unsafe bool SortedSetRemove(ref TGarnetApi storageApi) switch (status) { case GarnetStatus.OK: - while (!RespWriteUtils.TryWriteInt32(rmwOutput.result1, ref dcurr, dend)) - SendAndReset(); + WriteInt32(rmwOutput.result1); break; case GarnetStatus.NOTFOUND: - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_RETURN_VAL_0, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_RETURN_VAL_0); break; case GarnetStatus.WRONGTYPE: while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) @@ -121,12 +119,10 @@ private unsafe bool SortedSetLength(ref TGarnetApi storageApi) { case GarnetStatus.OK: // Process output - while (!RespWriteUtils.TryWriteInt32(output.result1, ref dcurr, dend)) - SendAndReset(); + WriteInt32(output.result1); break; case GarnetStatus.NOTFOUND: - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_RETURN_VAL_0, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_RETURN_VAL_0); break; case GarnetStatus.WRONGTYPE: while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) @@ -197,8 +193,7 @@ private unsafe bool SortedSetRange(RespCommand command, ref TGarnetA ProcessOutput(output.SpanByteAndMemory); break; case GarnetStatus.NOTFOUND: - while (!RespWriteUtils.TryWriteEmptyArray(ref dcurr, dend)) - SendAndReset(); + WriteEmptyArray(); break; case GarnetStatus.WRONGTYPE: while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) @@ -229,8 +224,7 @@ private unsafe bool SortedSetRangeStore(ref TGarnetApi storageApi) switch (status) { case GarnetStatus.OK: - while (!RespWriteUtils.TryWriteInt32(result, ref dcurr, dend)) - SendAndReset(); + WriteInt32(result); break; case GarnetStatus.WRONGTYPE: while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) @@ -323,8 +317,7 @@ private unsafe bool SortedSetScores(ref TGarnetApi storageApi) break; case GarnetStatus.NOTFOUND: // Write an empty array of count - 1 elements with null values. - while (!RespWriteUtils.TryWriteArrayLength(parseState.Count - 1, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(parseState.Count - 1); for (var i = 0; i < parseState.Count - 1; ++i) WriteNull(); @@ -392,8 +385,7 @@ private unsafe bool SortedSetPop(RespCommand command, ref TGarnetApi ProcessOutput(output.SpanByteAndMemory); break; case GarnetStatus.NOTFOUND: - while (!RespWriteUtils.TryWriteEmptyArray(ref dcurr, dend)) - SendAndReset(); + WriteEmptyArray(); break; case GarnetStatus.WRONGTYPE: while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) @@ -487,23 +479,18 @@ private unsafe bool SortedSetMPop(ref TGarnetApi storageApi) else { // Write array with 2 elements: key and array of elements - while (!RespWriteUtils.TryWriteArrayLength(2, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(2); // Write key - while (!RespWriteUtils.TryWriteBulkString(poppedKey.ReadOnlySpan, ref dcurr, dend)) - SendAndReset(); + WriteBulkString(poppedKey.ReadOnlySpan); // Write array of member-score pairs - while (!RespWriteUtils.TryWriteArrayLength(pairs.Length, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(pairs.Length); foreach (var (member, score) in pairs) { - while (!RespWriteUtils.TryWriteArrayLength(2, ref dcurr, dend)) - SendAndReset(); - while (!RespWriteUtils.TryWriteBulkString(member.ReadOnlySpan, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(2); + WriteBulkString(member.ReadOnlySpan); if (respProtocolVersion >= 3) { @@ -511,8 +498,7 @@ private unsafe bool SortedSetMPop(ref TGarnetApi storageApi) } else { - while (!RespWriteUtils.TryWriteBulkString(score.ReadOnlySpan, ref dcurr, dend)) - SendAndReset(); + WriteBulkString(score.ReadOnlySpan); } } } @@ -560,8 +546,7 @@ private unsafe bool SortedSetCount(ref TGarnetApi storageApi) ProcessOutput(output.SpanByteAndMemory); break; case GarnetStatus.NOTFOUND: - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_RETURN_VAL_0, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_RETURN_VAL_0); break; case GarnetStatus.WRONGTYPE: while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) @@ -624,12 +609,10 @@ private unsafe bool SortedSetLengthByValue(RespCommand command, ref else if (output.result1 == int.MinValue) // command partially executed return false; else - while (!RespWriteUtils.TryWriteInt32(output.result1, ref dcurr, dend)) - SendAndReset(); + WriteInt32(output.result1); break; case GarnetStatus.NOTFOUND: - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_RETURN_VAL_0, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_RETURN_VAL_0); break; case GarnetStatus.WRONGTYPE: while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) @@ -796,8 +779,7 @@ private unsafe bool SortedSetRemoveRange(RespCommand command, ref TG ProcessOutput(output.SpanByteAndMemory); break; case GarnetStatus.NOTFOUND: - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_RETURN_VAL_0, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_RETURN_VAL_0); break; case GarnetStatus.WRONGTYPE: while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) @@ -882,8 +864,7 @@ private unsafe bool SortedSetRandomMember(ref TGarnetApi storageApi) case GarnetStatus.NOTFOUND: if (includedCount) { - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_EMPTYLIST, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_EMPTYLIST); } else { @@ -959,26 +940,21 @@ private unsafe bool SortedSetDifference(ref TGarnetApi storageApi) default: if (result == null) { - while (!RespWriteUtils.TryWriteEmptyArray(ref dcurr, dend)) - SendAndReset(); + WriteEmptyArray(); } else { // write the size of the array reply - while (!RespWriteUtils.TryWriteArrayLength( - includeWithScores && (respProtocolVersion == 2) ? result.Count * 2 : result.Count, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(includeWithScores && (respProtocolVersion == 2) ? result.Count * 2 : result.Count); if (result != null) { foreach (var (score, element) in result) { if (respProtocolVersion >= 3 && includeWithScores) - while (!RespWriteUtils.TryWriteArrayLength(2, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(2); - while (!RespWriteUtils.TryWriteBulkString(element, ref dcurr, dend)) - SendAndReset(); + WriteBulkString(element); if (includeWithScores) { @@ -1032,8 +1008,7 @@ private unsafe bool SortedSetDifferenceStore(ref TGarnetApi storageA SendAndReset(); break; default: - while (!RespWriteUtils.TryWriteInt32(count, ref dcurr, dend)) - SendAndReset(); + WriteInt32(count); break; } @@ -1130,8 +1105,7 @@ private unsafe bool SortedSetIntersect(ref TGarnetApi storageApi) default: if (result == null || result.Count == 0) { - while (!RespWriteUtils.TryWriteEmptyArray(ref dcurr, dend)) - SendAndReset(); + WriteEmptyArray(); break; } @@ -1139,19 +1113,16 @@ private unsafe bool SortedSetIntersect(ref TGarnetApi storageApi) var arrayLength = result.Count; if (includeWithScores && respProtocolVersion == 2) arrayLength *= 2; - while (!RespWriteUtils.TryWriteArrayLength(arrayLength, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(arrayLength); foreach (var (score, element) in result) { if (includeWithScores && respProtocolVersion >= 3) { - while (!RespWriteUtils.TryWriteArrayLength(2, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(2); } - while (!RespWriteUtils.TryWriteBulkString(element, ref dcurr, dend)) - SendAndReset(); + WriteBulkString(element); if (includeWithScores) { @@ -1227,8 +1198,7 @@ private unsafe bool SortedSetIntersectLength(ref TGarnetApi storageA SendAndReset(); break; default: - while (!RespWriteUtils.TryWriteInt32(count, ref dcurr, dend)) - SendAndReset(); + WriteInt32(count); break; } @@ -1315,8 +1285,7 @@ private unsafe bool SortedSetIntersectStore(ref TGarnetApi storageAp SendAndReset(); break; default: - while (!RespWriteUtils.TryWriteInt32(count, ref dcurr, dend)) - SendAndReset(); + WriteInt32(count); break; } @@ -1419,8 +1388,7 @@ private unsafe bool SortedSetUnion(ref TGarnetApi storageApi) default: if (result == null || result.Count == 0) { - while (!RespWriteUtils.TryWriteEmptyArray(ref dcurr, dend)) - SendAndReset(); + WriteEmptyArray(); break; } @@ -1428,19 +1396,16 @@ private unsafe bool SortedSetUnion(ref TGarnetApi storageApi) var arrayLength = result.Count; if (includeWithScores && respProtocolVersion == 2) arrayLength *= 2; - while (!RespWriteUtils.TryWriteArrayLength(arrayLength, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(arrayLength); foreach (var (score, element) in result) { if (includeWithScores && respProtocolVersion >= 3) { - while (!RespWriteUtils.TryWriteArrayLength(2, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(2); } - while (!RespWriteUtils.TryWriteBulkString(element, ref dcurr, dend)) - SendAndReset(); + WriteBulkString(element); if (includeWithScores) { @@ -1541,8 +1506,7 @@ private unsafe bool SortedSetUnionStore(ref TGarnetApi storageApi) SendAndReset(); break; default: - while (!RespWriteUtils.TryWriteInt32(count, ref dcurr, dend)) - SendAndReset(); + WriteInt32(count); break; } @@ -1597,14 +1561,11 @@ private unsafe bool SortedSetBlockingPop(RespCommand command) } else { - while (!RespWriteUtils.TryWriteArrayLength(3, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(3); - while (!RespWriteUtils.TryWriteBulkString(result.Key, ref dcurr, dend)) - SendAndReset(); + WriteBulkString(result.Key); - while (!RespWriteUtils.TryWriteBulkString(result.Item, ref dcurr, dend)) - SendAndReset(); + WriteBulkString(result.Item); WriteDoubleNumeric(result.Score); } @@ -1711,21 +1672,16 @@ private unsafe bool SortedSetBlockingMPop() } // Write array with 2 elements: key and array of member-score pairs - while (!RespWriteUtils.TryWriteArrayLength(2, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(2); - while (!RespWriteUtils.TryWriteBulkString(result.Key, ref dcurr, dend)) - SendAndReset(); + WriteBulkString(result.Key); - while (!RespWriteUtils.TryWriteArrayLength(result.Items.Length, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(result.Items.Length); for (var i = 0; i < result.Items.Length; ++i) { - while (!RespWriteUtils.TryWriteArrayLength(2, ref dcurr, dend)) - SendAndReset(); - while (!RespWriteUtils.TryWriteBulkString(result.Items[i], ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(2); + WriteBulkString(result.Items[i]); WriteDoubleNumeric(result.Scores[i]); } @@ -1814,12 +1770,10 @@ private unsafe bool SortedSetExpire(RespCommand command, ref TGarnet SendAndReset(); break; case GarnetStatus.NOTFOUND: - while (!RespWriteUtils.TryWriteArrayLength(numMembers, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(numMembers); for (var i = 0; i < numMembers; i++) { - while (!RespWriteUtils.TryWriteInt32(-2, ref dcurr, dend)) - SendAndReset(); + WriteInt32(-2); } break; default: @@ -1907,12 +1861,10 @@ private unsafe bool SortedSetTimeToLive(RespCommand command, ref TGa SendAndReset(); break; case GarnetStatus.NOTFOUND: - while (!RespWriteUtils.TryWriteArrayLength(numMembers, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(numMembers); for (var i = 0; i < numMembers; i++) { - while (!RespWriteUtils.TryWriteInt32(-2, ref dcurr, dend)) - SendAndReset(); + WriteInt32(-2); } break; default: @@ -1976,12 +1928,10 @@ private unsafe bool SortedSetPersist(ref TGarnetApi storageApi) SendAndReset(); break; case GarnetStatus.NOTFOUND: - while (!RespWriteUtils.TryWriteArrayLength(numMembers, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(numMembers); for (var i = 0; i < numMembers; i++) { - while (!RespWriteUtils.TryWriteInt32(-2, ref dcurr, dend)) - SendAndReset(); + WriteInt32(-2); } break; default: diff --git a/libs/server/Resp/Objects/SortedSetGeoCommands.cs b/libs/server/Resp/Objects/SortedSetGeoCommands.cs index 3c641ff9ac5..961d046e165 100644 --- a/libs/server/Resp/Objects/SortedSetGeoCommands.cs +++ b/libs/server/Resp/Objects/SortedSetGeoCommands.cs @@ -183,8 +183,7 @@ private unsafe bool GeoCommands(RespCommand command, ref TGarnetApi break; default: var inputCount = parseState.Count - 1; - while (!RespWriteUtils.TryWriteArrayLength(inputCount, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(inputCount); for (var i = 0; i < inputCount; i++) { WriteNullArray(); @@ -296,8 +295,7 @@ private unsafe bool GeoSearchCommands(RespCommand command, ref TGarn switch (status) { case GarnetStatus.NOTFOUND: - while (!RespWriteUtils.TryWriteEmptyArray(ref dcurr, dend)) - SendAndReset(); + WriteEmptyArray(); break; case GarnetStatus.WRONGTYPE: diff --git a/libs/server/Resp/PubSubCommands.cs b/libs/server/Resp/PubSubCommands.cs index d353b7b5a7a..9e1d918c25b 100644 --- a/libs/server/Resp/PubSubCommands.cs +++ b/libs/server/Resp/PubSubCommands.cs @@ -26,8 +26,7 @@ public override unsafe void Publish(ArgSlice key, ArgSlice value) WritePushLength(3); - while (!RespWriteUtils.TryWriteBulkString("message"u8, ref dcurr, dend)) - SendAndReset(); + WriteBulkString("message"u8); // Write key and value to the network WriteDirectLargeRespString(key.ReadOnlySpan); @@ -56,8 +55,7 @@ public override unsafe void PatternPublish(ArgSlice pattern, ArgSlice key, ArgSl WritePushLength(4); - while (!RespWriteUtils.TryWriteBulkString("pmessage"u8, ref dcurr, dend)) - SendAndReset(); + WriteBulkString("pmessage"u8); // Write pattern, key, and value to the network WriteDirectLargeRespString(pattern.ReadOnlySpan); @@ -120,8 +118,7 @@ private bool NetworkPUBLISH(RespCommand cmd) AsyncUtils.BlockingWait(storeWrapper.clusterProvider.ClusterPublishAsync(cmd, _key, _val)); } - while (!RespWriteUtils.TryWriteInt32(numClients, ref dcurr, dend)) - SendAndReset(); + WriteInt32(numClients); return true; } @@ -161,20 +158,16 @@ private bool NetworkSUBSCRIBE(RespCommand cmd) if (disabledBroker) continue; - while (!RespWriteUtils.TryWriteArrayLength(3, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(3); - while (!RespWriteUtils.TryWriteBulkString(header, ref dcurr, dend)) - SendAndReset(); + WriteBulkString(header); - while (!RespWriteUtils.TryWriteBulkString(key.ReadOnlySpan, ref dcurr, dend)) - SendAndReset(); + WriteBulkString(key.ReadOnlySpan); if (subscribeBroker.Subscribe(key, this)) numActiveChannels++; - while (!RespWriteUtils.TryWriteInt32(numActiveChannels, ref dcurr, dend)) - SendAndReset(); + WriteInt32(numActiveChannels); } if (disabledBroker) @@ -204,19 +197,15 @@ private bool NetworkPSUBSCRIBE() if (disabledBroker) continue; - while (!RespWriteUtils.TryWriteArrayLength(3, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(3); - while (!RespWriteUtils.TryWriteBulkString("psubscribe"u8, ref dcurr, dend)) - SendAndReset(); - while (!RespWriteUtils.TryWriteBulkString(key.ReadOnlySpan, ref dcurr, dend)) - SendAndReset(); + WriteBulkString("psubscribe"u8); + WriteBulkString(key.ReadOnlySpan); if (subscribeBroker.PatternSubscribe(key, this)) numActiveChannels++; - while (!RespWriteUtils.TryWriteInt32(numActiveChannels, ref dcurr, dend)) - SendAndReset(); + WriteInt32(numActiveChannels); } if (disabledBroker) @@ -244,31 +233,24 @@ private bool NetworkUNSUBSCRIBE() var channels = subscribeBroker.ListAllSubscriptions(this); foreach (var channel in channels) { - while (!RespWriteUtils.TryWriteArrayLength(3, ref dcurr, dend)) - SendAndReset(); - while (!RespWriteUtils.TryWriteBulkString("unsubscribe"u8, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(3); + WriteBulkString("unsubscribe"u8); - while (!RespWriteUtils.TryWriteBulkString(channel.ReadOnlySpan, ref dcurr, dend)) - SendAndReset(); + WriteBulkString(channel.ReadOnlySpan); if (subscribeBroker.Unsubscribe(channel, this)) numActiveChannels--; - while (!RespWriteUtils.TryWriteInt32(numActiveChannels, ref dcurr, dend)) - SendAndReset(); + WriteInt32(numActiveChannels); } if (channels.Count == 0) { - while (!RespWriteUtils.TryWriteArrayLength(3, ref dcurr, dend)) - SendAndReset(); - while (!RespWriteUtils.TryWriteBulkString("unsubscribe"u8, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(3); + WriteBulkString("unsubscribe"u8); WriteNull(); - while (!RespWriteUtils.TryWriteInt32(numActiveChannels, ref dcurr, dend)) - SendAndReset(); + WriteInt32(numActiveChannels); } if (numActiveChannels == 0) @@ -283,18 +265,14 @@ private bool NetworkUNSUBSCRIBE() if (subscribeBroker != null) { - while (!RespWriteUtils.TryWriteArrayLength(3, ref dcurr, dend)) - SendAndReset(); - while (!RespWriteUtils.TryWriteBulkString("unsubscribe"u8, ref dcurr, dend)) - SendAndReset(); - while (!RespWriteUtils.TryWriteBulkString(key.ReadOnlySpan, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(3); + WriteBulkString("unsubscribe"u8); + WriteBulkString(key.ReadOnlySpan); if (subscribeBroker.Unsubscribe(new ByteArrayWrapper(key), this)) numActiveChannels--; - while (!RespWriteUtils.TryWriteInt32(numActiveChannels, ref dcurr, dend)) - SendAndReset(); + WriteInt32(numActiveChannels); } } @@ -324,33 +302,26 @@ private bool NetworkPUNSUBSCRIBE() List channels = subscribeBroker.ListAllPatternSubscriptions(this); foreach (var channel in channels) { - while (!RespWriteUtils.TryWriteArrayLength(3, ref dcurr, dend)) - SendAndReset(); - while (!RespWriteUtils.TryWriteBulkString("punsubscribe"u8, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(3); + WriteBulkString("punsubscribe"u8); - while (!RespWriteUtils.TryWriteBulkString(channel.ReadOnlySpan, ref dcurr, dend)) - SendAndReset(); + WriteBulkString(channel.ReadOnlySpan); if (subscribeBroker.PatternUnsubscribe(channel, this)) numActiveChannels--; - while (!RespWriteUtils.TryWriteInt32(numActiveChannels, ref dcurr, dend)) - SendAndReset(); + WriteInt32(numActiveChannels); } if (channels.Count == 0) { - while (!RespWriteUtils.TryWriteArrayLength(3, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(3); - while (!RespWriteUtils.TryWriteBulkString("punsubscribe"u8, ref dcurr, dend)) - SendAndReset(); + WriteBulkString("punsubscribe"u8); WriteNull(); - while (!RespWriteUtils.TryWriteInt32(0, ref dcurr, dend)) - SendAndReset(); + WriteInt32(0); } if (numActiveChannels == 0) @@ -365,18 +336,14 @@ private bool NetworkPUNSUBSCRIBE() if (subscribeBroker != null) { - while (!RespWriteUtils.TryWriteArrayLength(3, ref dcurr, dend)) - SendAndReset(); - while (!RespWriteUtils.TryWriteBulkString("punsubscribe"u8, ref dcurr, dend)) - SendAndReset(); - while (!RespWriteUtils.TryWriteBulkString(key.ReadOnlySpan, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(3); + WriteBulkString("punsubscribe"u8); + WriteBulkString(key.ReadOnlySpan); if (subscribeBroker.PatternUnsubscribe(new ByteArrayWrapper(key), this)) numActiveChannels--; - while (!RespWriteUtils.TryWriteInt32(numActiveChannels, ref dcurr, dend)) - SendAndReset(); + WriteInt32(numActiveChannels); } } @@ -410,13 +377,11 @@ private bool NetworkPUBSUB_CHANNELS() else channels = subscribeBroker.GetChannels(parseState.GetArgSliceByRef(0)); - while (!RespWriteUtils.TryWriteArrayLength(channels.Count, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(channels.Count); foreach (var channel in channels) { - while (!RespWriteUtils.TryWriteBulkString(channel.ReadOnlySpan, ref dcurr, dend)) - SendAndReset(); + WriteBulkString(channel.ReadOnlySpan); } return true; } @@ -435,8 +400,7 @@ private bool NetworkPUBSUB_NUMPAT() var numPatSubs = subscribeBroker.NumPatternSubscriptions(); - while (!RespWriteUtils.TryWriteInt32(numPatSubs, ref dcurr, dend)) - SendAndReset(); + WriteInt32(numPatSubs); return true; } @@ -449,17 +413,14 @@ private bool NetworkPUBSUB_NUMSUB() } var numChannels = parseState.Count; - while (!RespWriteUtils.TryWriteArrayLength(numChannels * 2, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(numChannels * 2); for (int c = 0; c < numChannels; c++) { var channel = parseState.GetArgSliceByRef(c); - while (!RespWriteUtils.TryWriteBulkString(channel.ReadOnlySpan, ref dcurr, dend)) - SendAndReset(); - while (!RespWriteUtils.TryWriteInt32(subscribeBroker.NumSubscriptions(channel), ref dcurr, dend)) - SendAndReset(); + WriteBulkString(channel.ReadOnlySpan); + WriteInt32(subscribeBroker.NumSubscriptions(channel)); } return true; } diff --git a/libs/server/Resp/PurgeBPCommand.cs b/libs/server/Resp/PurgeBPCommand.cs index d0cde4b03f4..584d1db8d4f 100644 --- a/libs/server/Resp/PurgeBPCommand.cs +++ b/libs/server/Resp/PurgeBPCommand.cs @@ -80,8 +80,7 @@ private bool NetworkPurgeBP() if (success) { GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true); - while (!RespWriteUtils.TryWriteSimpleString(managerType.ToReadOnlySpan(), ref dcurr, dend)) - SendAndReset(); + WriteSimpleString(managerType.ToReadOnlySpan()); } } catch (Exception ex) diff --git a/libs/server/Resp/RespServerSession.cs b/libs/server/Resp/RespServerSession.cs index f91c2bfcd45..94568928793 100644 --- a/libs/server/Resp/RespServerSession.cs +++ b/libs/server/Resp/RespServerSession.cs @@ -1075,8 +1075,7 @@ bool NetworkCLIENTID() return AbortWithWrongNumberOfArguments("client|id"); } - while (!RespWriteUtils.TryWriteInt64(Id, ref dcurr, dend)) - SendAndReset(); + WriteInt64(Id); return true; } diff --git a/libs/server/Resp/Vector/RespServerSessionVectors.cs b/libs/server/Resp/Vector/RespServerSessionVectors.cs index cf3213dc822..c93c94d2c82 100644 --- a/libs/server/Resp/Vector/RespServerSessionVectors.cs +++ b/libs/server/Resp/Vector/RespServerSessionVectors.cs @@ -393,8 +393,7 @@ private bool NetworkVADD(ref TGarnetApi storageApi) } else { - while (!RespWriteUtils.TryWriteInt32(1, ref dcurr, dend)) - SendAndReset(); + WriteInt32(1); } } else if (result == VectorManagerResult.Duplicate) @@ -406,8 +405,7 @@ private bool NetworkVADD(ref TGarnetApi storageApi) } else { - while (!RespWriteUtils.TryWriteInt32(0, ref dcurr, dend)) - SendAndReset(); + WriteInt32(0); } } else if (result == VectorManagerResult.BadParams) @@ -788,8 +786,7 @@ private bool NetworkVSIM(ref TGarnetApi storageApi) { // Vector Set does not exist - while (!RespWriteUtils.TryWriteEmptyArray(ref dcurr, dend)) - SendAndReset(); + WriteEmptyArray(); } else if (res == GarnetStatus.OK) { @@ -838,8 +835,7 @@ private bool NetworkVSIM(ref TGarnetApi storageApi) arrayItemCount += outputCount; } - while (!RespWriteUtils.TryWriteArrayLength(arrayItemCount, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(arrayItemCount); for (var resultIndex = 0; resultIndex < totalFound; resultIndex++) { @@ -889,8 +885,7 @@ private bool NetworkVSIM(ref TGarnetApi storageApi) continue; } - while (!RespWriteUtils.TryWriteBulkString(elementData, ref dcurr, dend)) - SendAndReset(); + WriteBulkString(elementData); if (withScores.Value) { @@ -911,8 +906,7 @@ private bool NetworkVSIM(ref TGarnetApi storageApi) var attr = remaininingAttributes.Slice(sizeof(int), attrLen); remaininingAttributes = remaininingAttributes[(sizeof(int) + attrLen)..]; - while (!RespWriteUtils.TryWriteBulkString(attr, ref dcurr, dend)) - SendAndReset(); + WriteBulkString(attr); } else if (!remaininingAttributes.IsEmpty) { @@ -1009,8 +1003,7 @@ private bool NetworkVEMB(ref TGarnetApi storageApi) { var distanceSpan = MemoryMarshal.Cast(distanceResult.AsReadOnlySpan()); - while (!RespWriteUtils.TryWriteArrayLength(distanceSpan.Length, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(distanceSpan.Length); for (var i = 0; i < distanceSpan.Length; i++) { @@ -1028,8 +1021,7 @@ private bool NetworkVEMB(ref TGarnetApi storageApi) } else { - while (!RespWriteUtils.TryWriteEmptyArray(ref dcurr, dend)) - SendAndReset(); + WriteEmptyArray(); } return true; @@ -1053,8 +1045,7 @@ private bool NetworkVCARD(ref TGarnetApi storageApi) // TODO: implement! - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); return true; } @@ -1089,8 +1080,7 @@ private bool NetworkVDIM(ref TGarnetApi storageApi) } else { - while (!RespWriteUtils.TryWriteInt32(dimensions, ref dcurr, dend)) - SendAndReset(); + WriteInt32(dimensions); } return true; @@ -1229,8 +1219,7 @@ private bool NetworkVISMEMBER(ref TGarnetApi storageApi) // TODO: implement! - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); return true; } @@ -1245,8 +1234,7 @@ private bool NetworkVLINKS(ref TGarnetApi storageApi) // TODO: implement! - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); return true; } @@ -1261,8 +1249,7 @@ private bool NetworkVRANDMEMBER(ref TGarnetApi storageApi) // TODO: implement! - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); return true; } @@ -1295,8 +1282,7 @@ private bool NetworkVREM(ref TGarnetApi storageApi) { var resp = res == GarnetStatus.OK ? 1 : 0; - while (!RespWriteUtils.TryWriteInt32(resp, ref dcurr, dend)) - SendAndReset(); + WriteInt32(resp); } return true; @@ -1312,8 +1298,7 @@ private bool NetworkVSETATTR(ref TGarnetApi storageApi) // TODO: implement! - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); return true; } diff --git a/libs/server/ServerConfig.cs b/libs/server/ServerConfig.cs index 02856cccd79..e1ef49bdf31 100644 --- a/libs/server/ServerConfig.cs +++ b/libs/server/ServerConfig.cs @@ -93,14 +93,12 @@ ReadOnlySpan GetDatabases() return Encoding.ASCII.GetBytes($"$9\r\ndatabases\r\n${databases.Length}\r\n{databases}\r\n"); } - while (!RespWriteUtils.TryWriteDirect(parameterValue, ref dcurr, dend)) - SendAndReset(); + WriteDirect(parameterValue); } } else { - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_EMPTYLIST, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_EMPTYLIST); } return true; @@ -114,8 +112,7 @@ private unsafe bool NetworkCONFIG_REWRITE() } storeWrapper.clusterProvider?.FlushConfig(); - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); return true; } @@ -232,8 +229,7 @@ private unsafe bool NetworkCONFIG_SET() if (sbErrorMsg.Length == 0) { - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); } else { diff --git a/libs/server/Transaction/TxnRespCommands.cs b/libs/server/Transaction/TxnRespCommands.cs index 6b31be3c015..3c63e7721c1 100644 --- a/libs/server/Transaction/TxnRespCommands.cs +++ b/libs/server/Transaction/TxnRespCommands.cs @@ -31,8 +31,7 @@ private bool NetworkMULTI() //Keep track of ptr for key verification when cluster mode is enabled txnManager.saveKeyRecvBufferPtr = recvBufferPtr; - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); return true; } @@ -73,8 +72,7 @@ private bool NetworkEXEC() if (startTxn) { - while (!RespWriteUtils.TryWriteArrayLength(txnManager.operationCntTxn, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(txnManager.operationCntTxn); } else { @@ -178,8 +176,7 @@ private bool NetworkSKIP(RespCommand cmd) txnManager.LockKeys(commandInfo); - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_QUEUED, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_QUEUED); txnManager.operationCntTxn++; return true; @@ -194,8 +191,7 @@ private bool NetworkDISCARD() { return AbortWithErrorMessage(CmdStrings.RESP_ERR_GENERIC_DISCARD_WO_MULTI); } - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); txnManager.Reset(false); return true; } @@ -227,8 +223,7 @@ private bool CommonWATCH(StoreType type) txnManager.Watch(toWatch, type); } - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); return true; } @@ -260,8 +255,7 @@ private bool NetworkUNWATCH() { txnManager.watchContainer.Reset(); } - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); return true; } From 7596550ec5ba0659cb7cb2e1d62424bbe7a4bc6c Mon Sep 17 00:00:00 2001 From: Kevin Montrose Date: Thu, 7 May 2026 10:18:11 -0400 Subject: [PATCH 02/16] do some manual conversions of callsites that copilot missed --- libs/common/RespWriteUtils.cs | 3 +++ libs/server/Custom/CustomRespCommands.cs | 9 +++------ libs/server/Lua/LuaCommands.cs | 12 ++++-------- libs/server/Metrics/Info/InfoCommand.cs | 3 +-- libs/server/Metrics/Latency/RespLatencyCommands.cs | 6 ++---- libs/server/Resp/ACLCommands.cs | 5 ++--- libs/server/Resp/AdminCommands.cs | 8 ++------ libs/server/Resp/RespServerSessionOutput.cs | 7 +++++++ 8 files changed, 24 insertions(+), 29 deletions(-) diff --git a/libs/common/RespWriteUtils.cs b/libs/common/RespWriteUtils.cs index a953eb82637..20d4aedfba0 100644 --- a/libs/common/RespWriteUtils.cs +++ b/libs/common/RespWriteUtils.cs @@ -94,6 +94,9 @@ public static bool TryWriteSetLength(int len, ref byte* curr, byte* end) return true; } + /// + /// Writes an array length + /// public static bool TryWriteArrayLength(int len, ref byte* curr, byte* end, out int numDigits, out int totalLen) { numDigits = NumUtils.CountDigits(len); diff --git a/libs/server/Custom/CustomRespCommands.cs b/libs/server/Custom/CustomRespCommands.cs index 30216e23ee0..3bf6b649b09 100644 --- a/libs/server/Custom/CustomRespCommands.cs +++ b/libs/server/Custom/CustomRespCommands.cs @@ -78,8 +78,7 @@ private void TryCustomProcedure(CustomProcedure proc, int startIdx = 0) if (output.MemoryOwner != null) SendAndReset(output.MemoryOwner, output.Length); else - while (!RespWriteUtils.TryWriteError($"ERR Command failed.", ref dcurr, dend)) - SendAndReset(); + WriteError("ERR Command failed."); } } @@ -153,8 +152,7 @@ private bool TryCustomObjectCommand(GarnetObjectType objType, byte s switch (status) { case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; default: if (output.SpanByteAndMemory.Memory != null) @@ -182,8 +180,7 @@ private bool TryCustomObjectCommand(GarnetObjectType objType, byte s WriteNull(); break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } } diff --git a/libs/server/Lua/LuaCommands.cs b/libs/server/Lua/LuaCommands.cs index ad03c94ea1a..3e8c13f3b58 100644 --- a/libs/server/Lua/LuaCommands.cs +++ b/libs/server/Lua/LuaCommands.cs @@ -73,8 +73,7 @@ private unsafe bool TryEVALSHA() if (runner == null) { - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_NO_SCRIPT, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_NO_SCRIPT); } else { @@ -163,8 +162,7 @@ private unsafe bool TryEVAL() if (runner == null) { - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_NO_SCRIPT, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_NO_SCRIPT); } else { @@ -336,8 +334,7 @@ private bool CheckLuaEnabled() { if (!storeWrapper.serverOptions.EnableLua) { - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_LUA_DISABLED, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_LUA_DISABLED); return false; } @@ -360,8 +357,7 @@ private bool TryExecuteScript(int count, LuaRunner scriptRunner) catch (Exception ex) { logger?.LogError(ex, "Error executing Lua script"); - while (!RespWriteUtils.TryWriteError("ERR " + ex.Message, ref dcurr, dend)) - SendAndReset(); + WriteError("ERR " + ex.Message); // Exceptions shouldn't happen, so if they did the runner is probably in a bad state return false; diff --git a/libs/server/Metrics/Info/InfoCommand.cs b/libs/server/Metrics/Info/InfoCommand.cs index 09abee868e3..08c1e4ef18b 100644 --- a/libs/server/Metrics/Info/InfoCommand.cs +++ b/libs/server/Metrics/Info/InfoCommand.cs @@ -48,8 +48,7 @@ private bool NetworkINFO() if (invalid) { - while (!RespWriteUtils.TryWriteError($"ERR Invalid section {invalidSection}. Try INFO HELP", ref dcurr, dend)) - SendAndReset(); + WriteError($"ERR Invalid section {invalidSection}. Try INFO HELP"); return true; } diff --git a/libs/server/Metrics/Latency/RespLatencyCommands.cs b/libs/server/Metrics/Latency/RespLatencyCommands.cs index 7d9f1184f51..a778dcc75f1 100644 --- a/libs/server/Metrics/Latency/RespLatencyCommands.cs +++ b/libs/server/Metrics/Latency/RespLatencyCommands.cs @@ -63,8 +63,7 @@ private bool NetworkLatencyHistogram() if (invalid) { - while (!RespWriteUtils.TryWriteError($"ERR Invalid event {invalidEvent}. Try LATENCY HELP", ref dcurr, dend)) - SendAndReset(); + WriteError($"ERR Invalid event {invalidEvent}. Try LATENCY HELP"); } else { @@ -108,8 +107,7 @@ private bool NetworkLatencyReset() if (invalid) { - while (!RespWriteUtils.TryWriteError($"ERR Invalid type {invalidEvent}", ref dcurr, dend)) - SendAndReset(); + WriteError($"ERR Invalid type {invalidEvent}"); } else { diff --git a/libs/server/Resp/ACLCommands.cs b/libs/server/Resp/ACLCommands.cs index e3a6dc52146..707ff01cc54 100644 --- a/libs/server/Resp/ACLCommands.cs +++ b/libs/server/Resp/ACLCommands.cs @@ -20,8 +20,7 @@ private bool ValidateACLAuthenticator() { if (_authenticator is null or not GarnetACLAuthenticator) { - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_ACL_AUTH_DISABLED, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_ACL_AUTH_DISABLED); return false; } return true; @@ -117,7 +116,7 @@ private bool NetworkAclCat() return true; var categories = ACLParser.ListCategories(); - RespWriteUtils.TryWriteArrayLength(categories.Count, ref dcurr, dend); + WriteArrayLength(categories.Count); foreach (var category in categories) { diff --git a/libs/server/Resp/AdminCommands.cs b/libs/server/Resp/AdminCommands.cs index 556934bde0d..69e230e0de8 100644 --- a/libs/server/Resp/AdminCommands.cs +++ b/libs/server/Resp/AdminCommands.cs @@ -905,12 +905,8 @@ private bool NetworkEXPDELSCAN() int requiredSpace = 5 + NumUtils.CountDigits(recordsExpired) + 3 + NumUtils.CountDigits(recordsScanned) + 2; WriteArrayLength(2); - - while (!RespWriteUtils.TryWriteArrayItem(recordsExpired, ref dcurr, dend)) - SendAndReset(); - - while (!RespWriteUtils.TryWriteArrayItem(recordsScanned, ref dcurr, dend)) - SendAndReset(); + WriteArrayItem(recordsExpired); + WriteArrayItem(recordsScanned); return true; } diff --git a/libs/server/Resp/RespServerSessionOutput.cs b/libs/server/Resp/RespServerSessionOutput.cs index 969c5059e89..d106e339415 100644 --- a/libs/server/Resp/RespServerSessionOutput.cs +++ b/libs/server/Resp/RespServerSessionOutput.cs @@ -48,6 +48,13 @@ private void WriteArrayLength(int len) SendAndReset(); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void WriteArrayItem(long recordsExpired) + { + while (!RespWriteUtils.TryWriteArrayItem(recordsExpired, ref dcurr, dend)) + SendAndReset(); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private void WriteBulkString(scoped ReadOnlySpan message) { From c5f07e36ef45e48c9de147b780fc6b7e58e008dc Mon Sep 17 00:00:00 2001 From: Kevin Montrose Date: Thu, 7 May 2026 10:32:38 -0400 Subject: [PATCH 03/16] nudge copilot into converting the remaining TryWriteError calls --- libs/server/Custom/CustomRespCommands.cs | 3 +- libs/server/Resp/ACLCommands.cs | 22 ++--- libs/server/Resp/AdminCommands.cs | 33 +++---- libs/server/Resp/ArrayCommands.cs | 9 +- libs/server/Resp/AsyncProcessor.cs | 3 +- libs/server/Resp/BasicCommands.cs | 33 +++---- libs/server/Resp/BasicEtagCommands.cs | 3 +- libs/server/Resp/ClientCommands.cs | 6 +- .../Resp/HyperLogLog/HyperLogLogCommands.cs | 9 +- libs/server/Resp/KeyAdminCommands.cs | 32 ++----- libs/server/Resp/Objects/HashCommands.cs | 42 +++----- libs/server/Resp/Objects/ListCommands.cs | 72 +++++--------- libs/server/Resp/Objects/ObjectStoreUtils.cs | 4 +- libs/server/Resp/Objects/SetCommands.cs | 45 +++------ .../Resp/Objects/SharedObjectCommands.cs | 5 +- libs/server/Resp/Objects/SortedSetCommands.cs | 96 +++++++------------ .../Resp/Objects/SortedSetGeoCommands.cs | 9 +- libs/server/Resp/Parser/RespCommand.cs | 6 +- libs/server/Resp/PubSubCommands.cs | 12 +-- libs/server/Resp/PurgeBPCommand.cs | 9 +- libs/server/Resp/RespServerSession.cs | 15 +-- .../Resp/Vector/RespServerSessionVectors.cs | 12 +-- libs/server/ServerConfig.cs | 3 +- libs/server/Transaction/TxnRespCommands.cs | 35 +++---- 24 files changed, 170 insertions(+), 348 deletions(-) diff --git a/libs/server/Custom/CustomRespCommands.cs b/libs/server/Custom/CustomRespCommands.cs index 3bf6b649b09..e3a0c2bc871 100644 --- a/libs/server/Custom/CustomRespCommands.cs +++ b/libs/server/Custom/CustomRespCommands.cs @@ -44,8 +44,7 @@ private bool TryTransactionProc(byte id, CustomTransactionProcedure proc, int st if (output.MemoryOwner != null) SendAndReset(output.MemoryOwner, output.Length); else - while (!RespWriteUtils.TryWriteError($"ERR Transaction failed.", ref dcurr, dend)) - SendAndReset(); + WriteError($"ERR Transaction failed."); } LatencyMetrics?.Stop(LatencyMetricsType.TX_PROC_LAT); diff --git a/libs/server/Resp/ACLCommands.cs b/libs/server/Resp/ACLCommands.cs index 707ff01cc54..847532de186 100644 --- a/libs/server/Resp/ACLCommands.cs +++ b/libs/server/Resp/ACLCommands.cs @@ -3,7 +3,6 @@ using System; using System.Diagnostics; -using Garnet.common; using Garnet.server.ACL; using Garnet.server.Auth; using Garnet.server.Auth.Settings; @@ -30,16 +29,14 @@ private bool ValidateACLFileUse() { if (storeWrapper.serverOptions.AuthSettings is not AclAuthenticationSettings) { - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_ACL_AUTH_DISABLED, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_ACL_AUTH_DISABLED); return false; } var aclAuthenticationSettings = (AclAuthenticationSettings)storeWrapper.serverOptions.AuthSettings; if (aclAuthenticationSettings.AclConfigurationFile == null) { - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_ACL_AUTH_FILE_DISABLED, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_ACL_AUTH_FILE_DISABLED); return false; } @@ -192,8 +189,7 @@ private bool NetworkAclSetUser() catch (ACLException exception) { // Abort command execution - while (!RespWriteUtils.TryWriteError($"ERR {exception.Message}", ref dcurr, dend)) - SendAndReset(); + WriteError($"ERR {exception.Message}"); return true; } @@ -239,8 +235,7 @@ private bool NetworkAclDelUser() logger?.LogDebug("ACLException: {message}", exception.Message); // Abort command execution - while (!RespWriteUtils.TryWriteError($"ERR {exception.Message}", ref dcurr, dend)) - SendAndReset(); + WriteError($"ERR {exception.Message}"); return true; } @@ -309,8 +304,7 @@ private bool NetworkAclLoad() } catch (ACLException exception) { - while (!RespWriteUtils.TryWriteError($"ERR {exception.Message}", ref dcurr, dend)) - SendAndReset(); + WriteError($"ERR {exception.Message}"); } return true; @@ -346,8 +340,7 @@ private bool NetworkAclSave() catch (Exception ex) { logger?.LogError(ex, "ACL SAVE faulted"); - while (!RespWriteUtils.TryWriteError($"ERR {ex.Message}", ref dcurr, dend)) - SendAndReset(); + WriteError($"ERR {ex.Message}"); return true; } @@ -422,8 +415,7 @@ private bool NetworkAclGetUser() catch (ACLException exception) { // Abort command execution - while (!RespWriteUtils.TryWriteError($"ERR {exception.Message}", ref dcurr, dend)) - SendAndReset(); + WriteError($"ERR {exception.Message}"); return true; } diff --git a/libs/server/Resp/AdminCommands.cs b/libs/server/Resp/AdminCommands.cs index 69e230e0de8..adc7821ee9d 100644 --- a/libs/server/Resp/AdminCommands.cs +++ b/libs/server/Resp/AdminCommands.cs @@ -32,8 +32,7 @@ private void ProcessAdminCommands(RespCommand command, ref TGarnetAp if (_authenticator.CanAuthenticate && !_authenticator.IsAuthenticated) { // If the current session is unauthenticated, we stop parsing, because no other commands are allowed - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_NOAUTH, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_NOAUTH); } var cmdFound = true; @@ -86,8 +85,7 @@ RespCommand.MIGRATE or return; } - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_GENERIC_UNK_CMD, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_GENERIC_UNK_CMD); } /// @@ -176,8 +174,7 @@ private bool NetworkMonitor() return AbortWithWrongNumberOfArguments(nameof(RespCommand.MONITOR)); } - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_GENERIC_UNK_CMD, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_GENERIC_UNK_CMD); return true; } @@ -514,8 +511,7 @@ private bool NetworkRegisterCs(CustomCommandManager customCommandManager) } else { - while (!RespWriteUtils.TryWriteError(errorMsg, ref dcurr, dend)) - SendAndReset(); + WriteError(errorMsg); } return true; @@ -654,8 +650,7 @@ private bool NetworkHCOLLECT(ref TGarnetApi storageApi) WriteDirect(CmdStrings.RESP_OK); break; default: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_HCOLLECT_ALREADY_IN_PROGRESS, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_HCOLLECT_ALREADY_IN_PROGRESS); break; } @@ -683,8 +678,7 @@ private bool NetworkZCOLLECT(ref TGarnetApi storageApi) WriteDirect(CmdStrings.RESP_OK); break; default: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_ZCOLLECT_ALREADY_IN_PROGRESS, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_ZCOLLECT_ALREADY_IN_PROGRESS); break; } @@ -862,8 +856,7 @@ private bool NetworkSAVE() if (!success) { - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_CHECKPOINT_ALREADY_IN_PROGRESS, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_CHECKPOINT_ALREADY_IN_PROGRESS); } else { @@ -981,8 +974,7 @@ private bool NetworkBGSAVE() } else { - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_CHECKPOINT_ALREADY_IN_PROGRESS, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_CHECKPOINT_ALREADY_IN_PROGRESS); } return true; @@ -993,23 +985,20 @@ private bool TryParseDatabaseId(int tokenIdx, out int dbId) dbId = -1; if (!parseState.TryGetInt(tokenIdx, out dbId)) { - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_GENERIC_VALUE_IS_NOT_INTEGER, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_GENERIC_VALUE_IS_NOT_INTEGER); return false; } if (dbId > 0 && storeWrapper.serverOptions.EnableCluster) { // Cluster mode does not allow DBID specification - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_DB_ID_CLUSTER_MODE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_DB_ID_CLUSTER_MODE); return false; } if (dbId >= storeWrapper.serverOptions.MaxDatabases || dbId < 0) { - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_DB_INDEX_OUT_OF_RANGE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_DB_INDEX_OUT_OF_RANGE); return false; } diff --git a/libs/server/Resp/ArrayCommands.cs b/libs/server/Resp/ArrayCommands.cs index 63216591903..d90eaf1e679 100644 --- a/libs/server/Resp/ArrayCommands.cs +++ b/libs/server/Resp/ArrayCommands.cs @@ -133,8 +133,7 @@ private bool NetworkSELECT() { // Should never reach here Debug.Fail("Database SELECT should have succeeded."); - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_SELECT_UNSUCCESSFUL, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_SELECT_UNSUCCESSFUL); } return true; @@ -182,8 +181,7 @@ private bool NetworkSWAPDB() } else { - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_SWAPDB_UNSUPPORTED, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_SWAPDB_UNSUPPORTED); } return true; @@ -270,8 +268,7 @@ private bool NetworkSCAN(ref TGarnetApi storageApi) // Validate count if (!parseState.TryGetLong(tokenIdx++, out countValue)) { - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_GENERIC_VALUE_IS_NOT_INTEGER, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_GENERIC_VALUE_IS_NOT_INTEGER); return true; } } diff --git a/libs/server/Resp/AsyncProcessor.cs b/libs/server/Resp/AsyncProcessor.cs index 62e9c6512d1..ded40f09beb 100644 --- a/libs/server/Resp/AsyncProcessor.cs +++ b/libs/server/Resp/AsyncProcessor.cs @@ -50,8 +50,7 @@ void NetworkGETPending(ref TGarnetApi storageApi) { unsafe { - while (!RespWriteUtils.TryWriteError($"ASYNC {asyncStarted}", ref dcurr, dend)) - SendAndReset(); + WriteError($"ASYNC {asyncStarted}"); } if (++asyncStarted == 1) // first async operation on the session, create the IO continuation processor diff --git a/libs/server/Resp/BasicCommands.cs b/libs/server/Resp/BasicCommands.cs index cdabfde436d..111d4d2d748 100644 --- a/libs/server/Resp/BasicCommands.cs +++ b/libs/server/Resp/BasicCommands.cs @@ -102,8 +102,7 @@ bool NetworkGETEX(ref TGarnetApi storageApi) break; default: - while (!RespWriteUtils.TryWriteError($"ERR Unsupported option {parseState.GetString(1)}", ref dcurr, dend)) - SendAndReset(); + WriteError($"ERR Unsupported option {parseState.GetString(1)}"); return true; } } @@ -554,8 +553,7 @@ private bool NetworkSETEXNX(ref TGarnetApi storageApi) if (!errorMessage.IsEmpty) { - while (!RespWriteUtils.TryWriteError(errorMessage, ref dcurr, dend)) - SendAndReset(); + WriteError(errorMessage); return true; } @@ -601,8 +599,7 @@ private bool NetworkSETEXNX(ref TGarnetApi storageApi) break; } - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_GENERIC_UNK_CMD, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_GENERIC_UNK_CMD); return true; } @@ -857,8 +854,7 @@ private bool NetworkFLUSHDB() if (storeWrapper.serverOptions.EnableCluster && storeWrapper.clusterProvider.IsReplica() && !clusterSession.ReadWriteSession) { - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_FLUSHALL_READONLY_REPLICA, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_FLUSHALL_READONLY_REPLICA); return true; } @@ -998,8 +994,7 @@ private bool NetworkCOMMAND() { var subCommand = parseState.GetString(0); var errorMsg = string.Format(CmdStrings.GenericErrUnknownSubCommand, subCommand, nameof(RespCommand.COMMAND)); - while (!RespWriteUtils.TryWriteError(errorMsg, ref dcurr, dend)) - SendAndReset(); + WriteError(errorMsg); } else { @@ -1311,8 +1306,7 @@ private bool NetworkHELLO() if (errorMsg != default) { - while (!RespWriteUtils.TryWriteError(errorMsg, ref dcurr, dend)) - SendAndReset(); + WriteError(errorMsg); return true; } @@ -1363,8 +1357,7 @@ private bool NetworkAUTH() // NOTE: Some authenticators cannot accept username/password pairs if (!_authenticator.CanAuthenticate) { - while (!RespWriteUtils.TryWriteError("ERR Client sent AUTH, but configured authenticator does not accept passwords"u8, ref dcurr, dend)) - SendAndReset(); + WriteError("ERR Client sent AUTH, but configured authenticator does not accept passwords"u8); return true; } @@ -1377,13 +1370,11 @@ private bool NetworkAUTH() { if (username.IsEmpty) { - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_WRONGPASS_INVALID_PASSWORD, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_WRONGPASS_INVALID_PASSWORD); } else { - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_WRONGPASS_INVALID_USERNAME_PASSWORD, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_WRONGPASS_INVALID_USERNAME_PASSWORD); } } return true; @@ -1482,8 +1473,7 @@ private bool NetworkASYNC() } else { - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_SYNTAX_ERROR, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_SYNTAX_ERROR); return true; } @@ -1624,8 +1614,7 @@ void FlushDb(RespCommand cmd) if (syntaxError) { - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_SYNTAX_ERROR, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_SYNTAX_ERROR); return; } diff --git a/libs/server/Resp/BasicEtagCommands.cs b/libs/server/Resp/BasicEtagCommands.cs index 06d69b99170..b10a1e505ae 100644 --- a/libs/server/Resp/BasicEtagCommands.cs +++ b/libs/server/Resp/BasicEtagCommands.cs @@ -207,8 +207,7 @@ private bool NetworkSetETagConditional(RespCommand cmd, ref TGarnetA if (!errorMessage.IsEmpty) { - while (!RespWriteUtils.TryWriteError(errorMessage, ref dcurr, dend)) - SendAndReset(); + WriteError(errorMessage); return true; } diff --git a/libs/server/Resp/ClientCommands.cs b/libs/server/Resp/ClientCommands.cs index 576e974b1c6..d9657ff8281 100644 --- a/libs/server/Resp/ClientCommands.cs +++ b/libs/server/Resp/ClientCommands.cs @@ -233,8 +233,7 @@ private bool NetworkCLIENTKILL() } } - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_NO_SUCH_CLIENT, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_NO_SUCH_CLIENT); return true; } @@ -625,8 +624,7 @@ private bool NetworkCLIENTUNBLOCK() } else { - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_UBLOCKING_CLINET, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_UBLOCKING_CLINET); } return true; diff --git a/libs/server/Resp/HyperLogLog/HyperLogLogCommands.cs b/libs/server/Resp/HyperLogLog/HyperLogLogCommands.cs index 62b23dcc635..d6b068bb02a 100644 --- a/libs/server/Resp/HyperLogLog/HyperLogLogCommands.cs +++ b/libs/server/Resp/HyperLogLog/HyperLogLogCommands.cs @@ -39,8 +39,7 @@ private bool HyperLogLogAdd(ref TGarnetApi storageApi) // Invalid HLL Type if (*output == 0xFF) { - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE_HLL, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE_HLL); return true; } @@ -79,8 +78,7 @@ private bool HyperLogLogLength(ref TGarnetApi storageApi) storageApi.HyperLogLogLength(ref input, out var cardinality, out var error); if (error) { - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE_HLL, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE_HLL); } else { @@ -109,8 +107,7 @@ private bool HyperLogLogMerge(ref TGarnetApi storageApi) // Invalid Type if (error) { - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE_HLL, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE_HLL); return true; } diff --git a/libs/server/Resp/KeyAdminCommands.cs b/libs/server/Resp/KeyAdminCommands.cs index 11465570ff5..ddf5301fd6b 100644 --- a/libs/server/Resp/KeyAdminCommands.cs +++ b/libs/server/Resp/KeyAdminCommands.cs @@ -44,16 +44,14 @@ bool NetworkRESTORE(ref TGarnetApi storageApi) // Restore is only implemented for string type if (valueSpan[0] != 0x00) { - while (!RespWriteUtils.TryWriteError("ERR RESTORE currently only supports string types", ref dcurr, dend)) - SendAndReset(); + WriteError("ERR RESTORE currently only supports string types"); return true; } // check if length of value is at least 10 if (valueSpan.Length < 10) { - while (!RespWriteUtils.TryWriteError("ERR DUMP payload version or checksum are wrong", ref dcurr, dend)) - SendAndReset(); + WriteError("ERR DUMP payload version or checksum are wrong"); return true; } @@ -64,8 +62,7 @@ bool NetworkRESTORE(ref TGarnetApi storageApi) if (rdbVersion > RDB_VERSION) { - while (!RespWriteUtils.TryWriteError("ERR DUMP payload version or checksum are wrong", ref dcurr, dend)) - SendAndReset(); + WriteError("ERR DUMP payload version or checksum are wrong"); return true; } @@ -78,16 +75,14 @@ bool NetworkRESTORE(ref TGarnetApi storageApi) if (calculatedCrc.SequenceCompareTo(payloadCrc) != 0) { - while (!RespWriteUtils.TryWriteError("ERR DUMP payload version or checksum are wrong", ref dcurr, dend)) - SendAndReset(); + WriteError("ERR DUMP payload version or checksum are wrong"); return true; } // decode the length of payload if (!RespLengthEncodingUtils.TryReadLength(valueSpan.Slice(1), out var length, out var payloadStart)) { - while (!RespWriteUtils.TryWriteError("ERR DUMP payload length format is invalid", ref dcurr, dend)) - SendAndReset(); + WriteError("ERR DUMP payload length format is invalid"); return true; } @@ -117,8 +112,7 @@ bool NetworkRESTORE(ref TGarnetApi storageApi) return true; } - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_BUSSYKEY, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_BUSSYKEY); return true; } @@ -148,8 +142,7 @@ bool NetworkDUMP(ref TGarnetApi storageApi) if (!RespLengthEncodingUtils.TryWriteLength(value.ReadOnlySpan.Length, encodedLength, out var bytesWritten)) { - while (!RespWriteUtils.TryWriteError("ERR DUMP payload length is invalid", ref dcurr, dend)) - SendAndReset(); + WriteError("ERR DUMP payload length is invalid"); return true; } @@ -251,8 +244,7 @@ private bool NetworkRENAME(ref TGarnetApi storageApi) WriteDirect(CmdStrings.RESP_OK); break; case GarnetStatus.NOTFOUND: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_GENERIC_NOSUCHKEY, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_GENERIC_NOSUCHKEY); break; } return true; @@ -294,8 +286,7 @@ private bool NetworkRENAMENX(ref TGarnetApi storageApi) } else { - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_GENERIC_NOSUCHKEY, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_GENERIC_NOSUCHKEY); } return true; @@ -427,10 +418,7 @@ private bool NetworkEXPIRE(RespCommand command, ref TGarnetApi stora } else { - while (!RespWriteUtils.TryWriteError( - "ERR NX and XX, GT or LT options at the same time are not compatible", ref dcurr, - dend)) - SendAndReset(); + WriteError("ERR NX and XX, GT or LT options at the same time are not compatible"); } } } diff --git a/libs/server/Resp/Objects/HashCommands.cs b/libs/server/Resp/Objects/HashCommands.cs index 6fe9e82fbc2..50620c68834 100644 --- a/libs/server/Resp/Objects/HashCommands.cs +++ b/libs/server/Resp/Objects/HashCommands.cs @@ -53,8 +53,7 @@ private unsafe bool HashSet(RespCommand command, ref TGarnetApi stor switch (status) { case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; default: if (command == RespCommand.HMSET) @@ -105,8 +104,7 @@ private bool HashGet(RespCommand command, ref TGarnetApi storageApi) WriteNull(); break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -148,8 +146,7 @@ private bool HashGetAll(RespCommand command, ref TGarnetApi storageA WriteDirect(CmdStrings.RESP_EMPTYLIST); break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -195,8 +192,7 @@ private bool HashGetMultiple(RespCommand command, ref TGarnetApi sto WriteNull(); break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -284,8 +280,7 @@ private bool HashRandomField(RespCommand command, ref TGarnetApi sto } break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -326,8 +321,7 @@ private unsafe bool HashLength(ref TGarnetApi storageApi) WriteDirect(CmdStrings.RESP_RETURN_VAL_0); break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -367,8 +361,7 @@ private unsafe bool HashStrLength(ref TGarnetApi storageApi) WriteDirect(CmdStrings.RESP_RETURN_VAL_0); break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } return true; @@ -407,8 +400,7 @@ private unsafe bool HashDelete(ref TGarnetApi storageApi) WriteDirect(CmdStrings.RESP_RETURN_VAL_0); break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } return true; @@ -446,8 +438,7 @@ private unsafe bool HashExists(ref TGarnetApi storageApi) WriteDirect(CmdStrings.RESP_RETURN_VAL_0); break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } return true; @@ -501,8 +492,7 @@ private unsafe bool HashKeys(RespCommand command, ref TGarnetApi sto WriteEmptyArray(); break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } return true; @@ -550,8 +540,7 @@ private unsafe bool HashIncrement(RespCommand command, ref TGarnetAp switch (status) { case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; default: ProcessOutput(output.SpanByteAndMemory); @@ -635,8 +624,7 @@ private unsafe bool HashExpire(RespCommand command, ref TGarnetApi s switch (status) { case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; case GarnetStatus.NOTFOUND: WriteArrayLength(numFields); @@ -723,8 +711,7 @@ private unsafe bool HashTimeToLive(RespCommand command, ref TGarnetA switch (status) { case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; case GarnetStatus.NOTFOUND: WriteArrayLength(numFields); @@ -783,8 +770,7 @@ private unsafe bool HashPersist(ref TGarnetApi storageApi) switch (status) { case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; case GarnetStatus.NOTFOUND: WriteArrayLength(numFields); diff --git a/libs/server/Resp/Objects/ListCommands.cs b/libs/server/Resp/Objects/ListCommands.cs index 8d2f27f0138..4a0a4cb385d 100644 --- a/libs/server/Resp/Objects/ListCommands.cs +++ b/libs/server/Resp/Objects/ListCommands.cs @@ -48,8 +48,7 @@ private unsafe bool ListPush(RespCommand command, ref TGarnetApi sto if (status == GarnetStatus.WRONGTYPE) { - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); } else { @@ -118,8 +117,7 @@ private unsafe bool ListPop(RespCommand command, ref TGarnetApi stor WriteNull(); break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -180,8 +178,7 @@ private unsafe bool ListPosition(ref TGarnetApi storageApi) } break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -272,8 +269,7 @@ private unsafe bool ListPopMultiple(ref TGarnetApi storageApi) WriteNullArray(); break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -307,15 +303,13 @@ private bool ListBlockingPop(RespCommand command) if (result.IsForceUnblocked) { - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_UNBLOCKED_CLIENT_VIA_CLIENT_UNBLOCK, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_UNBLOCKED_CLIENT_VIA_CLIENT_UNBLOCK); return true; } if (result.IsTypeMismatch) { - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); return true; } @@ -413,15 +407,13 @@ private bool ListBlockingMove(ArgSlice srcKey, ArgSlice dstKey, if (result.IsForceUnblocked) { - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_UNBLOCKED_CLIENT_VIA_CLIENT_UNBLOCK, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_UNBLOCKED_CLIENT_VIA_CLIENT_UNBLOCK); return true; } if (result.IsTypeMismatch) { - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); return true; } @@ -467,8 +459,7 @@ private bool ListLength(ref TGarnetApi storageApi) WriteDirect(CmdStrings.RESP_RETURN_VAL_0); break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; default: // Process output @@ -502,8 +493,7 @@ private bool ListTrim(ref TGarnetApi storageApi) if (!parseState.TryGetInt(1, out var start) || !parseState.TryGetInt(2, out var stop)) { - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_GENERIC_VALUE_IS_NOT_INTEGER, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_GENERIC_VALUE_IS_NOT_INTEGER); return true; } @@ -516,8 +506,7 @@ private bool ListTrim(ref TGarnetApi storageApi) switch (status) { case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; default: //GarnetStatus.OK or NOTFOUND have same result @@ -552,8 +541,7 @@ private bool ListRange(ref TGarnetApi storageApi) if (!parseState.TryGetInt(1, out var start) || !parseState.TryGetInt(2, out var end)) { - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_GENERIC_VALUE_IS_NOT_INTEGER, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_GENERIC_VALUE_IS_NOT_INTEGER); return true; } @@ -576,8 +564,7 @@ private bool ListRange(ref TGarnetApi storageApi) WriteDirect(CmdStrings.RESP_EMPTYLIST); break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } return true; @@ -605,8 +592,7 @@ private bool ListIndex(ref TGarnetApi storageApi) // Read index param if (!parseState.TryGetInt(1, out var index)) { - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_GENERIC_VALUE_IS_NOT_INTEGER, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_GENERIC_VALUE_IS_NOT_INTEGER); return true; } @@ -631,8 +617,7 @@ private bool ListIndex(ref TGarnetApi storageApi) WriteNull(); break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -677,8 +662,7 @@ private bool ListInsert(ref TGarnetApi storageApi) WriteDirect(CmdStrings.RESP_RETURN_VAL_0); break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -707,8 +691,7 @@ private bool ListRemove(ref TGarnetApi storageApi) // Get count parameter if (!parseState.TryGetInt(1, out var nCount)) { - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_GENERIC_VALUE_IS_NOT_INTEGER, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_GENERIC_VALUE_IS_NOT_INTEGER); return true; } @@ -731,8 +714,7 @@ private bool ListRemove(ref TGarnetApi storageApi) WriteDirect(CmdStrings.RESP_RETURN_VAL_0); break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -780,8 +762,7 @@ private bool ListMove(ref TGarnetApi storageApi) break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -822,8 +803,7 @@ private bool ListRightPopLeftPush(ref TGarnetApi storageApi) break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -890,12 +870,10 @@ public bool ListSet(ref TGarnetApi storageApi) ProcessOutput(output.SpanByteAndMemory); break; case GarnetStatus.NOTFOUND: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_GENERIC_NOSUCHKEY, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_GENERIC_NOSUCHKEY); break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -979,14 +957,12 @@ private unsafe bool ListBlockingPopMultiple() if (result.IsForceUnblocked) { - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_UNBLOCKED_CLIENT_VIA_CLIENT_UNBLOCK, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_UNBLOCKED_CLIENT_VIA_CLIENT_UNBLOCK); } if (result.IsTypeMismatch) { - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); return true; } diff --git a/libs/server/Resp/Objects/ObjectStoreUtils.cs b/libs/server/Resp/Objects/ObjectStoreUtils.cs index 08169de31d0..6e53e9a9fc9 100644 --- a/libs/server/Resp/Objects/ObjectStoreUtils.cs +++ b/libs/server/Resp/Objects/ObjectStoreUtils.cs @@ -3,7 +3,6 @@ using System; using System.Text; -using Garnet.common; namespace Garnet.server { @@ -48,8 +47,7 @@ private bool AbortWithErrorMessage(ReadOnlySpan errorMessage) { commandErrorWritten = true; // Print error message to result stream - while (!RespWriteUtils.TryWriteError(errorMessage, ref dcurr, dend)) - SendAndReset(); + WriteError(errorMessage); return true; } diff --git a/libs/server/Resp/Objects/SetCommands.cs b/libs/server/Resp/Objects/SetCommands.cs index aec2d2778b2..d051edb9e6e 100644 --- a/libs/server/Resp/Objects/SetCommands.cs +++ b/libs/server/Resp/Objects/SetCommands.cs @@ -42,8 +42,7 @@ private unsafe bool SetAdd(ref TGarnetApi storageApi) switch (status) { case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; default: // Write result to output @@ -98,8 +97,7 @@ private bool SetIntersect(ref TGarnetApi storageApi) break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -137,8 +135,7 @@ private bool SetIntersectStore(ref TGarnetApi storageApi) WriteInt32(output); break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -202,8 +199,7 @@ private bool SetIntersectLength(ref TGarnetApi storageApi) WriteInt32(result); break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -246,8 +242,7 @@ private bool SetUnion(ref TGarnetApi storageApi) } break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -285,8 +280,7 @@ private bool SetUnionStore(ref TGarnetApi storageApi) WriteInt32(output); break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -329,8 +323,7 @@ private unsafe bool SetRemove(ref TGarnetApi storageApi) WriteDirect(CmdStrings.RESP_RETURN_VAL_0); break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -371,8 +364,7 @@ private unsafe bool SetLength(ref TGarnetApi storageApi) WriteDirect(CmdStrings.RESP_RETURN_VAL_0); break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -416,8 +408,7 @@ private unsafe bool SetMembers(ref TGarnetApi storageApi) WriteEmptySet(); break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -482,8 +473,7 @@ private unsafe bool SetIsMember(RespCommand cmd, ref TGarnetApi stor break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -544,8 +534,7 @@ private unsafe bool SetPop(ref TGarnetApi storageApi) WriteNull(); break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -587,8 +576,7 @@ private unsafe bool SetMove(ref TGarnetApi storageApi) WriteDirect(CmdStrings.RESP_RETURN_VAL_0); break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -662,8 +650,7 @@ private unsafe bool SetRandomMember(ref TGarnetApi storageApi) WriteNull(); break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -709,8 +696,7 @@ private bool SetDiff(ref TGarnetApi storageApi) } break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -741,8 +727,7 @@ private bool SetDiffStore(ref TGarnetApi storageApi) WriteInt32(output); break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } diff --git a/libs/server/Resp/Objects/SharedObjectCommands.cs b/libs/server/Resp/Objects/SharedObjectCommands.cs index 9ef803c434d..72e59c307f8 100644 --- a/libs/server/Resp/Objects/SharedObjectCommands.cs +++ b/libs/server/Resp/Objects/SharedObjectCommands.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -using Garnet.common; - namespace Garnet.server { internal sealed unsafe partial class RespServerSession : ServerSessionBase @@ -82,8 +80,7 @@ private unsafe bool ObjectScan(GarnetObjectType objectType, ref TGar WriteEmptyArray(); break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } diff --git a/libs/server/Resp/Objects/SortedSetCommands.cs b/libs/server/Resp/Objects/SortedSetCommands.cs index 29f47d021f1..00c98784bba 100644 --- a/libs/server/Resp/Objects/SortedSetCommands.cs +++ b/libs/server/Resp/Objects/SortedSetCommands.cs @@ -41,8 +41,7 @@ private unsafe bool SortedSetAdd(ref TGarnetApi storageApi) switch (status) { case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; default: ProcessOutput(output.SpanByteAndMemory); @@ -85,8 +84,7 @@ private unsafe bool SortedSetRemove(ref TGarnetApi storageApi) WriteDirect(CmdStrings.RESP_RETURN_VAL_0); break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } return true; @@ -125,8 +123,7 @@ private unsafe bool SortedSetLength(ref TGarnetApi storageApi) WriteDirect(CmdStrings.RESP_RETURN_VAL_0); break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -196,8 +193,7 @@ private unsafe bool SortedSetRange(RespCommand command, ref TGarnetA WriteEmptyArray(); break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -227,8 +223,7 @@ private unsafe bool SortedSetRangeStore(ref TGarnetApi storageApi) WriteInt32(result); break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -273,8 +268,7 @@ private unsafe bool SortedSetScore(ref TGarnetApi storageApi) WriteNull(); break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -323,8 +317,7 @@ private unsafe bool SortedSetScores(ref TGarnetApi storageApi) WriteNull(); break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -388,8 +381,7 @@ private unsafe bool SortedSetPop(RespCommand command, ref TGarnetApi WriteEmptyArray(); break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -505,8 +497,7 @@ private unsafe bool SortedSetMPop(ref TGarnetApi storageApi) break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -549,8 +540,7 @@ private unsafe bool SortedSetCount(ref TGarnetApi storageApi) WriteDirect(CmdStrings.RESP_RETURN_VAL_0); break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -603,8 +593,7 @@ private unsafe bool SortedSetLengthByValue(RespCommand command, ref if (output.result1 == int.MaxValue) { // Error in arguments - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_MIN_MAX_NOT_VALID_STRING, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_MIN_MAX_NOT_VALID_STRING); } else if (output.result1 == int.MinValue) // command partially executed return false; @@ -615,8 +604,7 @@ private unsafe bool SortedSetLengthByValue(RespCommand command, ref WriteDirect(CmdStrings.RESP_RETURN_VAL_0); break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -659,8 +647,7 @@ private unsafe bool SortedSetIncrement(ref TGarnetApi storageApi) ProcessOutput(output.SpanByteAndMemory); break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -727,8 +714,7 @@ private unsafe bool SortedSetRank(RespCommand command, ref TGarnetAp WriteNull(); break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -782,8 +768,7 @@ private unsafe bool SortedSetRemoveRange(RespCommand command, ref TG WriteDirect(CmdStrings.RESP_RETURN_VAL_0); break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } return true; @@ -815,8 +800,7 @@ private unsafe bool SortedSetRandomMember(ref TGarnetApi storageApi) // Read count if (!parseState.TryGetInt(1, out paramCount)) { - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_GENERIC_VALUE_IS_NOT_INTEGER, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_GENERIC_VALUE_IS_NOT_INTEGER); return true; } @@ -872,8 +856,7 @@ private unsafe bool SortedSetRandomMember(ref TGarnetApi storageApi) } break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } return true; @@ -920,8 +903,7 @@ private unsafe bool SortedSetDifference(ref TGarnetApi storageApi) if (!withScores.EqualsUpperCaseSpanIgnoringCase(CmdStrings.WITHSCORES)) { - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_SYNTAX_ERROR, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_SYNTAX_ERROR); return true; } @@ -934,8 +916,7 @@ private unsafe bool SortedSetDifference(ref TGarnetApi storageApi) switch (status) { case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; default: if (result == null) @@ -1004,8 +985,7 @@ private unsafe bool SortedSetDifferenceStore(ref TGarnetApi storageA switch (status) { case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; default: WriteInt32(count); @@ -1099,8 +1079,7 @@ private unsafe bool SortedSetIntersect(ref TGarnetApi storageApi) switch (status) { case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; default: if (result == null || result.Count == 0) @@ -1194,8 +1173,7 @@ private unsafe bool SortedSetIntersectLength(ref TGarnetApi storageA switch (status) { case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; default: WriteInt32(count); @@ -1281,8 +1259,7 @@ private unsafe bool SortedSetIntersectStore(ref TGarnetApi storageAp switch (status) { case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; default: WriteInt32(count); @@ -1382,8 +1359,7 @@ private unsafe bool SortedSetUnion(ref TGarnetApi storageApi) switch (status) { case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; default: if (result == null || result.Count == 0) @@ -1502,8 +1478,7 @@ private unsafe bool SortedSetUnionStore(ref TGarnetApi storageApi) switch (status) { case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; default: WriteInt32(count); @@ -1543,15 +1518,13 @@ private unsafe bool SortedSetBlockingPop(RespCommand command) if (result.IsForceUnblocked) { - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_UNBLOCKED_CLIENT_VIA_CLIENT_UNBLOCK, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_UNBLOCKED_CLIENT_VIA_CLIENT_UNBLOCK); return true; } if (result.IsTypeMismatch) { - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); return true; } @@ -1653,15 +1626,13 @@ private unsafe bool SortedSetBlockingMPop() if (result.IsForceUnblocked) { - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_UNBLOCKED_CLIENT_VIA_CLIENT_UNBLOCK, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_UNBLOCKED_CLIENT_VIA_CLIENT_UNBLOCK); return true; } if (result.IsTypeMismatch) { - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); return true; } @@ -1766,8 +1737,7 @@ private unsafe bool SortedSetExpire(RespCommand command, ref TGarnet switch (status) { case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; case GarnetStatus.NOTFOUND: WriteArrayLength(numMembers); @@ -1857,8 +1827,7 @@ private unsafe bool SortedSetTimeToLive(RespCommand command, ref TGa switch (status) { case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; case GarnetStatus.NOTFOUND: WriteArrayLength(numMembers); @@ -1924,8 +1893,7 @@ private unsafe bool SortedSetPersist(ref TGarnetApi storageApi) switch (status) { case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; case GarnetStatus.NOTFOUND: WriteArrayLength(numMembers); diff --git a/libs/server/Resp/Objects/SortedSetGeoCommands.cs b/libs/server/Resp/Objects/SortedSetGeoCommands.cs index 961d046e165..d13d5e0b232 100644 --- a/libs/server/Resp/Objects/SortedSetGeoCommands.cs +++ b/libs/server/Resp/Objects/SortedSetGeoCommands.cs @@ -92,8 +92,7 @@ private unsafe bool GeoAdd(ref TGarnetApi storageApi) switch (status) { case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; default: ProcessOutput(output.SpanByteAndMemory); @@ -193,8 +192,7 @@ private unsafe bool GeoCommands(RespCommand command, ref TGarnetApi break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -299,8 +297,7 @@ private unsafe bool GeoSearchCommands(RespCommand command, ref TGarn break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } diff --git a/libs/server/Resp/Parser/RespCommand.cs b/libs/server/Resp/Parser/RespCommand.cs index 3ed6ce1554a..629f1143bfc 100644 --- a/libs/server/Resp/Parser/RespCommand.cs +++ b/libs/server/Resp/Parser/RespCommand.cs @@ -2909,14 +2909,12 @@ private RespCommand ArrayParseCommand(bool writeErrorOnFailure, ref int count, r { if (!specificErrorMessage.IsEmpty) { - while (!RespWriteUtils.TryWriteError(specificErrorMessage, ref dcurr, dend)) - SendAndReset(); + WriteError(specificErrorMessage); } else { // Return "Unknown RESP Command" message - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_GENERIC_UNK_CMD, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_GENERIC_UNK_CMD); } } return cmd; diff --git a/libs/server/Resp/PubSubCommands.cs b/libs/server/Resp/PubSubCommands.cs index 9e1d918c25b..f9d8eb2e545 100644 --- a/libs/server/Resp/PubSubCommands.cs +++ b/libs/server/Resp/PubSubCommands.cs @@ -172,8 +172,7 @@ private bool NetworkSUBSCRIBE(RespCommand cmd) if (disabledBroker) { - while (!RespWriteUtils.TryWriteError("ERR SUBSCRIBE is disabled, enable it with --pubsub option."u8, ref dcurr, dend)) - SendAndReset(); + WriteError("ERR SUBSCRIBE is disabled, enable it with --pubsub option."u8); return true; } @@ -210,8 +209,7 @@ private bool NetworkPSUBSCRIBE() if (disabledBroker) { - while (!RespWriteUtils.TryWriteError("ERR SUBSCRIBE is disabled, enable it with --pubsub option."u8, ref dcurr, dend)) - SendAndReset(); + WriteError("ERR SUBSCRIBE is disabled, enable it with --pubsub option."u8); return true; } @@ -278,8 +276,7 @@ private bool NetworkUNSUBSCRIBE() if (subscribeBroker == null) { - while (!RespWriteUtils.TryWriteError("ERR UNSUBSCRIBE is disabled, enable it with --pubsub option."u8, ref dcurr, dend)) - SendAndReset(); + WriteError("ERR UNSUBSCRIBE is disabled, enable it with --pubsub option."u8); } if (numActiveChannels == 0) @@ -349,8 +346,7 @@ private bool NetworkPUNSUBSCRIBE() if (subscribeBroker == null) { - while (!RespWriteUtils.TryWriteError("ERR PUNSUBSCRIBE is disabled, enable it with --pubsub option."u8, ref dcurr, dend)) - SendAndReset(); + WriteError("ERR PUNSUBSCRIBE is disabled, enable it with --pubsub option."u8); } if (numActiveChannels == 0) diff --git a/libs/server/Resp/PurgeBPCommand.cs b/libs/server/Resp/PurgeBPCommand.cs index 584d1db8d4f..f1a59166cd2 100644 --- a/libs/server/Resp/PurgeBPCommand.cs +++ b/libs/server/Resp/PurgeBPCommand.cs @@ -72,8 +72,7 @@ private bool NetworkPurgeBP() break; default: success = false; - while (!RespWriteUtils.TryWriteError($"ERR Could not purge {managerType}.", ref dcurr, dend)) - SendAndReset(); + WriteError($"ERR Could not purge {managerType}."); break; } @@ -86,8 +85,7 @@ private bool NetworkPurgeBP() catch (Exception ex) { logger?.LogError(ex, "PURGEBP {type}:{managerType}", managerType, managerType.ToString()); - while (!RespWriteUtils.TryWriteError($"ERR {ex.Message}", ref dcurr, dend)) - SendAndReset(); + WriteError($"ERR {ex.Message}"); return true; } @@ -95,8 +93,7 @@ bool ClusterPurgeBufferPool(ManagerType managerType) { if (clusterSession == null) { - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_GENERIC_CLUSTER_DISABLED, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_GENERIC_CLUSTER_DISABLED); return false; } storeWrapper.clusterProvider.PurgeBufferPool(managerType); diff --git a/libs/server/Resp/RespServerSession.cs b/libs/server/Resp/RespServerSession.cs index 94568928793..b04ee9828f2 100644 --- a/libs/server/Resp/RespServerSession.cs +++ b/libs/server/Resp/RespServerSession.cs @@ -490,8 +490,7 @@ public override int TryConsumeMessages(byte* reqBuffer, int bytesReceived) logger?.Log(ex.LogLevel, ex, "Aborting open session due to RESP parsing error"); // Forward parsing error as RESP error - while (!RespWriteUtils.TryWriteError($"ERR Protocol Error: {ex.Message}", ref dcurr, dend)) - SendAndReset(); + WriteError($"ERR Protocol Error: {ex.Message}"); // Send message and dispose the network sender to end the session if (dcurr > networkSender.GetResponseObjectHead()) @@ -508,8 +507,7 @@ public override int TryConsumeMessages(byte* reqBuffer, int bytesReceived) // Forward Garnet error as RESP error if (ex.ClientResponse) { - while (!RespWriteUtils.TryWriteError($"ERR Garnet Exception: {ex.Message}", ref dcurr, dend)) - SendAndReset(); + WriteError($"ERR Garnet Exception: {ex.Message}"); } // Send message and dispose the network sender to end the session @@ -650,13 +648,11 @@ private void ProcessMessages() { if (noScriptPassed) { - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_NOAUTH, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_NOAUTH); } else { - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_NOSCRIPT, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_NOSCRIPT); } // Track rejected command (ACL or script permission failure) @@ -1162,8 +1158,7 @@ private bool IsCommandArityValid(string cmdName, int arity, int count) if ((arity > 0 && count != arity - 1) || (arity < 0 && count < -arity - 1)) { - while (!RespWriteUtils.TryWriteError(string.Format(CmdStrings.GenericErrWrongNumArgs, cmdName), ref dcurr, dend)) - SendAndReset(); + WriteError(string.Format(CmdStrings.GenericErrWrongNumArgs, cmdName)); return false; } diff --git a/libs/server/Resp/Vector/RespServerSessionVectors.cs b/libs/server/Resp/Vector/RespServerSessionVectors.cs index c93c94d2c82..18b4bf02602 100644 --- a/libs/server/Resp/Vector/RespServerSessionVectors.cs +++ b/libs/server/Resp/Vector/RespServerSessionVectors.cs @@ -792,8 +792,7 @@ private bool NetworkVSIM(ref TGarnetApi storageApi) { if (vectorRes == VectorManagerResult.MissingElement) { - while (!RespWriteUtils.TryWriteError("Element not in Vector Set"u8, ref dcurr, dend)) - SendAndReset(); + WriteError("Element not in Vector Set"u8); } else if (vectorRes == VectorManagerResult.OK) { @@ -1067,8 +1066,7 @@ private bool NetworkVDIM(ref TGarnetApi storageApi) if (res == GarnetStatus.NOTFOUND) { - while (!RespWriteUtils.TryWriteError("ERR Key not found"u8, ref dcurr, dend)) - SendAndReset(); + WriteError("ERR Key not found"u8); } else if (res == GarnetStatus.WRONGTYPE) { @@ -1308,8 +1306,7 @@ private bool AbortVectorSetPartiallyDeleted(ref ArgSlice key) // TODO: We could _finish_ the delete here... though if we do that we should do it for ALL commands, not just Vector Set commands // That's more intrusive, and is more of a V2 thing... so lets just give a workaround for now - while (!RespWriteUtils.TryWriteError("ERR Vector Set is in a partially deleted state - re-execute DEL to complete deletion"u8, ref dcurr, dend)) - SendAndReset(); + WriteError("ERR Vector Set is in a partially deleted state - re-execute DEL to complete deletion"u8); return true; } @@ -1317,8 +1314,7 @@ private bool AbortVectorSetPartiallyDeleted(ref ArgSlice key) private bool AbortVectorSetWrongType() { // Matches Redis behavior - doesn't indicate the type involved - while (!RespWriteUtils.TryWriteError("WRONGTYPE Operation against a key holding the wrong kind of value"u8, ref dcurr, dend)) - SendAndReset(); + WriteError("WRONGTYPE Operation against a key holding the wrong kind of value"u8); return true; } diff --git a/libs/server/ServerConfig.cs b/libs/server/ServerConfig.cs index e1ef49bdf31..d7d47113944 100644 --- a/libs/server/ServerConfig.cs +++ b/libs/server/ServerConfig.cs @@ -233,8 +233,7 @@ private unsafe bool NetworkCONFIG_SET() } else { - while (!RespWriteUtils.TryWriteError(sbErrorMsg.ToString(), ref dcurr, dend)) - SendAndReset(); + WriteError(sbErrorMsg.ToString()); } return true; diff --git a/libs/server/Transaction/TxnRespCommands.cs b/libs/server/Transaction/TxnRespCommands.cs index 3c63e7721c1..e3564c8092d 100644 --- a/libs/server/Transaction/TxnRespCommands.cs +++ b/libs/server/Transaction/TxnRespCommands.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using Garnet.common; using Microsoft.Extensions.Logging; namespace Garnet.server @@ -20,8 +19,7 @@ private bool NetworkMULTI() { if (txnManager.state != TxnState.None) { - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_GENERIC_NESTED_MULTI, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_GENERIC_NESTED_MULTI); txnManager.Abort(); return true; } @@ -47,8 +45,7 @@ private bool NetworkEXEC() // Abort and reset the transaction else if (txnManager.state == TxnState.Aborted) { - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_EXEC_ABORT, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_EXEC_ABORT); txnManager.Reset(false); return true; } @@ -83,8 +80,7 @@ private bool NetworkEXEC() return true; } // EXEC without MULTI command - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_GENERIC_EXEC_WO_MULTI, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_GENERIC_EXEC_WO_MULTI); return true; } @@ -99,8 +95,7 @@ private bool NetworkSKIP(RespCommand cmd) cmd = cmd.NormalizeForACLs(); if (!RespCommandsInfo.TryGetSimpleRespCommandInfo(cmd, out var commandInfo, logger: logger) || !commandInfo.AllowedInTxn) { - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_GENERIC_UNK_CMD, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_GENERIC_UNK_CMD); txnManager.Abort(); return true; } @@ -124,16 +119,14 @@ private bool NetworkSKIP(RespCommand cmd) { if (isWatch) { - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_GENERIC_WATCH_IN_MULTI, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_GENERIC_WATCH_IN_MULTI); return true; } if (invalidNumArgs) { var err = string.Format(CmdStrings.GenericErrWrongNumArgs, RespCommandsInfo.GetRespCommandName(cmd)); - while (!RespWriteUtils.TryWriteError(err, ref dcurr, dend)) - SendAndReset(); + WriteError(err); txnManager.Abort(); return true; } @@ -157,8 +150,7 @@ private bool NetworkSKIP(RespCommand cmd) if (abort) { - while (!RespWriteUtils.TryWriteError(errMsg, ref dcurr, dend)) - SendAndReset(); + WriteError(errMsg); txnManager.Abort(); return true; } @@ -166,9 +158,8 @@ private bool NetworkSKIP(RespCommand cmd) if (cmd == RespCommand.DEBUG && !CanRunDebug()) { - while (!RespWriteUtils.TryWriteError(System.Text.Encoding.ASCII.GetBytes(string.Format( - CmdStrings.GenericErrCommandDisallowedWithOption, RespCommand.DEBUG, "enable-debug-command")), ref dcurr, dend)) - SendAndReset(); + WriteError(System.Text.Encoding.ASCII.GetBytes(string.Format( + CmdStrings.GenericErrCommandDisallowedWithOption, RespCommand.DEBUG, "enable-debug-command"))); txnManager.Abort(); return true; @@ -288,8 +279,7 @@ private bool NetworkRUNTXP() { logger?.LogError(e, "Getting customer transaction in RUNTXP failed"); - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_NO_TRANSACTION_PROCEDURE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_NO_TRANSACTION_PROCEDURE); return true; } @@ -297,10 +287,7 @@ private bool NetworkRUNTXP() if ((arity > 0 && count != arity) || (arity < 0 && count < -arity)) { var expectedParams = arity > 0 ? arity - 1 : -arity - 1; - while (!RespWriteUtils.TryWriteError( - string.Format(CmdStrings.GenericErrWrongNumArgsTxn, txId, expectedParams, count - 1), ref dcurr, - dend)) - SendAndReset(); + WriteError(string.Format(CmdStrings.GenericErrWrongNumArgsTxn, txId, expectedParams, count - 1)); } else TryTransactionProc((byte)txId, proc, 1); From 69c58c1704d0e928f76143248e00f638d126061a Mon Sep 17 00:00:00 2001 From: Kevin Montrose Date: Thu, 7 May 2026 10:53:05 -0400 Subject: [PATCH 04/16] finish conversion for Garnet.server --- libs/common/RespWriteUtils.cs | 13 ++- libs/server/Lua/LuaRunner.Functions.cs | 9 +-- libs/server/Lua/LuaRunner.cs | 84 +++++++++++++------- libs/server/Resp/AsyncProcessor.cs | 4 +- libs/server/Resp/MGetReadArgBatch.cs | 10 +-- libs/server/Resp/Objects/ObjectStoreUtils.cs | 1 - libs/server/Resp/RespServerSessionOutput.cs | 58 +++++++------- 7 files changed, 101 insertions(+), 78 deletions(-) diff --git a/libs/common/RespWriteUtils.cs b/libs/common/RespWriteUtils.cs index 20d4aedfba0..12e58234712 100644 --- a/libs/common/RespWriteUtils.cs +++ b/libs/common/RespWriteUtils.cs @@ -824,13 +824,18 @@ public static bool TryWriteOne(ref byte* curr, byte* end) public static void WriteEtagValArray(long etag, ref ReadOnlySpan value, ref byte* curr, byte* end, bool writeDirect) { // Writes a Resp encoded Array of Integer for ETAG as first element, and bulk string for value as second element - RespWriteUtils.TryWriteArrayLength(2, ref curr, end); - RespWriteUtils.TryWriteInt64(etag, ref curr, end); + var res = TryWriteArrayLength(2, ref curr, end); + Debug.Assert(res, "Caller should have ensured buffer is sufficiently large"); + + res = TryWriteInt64(etag, ref curr, end); + Debug.Assert(res, "Caller should have ensured buffer is sufficiently large"); if (writeDirect) - RespWriteUtils.TryWriteDirect(value, ref curr, end); + res = TryWriteDirect(value, ref curr, end); else - RespWriteUtils.TryWriteBulkString(value, ref curr, end); + res = TryWriteBulkString(value, ref curr, end); + + Debug.Assert(res, "Caller should have ensured buffer is sufficiently large"); } /// diff --git a/libs/server/Lua/LuaRunner.Functions.cs b/libs/server/Lua/LuaRunner.Functions.cs index e5b36a83dd8..9320d0e5da0 100644 --- a/libs/server/Lua/LuaRunner.Functions.cs +++ b/libs/server/Lua/LuaRunner.Functions.cs @@ -3084,8 +3084,7 @@ private unsafe int CompileCommon(nint luaState, ref TResponse resp) _ = state.RawGetInteger(LuaType.Function, (int)LuaRegistry.Index, loadSandboxedRegistryIndex); if (!state.TryPushBuffer(source.Span)) { - while (!RespWriteUtils.TryWriteError(CmdStrings.LUA_out_of_memory, ref resp.BufferCur, resp.BufferEnd)) - resp.SendAndReset(); + resp.WriteError(CmdStrings.LUA_out_of_memory); return 0; } @@ -3105,8 +3104,7 @@ private unsafe int CompileCommon(nint luaState, ref TResponse resp) if (!state.TryRef(out functionRegistryIndex)) { // Uh-oh, couldn't save the function under the registry - while (!RespWriteUtils.TryWriteError(CmdStrings.LUA_out_of_memory, ref resp.BufferCur, resp.BufferEnd)) - resp.SendAndReset(); + resp.WriteError(CmdStrings.LUA_out_of_memory); return 0; } @@ -3125,8 +3123,7 @@ private unsafe int CompileCommon(nint luaState, ref TResponse resp) errStr = "Compilation error, cause unknown"; } - while (!RespWriteUtils.TryWriteError(errStr, ref resp.BufferCur, resp.BufferEnd)) - resp.SendAndReset(); + resp.WriteError(Encoding.UTF8.GetBytes(errStr)); } return 0; diff --git a/libs/server/Lua/LuaRunner.cs b/libs/server/Lua/LuaRunner.cs index cd5aea8dc2c..6108baf7971 100644 --- a/libs/server/Lua/LuaRunner.cs +++ b/libs/server/Lua/LuaRunner.cs @@ -47,6 +47,16 @@ private unsafe interface IResponseAdapter /// Equivalent to . /// void SendAndReset(); + + /// + /// Equivalent to . + /// + void WriteError(ReadOnlySpan error); + + /// + /// Equivalent to . + /// + void WriteDirect(ReadOnlySpan data); } /// @@ -77,6 +87,14 @@ public byte RespProtocolVersion /// public void SendAndReset() => session.SendAndReset(); + + /// + public void WriteError(ReadOnlySpan error) + => session.WriteError(error); + + /// + public void WriteDirect(ReadOnlySpan data) + => session.WriteDirect(data); } /// @@ -143,6 +161,24 @@ public void SendAndReset() BufferEnd = origin + scratchSpace.Length; curHead = origin + len; } + + /// + public void WriteError(ReadOnlySpan error) + { + while (!RespWriteUtils.TryWriteError(error, ref BufferCur, BufferEnd)) + { + SendAndReset(); + } + } + + /// + public void WriteDirect(ReadOnlySpan data) + { + while (!RespWriteUtils.TryWriteDirect(data, ref BufferCur, BufferEnd)) + { + SendAndReset(); + } + } } private static (int Start, ulong[] ByteMask) NoScriptDetails = InitializeNoScriptDetails(); @@ -1156,8 +1192,7 @@ public unsafe void RunForSession(int count, RespServerSession outerSession) err = "Internal Lua Error"u8; } - while (!RespWriteUtils.TryWriteError(err, ref outerSession.dcurr, outerSession.dend)) - outerSession.SendAndReset(); + outerSession.WriteError(err); return; } @@ -1503,8 +1538,7 @@ private unsafe void RunCommon(ref TResponse resp) if (state.StackTop == 0) { - while (!RespWriteUtils.TryWriteError("ERR An error occurred while invoking a Lua script"u8, ref resp.BufferCur, resp.BufferEnd)) - resp.SendAndReset(); + resp.WriteError("ERR An error occurred while invoking a Lua script"u8); return; } @@ -1516,22 +1550,18 @@ private unsafe void RunCommon(ref TResponse resp) if (errBuf.Length >= 4 && MemoryMarshal.Read("ERR "u8) == Unsafe.As(ref MemoryMarshal.GetReference(errBuf))) { // Response came back with a ERR, already - just pass it along - while (!RespWriteUtils.TryWriteError(errBuf, ref resp.BufferCur, resp.BufferEnd)) - resp.SendAndReset(); + resp.WriteError(errBuf); } else { // Otherwise, this is probably a Lua error - and those aren't very descriptive // So slap some more information in - while (!RespWriteUtils.TryWriteDirect("-ERR Lua encountered an error: "u8, ref resp.BufferCur, resp.BufferEnd)) - resp.SendAndReset(); + resp.WriteDirect("-ERR Lua encountered an error: "u8); - while (!RespWriteUtils.TryWriteDirect(errBuf, ref resp.BufferCur, resp.BufferEnd)) - resp.SendAndReset(); + resp.WriteDirect(errBuf); - while (!RespWriteUtils.TryWriteDirect("\r\n"u8, ref resp.BufferCur, resp.BufferEnd)) - resp.SendAndReset(); + resp.WriteDirect("\r\n"u8); } state.Pop(1); @@ -1542,8 +1572,7 @@ private unsafe void RunCommon(ref TResponse resp) { logger?.LogError("Got an unexpected number of values back from a pcall error {callRes}", callRes); - while (!RespWriteUtils.TryWriteError("ERR Unexpected error response"u8, ref resp.BufferCur, resp.BufferEnd)) - resp.SendAndReset(); + resp.WriteError("ERR Unexpected error response"u8); state.ClearStack(); @@ -1648,8 +1677,7 @@ private unsafe void WriteResponse(ref TResponse resp) state.PushConstantString(err); state.KnownStringToBuffer(1, out var errBuff); - while (!RespWriteUtils.TryWriteError(errBuff, ref resp.BufferCur, resp.BufferEnd)) - resp.SendAndReset(); + resp.WriteError(errBuff); return; } @@ -1945,7 +1973,7 @@ static bool TryWriteSingleItem(LuaRunner runner, bool canSend, ref TResponse res } // Write out $-1\r\n (the RESP2 null) and (optionally) pop the null value off the stack, returning true if all fit in the current send buffer - static unsafe bool TryWriteResp2Null(LuaRunner runner, bool canSend, bool pop, ref TResponse resp, out int errConstStrIndex) + static bool TryWriteResp2Null(LuaRunner runner, bool canSend, bool pop, ref TResponse resp, out int errConstStrIndex) { var fitInBuffer = true; @@ -1973,7 +2001,7 @@ static unsafe bool TryWriteResp2Null(LuaRunner runner, bool canSend, bool pop, r } // Write out _\r\n (the RESP3 null) and (optionally) pop the null value off the stack, returning true if all fit in the current send buffer - static unsafe bool TryWriteResp3Null(LuaRunner runner, bool canSend, bool pop, ref TResponse resp, out int errConstStrIndex) + static bool TryWriteResp3Null(LuaRunner runner, bool canSend, bool pop, ref TResponse resp, out int errConstStrIndex) { var fitInBuffer = true; @@ -2001,7 +2029,7 @@ static unsafe bool TryWriteResp3Null(LuaRunner runner, bool canSend, bool pop, r } // Writes the number on the top of the stack, removes it from the stack, returning true if all fit in the current send buffer - static unsafe bool TryWriteNumber(LuaRunner runner, bool canSend, ref TResponse resp, out int errConstStrIndex) + static bool TryWriteNumber(LuaRunner runner, bool canSend, ref TResponse resp, out int errConstStrIndex) { Debug.Assert(runner.state.Type(runner.state.StackTop) == LuaType.Number, "Number was not on top of stack"); @@ -2033,7 +2061,7 @@ static unsafe bool TryWriteNumber(LuaRunner runner, bool canSend, ref TResponse } // Writes the string on the top of the stack, removes it from the stack, returning true if all fit in the current send buffer - static unsafe bool TryWriteString(LuaRunner runner, bool canSend, ref TResponse resp, out int errConstStrIndex) + static bool TryWriteString(LuaRunner runner, bool canSend, ref TResponse resp, out int errConstStrIndex) { runner.state.KnownStringToBuffer(runner.state.StackTop, out var buf); @@ -2107,7 +2135,7 @@ static unsafe bool TryWriteString(LuaRunner runner, bool canSend, ref TResponse } // Writes the boolean on the top of the stack, removes it from the stack, returning true if all fit in the current send buffer - static unsafe bool TryWriteResp3Boolean(LuaRunner runner, bool canSend, ref TResponse resp, out int errConstStrIndex) + static bool TryWriteResp3Boolean(LuaRunner runner, bool canSend, ref TResponse resp, out int errConstStrIndex) { Debug.Assert(runner.state.Type(runner.state.StackTop) == LuaType.Boolean, "Boolean was not on top of stack"); @@ -2154,7 +2182,7 @@ static unsafe bool TryWriteResp3Boolean(LuaRunner runner, bool canSend, ref TRes } // Writes the number on the top of the stack as a double, removes it from the stack, returning true if all fit in the current send buffer - static unsafe bool TryWriteDouble(LuaRunner runner, bool canSend, ref TResponse resp, out int errConstStrIndex) + static bool TryWriteDouble(LuaRunner runner, bool canSend, ref TResponse resp, out int errConstStrIndex) { Debug.Assert(runner.state.Type(runner.state.StackTop) == LuaType.Number, "Number was not on top of stack"); @@ -2183,7 +2211,7 @@ static unsafe bool TryWriteDouble(LuaRunner runner, bool canSend, ref TResponse } // Write a table on the top of the stack as a map, removes it from the stack, returning true if all fit in the current send buffer - static unsafe bool TryWriteMap(LuaRunner runner, bool canSend, ref TResponse resp, out int errConstStrIndex) + static bool TryWriteMap(LuaRunner runner, bool canSend, ref TResponse resp, out int errConstStrIndex) { Debug.Assert(runner.state.Type(runner.state.StackTop) == LuaType.Table, "Table was not on top of stack"); @@ -2268,7 +2296,7 @@ static unsafe bool TryWriteMap(LuaRunner runner, bool canSend, ref TResponse res } // Convert a table to an array, where each key-value pair is converted to 2 entries, returning true if all fit in the current send buffer - static unsafe bool TryWriteMapToArray(LuaRunner runner, bool canSend, ref TResponse resp, out int errConstStrIndex) + static bool TryWriteMapToArray(LuaRunner runner, bool canSend, ref TResponse resp, out int errConstStrIndex) { Debug.Assert(runner.state.Type(runner.state.StackTop) == LuaType.Table, "Table was not on top of stack"); @@ -2352,7 +2380,7 @@ static unsafe bool TryWriteMapToArray(LuaRunner runner, bool canSend, ref TRespo } // Write a table on the top of the stack as a set, removes it from the stack, returning true if all fit in the current send buffer - static unsafe bool TryWriteSet(LuaRunner runner, bool canSend, ref TResponse resp, out int errConstStrIndex) + static bool TryWriteSet(LuaRunner runner, bool canSend, ref TResponse resp, out int errConstStrIndex) { Debug.Assert(runner.state.Type(runner.state.StackTop) == LuaType.Table, "Table was not on top of stack"); @@ -2428,7 +2456,7 @@ static unsafe bool TryWriteSet(LuaRunner runner, bool canSend, ref TResponse res // Write a table on the top of the stack as an array that contains only the keys of the // table, then remove the table from the stack, returning true if all fit in the current send buffer - static unsafe bool TryWriteSetToArray(LuaRunner runner, bool canSend, ref TResponse resp, out int errConstStrIndex) + static bool TryWriteSetToArray(LuaRunner runner, bool canSend, ref TResponse resp, out int errConstStrIndex) { Debug.Assert(runner.state.Type(runner.state.StackTop) == LuaType.Table, "Table was not on top of stack"); @@ -2505,7 +2533,7 @@ static unsafe bool TryWriteSetToArray(LuaRunner runner, bool canSend, ref TRespo } // Writes the string on the top of the stack out as an error, removes the string from the stack, returning true if all fit in the current send buffer - static unsafe bool TryWriteError(LuaRunner runner, bool canSend, ref TResponse resp, out int errConstStrIndex) + static bool TryWriteError(LuaRunner runner, bool canSend, ref TResponse resp, out int errConstStrIndex) { runner.state.KnownStringToBuffer(runner.state.StackTop, out var errBuff); @@ -2532,7 +2560,7 @@ static unsafe bool TryWriteError(LuaRunner runner, bool canSend, ref TResponse r } // Writes the table on the top of the stack out as an array, removed table from the stack, returning true if all fit in the current send buffer - static unsafe bool TryWriteTableToArray(LuaRunner runner, bool canSend, ref TResponse resp, out int errConstStrIndex) + static bool TryWriteTableToArray(LuaRunner runner, bool canSend, ref TResponse resp, out int errConstStrIndex) { // Redis does not respect metatables, so RAW access is ok here Debug.Assert(runner.state.Type(runner.state.StackTop) == LuaType.Table, "Table was not on top of stack"); diff --git a/libs/server/Resp/AsyncProcessor.cs b/libs/server/Resp/AsyncProcessor.cs index ded40f09beb..59d7419d1f3 100644 --- a/libs/server/Resp/AsyncProcessor.cs +++ b/libs/server/Resp/AsyncProcessor.cs @@ -4,7 +4,6 @@ using System.Diagnostics; using System.Threading; using System.Threading.Tasks; -using Garnet.common; using Tsavorite.core; namespace Garnet.server @@ -103,8 +102,7 @@ async Task AsyncGetProcessorAsync(TGarnetApi storageApi) var o = completedOutputs.Current.Output; // We write async push response as an array: [ "async", "", "" ] - while (!RespWriteUtils.TryWritePushLength(3, ref dcurr, dend)) - SendAndReset(); + WritePushLength(3); WriteBulkString(CmdStrings.async); WriteInt32AsBulkString((int)completedOutputs.Current.Context); if (completedOutputs.Current.Status.Found) diff --git a/libs/server/Resp/MGetReadArgBatch.cs b/libs/server/Resp/MGetReadArgBatch.cs index 77bcfc36006..95837bc918f 100644 --- a/libs/server/Resp/MGetReadArgBatch.cs +++ b/libs/server/Resp/MGetReadArgBatch.cs @@ -6,7 +6,6 @@ using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using Garnet.common; using Tsavorite.core; namespace Garnet.server @@ -105,8 +104,7 @@ public readonly unsafe void SetOutput(int i, SpanByteAndMemory output) session.storageSession.incr_session_notfound(); // Not found, write a null out - while (!RespWriteUtils.TryWriteNull(ref session.dcurr, session.dend)) - session.SendAndReset(); + session.WriteNull(); } } } @@ -167,8 +165,7 @@ public readonly unsafe void SetOutput(int i, SpanByteAndMemory output) { if (pendingNullWrite) { - while (!RespWriteUtils.TryWriteNull(ref session.dcurr, session.dend)) - session.SendAndReset(); + session.WriteNull(); } else { @@ -324,8 +321,7 @@ public readonly unsafe void CompletePending(ref TGarnetApi storageAp else { // Did not find it, was probably synchronous but we couldn't handle it until now - while (!RespWriteUtils.TryWriteNull(ref session.dcurr, session.dend)) - session.SendAndReset(); + session.WriteNull(); } } } diff --git a/libs/server/Resp/Objects/ObjectStoreUtils.cs b/libs/server/Resp/Objects/ObjectStoreUtils.cs index 6e53e9a9fc9..deecf312829 100644 --- a/libs/server/Resp/Objects/ObjectStoreUtils.cs +++ b/libs/server/Resp/Objects/ObjectStoreUtils.cs @@ -45,7 +45,6 @@ private bool AbortWithWrongNumberOfArgumentsOrUnknownSubcommand(string subComman /// true if the command was completely consumed, false if the input on the receive buffer was incomplete. private bool AbortWithErrorMessage(ReadOnlySpan errorMessage) { - commandErrorWritten = true; // Print error message to result stream WriteError(errorMessage); diff --git a/libs/server/Resp/RespServerSessionOutput.cs b/libs/server/Resp/RespServerSessionOutput.cs index d106e339415..6f80ca8a1f9 100644 --- a/libs/server/Resp/RespServerSessionOutput.cs +++ b/libs/server/Resp/RespServerSessionOutput.cs @@ -19,7 +19,7 @@ internal sealed unsafe partial class RespServerSession : ServerSessionBase /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - private unsafe void ProcessOutput(SpanByteAndMemory output) + internal void ProcessOutput(SpanByteAndMemory output) { if (!output.IsSpanByte) SendAndReset(output.Memory, output.Length); @@ -28,42 +28,42 @@ private unsafe void ProcessOutput(SpanByteAndMemory output) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WriteAsciiBulkString(ReadOnlySpan message) + internal void WriteAsciiBulkString(ReadOnlySpan message) { while (!RespWriteUtils.TryWriteAsciiBulkString(message, ref dcurr, dend)) SendAndReset(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WriteAsciiDirect(ReadOnlySpan message) + internal void WriteAsciiDirect(ReadOnlySpan message) { while (!RespWriteUtils.TryWriteAsciiDirect(message, ref dcurr, dend)) SendAndReset(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WriteArrayLength(int len) + internal void WriteArrayLength(int len) { while (!RespWriteUtils.TryWriteArrayLength(len, ref dcurr, dend)) SendAndReset(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WriteArrayItem(long recordsExpired) + internal void WriteArrayItem(long recordsExpired) { while (!RespWriteUtils.TryWriteArrayItem(recordsExpired, ref dcurr, dend)) SendAndReset(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WriteBulkString(scoped ReadOnlySpan message) + internal void WriteBulkString(scoped ReadOnlySpan message) { while (!RespWriteUtils.TryWriteBulkString(message, ref dcurr, dend)) SendAndReset(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WriteDirectLargeRespString(ReadOnlySpan message) + internal void WriteDirectLargeRespString(ReadOnlySpan message) { while (!RespWriteUtils.TryWriteBulkStringLength(message, ref dcurr, dend)) SendAndReset(); @@ -75,14 +75,14 @@ private void WriteDirectLargeRespString(ReadOnlySpan message) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WriteDirect(scoped ReadOnlySpan span) + internal void WriteDirect(scoped ReadOnlySpan span) { while (!RespWriteUtils.TryWriteDirect(span, ref dcurr, dend)) SendAndReset(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WriteDoubleNumeric(double value) + internal void WriteDoubleNumeric(double value) { if (respProtocolVersion >= 3) { @@ -97,14 +97,14 @@ private void WriteDoubleNumeric(double value) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WriteEmptyArray() + internal void WriteEmptyArray() { while (!RespWriteUtils.TryWriteEmptyArray(ref dcurr, dend)) SendAndReset(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WriteEmptySet() + internal void WriteEmptySet() { if (respProtocolVersion >= 3) { @@ -118,14 +118,14 @@ private void WriteEmptySet() } } - private void WriteError(scoped ReadOnlySpan errorString) + internal void WriteError(scoped ReadOnlySpan errorString) { commandErrorWritten = true; while (!RespWriteUtils.TryWriteError(errorString, ref dcurr, dend)) SendAndReset(); } - private void WriteError(ReadOnlySpan errorString) + internal void WriteError(ReadOnlySpan errorString) { commandErrorWritten = true; while (!RespWriteUtils.TryWriteError(errorString, ref dcurr, dend)) @@ -133,42 +133,42 @@ private void WriteError(ReadOnlySpan errorString) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WriteInt32(int value) + internal void WriteInt32(int value) { while (!RespWriteUtils.TryWriteInt32(value, ref dcurr, dend)) SendAndReset(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WriteInt32AsBulkString(int value) + internal void WriteInt32AsBulkString(int value) { while (!RespWriteUtils.TryWriteInt32AsBulkString(value, ref dcurr, dend)) SendAndReset(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WriteInt64(long value) + internal void WriteInt64(long value) { while (!RespWriteUtils.TryWriteInt64(value, ref dcurr, dend)) SendAndReset(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WriteInt64AsBulkString(long value) + internal void WriteInt64AsBulkString(long value) { while (!RespWriteUtils.TryWriteInt64AsBulkString(value, ref dcurr, dend, out _)) SendAndReset(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WriteIntegerFromBytes(ReadOnlySpan integerBytes) + internal void WriteIntegerFromBytes(ReadOnlySpan integerBytes) { while (!RespWriteUtils.TryWriteIntegerFromBytes(integerBytes, ref dcurr, dend)) SendAndReset(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WriteMapLength(int count) + internal void WriteMapLength(int count) { if (respProtocolVersion >= 3) { @@ -183,21 +183,21 @@ private void WriteMapLength(int count) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WriteZero() + internal void WriteZero() { while (!RespWriteUtils.TryWriteZero(ref dcurr, dend)) SendAndReset(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WriteOne() + internal void WriteOne() { while (!RespWriteUtils.TryWriteOne(ref dcurr, dend)) SendAndReset(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WriteNull() + internal void WriteNull() { if (respProtocolVersion >= 3) { @@ -212,7 +212,7 @@ private void WriteNull() } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WriteNullArray() + internal void WriteNullArray() { if (respProtocolVersion >= 3) { @@ -227,7 +227,7 @@ private void WriteNullArray() } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WritePushLength(int count) + internal void WritePushLength(int count) { if (respProtocolVersion >= 3) { @@ -242,7 +242,7 @@ private void WritePushLength(int count) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WriteSetLength(int count) + internal void WriteSetLength(int count) { if (respProtocolVersion >= 3) { @@ -257,14 +257,14 @@ private void WriteSetLength(int count) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WriteSimpleString(scoped ReadOnlySpan simpleString) + internal void WriteSimpleString(scoped ReadOnlySpan simpleString) { while (!RespWriteUtils.TryWriteSimpleString(simpleString, ref dcurr, dend)) SendAndReset(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WriteSimpleString(ReadOnlySpan simpleString) + internal void WriteSimpleString(ReadOnlySpan simpleString) { while (!RespWriteUtils.TryWriteSimpleString(simpleString, ref dcurr, dend)) SendAndReset(); @@ -272,14 +272,14 @@ private void WriteSimpleString(ReadOnlySpan simpleString) [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WriteUtf8BulkString(ReadOnlySpan chars) + internal void WriteUtf8BulkString(ReadOnlySpan chars) { while (!RespWriteUtils.TryWriteUtf8BulkString(chars, ref dcurr, dend)) SendAndReset(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WriteVerbatimString(scoped ReadOnlySpan item, scoped ReadOnlySpan ext = default) + internal void WriteVerbatimString(scoped ReadOnlySpan item, scoped ReadOnlySpan ext = default) { if (respProtocolVersion >= 3) { From 54f74a5595f07bb0a1c1648990c76af418f3061a Mon Sep 17 00:00:00 2001 From: Kevin Montrose Date: Thu, 7 May 2026 11:53:34 -0400 Subject: [PATCH 05/16] cleanup errors a bit before diving into analyzers --- libs/server/Lua/LuaCommands.cs | 2 +- libs/server/Resp/ACLCommands.cs | 10 +++++----- libs/server/Resp/PurgeBPCommand.cs | 2 +- libs/server/Resp/RespServerSessionOutput.cs | 15 +++++++++++++++ 4 files changed, 22 insertions(+), 7 deletions(-) diff --git a/libs/server/Lua/LuaCommands.cs b/libs/server/Lua/LuaCommands.cs index 3e8c13f3b58..0d9936f20b9 100644 --- a/libs/server/Lua/LuaCommands.cs +++ b/libs/server/Lua/LuaCommands.cs @@ -357,7 +357,7 @@ private bool TryExecuteScript(int count, LuaRunner scriptRunner) catch (Exception ex) { logger?.LogError(ex, "Error executing Lua script"); - WriteError("ERR " + ex.Message); + WriteError(ex); // Exceptions shouldn't happen, so if they did the runner is probably in a bad state return false; diff --git a/libs/server/Resp/ACLCommands.cs b/libs/server/Resp/ACLCommands.cs index 847532de186..20346905771 100644 --- a/libs/server/Resp/ACLCommands.cs +++ b/libs/server/Resp/ACLCommands.cs @@ -189,7 +189,7 @@ private bool NetworkAclSetUser() catch (ACLException exception) { // Abort command execution - WriteError($"ERR {exception.Message}"); + WriteError(exception); return true; } @@ -235,7 +235,7 @@ private bool NetworkAclDelUser() logger?.LogDebug("ACLException: {message}", exception.Message); // Abort command execution - WriteError($"ERR {exception.Message}"); + WriteError(exception); return true; } @@ -304,7 +304,7 @@ private bool NetworkAclLoad() } catch (ACLException exception) { - WriteError($"ERR {exception.Message}"); + WriteError(exception); } return true; @@ -340,7 +340,7 @@ private bool NetworkAclSave() catch (Exception ex) { logger?.LogError(ex, "ACL SAVE faulted"); - WriteError($"ERR {ex.Message}"); + WriteError(ex); return true; } @@ -415,7 +415,7 @@ private bool NetworkAclGetUser() catch (ACLException exception) { // Abort command execution - WriteError($"ERR {exception.Message}"); + WriteError(exception); return true; } diff --git a/libs/server/Resp/PurgeBPCommand.cs b/libs/server/Resp/PurgeBPCommand.cs index f1a59166cd2..24fe2ab2bf6 100644 --- a/libs/server/Resp/PurgeBPCommand.cs +++ b/libs/server/Resp/PurgeBPCommand.cs @@ -85,7 +85,7 @@ private bool NetworkPurgeBP() catch (Exception ex) { logger?.LogError(ex, "PURGEBP {type}:{managerType}", managerType, managerType.ToString()); - WriteError($"ERR {ex.Message}"); + WriteError(ex); return true; } diff --git a/libs/server/Resp/RespServerSessionOutput.cs b/libs/server/Resp/RespServerSessionOutput.cs index 6f80ca8a1f9..a1bc96ce68e 100644 --- a/libs/server/Resp/RespServerSessionOutput.cs +++ b/libs/server/Resp/RespServerSessionOutput.cs @@ -132,6 +132,21 @@ internal void WriteError(ReadOnlySpan errorString) SendAndReset(); } + internal void WriteError(Exception ex) + { + commandErrorWritten = true; + while (!RespWriteUtils.TryWriteDirect("-ERR "u8, ref dcurr, dend)) + SendAndReset(); + + var msg = ex.Message; + var msgBytes = System.Text.Encoding.UTF8.GetBytes(msg); + while (!RespWriteUtils.TryWriteDirect(msgBytes, ref dcurr, dend)) + SendAndReset(); + + while (!RespWriteUtils.TryWriteNewLine(ref dcurr, dend)) + SendAndReset(); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void WriteInt32(int value) { From 7f34e493b5a95ca7e87292d357ba9e55aacbac9e Mon Sep 17 00:00:00 2001 From: Kevin Montrose Date: Thu, 7 May 2026 13:24:28 -0400 Subject: [PATCH 06/16] add analyzer GARNET0001 which flags uses of RespWriteUtils when there's an equivalent version in RespServerSessionOutput.cs --- Directory.Packages.props | 1 + Garnet.slnx | 13 +- .../AvoidRespWriteUtilsAnalyzer.cs | 151 ++++++++++++++++++ .../Garnet.analyzers/Garnet.analyzers.csproj | 13 ++ libs/server/ArgSlice/ScratchBufferBuilder.cs | 2 + libs/server/Custom/CustomProcedureBase.cs | 2 + libs/server/Garnet.server.csproj | 3 +- libs/server/Lua/LuaRunner.cs | 9 +- .../Functions/MainStore/PrivateMethods.cs | 2 + .../Functions/ObjectStore/PrivateMethods.cs | 2 + 10 files changed, 189 insertions(+), 9 deletions(-) create mode 100644 analyzer/Garnet.analyzers/AvoidRespWriteUtilsAnalyzer.cs create mode 100644 analyzer/Garnet.analyzers/Garnet.analyzers.csproj diff --git a/Directory.Packages.props b/Directory.Packages.props index b6059652e5d..e933fb6f406 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -13,6 +13,7 @@ + diff --git a/Garnet.slnx b/Garnet.slnx index 59c0a0b3cb3..84dbfcb940d 100644 --- a/Garnet.slnx +++ b/Garnet.slnx @@ -1,4 +1,5 @@ + @@ -12,11 +13,15 @@ + + + + @@ -29,12 +34,8 @@ - - - - - - + + diff --git a/analyzer/Garnet.analyzers/AvoidRespWriteUtilsAnalyzer.cs b/analyzer/Garnet.analyzers/AvoidRespWriteUtilsAnalyzer.cs new file mode 100644 index 00000000000..6495df4fe45 --- /dev/null +++ b/analyzer/Garnet.analyzers/AvoidRespWriteUtilsAnalyzer.cs @@ -0,0 +1,151 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +using System.Collections.Generic; +using System.Collections.Immutable; +using System.IO; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Garnet.analyzers +{ + /// + /// Analyzer which flags uses of RespWriteUtils outside of the RespServerSessionOutput helper methods. + /// + /// The intent is to force consistency in output for handling large outputs, error tracking, constant use, etc. + /// + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public sealed class AvoidRespWriteUtilsAnalyzer : DiagnosticAnalyzer + { + private static DiagnosticDescriptor AvoidRespWriteUtils { get; } = new("GARNET0001", "Avoid direct use of RespWriteUtils", "If possible use RespServerSession.{0} instead of RespWriteUtils.{1}", "Correctness", DiagnosticSeverity.Warning, isEnabledByDefault: true); + + /// + public override ImmutableArray SupportedDiagnostics { get; } = [AvoidRespWriteUtils]; + + /// + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.ReportDiagnostics | GeneratedCodeAnalysisFlags.Analyze); + context.EnableConcurrentExecution(); + + context.RegisterCompilationStartAction( + static compilationAction => + { + var respWriteUtilsType = compilationAction.Compilation.GetTypeByMetadataName("Garnet.common.RespWriteUtils"); + var respServerSessionType = compilationAction.Compilation.GetTypeByMetadataName("Garnet.server.RespServerSession"); + + if (respWriteUtilsType is not null && respServerSessionType is not null) + { + var suggestionLookup = BuildLookup(respWriteUtilsType, respServerSessionType); + + compilationAction.RegisterSyntaxNodeAction(syntaxNodeContext => AnalyzeCaller(syntaxNodeContext, respWriteUtilsType, suggestionLookup), SyntaxKind.InvocationExpression); + } + } + ); + + // Build a map of TryWriteXXX methods on RespWriteUtils -> WriteXXX methods on RespServerSession + static Dictionary BuildLookup(INamedTypeSymbol respWriteUtilsType, INamedTypeSymbol respServerSessionType) + { + var tryWriteMethods = new HashSet(); + + foreach (var member in respWriteUtilsType.GetMembers()) + { + if (member is not IMethodSymbol mtdSymbol) + { + continue; + } + + if (!member.Name.StartsWith("TryWrite")) + { + continue; + } + + tryWriteMethods.Add(member.Name); + } + + var lookup = new Dictionary(); + foreach (var member in respServerSessionType.GetMembers()) + { + if (member is not IMethodSymbol mtdSymbol) + { + continue; + } + + // Check that method is declared in RespServerSessionOutput.cs, otherwise we don't consider it a candidate for flagging + if (!member.DeclaringSyntaxReferences.Any(static decl => Path.GetFileName(decl.SyntaxTree.FilePath) == "RespServerSessionOutput.cs")) + { + continue; + } + + if (!member.Name.StartsWith("Write")) + { + continue; + } + + var tryEquivalent = $"Try{member.Name}"; + if (!tryWriteMethods.Contains(tryEquivalent)) + { + continue; + } + + lookup[tryEquivalent] = member.Name; + } + + return lookup; + } + } + + /// + /// Flag all calls to methods looking like RespWriteUtils.TryXXX + /// + private static void AnalyzeCaller(SyntaxNodeAnalysisContext context, INamedTypeSymbol respWriteUtilsType, Dictionary candidateLookup) + { + if (context.Node is not InvocationExpressionSyntax invoke) + { + return; + } + + if (invoke.Expression is not MemberAccessExpressionSyntax memberAccess) + { + return; + } + + if (memberAccess.Name is not IdentifierNameSyntax methodName) + { + return; + } + + // Quickly filter out anything that doesn't look like a TryXXX method + if (string.IsNullOrEmpty(methodName.Identifier.Text) || !methodName.Identifier.Text.StartsWith("Try")) + { + return; + } + + // Filter out anything in RespServerSessionOutput.cs, as it's intended to use these methods + if (Path.GetFileName(context.Node.SyntaxTree.FilePath) == "RespServerSessionOutput.cs") + { + return; + } + + // Ignore anything that isn't a call to RespWriteUtils + var leftHandType = context.SemanticModel.GetSymbolInfo(memberAccess.Expression); + if (leftHandType.Symbol is null || !SymbolEqualityComparer.Default.Equals(leftHandType.Symbol, respWriteUtilsType)) + { + return; + } + + // Lookup equivalent method, and raise diagnostic + if (!candidateLookup.TryGetValue(methodName.Identifier.Text, out var rewriteTo)) + { + return; + } + + // Raise the actual diagnostic + var diag = Diagnostic.Create(AvoidRespWriteUtils, context.Node.GetLocation(), rewriteTo, methodName.Identifier.Text); + context.ReportDiagnostic(diag); + } + } +} diff --git a/analyzer/Garnet.analyzers/Garnet.analyzers.csproj b/analyzer/Garnet.analyzers/Garnet.analyzers.csproj new file mode 100644 index 00000000000..72905e32202 --- /dev/null +++ b/analyzer/Garnet.analyzers/Garnet.analyzers.csproj @@ -0,0 +1,13 @@ + + + + netstandard2.0 + true + $(NoWarn);RS2008 + + + + + + + diff --git a/libs/server/ArgSlice/ScratchBufferBuilder.cs b/libs/server/ArgSlice/ScratchBufferBuilder.cs index 5decbd1e221..3630f399c58 100644 --- a/libs/server/ArgSlice/ScratchBufferBuilder.cs +++ b/libs/server/ArgSlice/ScratchBufferBuilder.cs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. +#pragma warning disable GARNET0001 // This manually manages a buffer, so calls to RespWriteUtils are fine + using System; using System.Diagnostics; using System.Numerics; diff --git a/libs/server/Custom/CustomProcedureBase.cs b/libs/server/Custom/CustomProcedureBase.cs index 5e18e42971c..3ba768088e0 100644 --- a/libs/server/Custom/CustomProcedureBase.cs +++ b/libs/server/Custom/CustomProcedureBase.cs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. +#pragma warning disable GARNET0001 // This manually manages a buffer, so calls to RespWriteUtils are fine + using System; using System.Buffers; using System.Collections.Generic; diff --git a/libs/server/Garnet.server.csproj b/libs/server/Garnet.server.csproj index de66b80f463..4a137173748 100644 --- a/libs/server/Garnet.server.csproj +++ b/libs/server/Garnet.server.csproj @@ -8,6 +8,7 @@ + @@ -23,7 +24,7 @@ - + \ No newline at end of file diff --git a/libs/server/Lua/LuaRunner.cs b/libs/server/Lua/LuaRunner.cs index 6108baf7971..be83ba24f0d 100644 --- a/libs/server/Lua/LuaRunner.cs +++ b/libs/server/Lua/LuaRunner.cs @@ -162,6 +162,8 @@ public void SendAndReset() curHead = origin + len; } +#pragma warning disable GARNET0001 // These methods manually manage a buffer, so must directly use RespWriteUtils + /// public void WriteError(ReadOnlySpan error) { @@ -179,6 +181,7 @@ public void WriteDirect(ReadOnlySpan data) SendAndReset(); } } +#pragma warning restore GARNET0001 } private static (int Start, ulong[] ByteMask) NoScriptDetails = InitializeNoScriptDetails(); @@ -504,8 +507,7 @@ public unsafe bool CompileForSession(RespServerSession session) var res = state.PCall(0, 0); if (res != LuaStatus.OK) { - while (!RespWriteUtils.TryWriteError("Internal Lua Error"u8, ref session.dcurr, session.dend)) - session.SendAndReset(); + session.WriteError("Internal Lua Error"u8); return false; } @@ -1972,6 +1974,8 @@ static bool TryWriteSingleItem(LuaRunner runner, bool canSend, ref TResponse res return true; } +#pragma warning disable GARNET0001 // These methods have to track extra state, and so can directly use RespWriteUtils + // Write out $-1\r\n (the RESP2 null) and (optionally) pop the null value off the stack, returning true if all fit in the current send buffer static bool TryWriteResp2Null(LuaRunner runner, bool canSend, bool pop, ref TResponse resp, out int errConstStrIndex) { @@ -2627,6 +2631,7 @@ static bool TryWriteTableToArray(LuaRunner runner, bool canSend, ref TResponse r errConstStrIndex = -1; return fitInBuffer; } +#pragma warning restore GARNET0001 } /// diff --git a/libs/server/Storage/Functions/MainStore/PrivateMethods.cs b/libs/server/Storage/Functions/MainStore/PrivateMethods.cs index c0168ebd614..bc6343c46c8 100644 --- a/libs/server/Storage/Functions/MainStore/PrivateMethods.cs +++ b/libs/server/Storage/Functions/MainStore/PrivateMethods.cs @@ -4,6 +4,7 @@ using System; using System.Buffers; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using Garnet.common; using Tsavorite.core; @@ -648,6 +649,7 @@ void CopyDefaultResp(ReadOnlySpan resp, ref SpanByteAndMemory dst) resp.CopyTo(dst.Memory.Memory.Span); } + [SuppressMessage("Correctness", "GARNET0001", Justification = "Manually managing a buffer, has to use RespWriteUtils directly")] void CopyRespNumber(long number, ref SpanByteAndMemory dst) { byte* curr = dst.SpanByte.ToPointer(); diff --git a/libs/server/Storage/Functions/ObjectStore/PrivateMethods.cs b/libs/server/Storage/Functions/ObjectStore/PrivateMethods.cs index 44b7e82d1b4..94ce10c942d 100644 --- a/libs/server/Storage/Functions/ObjectStore/PrivateMethods.cs +++ b/libs/server/Storage/Functions/ObjectStore/PrivateMethods.cs @@ -4,6 +4,7 @@ using System; using System.Buffers; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using Garnet.common; using Tsavorite.core; @@ -86,6 +87,7 @@ void WriteLogDelete(ref byte[] key, long version, int sessionID, internal static bool CheckExpiry(IGarnetObject src) => src.Expiration < DateTimeOffset.UtcNow.Ticks; + [SuppressMessage("Correctness", "GARNET0001", Justification = "Manually managing a buffer, has to use RespWriteUtils directly")] static void CopyRespNumber(long number, ref SpanByteAndMemory dst) { byte* curr = dst.SpanByte.ToPointer(); From 975007cb5fe18e8b00ba2b7793d470484dfd31ab Mon Sep 17 00:00:00 2001 From: Kevin Montrose Date: Thu, 7 May 2026 15:35:07 -0400 Subject: [PATCH 07/16] start flagging cases where output isn't obviously bounded --- .../AvoidRespWriteUtilsAnalyzer.cs | 8 +- ...seLargeOrConstantsForRespWritesAnalyzer.cs | 208 ++++++++++++++++++ .../Metrics/Slowlog/RespSlowlogCommands.cs | 6 + libs/server/Resp/ArrayCommands.cs | 4 +- libs/server/Resp/BasicCommands.cs | 2 +- libs/server/Resp/KeyAdminCommands.cs | 2 +- libs/server/Resp/PubSubCommands.cs | 10 +- libs/server/Resp/RespServerSession.cs | 2 +- libs/server/Resp/RespServerSessionOutput.cs | 4 +- 9 files changed, 230 insertions(+), 16 deletions(-) create mode 100644 analyzer/Garnet.analyzers/UseLargeOrConstantsForRespWritesAnalyzer.cs diff --git a/analyzer/Garnet.analyzers/AvoidRespWriteUtilsAnalyzer.cs b/analyzer/Garnet.analyzers/AvoidRespWriteUtilsAnalyzer.cs index 6495df4fe45..32015543a9d 100644 --- a/analyzer/Garnet.analyzers/AvoidRespWriteUtilsAnalyzer.cs +++ b/analyzer/Garnet.analyzers/AvoidRespWriteUtilsAnalyzer.cs @@ -32,16 +32,16 @@ public override void Initialize(AnalysisContext context) context.EnableConcurrentExecution(); context.RegisterCompilationStartAction( - static compilationAction => + static compilationStartContext => { - var respWriteUtilsType = compilationAction.Compilation.GetTypeByMetadataName("Garnet.common.RespWriteUtils"); - var respServerSessionType = compilationAction.Compilation.GetTypeByMetadataName("Garnet.server.RespServerSession"); + var respWriteUtilsType = compilationStartContext.Compilation.GetTypeByMetadataName("Garnet.common.RespWriteUtils"); + var respServerSessionType = compilationStartContext.Compilation.GetTypeByMetadataName("Garnet.server.RespServerSession"); if (respWriteUtilsType is not null && respServerSessionType is not null) { var suggestionLookup = BuildLookup(respWriteUtilsType, respServerSessionType); - compilationAction.RegisterSyntaxNodeAction(syntaxNodeContext => AnalyzeCaller(syntaxNodeContext, respWriteUtilsType, suggestionLookup), SyntaxKind.InvocationExpression); + compilationStartContext.RegisterSyntaxNodeAction(syntaxNodeContext => AnalyzeCaller(syntaxNodeContext, respWriteUtilsType, suggestionLookup), SyntaxKind.InvocationExpression); } } ); diff --git a/analyzer/Garnet.analyzers/UseLargeOrConstantsForRespWritesAnalyzer.cs b/analyzer/Garnet.analyzers/UseLargeOrConstantsForRespWritesAnalyzer.cs new file mode 100644 index 00000000000..69cf1c3b527 --- /dev/null +++ b/analyzer/Garnet.analyzers/UseLargeOrConstantsForRespWritesAnalyzer.cs @@ -0,0 +1,208 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +using System.Collections.Generic; +using System.Collections.Immutable; +using System.IO; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Garnet.analyzers +{ + /// + /// A number of analyzers in here to keep us honest about large responses and using CmdStrings + /// - Flag any call to WriteXXX methods in RespServerSessionOutput.cs which are variable size UNLESS they call a method with Large in its name. + /// - Flag constants pass to WriteXXX methods which are not placed in CmdStrings classes + /// - Flag constants which are larger than minimum send buffer sizes + /// + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public sealed class UseLargeOrConstantsForRespWritesAnalyzer : DiagnosticAnalyzer + { + private static DiagnosticDescriptor UseLargeOverridesWithVariableSizeResponses { get; } = new("GARNET0002", "Use Large Overrides With Variable Size Responses", "Use {0} instead of {1} for variable size responses", "Correctness", DiagnosticSeverity.Warning, isEnabledByDefault: true); + private static DiagnosticDescriptor AddLargeOverridesForVariableSizeResponses { get; } = new("GARNET0002", "Add Large Override For Variable Size Responses", "Add and use {0} to RespServerSessionOutput.cs in place of using {1} for variable size responses", "Correctness", DiagnosticSeverity.Warning, isEnabledByDefault: true); + + /// + public override ImmutableArray SupportedDiagnostics { get; } = [UseLargeOverridesWithVariableSizeResponses, AddLargeOverridesForVariableSizeResponses]; + + /// + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.ReportDiagnostics | GeneratedCodeAnalysisFlags.Analyze); + context.EnableConcurrentExecution(); + + context.RegisterCompilationStartAction( + static compilationStartContext => + { + var respServerSessionType = compilationStartContext.Compilation.GetTypeByMetadataName("Garnet.server.RespServerSession"); + var cmdStringType = compilationStartContext.Compilation.GetTypeByMetadataName("Garnet.server.CmdStrings"); + + var byteType = compilationStartContext.Compilation.GetSpecialType(SpecialType.System_Byte); + var charType = compilationStartContext.Compilation.GetSpecialType(SpecialType.System_Char); + var stringType = compilationStartContext.Compilation.GetSpecialType(SpecialType.System_String); + + var spanType = compilationStartContext.Compilation.GetTypeByMetadataName("System.Span`1"); + var readOnlySpanType = compilationStartContext.Compilation.GetTypeByMetadataName("System.ReadOnlySpan`1"); + + var spanByteType = spanType.Construct(byteType); + var spanCharType = spanType.Construct(charType); + + var readOnlySpanByteType = readOnlySpanType.Construct(byteType); + var readOnlySpanCharType = readOnlySpanType.Construct(charType); + + var byteArrayType = compilationStartContext.Compilation.CreateArrayTypeSymbol(byteType); + var charArrayType = compilationStartContext.Compilation.CreateArrayTypeSymbol(charType); + + HashSet variableLengthTypes = + new(SymbolEqualityComparer.Default) + { + spanByteType, + spanCharType, + readOnlySpanByteType, + readOnlySpanCharType, + stringType, + byteArrayType, + charArrayType, + }; + + if (respServerSessionType is not null && cmdStringType is not null) + { + var suggestionLookup = BuildLookup(respServerSessionType); + + compilationStartContext.RegisterSyntaxNodeAction( + syntaxNodeContext => + { + AnalyzeCallerForVariableSizeViolations(syntaxNodeContext, cmdStringType, variableLengthTypes, suggestionLookup); + }, + SyntaxKind.InvocationExpression + ); + } + } + ); + + // Build a map of WriteXXX -> WriteLargeXXX methods on RespServerSession + static Dictionary BuildLookup(INamedTypeSymbol respServerSessionType) + { + var writeMethods = new HashSet(); + var writeLargeMethods = new HashSet(); + foreach (var member in respServerSessionType.GetMembers()) + { + if (member is not IMethodSymbol mtdSymbol) + { + continue; + } + + // Check that method is declared in RespServerSessionOutput.cs, otherwise we don't consider it a candidate for flagging + if (!member.DeclaringSyntaxReferences.Any(static decl => Path.GetFileName(decl.SyntaxTree.FilePath) == "RespServerSessionOutput.cs")) + { + continue; + } + + if (member.Name.StartsWith("WriteLarge")) + { + _ = writeLargeMethods.Add(member.Name); + } + else if (member.Name.StartsWith("Write")) + { + _ = writeMethods.Add(member.Name); + } + } + + var lookup = new Dictionary(); + + foreach (var largeMtd in writeLargeMethods) + { + var writeEquiv = $"Write{largeMtd.Substring("WriteLarge".Length)}"; + + if (writeMethods.Contains(writeEquiv)) + { + lookup[writeEquiv] = largeMtd; + } + } + + return lookup; + } + } + + /// + /// Raises UseLargeOverridesWithVariableSizeResponses and AddLargeOverridesForVariableSizeResponses. + /// + /// Finds any calls to WriteXXX where the input is variable length type AND not a constant, suggests uing WriteLargeXXX instead (or implementing it if it's not avaiable). + /// + private static void AnalyzeCallerForVariableSizeViolations(SyntaxNodeAnalysisContext context, INamedTypeSymbol cmdStringsType, HashSet variableLengthTypes, Dictionary largeEquivLookup) + { + if (context.Node is not InvocationExpressionSyntax invoke) + { + return; + } + + if (invoke.Expression is not IdentifierNameSyntax methodName) + { + return; + } + + // Quickly filter out anything that doesn't look like a plain Write method + if (string.IsNullOrEmpty(methodName.Identifier.Text) || !methodName.Identifier.Text.StartsWith("Write") || methodName.Identifier.Text.StartsWith("WriteLarge")) + { + return; + } + + // Ignore anything that isn't a call to a method in RespServerSessionOutput.cs + var method = context.SemanticModel.GetSymbolInfo(methodName); + if (method.Symbol is not IMethodSymbol methodSymbol || !method.Symbol.DeclaringSyntaxReferences.Any(static decl => Path.GetFileName(decl.SyntaxTree.FilePath) == "RespServerSessionOutput.cs")) + { + return; + } + + // Skip anything without parameters or arguments + if (methodSymbol.Parameters.Length == 0 || invoke.ArgumentList.Arguments.Count == 0) + { + return; + } + + // Skip anything where the "to write" parameter isn't a variable length type + var param0 = methodSymbol.Parameters[0]; + var isVariableLengthType = variableLengthTypes.Contains(param0.Type); + if (!isVariableLengthType) + { + return; + } + + // If the syntax tree or semantic thinks is a constant, just roll with it - other analyzers will refine this further + var arg0 = invoke.ArgumentList.Arguments[0]; + var isConstant = arg0.Expression is LiteralExpressionSyntax || context.SemanticModel.GetConstantValue(arg0).HasValue; + if (isConstant) + { + return; + } + + // Skip anything that references a CmdStrings property + if (arg0.Expression is MemberAccessExpressionSyntax argMemberAccess) + { + var leftType = context.SemanticModel.GetSymbolInfo(argMemberAccess.Expression); + var rightType = context.SemanticModel.GetSymbolInfo(argMemberAccess.Name); + if (leftType.Symbol is INamedTypeSymbol type && SymbolEqualityComparer.Default.Equals(type, cmdStringsType) && rightType.Symbol is IPropertySymbol) + { + return; + } + } + + // Raise the diagnostic, based on whether we have an existing override to use + Diagnostic diag; + if (largeEquivLookup.TryGetValue(methodName.Identifier.Text, out var largeMtdToUse)) + { + diag = Diagnostic.Create(UseLargeOverridesWithVariableSizeResponses, methodName.GetLocation(), largeMtdToUse, methodName.Identifier.Text); + } + else + { + var largeEquivMtdName = $"WriteLarge{methodName.Identifier.Text.Substring("Write".Length)}"; + + diag = Diagnostic.Create(AddLargeOverridesForVariableSizeResponses, methodName.GetLocation(), largeEquivMtdName, methodName.Identifier.Text); + } + + context.ReportDiagnostic(diag); + } + } +} diff --git a/libs/server/Metrics/Slowlog/RespSlowlogCommands.cs b/libs/server/Metrics/Slowlog/RespSlowlogCommands.cs index f916d42b1f2..150bec7f320 100644 --- a/libs/server/Metrics/Slowlog/RespSlowlogCommands.cs +++ b/libs/server/Metrics/Slowlog/RespSlowlogCommands.cs @@ -84,6 +84,12 @@ private bool NetworkSlowLogGet() WriteAsciiBulkString(entry.Command.ToString()); for (int i = 0; i < sps.Count; i++) { + // Hack added for demonstration + WriteAsciiBulkString("hello world"); + WriteBulkString("hello world"u8); + // End hack + + // Already existing WriteAsciiBulkString(sps.GetString(i)); } } diff --git a/libs/server/Resp/ArrayCommands.cs b/libs/server/Resp/ArrayCommands.cs index d90eaf1e679..7eb13351240 100644 --- a/libs/server/Resp/ArrayCommands.cs +++ b/libs/server/Resp/ArrayCommands.cs @@ -344,7 +344,7 @@ private void WriteOutputForScan(long cursorValue, List keys) // Write the keys matching the pattern for (int i = 0; i < keys.Count; i++) { - WriteDirectLargeRespString(keys[i]); + WriteLargeDirectRespString(keys[i]); } } @@ -362,7 +362,7 @@ private bool NetworkArrayPING() } var message = parseState.GetArgSliceByRef(0).ReadOnlySpan; - WriteDirectLargeRespString(message); + WriteLargeDirectRespString(message); return true; } diff --git a/libs/server/Resp/BasicCommands.cs b/libs/server/Resp/BasicCommands.cs index 111d4d2d748..f46666d7228 100644 --- a/libs/server/Resp/BasicCommands.cs +++ b/libs/server/Resp/BasicCommands.cs @@ -1231,7 +1231,7 @@ private bool NetworkECHO() } var message = parseState.GetArgSliceByRef(0).ReadOnlySpan; - WriteDirectLargeRespString(message); + WriteLargeDirectRespString(message); return true; } diff --git a/libs/server/Resp/KeyAdminCommands.cs b/libs/server/Resp/KeyAdminCommands.cs index ddf5301fd6b..0401974386a 100644 --- a/libs/server/Resp/KeyAdminCommands.cs +++ b/libs/server/Resp/KeyAdminCommands.cs @@ -199,7 +199,7 @@ bool NetworkDUMP(ref TGarnetApi storageApi) if (rentedBuffer is not null) { - WriteDirectLarge(buffer.Slice(0, totalLength)); + WriteLargeDirect(buffer.Slice(0, totalLength)); ArrayPool.Shared.Return(rentedBuffer); } else diff --git a/libs/server/Resp/PubSubCommands.cs b/libs/server/Resp/PubSubCommands.cs index f9d8eb2e545..8a85f7128f8 100644 --- a/libs/server/Resp/PubSubCommands.cs +++ b/libs/server/Resp/PubSubCommands.cs @@ -29,8 +29,8 @@ public override unsafe void Publish(ArgSlice key, ArgSlice value) WriteBulkString("message"u8); // Write key and value to the network - WriteDirectLargeRespString(key.ReadOnlySpan); - WriteDirectLargeRespString(value.ReadOnlySpan); + WriteLargeDirectRespString(key.ReadOnlySpan); + WriteLargeDirectRespString(value.ReadOnlySpan); // Flush the publish message for this subscriber if (dcurr > networkSender.GetResponseObjectHead()) @@ -58,9 +58,9 @@ public override unsafe void PatternPublish(ArgSlice pattern, ArgSlice key, ArgSl WriteBulkString("pmessage"u8); // Write pattern, key, and value to the network - WriteDirectLargeRespString(pattern.ReadOnlySpan); - WriteDirectLargeRespString(key.ReadOnlySpan); - WriteDirectLargeRespString(value.ReadOnlySpan); + WriteLargeDirectRespString(pattern.ReadOnlySpan); + WriteLargeDirectRespString(key.ReadOnlySpan); + WriteLargeDirectRespString(value.ReadOnlySpan); if (dcurr > networkSender.GetResponseObjectHead()) Send(networkSender.GetResponseObjectHead()); diff --git a/libs/server/Resp/RespServerSession.cs b/libs/server/Resp/RespServerSession.cs index b04ee9828f2..c8c2295de6e 100644 --- a/libs/server/Resp/RespServerSession.cs +++ b/libs/server/Resp/RespServerSession.cs @@ -1337,7 +1337,7 @@ internal void SendAndReset(IMemoryOwner memory, int length) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WriteDirectLarge(ReadOnlySpan src) + private void WriteLargeDirect(ReadOnlySpan src) { // Repeat while we have bytes left to write while (src.Length > 0) diff --git a/libs/server/Resp/RespServerSessionOutput.cs b/libs/server/Resp/RespServerSessionOutput.cs index a1bc96ce68e..071e7d73e86 100644 --- a/libs/server/Resp/RespServerSessionOutput.cs +++ b/libs/server/Resp/RespServerSessionOutput.cs @@ -63,12 +63,12 @@ internal void WriteBulkString(scoped ReadOnlySpan message) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void WriteDirectLargeRespString(ReadOnlySpan message) + internal void WriteLargeDirectRespString(ReadOnlySpan message) { while (!RespWriteUtils.TryWriteBulkStringLength(message, ref dcurr, dend)) SendAndReset(); - WriteDirectLarge(message); + WriteLargeDirect(message); while (!RespWriteUtils.TryWriteNewLine(ref dcurr, dend)) SendAndReset(); From b527f57fe0ab84caa4ea229ae7ef0936518a2450 Mon Sep 17 00:00:00 2001 From: Kevin Montrose Date: Thu, 7 May 2026 16:40:29 -0400 Subject: [PATCH 08/16] start flagging cases where we should be using CmdStrings instead --- ...seLargeOrConstantsForRespWritesAnalyzer.cs | 136 ++++++++++++++++-- 1 file changed, 126 insertions(+), 10 deletions(-) diff --git a/analyzer/Garnet.analyzers/UseLargeOrConstantsForRespWritesAnalyzer.cs b/analyzer/Garnet.analyzers/UseLargeOrConstantsForRespWritesAnalyzer.cs index 69cf1c3b527..ab104ac3b1b 100644 --- a/analyzer/Garnet.analyzers/UseLargeOrConstantsForRespWritesAnalyzer.cs +++ b/analyzer/Garnet.analyzers/UseLargeOrConstantsForRespWritesAnalyzer.cs @@ -5,6 +5,7 @@ using System.Collections.Immutable; using System.IO; using System.Linq; +using System.Threading; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -14,7 +15,7 @@ namespace Garnet.analyzers { /// /// A number of analyzers in here to keep us honest about large responses and using CmdStrings - /// - Flag any call to WriteXXX methods in RespServerSessionOutput.cs which are variable size UNLESS they call a method with Large in its name. + /// - Flag any call to WriteXXX methods in RespServerSessionOutput.cs which are variable size UNLESS they call a method with Large in its name /// - Flag constants pass to WriteXXX methods which are not placed in CmdStrings classes /// - Flag constants which are larger than minimum send buffer sizes /// @@ -22,16 +23,19 @@ namespace Garnet.analyzers public sealed class UseLargeOrConstantsForRespWritesAnalyzer : DiagnosticAnalyzer { private static DiagnosticDescriptor UseLargeOverridesWithVariableSizeResponses { get; } = new("GARNET0002", "Use Large Overrides With Variable Size Responses", "Use {0} instead of {1} for variable size responses", "Correctness", DiagnosticSeverity.Warning, isEnabledByDefault: true); - private static DiagnosticDescriptor AddLargeOverridesForVariableSizeResponses { get; } = new("GARNET0002", "Add Large Override For Variable Size Responses", "Add and use {0} to RespServerSessionOutput.cs in place of using {1} for variable size responses", "Correctness", DiagnosticSeverity.Warning, isEnabledByDefault: true); + private static DiagnosticDescriptor AddLargeOverridesForVariableSizeResponses { get; } = new("GARNET0003", "Add Large Override For Variable Size Responses", "Add and use {0} to RespServerSessionOutput.cs in place of using {1} for variable size responses", "Correctness", DiagnosticSeverity.Warning, isEnabledByDefault: true); + private static DiagnosticDescriptor MoveOutputConstantsToCmdStrings { get; } = new("GARNET0004", "Move Output Constants To CmdStrings", "Add {0} to CmdStrings and use it in place of an inline literal", "Correctness", DiagnosticSeverity.Warning, isEnabledByDefault: true); + private static DiagnosticDescriptor UseExistingConstantInCmdStrings { get; } = new("GARNET0005", "Use Existing Constants In CmdStrings", "Use CmdStrings.{0} instead of inline literal", "Correctness", DiagnosticSeverity.Warning, isEnabledByDefault: true); /// - public override ImmutableArray SupportedDiagnostics { get; } = [UseLargeOverridesWithVariableSizeResponses, AddLargeOverridesForVariableSizeResponses]; + public override ImmutableArray SupportedDiagnostics { get; } = [UseLargeOverridesWithVariableSizeResponses, AddLargeOverridesForVariableSizeResponses, MoveOutputConstantsToCmdStrings, UseExistingConstantInCmdStrings]; +#pragma warning disable RS1026 /// public override void Initialize(AnalysisContext context) { context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.ReportDiagnostics | GeneratedCodeAnalysisFlags.Analyze); - context.EnableConcurrentExecution(); + //context.EnableConcurrentExecution(); context.RegisterCompilationStartAction( static compilationStartContext => @@ -69,12 +73,22 @@ public override void Initialize(AnalysisContext context) if (respServerSessionType is not null && cmdStringType is not null) { - var suggestionLookup = BuildLookup(respServerSessionType); + var suggestionLookup = BuildWriteLargeLookup(respServerSessionType); compilationStartContext.RegisterSyntaxNodeAction( syntaxNodeContext => { - AnalyzeCallerForVariableSizeViolations(syntaxNodeContext, cmdStringType, variableLengthTypes, suggestionLookup); + AnalyzeCallerForVariableSizeViolations(syntaxNodeContext, cmdStringType, variableLengthTypes, suggestionLookup, syntaxNodeContext.CancellationToken); + }, + SyntaxKind.InvocationExpression + ); + + var knownConstants = BuildKnownConstantsLookup(cmdStringType, compilationStartContext.CancellationToken); + + compilationStartContext.RegisterSyntaxNodeAction( + syntaxNodeAction => + { + AnalyzeCallerForNonCmdStringsConstants(syntaxNodeAction, cmdStringType, respServerSessionType, knownConstants, syntaxNodeAction.CancellationToken); }, SyntaxKind.InvocationExpression ); @@ -83,7 +97,7 @@ public override void Initialize(AnalysisContext context) ); // Build a map of WriteXXX -> WriteLargeXXX methods on RespServerSession - static Dictionary BuildLookup(INamedTypeSymbol respServerSessionType) + static Dictionary BuildWriteLargeLookup(INamedTypeSymbol respServerSessionType) { var writeMethods = new HashSet(); var writeLargeMethods = new HashSet(); @@ -124,14 +138,49 @@ static Dictionary BuildLookup(INamedTypeSymbol respServerSession return lookup; } + + // Build a map of literals ("foo" and "bar"u8) to the corresponding properties on CmdStrings + static Dictionary BuildKnownConstantsLookup(INamedTypeSymbol cmdStringsType, CancellationToken cancellation) + { + var lookup = new Dictionary(); + + foreach (var prop in cmdStringsType.GetMembers().OfType()) + { + if (prop.GetMethod is null || prop.SetMethod is not null) + { + continue; + } + + var getDecl = prop.GetMethod.DeclaringSyntaxReferences.SingleOrDefault(); + if (getDecl == null) + { + continue; + } + + var getDeclSyntax = getDecl.GetSyntax(cancellation); + if (getDeclSyntax is not ArrowExpressionClauseSyntax arrowDecl) + { + continue; + } + + if (arrowDecl.Expression is not LiteralExpressionSyntax literal) + { + continue; + } + + lookup[literal.Token.Text] = prop.Name; + } + + return lookup; + } } /// - /// Raises UseLargeOverridesWithVariableSizeResponses and AddLargeOverridesForVariableSizeResponses. + /// Raises and . /// /// Finds any calls to WriteXXX where the input is variable length type AND not a constant, suggests uing WriteLargeXXX instead (or implementing it if it's not avaiable). /// - private static void AnalyzeCallerForVariableSizeViolations(SyntaxNodeAnalysisContext context, INamedTypeSymbol cmdStringsType, HashSet variableLengthTypes, Dictionary largeEquivLookup) + private static void AnalyzeCallerForVariableSizeViolations(SyntaxNodeAnalysisContext context, INamedTypeSymbol cmdStringsType, HashSet variableLengthTypes, Dictionary largeEquivLookup, CancellationToken cancellation) { if (context.Node is not InvocationExpressionSyntax invoke) { @@ -172,7 +221,7 @@ private static void AnalyzeCallerForVariableSizeViolations(SyntaxNodeAnalysisCon // If the syntax tree or semantic thinks is a constant, just roll with it - other analyzers will refine this further var arg0 = invoke.ArgumentList.Arguments[0]; - var isConstant = arg0.Expression is LiteralExpressionSyntax || context.SemanticModel.GetConstantValue(arg0).HasValue; + var isConstant = arg0.Expression is LiteralExpressionSyntax || context.SemanticModel.GetConstantValue(arg0, cancellation).HasValue; if (isConstant) { return; @@ -204,5 +253,72 @@ private static void AnalyzeCallerForVariableSizeViolations(SyntaxNodeAnalysisCon context.ReportDiagnostic(diag); } + + /// + /// Raises and if constants are used in WriteXXX methods which aren't on CmdStrings. + /// + private static void AnalyzeCallerForNonCmdStringsConstants(SyntaxNodeAnalysisContext context, INamedTypeSymbol cmdStringsType, INamedTypeSymbol respServerSession, Dictionary knownConstants, CancellationToken cancellation) + { + if (context.Node is not InvocationExpressionSyntax invoke) + { + return; + } + + if (invoke.Expression is not IdentifierNameSyntax methodName) + { + return; + } + + // Quickly filter out anything that doesn't look like a plain Write method + if (string.IsNullOrEmpty(methodName.Identifier.Text) || !methodName.Identifier.Text.StartsWith("Write")) + { + return; + } + + // Ignore anything that isn't a call to a method in RespServerSessionOutput.cs + var method = context.SemanticModel.GetSymbolInfo(methodName); + if (method.Symbol is not IMethodSymbol methodSymbol || !method.Symbol.DeclaringSyntaxReferences.Any(static decl => Path.GetFileName(decl.SyntaxTree.FilePath) == "RespServerSessionOutput.cs")) + { + return; + } + + // Skip anything without parameters or arguments + if (methodSymbol.Parameters.Length == 0 || invoke.ArgumentList.Arguments.Count == 0) + { + return; + } + + // If the first argument isn't a constant, ignore it + string literalStr; + var arg0 = invoke.ArgumentList.Arguments[0]; + if (arg0.Expression is LiteralExpressionSyntax literal) + { + literalStr = literal.Token.Text; + } + else + { + var inferredConstant = context.SemanticModel.GetConstantValue(arg0.Expression, cancellation); + if (inferredConstant.HasValue && inferredConstant.Value is string constStr) + { + literalStr = $"\"{constStr}\"u8"; + } + else + { + return; + } + } + + Diagnostic diag; + if (knownConstants.TryGetValue(literalStr, out var propertyName)) + { + diag = Diagnostic.Create(UseExistingConstantInCmdStrings, arg0.GetLocation(), propertyName); + } + else + { + diag = Diagnostic.Create(MoveOutputConstantsToCmdStrings, arg0.GetLocation(), literalStr); + } + + context.ReportDiagnostic(diag); + } } } From 45b74c5b3a26bf32f24ab1391bca5c93e6f6b235 Mon Sep 17 00:00:00 2001 From: Kevin Montrose Date: Thu, 7 May 2026 17:49:09 -0400 Subject: [PATCH 09/16] stopgap commit; cleanup a good number of flagged issues --- ...seLargeOrConstantsForRespWritesAnalyzer.cs | 5 +- libs/server/Custom/CustomRespCommands.cs | 4 +- libs/server/Lua/LuaCommands.cs | 2 +- libs/server/Metrics/Info/InfoCommand.cs | 4 +- .../Metrics/Latency/RespLatencyCommands.cs | 4 +- .../Metrics/Slowlog/RespSlowlogCommands.cs | 12 +--- libs/server/Resp/ACLCommands.cs | 29 +++++--- libs/server/Resp/AdminCommands.cs | 22 +++--- libs/server/Resp/ArrayCommands.cs | 8 +-- libs/server/Resp/BasicCommands.cs | 22 +++--- libs/server/Resp/CmdStrings.cs | 38 +++++++++++ libs/server/Resp/KeyAdminCommands.cs | 14 ++-- libs/server/Resp/PubSubCommands.cs | 54 +++++++-------- libs/server/Resp/PurgeBPCommand.cs | 4 +- libs/server/Resp/RespServerSessionOutput.cs | 68 ++++++++++++++++++- .../Resp/Vector/RespServerSessionVectors.cs | 38 +++++------ libs/server/ServerConfig.cs | 2 +- 17 files changed, 217 insertions(+), 113 deletions(-) diff --git a/analyzer/Garnet.analyzers/UseLargeOrConstantsForRespWritesAnalyzer.cs b/analyzer/Garnet.analyzers/UseLargeOrConstantsForRespWritesAnalyzer.cs index ab104ac3b1b..bb258263718 100644 --- a/analyzer/Garnet.analyzers/UseLargeOrConstantsForRespWritesAnalyzer.cs +++ b/analyzer/Garnet.analyzers/UseLargeOrConstantsForRespWritesAnalyzer.cs @@ -30,12 +30,11 @@ public sealed class UseLargeOrConstantsForRespWritesAnalyzer : DiagnosticAnalyze /// public override ImmutableArray SupportedDiagnostics { get; } = [UseLargeOverridesWithVariableSizeResponses, AddLargeOverridesForVariableSizeResponses, MoveOutputConstantsToCmdStrings, UseExistingConstantInCmdStrings]; -#pragma warning disable RS1026 /// public override void Initialize(AnalysisContext context) { context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.ReportDiagnostics | GeneratedCodeAnalysisFlags.Analyze); - //context.EnableConcurrentExecution(); + context.EnableConcurrentExecution(); context.RegisterCompilationStartAction( static compilationStartContext => @@ -291,7 +290,7 @@ private static void AnalyzeCallerForNonCmdStringsConstants(SyntaxNodeAnalysisCon // If the first argument isn't a constant, ignore it string literalStr; var arg0 = invoke.ArgumentList.Arguments[0]; - if (arg0.Expression is LiteralExpressionSyntax literal) + if (arg0.Expression is LiteralExpressionSyntax literal && literal.Kind() is SyntaxKind.StringLiteralExpression or SyntaxKind.Utf8StringLiteralExpression) { literalStr = literal.Token.Text; } diff --git a/libs/server/Custom/CustomRespCommands.cs b/libs/server/Custom/CustomRespCommands.cs index e3a0c2bc871..dff0f223bf6 100644 --- a/libs/server/Custom/CustomRespCommands.cs +++ b/libs/server/Custom/CustomRespCommands.cs @@ -44,7 +44,7 @@ private bool TryTransactionProc(byte id, CustomTransactionProcedure proc, int st if (output.MemoryOwner != null) SendAndReset(output.MemoryOwner, output.Length); else - WriteError($"ERR Transaction failed."); + WriteError(CmdStrings.ERR_transaction_failed); } LatencyMetrics?.Stop(LatencyMetricsType.TX_PROC_LAT); @@ -77,7 +77,7 @@ private void TryCustomProcedure(CustomProcedure proc, int startIdx = 0) if (output.MemoryOwner != null) SendAndReset(output.MemoryOwner, output.Length); else - WriteError("ERR Command failed."); + WriteError(CmdStrings.ERR_command_failed); } } diff --git a/libs/server/Lua/LuaCommands.cs b/libs/server/Lua/LuaCommands.cs index 0d9936f20b9..b92dcc4b401 100644 --- a/libs/server/Lua/LuaCommands.cs +++ b/libs/server/Lua/LuaCommands.cs @@ -319,7 +319,7 @@ private bool NetworkScriptLoad() } } - WriteBulkString(digest); + WriteLargeBulkString(digest); } return true; diff --git a/libs/server/Metrics/Info/InfoCommand.cs b/libs/server/Metrics/Info/InfoCommand.cs index 08c1e4ef18b..306f9a5c68c 100644 --- a/libs/server/Metrics/Info/InfoCommand.cs +++ b/libs/server/Metrics/Info/InfoCommand.cs @@ -48,7 +48,7 @@ private bool NetworkINFO() if (invalid) { - WriteError($"ERR Invalid section {invalidSection}. Try INFO HELP"); + WriteLargeError($"ERR Invalid section {invalidSection}. Try INFO HELP"); return true; } @@ -86,7 +86,7 @@ private void GetHelpMessage() WriteArrayLength(sectionsHelp.Count); foreach (var sectionInfo in sectionsHelp) { - WriteAsciiBulkString(sectionInfo); + WriteLargeBulkString(sectionInfo); } } } diff --git a/libs/server/Metrics/Latency/RespLatencyCommands.cs b/libs/server/Metrics/Latency/RespLatencyCommands.cs index a778dcc75f1..aa53fc80b0d 100644 --- a/libs/server/Metrics/Latency/RespLatencyCommands.cs +++ b/libs/server/Metrics/Latency/RespLatencyCommands.cs @@ -63,7 +63,7 @@ private bool NetworkLatencyHistogram() if (invalid) { - WriteError($"ERR Invalid event {invalidEvent}. Try LATENCY HELP"); + WriteLargeError($"ERR Invalid event {invalidEvent}. Try LATENCY HELP"); } else { @@ -107,7 +107,7 @@ private bool NetworkLatencyReset() if (invalid) { - WriteError($"ERR Invalid type {invalidEvent}"); + WriteLargeError($"ERR Invalid type {invalidEvent}"); } else { diff --git a/libs/server/Metrics/Slowlog/RespSlowlogCommands.cs b/libs/server/Metrics/Slowlog/RespSlowlogCommands.cs index 150bec7f320..f5f7d50dde5 100644 --- a/libs/server/Metrics/Slowlog/RespSlowlogCommands.cs +++ b/libs/server/Metrics/Slowlog/RespSlowlogCommands.cs @@ -71,7 +71,7 @@ private bool NetworkSlowLogGet() if (entry.Arguments == null) { WriteArrayLength(1); - WriteAsciiBulkString(entry.Command.ToString()); + WriteEnumAsBulkString(entry.Command); } else { @@ -81,16 +81,10 @@ private bool NetworkSlowLogGet() sps.DeserializeFrom(ptr); } WriteArrayLength(sps.Count + 1); - WriteAsciiBulkString(entry.Command.ToString()); + WriteEnumAsBulkString(entry.Command); for (int i = 0; i < sps.Count; i++) { - // Hack added for demonstration - WriteAsciiBulkString("hello world"); - WriteBulkString("hello world"u8); - // End hack - - // Already existing - WriteAsciiBulkString(sps.GetString(i)); + WriteLargeBulkString(sps.GetArgSliceByRef(i).Span); } } WriteAsciiBulkString(entry.ClientIpPort); diff --git a/libs/server/Resp/ACLCommands.cs b/libs/server/Resp/ACLCommands.cs index 20346905771..bcf2bc0fc4f 100644 --- a/libs/server/Resp/ACLCommands.cs +++ b/libs/server/Resp/ACLCommands.cs @@ -64,7 +64,7 @@ private bool NetworkAclList() foreach (var userHandle in userHandles) { - WriteAsciiBulkString(userHandle.Value.User.DescribeUser()); + WriteLargeBulkString(userHandle.Value.User.DescribeUser()); } return true; @@ -91,7 +91,7 @@ private bool NetworkAclUsers() foreach (var user in users) { - WriteAsciiBulkString(user.Key); + WriteLargeBulkString(user.Key); } return true; @@ -117,7 +117,7 @@ private bool NetworkAclCat() foreach (var category in categories) { - WriteAsciiBulkString(category); + WriteLargeBulkString(category); } return true; @@ -266,7 +266,7 @@ private bool NetworkAclWhoAmI() // Return the name of the currently authenticated user. Debug.Assert(aclAuthenticator.GetUserHandle()?.User != null); - WriteAsciiBulkString(aclAuthenticator.GetUserHandle().User.Name); + WriteLargeBulkString(aclAuthenticator.GetUserHandle().User.Name); return true; } @@ -379,7 +379,7 @@ private bool NetworkAclGenPass() length = (int)(bits / 4) + (bits % 4 == 0 ? 0 : 1); } - WriteAsciiBulkString(System.Security.Cryptography.RandomNumberGenerator.GetHexString(length, true)); + WriteLargeBulkString(System.Security.Cryptography.RandomNumberGenerator.GetHexString(length, true)); return true; } @@ -428,25 +428,32 @@ private bool NetworkAclGetUser() { WriteMapLength(3); - WriteAsciiBulkString("flags"); + WriteBulkString(CmdStrings.flags); WriteSetLength(1); - WriteAsciiBulkString(user.IsEnabled ? "on" : "off"); + if (user.IsEnabled) + { + WriteBulkString(CmdStrings.on); + } + else + { + WriteBulkString(CmdStrings.off); + } var passwords = user.Passwords; - WriteAsciiBulkString("passwords"); + WriteBulkString(CmdStrings.passwords); WriteArrayLength(passwords.Count); foreach (var password in passwords) { - WriteAsciiBulkString($"#{password.ToString()}"); + WriteLargeBulkString($"#{password.ToString()}"); } - WriteAsciiBulkString("commands"); + WriteBulkString(CmdStrings.commands); - WriteAsciiBulkString(user.GetEnabledCommandsDescription()); + WriteLargeBulkString(user.GetEnabledCommandsDescription()); } return true; diff --git a/libs/server/Resp/AdminCommands.cs b/libs/server/Resp/AdminCommands.cs index adc7821ee9d..420f3ab1d2c 100644 --- a/libs/server/Resp/AdminCommands.cs +++ b/libs/server/Resp/AdminCommands.cs @@ -511,7 +511,7 @@ private bool NetworkRegisterCs(CustomCommandManager customCommandManager) } else { - WriteError(errorMsg); + WriteLargeError(errorMsg); } return true; @@ -550,7 +550,7 @@ private bool NetworkModuleLoad(CustomCommandManager customCommandManager) { if (!errorMsg.IsEmpty) { - WriteError(errorMsg); + WriteLargeError(errorMsg); } return true; @@ -572,7 +572,7 @@ private bool NetworkModuleLoad(CustomCommandManager customCommandManager) if (!errorMsg.IsEmpty) { - WriteError(errorMsg); + WriteLargeError(errorMsg); } return true; @@ -602,7 +602,7 @@ private bool NetworkCOMMITAOF() // Have to block as we're on network thread _ = AsyncUtils.BlockingWait(CommitAofAsync(dbId)); - WriteSimpleString("AOF file committed"u8); + WriteSimpleString(CmdStrings.AOF_file_committed); return true; } @@ -624,7 +624,7 @@ private bool NetworkFORCEGC() } GC.Collect(generation, GCCollectionMode.Forced, true); - WriteSimpleString("GC completed"u8); + WriteSimpleString(CmdStrings.GC_completed); return true; } @@ -722,7 +722,7 @@ private bool NetworkDebug() nameof(RespCommand.DEBUG)); } - WriteError(parseState.GetString(1)); + WriteLargeError(parseState.GetArgSliceByRef(1).Span); return true; } @@ -764,7 +764,7 @@ private bool NetworkDebug() return true; } - WriteError(string.Format(CmdStrings.GenericErrUnknownSubCommand, parseState.GetString(0), nameof(RespCommand.DEBUG))); + WriteLargeError(string.Format(CmdStrings.GenericErrUnknownSubCommand, parseState.GetString(0), nameof(RespCommand.DEBUG))); return true; } @@ -779,7 +779,7 @@ private bool NetworkROLE() { WriteArrayLength(3); - WriteAsciiBulkString("master"); + WriteBulkString(CmdStrings.master); WriteInt32(0); @@ -793,7 +793,7 @@ private bool NetworkROLE() WriteArrayLength(3); - WriteAsciiBulkString("master"); + WriteBulkString(CmdStrings.master); WriteInt64(replication_offset); @@ -813,7 +813,7 @@ private bool NetworkROLE() WriteArrayLength(5); - WriteAsciiBulkString("slave"); + WriteBulkString(CmdStrings.slave); WriteAsciiBulkString(role.address); @@ -970,7 +970,7 @@ private bool NetworkBGSAVE() if (success) { - WriteSimpleString("Background saving started"u8); + WriteSimpleString(CmdStrings.Background_saving_started); } else { diff --git a/libs/server/Resp/ArrayCommands.cs b/libs/server/Resp/ArrayCommands.cs index 7eb13351240..aa535b90e00 100644 --- a/libs/server/Resp/ArrayCommands.cs +++ b/libs/server/Resp/ArrayCommands.cs @@ -221,7 +221,7 @@ private bool NetworkKEYS(ref TGarnetApi storageApi) // Write the keys matching the pattern foreach (var item in keys) { - WriteBulkString(item); + WriteLargeBulkString(item); } } else @@ -321,7 +321,7 @@ private bool NetworkTYPE(ref TGarnetApi storageApi) } else { - WriteSimpleString("none"u8); + WriteSimpleString(CmdStrings.none); } return true; @@ -344,7 +344,7 @@ private void WriteOutputForScan(long cursorValue, List keys) // Write the keys matching the pattern for (int i = 0; i < keys.Count; i++) { - WriteLargeDirectRespString(keys[i]); + WriteLargeBulkString(keys[i]); } } @@ -362,7 +362,7 @@ private bool NetworkArrayPING() } var message = parseState.GetArgSliceByRef(0).ReadOnlySpan; - WriteLargeDirectRespString(message); + WriteLargeBulkString(message); return true; } diff --git a/libs/server/Resp/BasicCommands.cs b/libs/server/Resp/BasicCommands.cs index f46666d7228..195060d5c1c 100644 --- a/libs/server/Resp/BasicCommands.cs +++ b/libs/server/Resp/BasicCommands.cs @@ -102,7 +102,7 @@ bool NetworkGETEX(ref TGarnetApi storageApi) break; default: - WriteError($"ERR Unsupported option {parseState.GetString(1)}"); + WriteLargeError($"ERR Unsupported option {parseState.GetString(1)}"); return true; } } @@ -553,7 +553,7 @@ private bool NetworkSETEXNX(ref TGarnetApi storageApi) if (!errorMessage.IsEmpty) { - WriteError(errorMessage); + WriteLargeError(errorMessage); return true; } @@ -770,7 +770,7 @@ private bool NetworkIncrementByFloat(ref TGarnetApi storageApi) switch (status) { case GarnetStatus.OK: - WriteBulkString(output.ReadOnlySpan); + WriteLargeBulkString(output.ReadOnlySpan); break; case GarnetStatus.WRONGTYPE: default: @@ -994,7 +994,7 @@ private bool NetworkCOMMAND() { var subCommand = parseState.GetString(0); var errorMsg = string.Format(CmdStrings.GenericErrUnknownSubCommand, subCommand, nameof(RespCommand.COMMAND)); - WriteError(errorMsg); + WriteLargeError(errorMsg); } else { @@ -1172,7 +1172,7 @@ private bool NetworkCOMMAND_GETKEYS() foreach (var key in keys) { - WriteBulkString(key.Span); + WriteLargeBulkString(key.Span); } return true; @@ -1209,14 +1209,14 @@ private bool NetworkCOMMAND_GETKEYSANDFLAGS() { WriteArrayLength(2); - WriteBulkString(keysAndFlags[i].Item1.Span); + WriteLargeBulkString(keysAndFlags[i].Item1.Span); var flags = EnumUtils.GetEnumDescriptions(keysAndFlags[i].Item2); WriteSetLength(flags.Length); foreach (var flag in flags) { - WriteBulkString(Encoding.ASCII.GetBytes(flag)); + WriteLargeBulkString(Encoding.ASCII.GetBytes(flag)); } } @@ -1231,7 +1231,7 @@ private bool NetworkECHO() } var message = parseState.GetArgSliceByRef(0).ReadOnlySpan; - WriteLargeDirectRespString(message); + WriteLargeBulkString(message); return true; } @@ -1306,7 +1306,7 @@ private bool NetworkHELLO() if (errorMsg != default) { - WriteError(errorMsg); + WriteLargeError(errorMsg); return true; } @@ -1357,7 +1357,7 @@ private bool NetworkAUTH() // NOTE: Some authenticators cannot accept username/password pairs if (!_authenticator.CanAuthenticate) { - WriteError("ERR Client sent AUTH, but configured authenticator does not accept passwords"u8); + WriteError(CmdStrings.ERR_Client_sent_AUTH_no_pass); return true; } @@ -1554,7 +1554,7 @@ void ProcessHelloCommand(byte? respProtocolVersion, ReadOnlySpan username, WriteAsciiBulkString(helloResult[i].Item2.ToString()); } } - WriteAsciiBulkString("modules"); + WriteBulkString(CmdStrings.modules); WriteEmptyArray(); } diff --git a/libs/server/Resp/CmdStrings.cs b/libs/server/Resp/CmdStrings.cs index e8c5ba5fb9e..53247deac3b 100644 --- a/libs/server/Resp/CmdStrings.cs +++ b/libs/server/Resp/CmdStrings.cs @@ -531,5 +531,43 @@ static partial class CmdStrings public static ReadOnlySpan LUA_KEYS => "KEYS"u8; public static ReadOnlySpan LUA_ARGV => "ARGV"u8; public static ReadOnlySpan EXPDELSCAN => "EXPDELSCAN"u8; + public static ReadOnlySpan master => "master"u8; + public static ReadOnlySpan slave => "slave"u8; + public static ReadOnlySpan ERR_transaction_failed => "ERR Transaction failed."u8; + public static ReadOnlySpan ERR_command_failed => "ERR Command failed."u8; + public static ReadOnlySpan flags => "flags"u8; + public static ReadOnlySpan passwords => "passwords"u8; + public static ReadOnlySpan commands => "commands"u8; + public static ReadOnlySpan AOF_file_committed => "AOF file committed"u8; + public static ReadOnlySpan GC_completed => "GC completed"u8; + public static ReadOnlySpan Background_saving_started => "Background saving started"u8; + public static ReadOnlySpan none => "none"u8; + public static ReadOnlySpan ERR_Client_sent_AUTH_no_pass => "ERR Client sent AUTH, but configured authenticator does not accept passwords"u8; + public static ReadOnlySpan modules => "modules"u8; + public static ReadOnlySpan ERR_RESTORE_currently_only_supports => "ERR RESTORE currently only supports string types"u8; + public static ReadOnlySpan ERR_DUMP_payload_version_or_checksum_wrong => "ERR DUMP payload version or checksum are wrong"u8; + public static ReadOnlySpan ERR_DUMP_payload_length_format_invalid => "ERR DUMP payload length format is invalid"u8; + public static ReadOnlySpan ERR_DUMP_payload_length_is_invalid => "ERR DUMP payload length is invalid"u8; + public static ReadOnlySpan ERR_NX_and_XX_GT_or_LT_not_compatible => "ERR NX and XX, GT or LT options at the same time are not compatible"u8; + public static ReadOnlySpan message => "message"u8; + public static ReadOnlySpan pmessage => "pmessage"u8; + public static ReadOnlySpan ERR_SUBSCRIBE_is_disabled => "ERR SUBSCRIBE is disabled, enable it with --pubsub option."u8; + public static ReadOnlySpan psubscribe => "psubscribe"u8; + public static ReadOnlySpan unsubscribe => "unsubscribe"u8; + public static ReadOnlySpan ERR_UNSUBSCRIBE_is_disabled => "ERR UNSUBSCRIBE is disabled, enable it with --pubsub option."u8; + public static ReadOnlySpan punsubscribe => "punsubscribe"u8; + public static ReadOnlySpan ERR_PUNSUBSCRIBE_is_disabled => "ERR PUNSUBSCRIBE is disabled, enable it with --pubsub option."u8; + public static ReadOnlySpan ERR_Vector_exceeded_configured_page_size => "ERR Vector exceed configured page size"u8; + public static ReadOnlySpan ERR_Unsupported_quantization_type => "ERR Unsupported quantization type"u8; + public static ReadOnlySpan ERR_Element_not_in_Vector_Set => "ERR Element not in Vector Set"u8; + public static ReadOnlySpan ERR_Key_not_found => "ERR Key not found"u8; + public static ReadOnlySpan quant_type => "quant-type"u8; + public static ReadOnlySpan distance_metric => "distance-metric"u8; + public static ReadOnlySpan input_vector_dimensions => "input-vector-dimensions"u8; + public static ReadOnlySpan reduced_dimensions => "reduced-dimensions"u8; + public static ReadOnlySpan build_exploration_factor => "build-exploration-factor"u8; + public static ReadOnlySpan num_links => "num_links"u8; + public static ReadOnlySpan size => "size"u8; + public static ReadOnlySpan ERR_Vector_Set_partially_deleted => "ERR Vector Set is in a partially deleted state - re-execute DEL to complete deletion"u8; } } \ No newline at end of file diff --git a/libs/server/Resp/KeyAdminCommands.cs b/libs/server/Resp/KeyAdminCommands.cs index 0401974386a..1c13902fa89 100644 --- a/libs/server/Resp/KeyAdminCommands.cs +++ b/libs/server/Resp/KeyAdminCommands.cs @@ -44,14 +44,14 @@ bool NetworkRESTORE(ref TGarnetApi storageApi) // Restore is only implemented for string type if (valueSpan[0] != 0x00) { - WriteError("ERR RESTORE currently only supports string types"); + WriteError(CmdStrings.ERR_RESTORE_currently_only_supports); return true; } // check if length of value is at least 10 if (valueSpan.Length < 10) { - WriteError("ERR DUMP payload version or checksum are wrong"); + WriteError(CmdStrings.ERR_DUMP_payload_version_or_checksum_wrong); return true; } @@ -62,7 +62,7 @@ bool NetworkRESTORE(ref TGarnetApi storageApi) if (rdbVersion > RDB_VERSION) { - WriteError("ERR DUMP payload version or checksum are wrong"); + WriteError(CmdStrings.ERR_DUMP_payload_version_or_checksum_wrong); return true; } @@ -75,14 +75,14 @@ bool NetworkRESTORE(ref TGarnetApi storageApi) if (calculatedCrc.SequenceCompareTo(payloadCrc) != 0) { - WriteError("ERR DUMP payload version or checksum are wrong"); + WriteError(CmdStrings.ERR_DUMP_payload_version_or_checksum_wrong); return true; } // decode the length of payload if (!RespLengthEncodingUtils.TryReadLength(valueSpan.Slice(1), out var length, out var payloadStart)) { - WriteError("ERR DUMP payload length format is invalid"); + WriteError(CmdStrings.ERR_DUMP_payload_length_format_invalid); return true; } @@ -142,7 +142,7 @@ bool NetworkDUMP(ref TGarnetApi storageApi) if (!RespLengthEncodingUtils.TryWriteLength(value.ReadOnlySpan.Length, encodedLength, out var bytesWritten)) { - WriteError("ERR DUMP payload length is invalid"); + WriteError(CmdStrings.ERR_DUMP_payload_length_is_invalid); return true; } @@ -418,7 +418,7 @@ private bool NetworkEXPIRE(RespCommand command, ref TGarnetApi stora } else { - WriteError("ERR NX and XX, GT or LT options at the same time are not compatible"); + WriteError(CmdStrings.ERR_NX_and_XX_GT_or_LT_not_compatible); } } } diff --git a/libs/server/Resp/PubSubCommands.cs b/libs/server/Resp/PubSubCommands.cs index 8a85f7128f8..1d59c7f7887 100644 --- a/libs/server/Resp/PubSubCommands.cs +++ b/libs/server/Resp/PubSubCommands.cs @@ -26,11 +26,11 @@ public override unsafe void Publish(ArgSlice key, ArgSlice value) WritePushLength(3); - WriteBulkString("message"u8); + WriteBulkString(CmdStrings.message); // Write key and value to the network - WriteLargeDirectRespString(key.ReadOnlySpan); - WriteLargeDirectRespString(value.ReadOnlySpan); + WriteLargeBulkString(key.ReadOnlySpan); + WriteLargeBulkString(value.ReadOnlySpan); // Flush the publish message for this subscriber if (dcurr > networkSender.GetResponseObjectHead()) @@ -55,12 +55,12 @@ public override unsafe void PatternPublish(ArgSlice pattern, ArgSlice key, ArgSl WritePushLength(4); - WriteBulkString("pmessage"u8); + WriteBulkString(CmdStrings.pmessage); // Write pattern, key, and value to the network - WriteLargeDirectRespString(pattern.ReadOnlySpan); - WriteLargeDirectRespString(key.ReadOnlySpan); - WriteLargeDirectRespString(value.ReadOnlySpan); + WriteLargeBulkString(pattern.ReadOnlySpan); + WriteLargeBulkString(key.ReadOnlySpan); + WriteLargeBulkString(value.ReadOnlySpan); if (dcurr > networkSender.GetResponseObjectHead()) Send(networkSender.GetResponseObjectHead()); @@ -160,9 +160,9 @@ private bool NetworkSUBSCRIBE(RespCommand cmd) WriteArrayLength(3); - WriteBulkString(header); + WriteLargeBulkString(header); - WriteBulkString(key.ReadOnlySpan); + WriteLargeBulkString(key.ReadOnlySpan); if (subscribeBroker.Subscribe(key, this)) numActiveChannels++; @@ -172,7 +172,7 @@ private bool NetworkSUBSCRIBE(RespCommand cmd) if (disabledBroker) { - WriteError("ERR SUBSCRIBE is disabled, enable it with --pubsub option."u8); + WriteError(CmdStrings.ERR_SUBSCRIBE_is_disabled); return true; } @@ -198,8 +198,8 @@ private bool NetworkPSUBSCRIBE() WriteArrayLength(3); - WriteBulkString("psubscribe"u8); - WriteBulkString(key.ReadOnlySpan); + WriteBulkString(CmdStrings.psubscribe); + WriteLargeBulkString(key.ReadOnlySpan); if (subscribeBroker.PatternSubscribe(key, this)) numActiveChannels++; @@ -209,7 +209,7 @@ private bool NetworkPSUBSCRIBE() if (disabledBroker) { - WriteError("ERR SUBSCRIBE is disabled, enable it with --pubsub option."u8); + WriteError(CmdStrings.ERR_SUBSCRIBE_is_disabled); return true; } @@ -232,9 +232,9 @@ private bool NetworkUNSUBSCRIBE() foreach (var channel in channels) { WriteArrayLength(3); - WriteBulkString("unsubscribe"u8); + WriteBulkString(CmdStrings.unsubscribe); - WriteBulkString(channel.ReadOnlySpan); + WriteLargeBulkString(channel.ReadOnlySpan); if (subscribeBroker.Unsubscribe(channel, this)) numActiveChannels--; @@ -244,7 +244,7 @@ private bool NetworkUNSUBSCRIBE() if (channels.Count == 0) { WriteArrayLength(3); - WriteBulkString("unsubscribe"u8); + WriteBulkString(CmdStrings.unsubscribe); WriteNull(); @@ -264,8 +264,8 @@ private bool NetworkUNSUBSCRIBE() if (subscribeBroker != null) { WriteArrayLength(3); - WriteBulkString("unsubscribe"u8); - WriteBulkString(key.ReadOnlySpan); + WriteBulkString(CmdStrings.unsubscribe); + WriteLargeBulkString(key.ReadOnlySpan); if (subscribeBroker.Unsubscribe(new ByteArrayWrapper(key), this)) numActiveChannels--; @@ -276,7 +276,7 @@ private bool NetworkUNSUBSCRIBE() if (subscribeBroker == null) { - WriteError("ERR UNSUBSCRIBE is disabled, enable it with --pubsub option."u8); + WriteError(CmdStrings.ERR_UNSUBSCRIBE_is_disabled); } if (numActiveChannels == 0) @@ -300,9 +300,9 @@ private bool NetworkPUNSUBSCRIBE() foreach (var channel in channels) { WriteArrayLength(3); - WriteBulkString("punsubscribe"u8); + WriteBulkString(CmdStrings.punsubscribe); - WriteBulkString(channel.ReadOnlySpan); + WriteLargeBulkString(channel.ReadOnlySpan); if (subscribeBroker.PatternUnsubscribe(channel, this)) numActiveChannels--; @@ -314,7 +314,7 @@ private bool NetworkPUNSUBSCRIBE() { WriteArrayLength(3); - WriteBulkString("punsubscribe"u8); + WriteBulkString(CmdStrings.punsubscribe); WriteNull(); @@ -334,8 +334,8 @@ private bool NetworkPUNSUBSCRIBE() if (subscribeBroker != null) { WriteArrayLength(3); - WriteBulkString("punsubscribe"u8); - WriteBulkString(key.ReadOnlySpan); + WriteBulkString(CmdStrings.punsubscribe); + WriteLargeBulkString(key.ReadOnlySpan); if (subscribeBroker.PatternUnsubscribe(new ByteArrayWrapper(key), this)) numActiveChannels--; @@ -346,7 +346,7 @@ private bool NetworkPUNSUBSCRIBE() if (subscribeBroker == null) { - WriteError("ERR PUNSUBSCRIBE is disabled, enable it with --pubsub option."u8); + WriteError(CmdStrings.ERR_PUNSUBSCRIBE_is_disabled); } if (numActiveChannels == 0) @@ -377,7 +377,7 @@ private bool NetworkPUBSUB_CHANNELS() foreach (var channel in channels) { - WriteBulkString(channel.ReadOnlySpan); + WriteLargeBulkString(channel.ReadOnlySpan); } return true; } @@ -415,7 +415,7 @@ private bool NetworkPUBSUB_NUMSUB() { var channel = parseState.GetArgSliceByRef(c); - WriteBulkString(channel.ReadOnlySpan); + WriteLargeBulkString(channel.ReadOnlySpan); WriteInt32(subscribeBroker.NumSubscriptions(channel)); } return true; diff --git a/libs/server/Resp/PurgeBPCommand.cs b/libs/server/Resp/PurgeBPCommand.cs index 24fe2ab2bf6..1ba1c6ebfb8 100644 --- a/libs/server/Resp/PurgeBPCommand.cs +++ b/libs/server/Resp/PurgeBPCommand.cs @@ -72,14 +72,14 @@ private bool NetworkPurgeBP() break; default: success = false; - WriteError($"ERR Could not purge {managerType}."); + WriteLargeError($"ERR Could not purge {managerType}."); break; } if (success) { GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true); - WriteSimpleString(managerType.ToReadOnlySpan()); + WriteEnumAsBulkString(managerType); } } catch (Exception ex) diff --git a/libs/server/Resp/RespServerSessionOutput.cs b/libs/server/Resp/RespServerSessionOutput.cs index 071e7d73e86..b4af767ab6b 100644 --- a/libs/server/Resp/RespServerSessionOutput.cs +++ b/libs/server/Resp/RespServerSessionOutput.cs @@ -2,7 +2,9 @@ // Licensed under the MIT license. using System; +using System.Buffers; using System.Runtime.CompilerServices; +using System.Text; using Garnet.common; using Tsavorite.core; @@ -34,6 +36,16 @@ internal void WriteAsciiBulkString(ReadOnlySpan message) SendAndReset(); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void WriteEnumAsBulkString(TEnum value) + where TEnum : struct, Enum + { + var asStr = value.ToString(); + while (!RespWriteUtils.TryWriteAsciiBulkString(asStr, ref dcurr, dend)) + SendAndReset(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void WriteAsciiDirect(ReadOnlySpan message) { @@ -63,7 +75,7 @@ internal void WriteBulkString(scoped ReadOnlySpan message) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void WriteLargeDirectRespString(ReadOnlySpan message) + internal void WriteLargeBulkString(ReadOnlySpan message) { while (!RespWriteUtils.TryWriteBulkStringLength(message, ref dcurr, dend)) SendAndReset(); @@ -74,6 +86,29 @@ internal void WriteLargeDirectRespString(ReadOnlySpan message) SendAndReset(); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void WriteLargeBulkString(ReadOnlySpan message) + { + var buff = ArrayPool.Shared.Rent(Encoding.UTF8.GetByteCount(message)); + try + { + var written = Encoding.UTF8.GetBytes(message, buff); + var asBytes = buff.AsSpan()[..written]; + + while (!RespWriteUtils.TryWriteBulkStringLength(asBytes, ref dcurr, dend)) + SendAndReset(); + + WriteLargeDirect(asBytes); + + while (!RespWriteUtils.TryWriteNewLine(ref dcurr, dend)) + SendAndReset(); + } + finally + { + ArrayPool.Shared.Return(buff); + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void WriteDirect(scoped ReadOnlySpan span) { @@ -125,6 +160,37 @@ internal void WriteError(scoped ReadOnlySpan errorString) SendAndReset(); } + internal void WriteLargeError(scoped ReadOnlySpan errorString) + { + commandErrorWritten = true; + + if ((int)(dend - dcurr) == 0) + SendAndReset(); + + *dcurr++ = (byte)'-'; + + WriteLargeDirect(errorString); + + while (!RespWriteUtils.TryWriteNewLine(ref dcurr, dend)) + SendAndReset(); + } + + internal void WriteLargeError(scoped ReadOnlySpan errorString) + { + var buff = ArrayPool.Shared.Rent(Encoding.UTF8.GetByteCount(errorString)); + try + { + var written = Encoding.UTF8.GetBytes(errorString, buff); + var asBytes = buff.AsSpan()[..written]; + + WriteLargeError(asBytes); + } + finally + { + ArrayPool.Shared.Return(buff); + } + } + internal void WriteError(ReadOnlySpan errorString) { commandErrorWritten = true; diff --git a/libs/server/Resp/Vector/RespServerSessionVectors.cs b/libs/server/Resp/Vector/RespServerSessionVectors.cs index 18b4bf02602..d8fa4318d72 100644 --- a/libs/server/Resp/Vector/RespServerSessionVectors.cs +++ b/libs/server/Resp/Vector/RespServerSessionVectors.cs @@ -351,19 +351,19 @@ private bool NetworkVADD(ref TGarnetApi storageApi) // Note that this goes away in store v2 if (values.Length > maximumVectorSetValueBytes) { - WriteError("ERR Vector exceed configured page size"u8); + WriteError(CmdStrings.ERR_Vector_exceeded_configured_page_size); return true; } if (attributes.Value.Length > maximumVectorSetValueBytes) { - WriteError("ERR Attribute exceed configured page size"u8); + WriteError(CmdStrings.ERR_Vector_exceeded_configured_page_size); return true; } if (quantType != VectorQuantType.XPreQ8 && quantType != VectorQuantType.NoQuant) { - WriteError("ERR Unsupported quantization type"u8); + WriteError(CmdStrings.ERR_Unsupported_quantization_type); return true; } @@ -792,7 +792,7 @@ private bool NetworkVSIM(ref TGarnetApi storageApi) { if (vectorRes == VectorManagerResult.MissingElement) { - WriteError("Element not in Vector Set"u8); + WriteError(CmdStrings.ERR_Element_not_in_Vector_Set); } else if (vectorRes == VectorManagerResult.OK) { @@ -884,7 +884,7 @@ private bool NetworkVSIM(ref TGarnetApi storageApi) continue; } - WriteBulkString(elementData); + WriteLargeBulkString(elementData); if (withScores.Value) { @@ -905,7 +905,7 @@ private bool NetworkVSIM(ref TGarnetApi storageApi) var attr = remaininingAttributes.Slice(sizeof(int), attrLen); remaininingAttributes = remaininingAttributes[(sizeof(int) + attrLen)..]; - WriteBulkString(attr); + WriteLargeBulkString(attr); } else if (!remaininingAttributes.IsEmpty) { @@ -1066,7 +1066,7 @@ private bool NetworkVDIM(ref TGarnetApi storageApi) if (res == GarnetStatus.NOTFOUND) { - WriteError("ERR Key not found"u8); + WriteError(CmdStrings.ERR_Key_not_found); } else if (res == GarnetStatus.WRONGTYPE) { @@ -1128,7 +1128,7 @@ private bool NetworkVGETATTR(ref TGarnetApi storageApi) return AbortWithErrorMessage($"Unexpected GarnetStatus: {res}"); } - WriteSimpleString(attributesOutput.AsReadOnlySpan()); + WriteLargeBulkString(attributesOutput.AsReadOnlySpan()); return true; } finally @@ -1190,19 +1190,19 @@ private bool NetworkVINFO(ref TGarnetApi storageApi) }; WriteArrayLength(14); - WriteSimpleString("quant-type"u8); - WriteSimpleString(quantTypeSpan); - WriteSimpleString("distance-metric"u8); - WriteSimpleString(distanceMetricTypeSpan); - WriteSimpleString("input-vector-dimensions"u8); + WriteSimpleString(CmdStrings.quant_type); + WriteLargeBulkString(quantTypeSpan); + WriteSimpleString(CmdStrings.distance_metric); + WriteLargeBulkString(distanceMetricTypeSpan); + WriteSimpleString(CmdStrings.input_vector_dimensions); WriteInt32AsBulkString((int)vectorDimensions); - WriteSimpleString("reduced-dimensions"u8); + WriteSimpleString(CmdStrings.reduced_dimensions); WriteInt32AsBulkString((int)reducedDimensions); - WriteSimpleString("build-exploration-factor"u8); + WriteSimpleString(CmdStrings.build_exploration_factor); WriteInt32AsBulkString((int)buildExplorationFactor); - WriteSimpleString("num-links"u8); + WriteSimpleString(CmdStrings.num_links); WriteInt32AsBulkString((int)numLinks); - WriteSimpleString("size"u8); + WriteSimpleString(CmdStrings.size); WriteInt64AsBulkString(size); return true; } @@ -1306,7 +1306,7 @@ private bool AbortVectorSetPartiallyDeleted(ref ArgSlice key) // TODO: We could _finish_ the delete here... though if we do that we should do it for ALL commands, not just Vector Set commands // That's more intrusive, and is more of a V2 thing... so lets just give a workaround for now - WriteError("ERR Vector Set is in a partially deleted state - re-execute DEL to complete deletion"u8); + WriteError(CmdStrings.ERR_Vector_Set_partially_deleted); return true; } @@ -1314,7 +1314,7 @@ private bool AbortVectorSetPartiallyDeleted(ref ArgSlice key) private bool AbortVectorSetWrongType() { // Matches Redis behavior - doesn't indicate the type involved - WriteError("WRONGTYPE Operation against a key holding the wrong kind of value"u8); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); return true; } diff --git a/libs/server/ServerConfig.cs b/libs/server/ServerConfig.cs index d7d47113944..77eae167c94 100644 --- a/libs/server/ServerConfig.cs +++ b/libs/server/ServerConfig.cs @@ -233,7 +233,7 @@ private unsafe bool NetworkCONFIG_SET() } else { - WriteError(sbErrorMsg.ToString()); + WriteLargeError(sbErrorMsg.ToString()); } return true; From f00f89c06465bfb1cdccb6bac3a59b9c03dbbe65 Mon Sep 17 00:00:00 2001 From: Kevin Montrose Date: Fri, 8 May 2026 12:35:05 -0400 Subject: [PATCH 10/16] convert more to LargeXXX --- libs/server/Resp/AsyncProcessor.cs | 2 +- libs/server/Resp/BasicEtagCommands.cs | 2 +- libs/server/Resp/Objects/ListCommands.cs | 18 ++++++++--------- libs/server/Resp/Objects/ObjectStoreUtils.cs | 2 +- libs/server/Resp/Objects/SetCommands.cs | 6 +++--- libs/server/Resp/Objects/SortedSetCommands.cs | 20 +++++++++---------- libs/server/Resp/Parser/RespCommand.cs | 2 +- libs/server/Resp/RespServerSession.cs | 6 +++--- libs/server/Transaction/TxnRespCommands.cs | 8 ++++---- 9 files changed, 33 insertions(+), 33 deletions(-) diff --git a/libs/server/Resp/AsyncProcessor.cs b/libs/server/Resp/AsyncProcessor.cs index 59d7419d1f3..5957326b1ed 100644 --- a/libs/server/Resp/AsyncProcessor.cs +++ b/libs/server/Resp/AsyncProcessor.cs @@ -49,7 +49,7 @@ void NetworkGETPending(ref TGarnetApi storageApi) { unsafe { - WriteError($"ASYNC {asyncStarted}"); + WriteLargeError($"ASYNC {asyncStarted}"); } if (++asyncStarted == 1) // first async operation on the session, create the IO continuation processor diff --git a/libs/server/Resp/BasicEtagCommands.cs b/libs/server/Resp/BasicEtagCommands.cs index b10a1e505ae..9d7391e7177 100644 --- a/libs/server/Resp/BasicEtagCommands.cs +++ b/libs/server/Resp/BasicEtagCommands.cs @@ -207,7 +207,7 @@ private bool NetworkSetETagConditional(RespCommand cmd, ref TGarnetA if (!errorMessage.IsEmpty) { - WriteError(errorMessage); + WriteLargeError(errorMessage); return true; } diff --git a/libs/server/Resp/Objects/ListCommands.cs b/libs/server/Resp/Objects/ListCommands.cs index 4a0a4cb385d..1cdd14d7d2c 100644 --- a/libs/server/Resp/Objects/ListCommands.cs +++ b/libs/server/Resp/Objects/ListCommands.cs @@ -255,13 +255,13 @@ private unsafe bool ListPopMultiple(ref TGarnetApi storageApi) case GarnetStatus.OK: WriteArrayLength(2); - WriteBulkString(key.Span); + WriteLargeBulkString(key.Span); WriteArrayLength(elements.Length); foreach (var element in elements) { - WriteBulkString(element.Span); + WriteLargeBulkString(element.Span); } break; @@ -321,9 +321,9 @@ private bool ListBlockingPop(RespCommand command) { WriteArrayLength(2); - WriteBulkString(new Span(result.Key)); + WriteLargeBulkString(new Span(result.Key)); - WriteBulkString(new Span(result.Item)); + WriteLargeBulkString(new Span(result.Item)); } return true; @@ -423,7 +423,7 @@ private bool ListBlockingMove(ArgSlice srcKey, ArgSlice dstKey, } else { - WriteBulkString(new Span(result.Item)); + WriteLargeBulkString(result.Item); } return true; @@ -753,7 +753,7 @@ private bool ListMove(ref TGarnetApi storageApi) case GarnetStatus.OK: if (node != null) { - WriteBulkString(node); + WriteLargeBulkString(node); } else { @@ -794,7 +794,7 @@ private bool ListRightPopLeftPush(ref TGarnetApi storageApi) case GarnetStatus.OK: if (node != null) { - WriteBulkString(node); + WriteLargeBulkString(node); } else { @@ -974,14 +974,14 @@ private unsafe bool ListBlockingPopMultiple() WriteArrayLength(2); - WriteBulkString(result.Key); + WriteLargeBulkString(result.Key); var elements = result.Items; WriteArrayLength(elements.Length); foreach (var element in elements) { - WriteBulkString(element); + WriteLargeBulkString(element); } return true; diff --git a/libs/server/Resp/Objects/ObjectStoreUtils.cs b/libs/server/Resp/Objects/ObjectStoreUtils.cs index deecf312829..6568cb04b9a 100644 --- a/libs/server/Resp/Objects/ObjectStoreUtils.cs +++ b/libs/server/Resp/Objects/ObjectStoreUtils.cs @@ -46,7 +46,7 @@ private bool AbortWithWrongNumberOfArgumentsOrUnknownSubcommand(string subComman private bool AbortWithErrorMessage(ReadOnlySpan errorMessage) { // Print error message to result stream - WriteError(errorMessage); + WriteLargeError(errorMessage); return true; } diff --git a/libs/server/Resp/Objects/SetCommands.cs b/libs/server/Resp/Objects/SetCommands.cs index d051edb9e6e..39a6a38b7b0 100644 --- a/libs/server/Resp/Objects/SetCommands.cs +++ b/libs/server/Resp/Objects/SetCommands.cs @@ -87,7 +87,7 @@ private bool SetIntersect(ref TGarnetApi storageApi) foreach (var item in result) { - WriteBulkString(item); + WriteLargeBulkString(item); } } else @@ -238,7 +238,7 @@ private bool SetUnion(ref TGarnetApi storageApi) foreach (var item in result) { - WriteBulkString(item); + WriteLargeBulkString(item); } break; case GarnetStatus.WRONGTYPE: @@ -691,7 +691,7 @@ private bool SetDiff(ref TGarnetApi storageApi) foreach (var item in output) { - WriteBulkString(item); + WriteLargeBulkString(item); } } break; diff --git a/libs/server/Resp/Objects/SortedSetCommands.cs b/libs/server/Resp/Objects/SortedSetCommands.cs index 00c98784bba..4ad13825504 100644 --- a/libs/server/Resp/Objects/SortedSetCommands.cs +++ b/libs/server/Resp/Objects/SortedSetCommands.cs @@ -474,7 +474,7 @@ private unsafe bool SortedSetMPop(ref TGarnetApi storageApi) WriteArrayLength(2); // Write key - WriteBulkString(poppedKey.ReadOnlySpan); + WriteLargeBulkString(poppedKey.ReadOnlySpan); // Write array of member-score pairs WriteArrayLength(pairs.Length); @@ -482,7 +482,7 @@ private unsafe bool SortedSetMPop(ref TGarnetApi storageApi) foreach (var (member, score) in pairs) { WriteArrayLength(2); - WriteBulkString(member.ReadOnlySpan); + WriteLargeBulkString(member.ReadOnlySpan); if (respProtocolVersion >= 3) { @@ -490,7 +490,7 @@ private unsafe bool SortedSetMPop(ref TGarnetApi storageApi) } else { - WriteBulkString(score.ReadOnlySpan); + WriteLargeBulkString(score.ReadOnlySpan); } } } @@ -935,7 +935,7 @@ private unsafe bool SortedSetDifference(ref TGarnetApi storageApi) if (respProtocolVersion >= 3 && includeWithScores) WriteArrayLength(2); - WriteBulkString(element); + WriteLargeBulkString(element); if (includeWithScores) { @@ -1101,7 +1101,7 @@ private unsafe bool SortedSetIntersect(ref TGarnetApi storageApi) WriteArrayLength(2); } - WriteBulkString(element); + WriteLargeBulkString(element); if (includeWithScores) { @@ -1381,7 +1381,7 @@ private unsafe bool SortedSetUnion(ref TGarnetApi storageApi) WriteArrayLength(2); } - WriteBulkString(element); + WriteLargeBulkString(element); if (includeWithScores) { @@ -1536,9 +1536,9 @@ private unsafe bool SortedSetBlockingPop(RespCommand command) { WriteArrayLength(3); - WriteBulkString(result.Key); + WriteLargeBulkString(result.Key); - WriteBulkString(result.Item); + WriteLargeBulkString(result.Item); WriteDoubleNumeric(result.Score); } @@ -1645,14 +1645,14 @@ private unsafe bool SortedSetBlockingMPop() // Write array with 2 elements: key and array of member-score pairs WriteArrayLength(2); - WriteBulkString(result.Key); + WriteLargeBulkString(result.Key); WriteArrayLength(result.Items.Length); for (var i = 0; i < result.Items.Length; ++i) { WriteArrayLength(2); - WriteBulkString(result.Items[i]); + WriteLargeBulkString(result.Items[i]); WriteDoubleNumeric(result.Scores[i]); } diff --git a/libs/server/Resp/Parser/RespCommand.cs b/libs/server/Resp/Parser/RespCommand.cs index 629f1143bfc..411fd1c9bdf 100644 --- a/libs/server/Resp/Parser/RespCommand.cs +++ b/libs/server/Resp/Parser/RespCommand.cs @@ -2909,7 +2909,7 @@ private RespCommand ArrayParseCommand(bool writeErrorOnFailure, ref int count, r { if (!specificErrorMessage.IsEmpty) { - WriteError(specificErrorMessage); + WriteLargeError(specificErrorMessage); } else { diff --git a/libs/server/Resp/RespServerSession.cs b/libs/server/Resp/RespServerSession.cs index c8c2295de6e..f9cb5d9382a 100644 --- a/libs/server/Resp/RespServerSession.cs +++ b/libs/server/Resp/RespServerSession.cs @@ -490,7 +490,7 @@ public override int TryConsumeMessages(byte* reqBuffer, int bytesReceived) logger?.Log(ex.LogLevel, ex, "Aborting open session due to RESP parsing error"); // Forward parsing error as RESP error - WriteError($"ERR Protocol Error: {ex.Message}"); + WriteLargeError($"ERR Protocol Error: {ex.Message}"); // Send message and dispose the network sender to end the session if (dcurr > networkSender.GetResponseObjectHead()) @@ -507,7 +507,7 @@ public override int TryConsumeMessages(byte* reqBuffer, int bytesReceived) // Forward Garnet error as RESP error if (ex.ClientResponse) { - WriteError($"ERR Garnet Exception: {ex.Message}"); + WriteLargeError($"ERR Garnet Exception: {ex.Message}"); } // Send message and dispose the network sender to end the session @@ -1158,7 +1158,7 @@ private bool IsCommandArityValid(string cmdName, int arity, int count) if ((arity > 0 && count != arity - 1) || (arity < 0 && count < -arity - 1)) { - WriteError(string.Format(CmdStrings.GenericErrWrongNumArgs, cmdName)); + WriteLargeError(string.Format(CmdStrings.GenericErrWrongNumArgs, cmdName)); return false; } diff --git a/libs/server/Transaction/TxnRespCommands.cs b/libs/server/Transaction/TxnRespCommands.cs index e3564c8092d..d86a19f02ff 100644 --- a/libs/server/Transaction/TxnRespCommands.cs +++ b/libs/server/Transaction/TxnRespCommands.cs @@ -126,7 +126,7 @@ private bool NetworkSKIP(RespCommand cmd) if (invalidNumArgs) { var err = string.Format(CmdStrings.GenericErrWrongNumArgs, RespCommandsInfo.GetRespCommandName(cmd)); - WriteError(err); + WriteLargeError(err); txnManager.Abort(); return true; } @@ -150,7 +150,7 @@ private bool NetworkSKIP(RespCommand cmd) if (abort) { - WriteError(errMsg); + WriteLargeError(errMsg); txnManager.Abort(); return true; } @@ -158,7 +158,7 @@ private bool NetworkSKIP(RespCommand cmd) if (cmd == RespCommand.DEBUG && !CanRunDebug()) { - WriteError(System.Text.Encoding.ASCII.GetBytes(string.Format( + WriteLargeError(System.Text.Encoding.ASCII.GetBytes(string.Format( CmdStrings.GenericErrCommandDisallowedWithOption, RespCommand.DEBUG, "enable-debug-command"))); txnManager.Abort(); @@ -287,7 +287,7 @@ private bool NetworkRUNTXP() if ((arity > 0 && count != arity) || (arity < 0 && count < -arity)) { var expectedParams = arity > 0 ? arity - 1 : -arity - 1; - WriteError(string.Format(CmdStrings.GenericErrWrongNumArgsTxn, txId, expectedParams, count - 1)); + WriteLargeError(string.Format(CmdStrings.GenericErrWrongNumArgsTxn, txId, expectedParams, count - 1)); } else TryTransactionProc((byte)txId, proc, 1); From 57105929ecfcea9126aef11d8336f8b300fccc4d Mon Sep 17 00:00:00 2001 From: Kevin Montrose Date: Fri, 8 May 2026 13:31:13 -0400 Subject: [PATCH 11/16] even more conversion --- .../Metrics/Latency/RespLatencyCommands.cs | 4 +- .../Metrics/Slowlog/RespSlowlogCommands.cs | 6 +-- libs/server/Resp/AdminCommands.cs | 2 +- libs/server/Resp/ArrayCommands.cs | 2 +- libs/server/Resp/Bitmap/BitmapCommands.cs | 2 +- libs/server/Resp/RespServerSessionOutput.cs | 50 +++++++++++++++++++ libs/server/ServerConfig.cs | 2 +- 7 files changed, 59 insertions(+), 9 deletions(-) diff --git a/libs/server/Metrics/Latency/RespLatencyCommands.cs b/libs/server/Metrics/Latency/RespLatencyCommands.cs index aa53fc80b0d..b9db7b5edd9 100644 --- a/libs/server/Metrics/Latency/RespLatencyCommands.cs +++ b/libs/server/Metrics/Latency/RespLatencyCommands.cs @@ -25,7 +25,7 @@ private bool NetworkLatencyHelp() foreach (string command in latencyCommands) { - WriteSimpleString(command); + WriteLargeSimpleString(command); } return true; @@ -69,7 +69,7 @@ private bool NetworkLatencyHistogram() { var garnetLatencyMetrics = storeWrapper.monitor?.GlobalMetrics.globalLatencyMetrics; string response = garnetLatencyMetrics != null ? garnetLatencyMetrics.GetRespHistograms(events) : "*0\r\n"; - WriteAsciiDirect(response); + WriteLargeAsciiDirect(response); } return true; diff --git a/libs/server/Metrics/Slowlog/RespSlowlogCommands.cs b/libs/server/Metrics/Slowlog/RespSlowlogCommands.cs index f5f7d50dde5..634f0270bbc 100644 --- a/libs/server/Metrics/Slowlog/RespSlowlogCommands.cs +++ b/libs/server/Metrics/Slowlog/RespSlowlogCommands.cs @@ -27,7 +27,7 @@ private bool NetworkSlowLogHelp() foreach (string command in slowLogCommands) { - WriteSimpleString(command); + WriteLargeSimpleString(command); } return true; @@ -87,8 +87,8 @@ private bool NetworkSlowLogGet() WriteLargeBulkString(sps.GetArgSliceByRef(i).Span); } } - WriteAsciiBulkString(entry.ClientIpPort); - WriteAsciiBulkString(entry.ClientName); + WriteLargeAsciiBulkString(entry.ClientIpPort); + WriteLargeAsciiBulkString(entry.ClientName); } return true; } diff --git a/libs/server/Resp/AdminCommands.cs b/libs/server/Resp/AdminCommands.cs index 420f3ab1d2c..fb3e7a24e9f 100644 --- a/libs/server/Resp/AdminCommands.cs +++ b/libs/server/Resp/AdminCommands.cs @@ -759,7 +759,7 @@ private bool NetworkDebug() WriteArrayLength(help.Length); foreach (var line in help) { - WriteSimpleString(line); + WriteLargeSimpleString(line); } return true; } diff --git a/libs/server/Resp/ArrayCommands.cs b/libs/server/Resp/ArrayCommands.cs index aa535b90e00..c361b0e774f 100644 --- a/libs/server/Resp/ArrayCommands.cs +++ b/libs/server/Resp/ArrayCommands.cs @@ -317,7 +317,7 @@ private bool NetworkTYPE(ref TGarnetApi storageApi) if (status == GarnetStatus.OK) { - WriteSimpleString(typeName); + WriteLargeSimpleString(typeName); } else { diff --git a/libs/server/Resp/Bitmap/BitmapCommands.cs b/libs/server/Resp/Bitmap/BitmapCommands.cs index fd3a70444e1..23c7c2a659b 100644 --- a/libs/server/Resp/Bitmap/BitmapCommands.cs +++ b/libs/server/Resp/Bitmap/BitmapCommands.cs @@ -340,7 +340,7 @@ private bool NetworkStringBitPosition(ref TGarnetApi storageApi) else if (status == GarnetStatus.NOTFOUND) { var resp = bSetValSlice[0] == '0' ? CmdStrings.RESP_RETURN_VAL_0 : CmdStrings.RESP_RETURN_VAL_N1; - WriteDirect(resp); + WriteLargeDirect(resp); } return true; diff --git a/libs/server/Resp/RespServerSessionOutput.cs b/libs/server/Resp/RespServerSessionOutput.cs index b4af767ab6b..ed0ec66e880 100644 --- a/libs/server/Resp/RespServerSessionOutput.cs +++ b/libs/server/Resp/RespServerSessionOutput.cs @@ -3,6 +3,8 @@ using System; using System.Buffers; +using System.ComponentModel; +using System.Diagnostics; using System.Runtime.CompilerServices; using System.Text; using Garnet.common; @@ -49,10 +51,32 @@ internal void WriteEnumAsBulkString(TEnum value) [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void WriteAsciiDirect(ReadOnlySpan message) { + Debug.Assert(!message.ContainsAnyExceptInRange((char)0, (char)128), "Only ASCII data allowed"); + while (!RespWriteUtils.TryWriteAsciiDirect(message, ref dcurr, dend)) SendAndReset(); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void WriteLargeAsciiDirect(ReadOnlySpan message) + { + Debug.Assert(!message.ContainsAnyExceptInRange((char)0, (char)128), "Only ASCII data allowed"); + + var buff = ArrayPool.Shared.Rent(message.Length); + try + { + var written = Encoding.ASCII.GetBytes(message, buff); + + var asSpan = buff.AsSpan()[..written]; + + WriteLargeDirect(asSpan); + } + finally + { + ArrayPool.Shared.Return(buff); + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void WriteArrayLength(int len) { @@ -351,6 +375,32 @@ internal void WriteSimpleString(ReadOnlySpan simpleString) SendAndReset(); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void WriteLargeSimpleString(ReadOnlySpan simpleString) + { + if ((int)(dend - dcurr) < 1) + { + SendAndReset(); + } + + *dcurr = (byte)'+'; + + var buff = ArrayPool.Shared.Rent(Encoding.UTF8.GetByteCount(simpleString)); + try + { + var written = Encoding.UTF8.GetBytes(simpleString, buff); + var asSpan = buff.AsSpan()[..written]; + + WriteLargeDirect(asSpan); + } + finally + { + ArrayPool.Shared.Return(buff); + } + + while (!RespWriteUtils.TryWriteNewLine(ref dcurr, dend)) + SendAndReset(); + } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void WriteUtf8BulkString(ReadOnlySpan chars) diff --git a/libs/server/ServerConfig.cs b/libs/server/ServerConfig.cs index 77eae167c94..0cccce32c96 100644 --- a/libs/server/ServerConfig.cs +++ b/libs/server/ServerConfig.cs @@ -93,7 +93,7 @@ ReadOnlySpan GetDatabases() return Encoding.ASCII.GetBytes($"$9\r\ndatabases\r\n${databases.Length}\r\n{databases}\r\n"); } - WriteDirect(parameterValue); + WriteLargeDirect(parameterValue); } } else From 500bd16ddf8572bbadade87442f009ed13e271aa Mon Sep 17 00:00:00 2001 From: Kevin Montrose Date: Fri, 8 May 2026 14:08:18 -0400 Subject: [PATCH 12/16] everything compiles now --- libs/common/RespWriteUtils.cs | 28 ++++++++++++ libs/server/Metrics/Info/InfoCommand.cs | 2 +- libs/server/Resp/AdminCommands.cs | 6 +-- libs/server/Resp/BasicCommands.cs | 12 ++--- libs/server/Resp/ClientCommands.cs | 6 +-- libs/server/Resp/RespServerSessionOutput.cs | 50 ++++++++++++++++++++- 6 files changed, 89 insertions(+), 15 deletions(-) diff --git a/libs/common/RespWriteUtils.cs b/libs/common/RespWriteUtils.cs index 12e58234712..df4f1155e7a 100644 --- a/libs/common/RespWriteUtils.cs +++ b/libs/common/RespWriteUtils.cs @@ -764,6 +764,34 @@ public static bool TryWriteVerbatimString(ReadOnlySpan str, ReadOnlySpan + /// Write verbtatim string header. + /// + /// That is ={len}\r\n{ext}: + /// + public static bool TryWriteVerbatimStringHeader(ReadOnlySpan str, ReadOnlySpan ext, ref byte* curr, byte* end) + { + Debug.Assert(ext.Length == 3); + + // Verbatim string length includes the type metadata. + // So ext (3 bytes) + ':' (1 byte separator) + str + var actualLength = 3 + 1 + str.Length; + var itemDigits = NumUtils.CountDigits(actualLength); + + var headerLen = 1 + itemDigits + 2 + 3 + 1; + if (headerLen > (int)(end - curr)) + return false; + + *curr++ = (byte)'='; + NumUtils.WriteInt32(actualLength, itemDigits, ref curr); + WriteNewline(ref curr); + ext.CopyTo(new Span(curr, 3)); + curr += 3; + *curr++ = (byte)':'; + + return true; + } + /// /// Write RESP3 true /// diff --git a/libs/server/Metrics/Info/InfoCommand.cs b/libs/server/Metrics/Info/InfoCommand.cs index 306f9a5c68c..23bdadc56f8 100644 --- a/libs/server/Metrics/Info/InfoCommand.cs +++ b/libs/server/Metrics/Info/InfoCommand.cs @@ -69,7 +69,7 @@ private bool NetworkINFO() var info = garnetInfo.GetRespInfo(sectionsArr, activeDbId, storeWrapper); if (!string.IsNullOrEmpty(info)) { - WriteVerbatimString(Encoding.ASCII.GetBytes(info)); + WriteLargeVerbatimString(Encoding.ASCII.GetBytes(info)); } else { diff --git a/libs/server/Resp/AdminCommands.cs b/libs/server/Resp/AdminCommands.cs index fb3e7a24e9f..440bdbfbdb2 100644 --- a/libs/server/Resp/AdminCommands.cs +++ b/libs/server/Resp/AdminCommands.cs @@ -802,7 +802,7 @@ private bool NetworkROLE() foreach (var replice in replicaInfo) { WriteArrayLength(3); - WriteAsciiBulkString(replice.address); + WriteLargeAsciiBulkString(replice.address); WriteInt32(replice.port); WriteInt64(replice.replication_offset); } @@ -815,11 +815,11 @@ private bool NetworkROLE() WriteBulkString(CmdStrings.slave); - WriteAsciiBulkString(role.address); + WriteLargeAsciiBulkString(role.address); WriteInt32(role.port); - WriteAsciiBulkString(role.replication_state); + WriteLargeAsciiBulkString(role.replication_state); WriteInt64(role.replication_offset); } diff --git a/libs/server/Resp/BasicCommands.cs b/libs/server/Resp/BasicCommands.cs index 195060d5c1c..f42156e17bf 100644 --- a/libs/server/Resp/BasicCommands.cs +++ b/libs/server/Resp/BasicCommands.cs @@ -331,7 +331,7 @@ private bool NetworkSetRange(ref TGarnetApi storageApi) storageApi.SETRANGE(key, ref input, ref output); - WriteIntegerFromBytes(outputBuffer.Slice(0, output.Length)); + WriteLargeIntegerFromBytes(outputBuffer.Slice(0, output.Length)); return true; } @@ -732,7 +732,7 @@ private bool NetworkIncrement(RespCommand cmd, ref TGarnetApi storag switch (errorFlag) { case OperationError.SUCCESS: - WriteIntegerFromBytes(outputBuffer.Slice(0, output.Length)); + WriteLargeIntegerFromBytes(outputBuffer.Slice(0, output.Length)); break; case OperationError.NAN_OR_INFINITY: case OperationError.INVALID_TYPE: @@ -799,7 +799,7 @@ private bool NetworkAppend(ref TGarnetApi storageApi) storageApi.APPEND(ref sbKey, ref input, ref output); - WriteIntegerFromBytes(outputBuffer.Slice(0, output.Length)); + WriteLargeIntegerFromBytes(outputBuffer.Slice(0, output.Length)); return true; } @@ -1327,7 +1327,7 @@ private bool NetworkTIME() var uSeconds = utcTime.ToString("ffffff"); var response = $"*2\r\n${seconds.ToString().Length}\r\n{seconds}\r\n${uSeconds.Length}\r\n{uSeconds}\r\n"; - WriteAsciiDirect(response); + WriteLargeAsciiDirect(response); return true; } @@ -1544,14 +1544,14 @@ void ProcessHelloCommand(byte? respProtocolVersion, ReadOnlySpan username, WriteMapLength(helloResult.Length + 1); for (var i = 0; i < helloResult.Length; i++) { - WriteAsciiBulkString(helloResult[i].Item1); + WriteLargeAsciiBulkString(helloResult[i].Item1); if (helloResult[i].Item2 is long value) { WriteInt64(value); } else { - WriteAsciiBulkString(helloResult[i].Item2.ToString()); + WriteLargeAsciiBulkString(helloResult[i].Item2.ToString()); } } WriteBulkString(CmdStrings.modules); diff --git a/libs/server/Resp/ClientCommands.cs b/libs/server/Resp/ClientCommands.cs index d9657ff8281..372ca0d5c59 100644 --- a/libs/server/Resp/ClientCommands.cs +++ b/libs/server/Resp/ClientCommands.cs @@ -160,7 +160,7 @@ private bool NetworkCLIENTLIST() resultSb.Append("\n"); var result = resultSb.ToString(); - WriteVerbatimString(Encoding.ASCII.GetBytes(result)); + WriteLargeVerbatimString(Encoding.ASCII.GetBytes(result)); return true; } @@ -194,7 +194,7 @@ private bool NetworkCLIENTINFO() resultSb.Append("\n"); var result = resultSb.ToString(); - WriteVerbatimString(Encoding.ASCII.GetBytes(result)); + WriteLargeVerbatimString(Encoding.ASCII.GetBytes(result)); return true; } @@ -502,7 +502,7 @@ private bool NetworkCLIENTGETNAME() } else { - WriteAsciiBulkString(this.clientName); + WriteLargeAsciiBulkString(this.clientName); } return true; diff --git a/libs/server/Resp/RespServerSessionOutput.cs b/libs/server/Resp/RespServerSessionOutput.cs index ed0ec66e880..5440b5a8163 100644 --- a/libs/server/Resp/RespServerSessionOutput.cs +++ b/libs/server/Resp/RespServerSessionOutput.cs @@ -3,7 +3,6 @@ using System; using System.Buffers; -using System.ComponentModel; using System.Diagnostics; using System.Runtime.CompilerServices; using System.Text; @@ -34,10 +33,31 @@ internal void ProcessOutput(SpanByteAndMemory output) [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void WriteAsciiBulkString(ReadOnlySpan message) { + Debug.Assert(!message.ContainsAnyExceptInRange((char)0, (char)127), "Can only write all ASCII values using this method"); + while (!RespWriteUtils.TryWriteAsciiBulkString(message, ref dcurr, dend)) SendAndReset(); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void WriteLargeAsciiBulkString(ReadOnlySpan message) + { + Debug.Assert(!message.ContainsAnyExceptInRange((char)0, (char)127), "Can only write all ASCII values using this method"); + + var buff = ArrayPool.Shared.Rent(message.Length); + try + { + var written = Encoding.ASCII.GetBytes(message, buff); + var asSpan = buff.AsSpan()[..written]; + + WriteLargeBulkString(asSpan); + } + finally + { + ArrayPool.Shared.Return(buff); + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void WriteEnumAsBulkString(TEnum value) where TEnum : struct, Enum @@ -266,8 +286,11 @@ internal void WriteInt64AsBulkString(long value) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void WriteIntegerFromBytes(ReadOnlySpan integerBytes) + internal void WriteLargeIntegerFromBytes(ReadOnlySpan integerBytes) { + // Despite being "Large" this is actually fixed size - could clean that up later + Debug.Assert(integerBytes.Length <= 20, "64-bit integer should not have more than 20 digits"); + while (!RespWriteUtils.TryWriteIntegerFromBytes(integerBytes, ref dcurr, dend)) SendAndReset(); } @@ -423,5 +446,28 @@ internal void WriteVerbatimString(scoped ReadOnlySpan item, scoped ReadOnl SendAndReset(); } } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void WriteLargeVerbatimString(scoped ReadOnlySpan item, scoped ReadOnlySpan ext = default) + { + if (respProtocolVersion >= 3) + { + ext = ext.IsEmpty ? RespStrings.VerbatimTxt : ext; + + while (!RespWriteUtils.TryWriteVerbatimStringHeader(item, ext, ref dcurr, dend)) + SendAndReset(); + + // Write out item + WriteLargeDirect(item); + + while (!RespWriteUtils.TryWriteNewLine(ref dcurr, dend)) + SendAndReset(); + } + else + { + // RESP2 just gets a bulk string + WriteLargeBulkString(item); + } + } } } \ No newline at end of file From 3081690e096775840aeea61dc3c89c00b14f0579 Mon Sep 17 00:00:00 2001 From: Kevin Montrose Date: Fri, 8 May 2026 16:28:27 -0400 Subject: [PATCH 13/16] add support for detecting a constant has grown too large in CmdStrings --- ...seLargeOrConstantsForRespWritesAnalyzer.cs | 147 +++++++++++++++++- libs/server/Resp/CmdStrings.cs | 5 + 2 files changed, 149 insertions(+), 3 deletions(-) diff --git a/analyzer/Garnet.analyzers/UseLargeOrConstantsForRespWritesAnalyzer.cs b/analyzer/Garnet.analyzers/UseLargeOrConstantsForRespWritesAnalyzer.cs index bb258263718..753c77b06a8 100644 --- a/analyzer/Garnet.analyzers/UseLargeOrConstantsForRespWritesAnalyzer.cs +++ b/analyzer/Garnet.analyzers/UseLargeOrConstantsForRespWritesAnalyzer.cs @@ -26,9 +26,10 @@ public sealed class UseLargeOrConstantsForRespWritesAnalyzer : DiagnosticAnalyze private static DiagnosticDescriptor AddLargeOverridesForVariableSizeResponses { get; } = new("GARNET0003", "Add Large Override For Variable Size Responses", "Add and use {0} to RespServerSessionOutput.cs in place of using {1} for variable size responses", "Correctness", DiagnosticSeverity.Warning, isEnabledByDefault: true); private static DiagnosticDescriptor MoveOutputConstantsToCmdStrings { get; } = new("GARNET0004", "Move Output Constants To CmdStrings", "Add {0} to CmdStrings and use it in place of an inline literal", "Correctness", DiagnosticSeverity.Warning, isEnabledByDefault: true); private static DiagnosticDescriptor UseExistingConstantInCmdStrings { get; } = new("GARNET0005", "Use Existing Constants In CmdStrings", "Use CmdStrings.{0} instead of inline literal", "Correctness", DiagnosticSeverity.Warning, isEnabledByDefault: true); + private static DiagnosticDescriptor CmdStringsConstantTooLarge { get; } = new("GARNET0006", "CmdStrings Constant Too Large", "CmdStrings.{0} estimated serialization length of {1} exceeds minimum send buffer size of {2}", "Correctness", DiagnosticSeverity.Warning, isEnabledByDefault: true); /// - public override ImmutableArray SupportedDiagnostics { get; } = [UseLargeOverridesWithVariableSizeResponses, AddLargeOverridesForVariableSizeResponses, MoveOutputConstantsToCmdStrings, UseExistingConstantInCmdStrings]; + public override ImmutableArray SupportedDiagnostics { get; } = [UseLargeOverridesWithVariableSizeResponses, AddLargeOverridesForVariableSizeResponses, MoveOutputConstantsToCmdStrings, UseExistingConstantInCmdStrings, CmdStringsConstantTooLarge]; /// public override void Initialize(AnalysisContext context) @@ -82,7 +83,7 @@ public override void Initialize(AnalysisContext context) SyntaxKind.InvocationExpression ); - var knownConstants = BuildKnownConstantsLookup(cmdStringType, compilationStartContext.CancellationToken); + var knownConstants = BuildKnownConstantsLookup(cmdStringType, readOnlySpanByteType, compilationStartContext.CancellationToken); compilationStartContext.RegisterSyntaxNodeAction( syntaxNodeAction => @@ -91,6 +92,14 @@ public override void Initialize(AnalysisContext context) }, SyntaxKind.InvocationExpression ); + + compilationStartContext.RegisterSyntaxNodeAction( + syntaxNodeAction => + { + AnalyzeDeclarationForTooLargeCmdStringConstants(syntaxNodeAction, cmdStringType, knownConstants); + }, + SyntaxKind.ClassDeclaration + ); } } ); @@ -139,7 +148,7 @@ static Dictionary BuildWriteLargeLookup(INamedTypeSymbol respSer } // Build a map of literals ("foo" and "bar"u8) to the corresponding properties on CmdStrings - static Dictionary BuildKnownConstantsLookup(INamedTypeSymbol cmdStringsType, CancellationToken cancellation) + static Dictionary BuildKnownConstantsLookup(INamedTypeSymbol cmdStringsType, INamedTypeSymbol readOnlySpanByteType, CancellationToken cancellation) { var lookup = new Dictionary(); @@ -150,6 +159,11 @@ static Dictionary BuildKnownConstantsLookup(INamedTypeSymbol cmd continue; } + if (!SymbolEqualityComparer.Default.Equals(prop.Type, readOnlySpanByteType)) + { + continue; + } + var getDecl = prop.GetMethod.DeclaringSyntaxReferences.SingleOrDefault(); if (getDecl == null) { @@ -319,5 +333,132 @@ private static void AnalyzeCallerForNonCmdStringsConstants(SyntaxNodeAnalysisCon context.ReportDiagnostic(diag); } + + /// + /// Checks for constants in CmdStrings that exceed CmdStrings.MaximumConstantSize in bytes. + /// + /// Constants that start with a RESP sigil are assumed to be literal, everything else is treated like a Bulk String. + /// + private static void AnalyzeDeclarationForTooLargeCmdStringConstants(SyntaxNodeAnalysisContext context, INamedTypeSymbol cmdStringsType, Dictionary constantLookup) + { + if (context.Node is not ClassDeclarationSyntax classDecl) + { + return; + } + + // Quick check for CmdStrings name + if (classDecl.Identifier.Text != "CmdStrings") + { + return; + } + + // Check for CmdStrings type + var declType = context.SemanticModel.GetDeclaredSymbol(classDecl); + if (declType is null || !SymbolEqualityComparer.Default.Equals(cmdStringsType, declType)) + { + return; + } + + // Get the MaximumConstantSize property + var maximumConstantSizeDecl = classDecl.Members.OfType().SingleOrDefault(static propDecl => propDecl.Identifier.Text == "MaximumConstantSize"); + if (maximumConstantSizeDecl is null) + { + return; + } + + // Get MaximumConstantSize literal value + var getDeclSyntax = maximumConstantSizeDecl.ExpressionBody; + if (getDeclSyntax is not ArrowExpressionClauseSyntax arrowDecl) + { + return; + } + + if (arrowDecl.Expression is not LiteralExpressionSyntax literal) + { + return; + } + + var maximumSizeLit = context.SemanticModel.GetConstantValue(literal); + if (!maximumSizeLit.HasValue) + { + return; + } + + var maximumSize = (int)maximumSizeLit.Value; + + // Consider all found constaints and calculate their effective length + foreach (var kv in constantLookup) + { + var lit = kv.Key; + var prop = kv.Value; + + if (!string.IsNullOrEmpty(lit) && lit[0] == '"') + { + lit = lit.Substring(1); + } + + if (!string.IsNullOrEmpty(lit) && lit.EndsWith("u8")) + { + lit = lit.Substring(0, lit.Length - 2); + } + + if (!string.IsNullOrEmpty(lit) && lit[lit.Length - 1] == '"') + { + lit = lit.Substring(0, lit.Length - 1); + } + + // Skip empty + if (string.IsNullOrEmpty(lit)) + { + continue; + } + + var maybeSigil = lit[0]; + var isRespEncoded = maybeSigil is '+' or '-' or ':' or '$' or '*' or '_' or '#' or ',' or '(' or '!' or '=' or '%' or '|' or '~' or '>'; + + int effectiveLength; + if (isRespEncoded) + { + effectiveLength = lit.Replace("\\", "").Length; + } + else + { + // Assume Bulk String: $\r\n\r\n + var rawLength = lit.Replace("\\", "").Length; + effectiveLength = 1 + CountDigits(rawLength) + 2 + rawLength + 2; + } + + if (effectiveLength > maximumSize) + { + var decl = classDecl.Members.OfType().FirstOrDefault(p => p.Identifier.Text == prop); + + if (decl is not null) + { + var diag = Diagnostic.Create(CmdStringsConstantTooLarge, decl.GetLocation(), prop, effectiveLength, maximumSize); + context.ReportDiagnostic(diag); + } + } + } + + // Figure out space needed to serialize a Bulk String length + static int CountDigits(int value) + { + value = value < 0 ? ((~value) + 1) : value; + + if (value < 10) return 1; + if (value < 100) return 2; + if (value < 1000) return 3; + if (value < 100000000L) + { + if (value < 1000000) + { + if (value < 10000) return 4; + return 5 + (value >= 100000 ? 1 : 0); + } + return 7 + (value >= 10000000L ? 1 : 0); + } + return 9 + (value >= 1000000000L ? 1 : 0); + } + } } } diff --git a/libs/server/Resp/CmdStrings.cs b/libs/server/Resp/CmdStrings.cs index 53247deac3b..198af3e4a19 100644 --- a/libs/server/Resp/CmdStrings.cs +++ b/libs/server/Resp/CmdStrings.cs @@ -10,6 +10,11 @@ namespace Garnet.server /// static partial class CmdStrings { + /// + /// Maximum size to allow - considered by the UseLargeOrConstantsForRespWritesAnalyzer. + /// + internal static int MaximumConstantSize => 512; + /// /// Request strings /// From 1414067730885c6d6c21ca44671e10639094c0c7 Mon Sep 17 00:00:00 2001 From: Kevin Montrose Date: Fri, 8 May 2026 17:25:43 -0400 Subject: [PATCH 14/16] analyzer for collapsable constants --- .../AvoidRespWriteUtilsAnalyzer.cs | 2 +- ...lapseRepeatedConstantWriteCallsAnalyzer.cs | 184 ++++++++++++++++++ 2 files changed, 185 insertions(+), 1 deletion(-) create mode 100644 analyzer/Garnet.analyzers/CollapseRepeatedConstantWriteCallsAnalyzer.cs diff --git a/analyzer/Garnet.analyzers/AvoidRespWriteUtilsAnalyzer.cs b/analyzer/Garnet.analyzers/AvoidRespWriteUtilsAnalyzer.cs index 32015543a9d..86cc5b993b3 100644 --- a/analyzer/Garnet.analyzers/AvoidRespWriteUtilsAnalyzer.cs +++ b/analyzer/Garnet.analyzers/AvoidRespWriteUtilsAnalyzer.cs @@ -63,7 +63,7 @@ static Dictionary BuildLookup(INamedTypeSymbol respWriteUtilsTyp continue; } - tryWriteMethods.Add(member.Name); + _ = tryWriteMethods.Add(member.Name); } var lookup = new Dictionary(); diff --git a/analyzer/Garnet.analyzers/CollapseRepeatedConstantWriteCallsAnalyzer.cs b/analyzer/Garnet.analyzers/CollapseRepeatedConstantWriteCallsAnalyzer.cs new file mode 100644 index 00000000000..f66bc6007d1 --- /dev/null +++ b/analyzer/Garnet.analyzers/CollapseRepeatedConstantWriteCallsAnalyzer.cs @@ -0,0 +1,184 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +using System.Collections.Generic; +using System.Collections.Immutable; +using System.IO; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Text; + +namespace Garnet.analyzers +{ + /// + /// Flag cases where two or more sequential calls to WriteXXX helpers (defined in RespServerSession) where the arguments are constants. + /// + /// IE. + /// WriteArrayLength(2) + /// WriteBulkString(CmdStrings.Foo) + /// + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public sealed class CollapseRepeatedConstantWriteCallsAnalyzer : DiagnosticAnalyzer + { + private static DiagnosticDescriptor CollapseRepeatedConstantWriteCalls { get; } = new("GARNET0007", "Collapse Repeated Constant Write Calls", "Define a CmdString constant for these {0} consecutive WriteXXX helper calls", "Correctness", DiagnosticSeverity.Warning, isEnabledByDefault: true); + + /// + public override ImmutableArray SupportedDiagnostics { get; } = [CollapseRepeatedConstantWriteCalls]; + + /// + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.ReportDiagnostics | GeneratedCodeAnalysisFlags.Analyze); + context.EnableConcurrentExecution(); + + context.RegisterCompilationStartAction( + compilationStartContext => + { + var respServerSessionType = compilationStartContext.Compilation.GetTypeByMetadataName("Garnet.server.RespServerSession"); + var cmdStringsType = compilationStartContext.Compilation.GetTypeByMetadataName("Garnet.server.CmdStrings"); + + if (respServerSessionType is not null && cmdStringsType is not null) + { + var lookup = BuildLookup(respServerSessionType); + + compilationStartContext.RegisterSyntaxNodeAction( + syntaxNodeContext => AnalyzeCodeBlockForRepeatedConstantWriteCalls(syntaxNodeContext, cmdStringsType, lookup), + SyntaxKind.Block + ); + } + } + ); + + // Lookup all WriteXXX methods declared in RespServerSessionOutput.cs + static HashSet BuildLookup(INamedTypeSymbol respServerSessionType) + { + var lookup = new HashSet(SymbolEqualityComparer.Default); + foreach (var member in respServerSessionType.GetMembers()) + { + if (member is not IMethodSymbol mtdSymbol) + { + continue; + } + + // Check that method is declared in RespServerSessionOutput.cs, otherwise we don't consider it a candidate for flagging + if (!member.DeclaringSyntaxReferences.Any(static decl => Path.GetFileName(decl.SyntaxTree.FilePath) == "RespServerSessionOutput.cs")) + { + continue; + } + + if (!member.Name.StartsWith("Write")) + { + continue; + } + + _ = lookup.Add(mtdSymbol); + } + + return lookup; + } + } + + /// + /// Raise the if 2 or more calls to methods in appear in a row with constant arguments. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("MicrosoftCodeAnalysisCorrectness", "RS1024:Symbols should be compared for equality", Justification = "writeMethods is constructed with the appropriate comparer")] + private static void AnalyzeCodeBlockForRepeatedConstantWriteCalls(SyntaxNodeAnalysisContext blockAnalyzeContext, INamedTypeSymbol cmdStringsType, HashSet writeMethods) + { + if (blockAnalyzeContext.Node is not BlockSyntax blockSyntax) + { + return; + } + + if (blockSyntax.Statements.Count <= 1) + { + return; + } + + // Walk through all statements and find runs of 2+ constant calls to methods in writeMethods + var constantWriteCalls = new List(); + foreach (var statement in blockSyntax.Statements) + { + // Skip trivia + if (statement.IsStructuredTrivia) + { + continue; + } + + // Only look at statements of the form: + // WriteXXX(); + if (statement is ExpressionStatementSyntax expressionSyntax && expressionSyntax.Expression is InvocationExpressionSyntax invocationSyntax && invocationSyntax.Expression is IdentifierNameSyntax mtdNameSyntax && mtdNameSyntax.Identifier.Text.StartsWith("Write")) + { + var methodType = blockAnalyzeContext.SemanticModel.GetSymbolInfo(mtdNameSyntax, blockAnalyzeContext.CancellationToken); + if (methodType.Symbol is not null && writeMethods.Contains(methodType.Symbol) && IsConstantMethodCall(blockAnalyzeContext, invocationSyntax, cmdStringsType)) + { + // This is a constant, remember it and move on + constantWriteCalls.Add(expressionSyntax); + continue; + } + } + + // If we encountered something we _didn't_ remember then we need to raise diagnostics for the previously found statements + if (constantWriteCalls.Count > 1) + { + RaiseDiagnostic(blockAnalyzeContext, constantWriteCalls); + } + + // A single statement cannot be collapsed + if (constantWriteCalls.Count != 0) + { + constantWriteCalls.Clear(); + } + } + + // At the end of the block, flush any previously found statements + if (constantWriteCalls.Count > 1) + { + RaiseDiagnostic(blockAnalyzeContext, constantWriteCalls); + } + + // Determine if a call to the given WriteXXX method should be considered a constant call + static bool IsConstantMethodCall(SyntaxNodeAnalysisContext blockAnalyzeContext, InvocationExpressionSyntax invocationSyntax, INamedTypeSymbol cmdStringsType) + { + foreach (var arg in invocationSyntax.ArgumentList.Arguments) + { + if (arg.Expression is MemberAccessExpressionSyntax memberAccess) + { + var beingAccessed = blockAnalyzeContext.SemanticModel.GetSymbolInfo(memberAccess.Expression, blockAnalyzeContext.CancellationToken); + if (beingAccessed.Symbol is not null && SymbolEqualityComparer.Default.Equals(beingAccessed.Symbol, cmdStringsType)) + { + continue; + } + } + + var constantValue = blockAnalyzeContext.SemanticModel.GetConstantValue(arg.Expression); + if (constantValue.HasValue) + { + continue; + } + + // Non-constant, can't collapse + return false; + } + + // All constants (including no argument), we can collapse + return true; + } + + // Raise a diagnostic that covers all the consecutive statements passed + static void RaiseDiagnostic(SyntaxNodeAnalysisContext blockAnalyzeContext, List toFlag) + { + var start = toFlag[0].GetLocation(); + var end = toFlag[toFlag.Count - 1].GetLocation(); + + var wholeSpan = new TextSpan(start.SourceSpan.Start, end.SourceSpan.End - start.SourceSpan.Start); + var location = Location.Create(start.SourceTree, wholeSpan); + + var diag = Diagnostic.Create(CollapseRepeatedConstantWriteCalls, location, toFlag.Count); + blockAnalyzeContext.ReportDiagnostic(diag); + } + } + } +} From 13742ff4f236eb1cc2f5531c5256ca559182d888 Mon Sep 17 00:00:00 2001 From: Kevin Montrose Date: Fri, 8 May 2026 17:46:31 -0400 Subject: [PATCH 15/16] fix flagged collapsable constant calls --- libs/server/Resp/ACLCommands.cs | 13 ++-- libs/server/Resp/AdminCommands.cs | 16 +---- libs/server/Resp/ArrayCommands.cs | 11 ++-- libs/server/Resp/AsyncProcessor.cs | 3 +- libs/server/Resp/BasicCommands.cs | 3 +- libs/server/Resp/CmdStrings.cs | 21 ++++++ libs/server/Resp/PubSubCommands.cs | 66 +++++++++++-------- .../Resp/Vector/RespServerSessionVectors.cs | 3 +- 8 files changed, 76 insertions(+), 60 deletions(-) diff --git a/libs/server/Resp/ACLCommands.cs b/libs/server/Resp/ACLCommands.cs index bcf2bc0fc4f..e6666843444 100644 --- a/libs/server/Resp/ACLCommands.cs +++ b/libs/server/Resp/ACLCommands.cs @@ -426,11 +426,14 @@ private bool NetworkAclGetUser() } else { - WriteMapLength(3); - - WriteBulkString(CmdStrings.flags); - - WriteSetLength(1); + if(respProtocolVersion == 3) + { + WriteDirect(CmdStrings.RESP_Map3_flags_Set1); + } + else + { + WriteDirect(CmdStrings.RESP_Array6_flags_Array1); + } if (user.IsEnabled) { diff --git a/libs/server/Resp/AdminCommands.cs b/libs/server/Resp/AdminCommands.cs index 440bdbfbdb2..84fae95b070 100644 --- a/libs/server/Resp/AdminCommands.cs +++ b/libs/server/Resp/AdminCommands.cs @@ -777,13 +777,7 @@ private bool NetworkROLE() if (!storeWrapper.serverOptions.EnableCluster) { - WriteArrayLength(3); - - WriteBulkString(CmdStrings.master); - - WriteInt32(0); - - WriteEmptyArray(); + WriteDirect(CmdStrings.RESP_Array3_master_0_EmptyArray); } else { @@ -791,9 +785,7 @@ private bool NetworkROLE() { var (replication_offset, replicaInfo) = storeWrapper.clusterProvider.GetPrimaryInfo(); - WriteArrayLength(3); - - WriteBulkString(CmdStrings.master); + WriteDirect(CmdStrings.RESP_Array3_master); WriteInt64(replication_offset); @@ -811,9 +803,7 @@ private bool NetworkROLE() { var role = storeWrapper.clusterProvider.GetReplicaInfo(); - WriteArrayLength(5); - - WriteBulkString(CmdStrings.slave); + WriteDirect(CmdStrings.RESP_Array5_slave); WriteLargeAsciiBulkString(role.address); diff --git a/libs/server/Resp/ArrayCommands.cs b/libs/server/Resp/ArrayCommands.cs index c361b0e774f..34ea9a63da5 100644 --- a/libs/server/Resp/ArrayCommands.cs +++ b/libs/server/Resp/ArrayCommands.cs @@ -284,13 +284,10 @@ private bool NetworkSCAN(ref TGarnetApi storageApi) // Prepare values for output if (keys.Count == 0) { - WriteArrayLength(2); - - // Number of keys "0" - WriteInt32AsBulkString(0); - - // Empty array - WriteEmptyArray(); + // 2 element array + // - Number of keys = "0" + // - Empty array + WriteDirect(CmdStrings.RESP_Array2_0String_EmptyArray); } else { diff --git a/libs/server/Resp/AsyncProcessor.cs b/libs/server/Resp/AsyncProcessor.cs index 5957326b1ed..2166f5cb1c2 100644 --- a/libs/server/Resp/AsyncProcessor.cs +++ b/libs/server/Resp/AsyncProcessor.cs @@ -102,8 +102,7 @@ async Task AsyncGetProcessorAsync(TGarnetApi storageApi) var o = completedOutputs.Current.Output; // We write async push response as an array: [ "async", "", "" ] - WritePushLength(3); - WriteBulkString(CmdStrings.async); + WriteDirect(CmdStrings.RESP_Push3_async); WriteInt32AsBulkString((int)completedOutputs.Current.Context); if (completedOutputs.Current.Status.Found) { diff --git a/libs/server/Resp/BasicCommands.cs b/libs/server/Resp/BasicCommands.cs index f42156e17bf..efe0cadfb39 100644 --- a/libs/server/Resp/BasicCommands.cs +++ b/libs/server/Resp/BasicCommands.cs @@ -1554,8 +1554,7 @@ void ProcessHelloCommand(byte? respProtocolVersion, ReadOnlySpan username, WriteLargeAsciiBulkString(helloResult[i].Item2.ToString()); } } - WriteBulkString(CmdStrings.modules); - WriteEmptyArray(); + WriteDirect(CmdStrings.RESP_modules_EmptyArray); } /// diff --git a/libs/server/Resp/CmdStrings.cs b/libs/server/Resp/CmdStrings.cs index 198af3e4a19..0bb10a4d032 100644 --- a/libs/server/Resp/CmdStrings.cs +++ b/libs/server/Resp/CmdStrings.cs @@ -574,5 +574,26 @@ static partial class CmdStrings public static ReadOnlySpan num_links => "num_links"u8; public static ReadOnlySpan size => "size"u8; public static ReadOnlySpan ERR_Vector_Set_partially_deleted => "ERR Vector Set is in a partially deleted state - re-execute DEL to complete deletion"u8; + public static ReadOnlySpan RESP_Map3_flags_Set1 => "%3\r\n$5\r\nflags\r\n~1\r\n"u8; + public static ReadOnlySpan RESP_Array6_flags_Array1 => "*6\r\n$5\r\nflags\r\n*1\r\n"u8; + public static ReadOnlySpan RESP_Array3_master_0_EmptyArray => "*3\r\n$6\r\nmaster\r\n:0\r\n*0\r\n"u8; + public static ReadOnlySpan RESP_Array3_master => "*3\r\n$6\r\nmaster\r\n"u8; + public static ReadOnlySpan RESP_Array5_slave => "*5\r\n$5\r\nslave\r\n"u8; + public static ReadOnlySpan RESP_Array2_0String_EmptyArray => "*2\r\n$1\r\n0\r\n*0\r\n"u8; + public static ReadOnlySpan RESP_Push3_async => ">3\r\n$5\r\nasync\r\n"u8; + public static ReadOnlySpan RESP_modules_EmptyArray => "$7\r\nmodules\r\n*0\r\n"u8; + public static ReadOnlySpan RESP_Push3_messages => ">3\r\n$8\r\nmessages\r\n"u8; + public static ReadOnlySpan RESP_Array3_messages => "*3\r\n$8\r\nmessages\r\n"u8; + public static ReadOnlySpan RESP_Push4_pmessages => ">4\r\n$9\r\npmessages\r\n"u8; + public static ReadOnlySpan RESP_Array4_pmessages => "*4\r\n$9\r\npmessages\r\n"u8; + public static ReadOnlySpan RESP_Array3_psubscribe => "*3\r\n$10\r\npsubscribe\r\n"u8; + public static ReadOnlySpan RESP_Array3_unsubscribe => "*3\r\n$11\r\nunsubscribe\r\n"u8; + public static ReadOnlySpan RESP_Array3_unsubscribe_null3 => "*3\r\n$11\r\nunsubscribe\r\n_\r\n"u8; + public static ReadOnlySpan RESP_Array3_unsubscribe_null2 => "*3\r\n$11\r\nunsubscribe\r\n$-1\r\n"u8; + public static ReadOnlySpan RESP_Array3_punsubscribe => "*3\r\n$12\r\npunsubscribe\r\n"u8; + + public static ReadOnlySpan RESP_Array3_punsubscribe_null3_0 => "*3\r\n$12\r\npunsubscribe\r\n_\r\n:0\r\n"u8; + public static ReadOnlySpan RESP_Array3_punsubscribe_null2_0 => "*3\r\n$12\r\npunsubscribe\r\n$-1\r\n:0\r\n"u8; + public static ReadOnlySpan RESP_Array14_quant_type => "*14\r\n$10\r\nquant_type\r\n"u8; } } \ No newline at end of file diff --git a/libs/server/Resp/PubSubCommands.cs b/libs/server/Resp/PubSubCommands.cs index 1d59c7f7887..87ba20a95d0 100644 --- a/libs/server/Resp/PubSubCommands.cs +++ b/libs/server/Resp/PubSubCommands.cs @@ -24,9 +24,14 @@ public override unsafe void Publish(ArgSlice key, ArgSlice value) { networkSender.EnterAndGetResponseObject(out dcurr, out dend); - WritePushLength(3); - - WriteBulkString(CmdStrings.message); + if (respProtocolVersion == 3) + { + WriteDirect(CmdStrings.RESP_Push3_messages); + } + else + { + WriteDirect(CmdStrings.RESP_Array3_messages); + } // Write key and value to the network WriteLargeBulkString(key.ReadOnlySpan); @@ -53,9 +58,14 @@ public override unsafe void PatternPublish(ArgSlice pattern, ArgSlice key, ArgSl { networkSender.EnterAndGetResponseObject(out dcurr, out dend); - WritePushLength(4); - - WriteBulkString(CmdStrings.pmessage); + if(respProtocolVersion == 3) + { + WriteDirect(CmdStrings.RESP_Push4_pmessages); + } + else + { + WriteDirect(CmdStrings.RESP_Array4_pmessages); + } // Write pattern, key, and value to the network WriteLargeBulkString(pattern.ReadOnlySpan); @@ -196,9 +206,7 @@ private bool NetworkPSUBSCRIBE() if (disabledBroker) continue; - WriteArrayLength(3); - - WriteBulkString(CmdStrings.psubscribe); + WriteDirect(CmdStrings.RESP_Array3_psubscribe); WriteLargeBulkString(key.ReadOnlySpan); if (subscribeBroker.PatternSubscribe(key, this)) @@ -231,9 +239,7 @@ private bool NetworkUNSUBSCRIBE() var channels = subscribeBroker.ListAllSubscriptions(this); foreach (var channel in channels) { - WriteArrayLength(3); - WriteBulkString(CmdStrings.unsubscribe); - + WriteDirect(CmdStrings.RESP_Array3_unsubscribe); WriteLargeBulkString(channel.ReadOnlySpan); if (subscribeBroker.Unsubscribe(channel, this)) @@ -243,10 +249,14 @@ private bool NetworkUNSUBSCRIBE() if (channels.Count == 0) { - WriteArrayLength(3); - WriteBulkString(CmdStrings.unsubscribe); - - WriteNull(); + if(respProtocolVersion == 3) + { + WriteDirect(CmdStrings.RESP_Array3_unsubscribe_null3); + } + else + { + WriteDirect(CmdStrings.RESP_Array3_unsubscribe_null2); + } WriteInt32(numActiveChannels); } @@ -263,8 +273,7 @@ private bool NetworkUNSUBSCRIBE() if (subscribeBroker != null) { - WriteArrayLength(3); - WriteBulkString(CmdStrings.unsubscribe); + WriteDirect(CmdStrings.RESP_Array3_unsubscribe); WriteLargeBulkString(key.ReadOnlySpan); if (subscribeBroker.Unsubscribe(new ByteArrayWrapper(key), this)) @@ -299,8 +308,7 @@ private bool NetworkPUNSUBSCRIBE() List channels = subscribeBroker.ListAllPatternSubscriptions(this); foreach (var channel in channels) { - WriteArrayLength(3); - WriteBulkString(CmdStrings.punsubscribe); + WriteDirect(CmdStrings.RESP_Array3_punsubscribe); WriteLargeBulkString(channel.ReadOnlySpan); @@ -312,13 +320,14 @@ private bool NetworkPUNSUBSCRIBE() if (channels.Count == 0) { - WriteArrayLength(3); - - WriteBulkString(CmdStrings.punsubscribe); - - WriteNull(); - - WriteInt32(0); + if(respProtocolVersion == 3) + { + WriteDirect(CmdStrings.RESP_Array3_punsubscribe_null3_0); + } + else + { + WriteDirect(CmdStrings.RESP_Array3_punsubscribe_null2_0); + } } if (numActiveChannels == 0) @@ -333,8 +342,7 @@ private bool NetworkPUNSUBSCRIBE() if (subscribeBroker != null) { - WriteArrayLength(3); - WriteBulkString(CmdStrings.punsubscribe); + WriteDirect(CmdStrings.RESP_Array3_punsubscribe); WriteLargeBulkString(key.ReadOnlySpan); if (subscribeBroker.PatternUnsubscribe(new ByteArrayWrapper(key), this)) diff --git a/libs/server/Resp/Vector/RespServerSessionVectors.cs b/libs/server/Resp/Vector/RespServerSessionVectors.cs index d8fa4318d72..f40e4230df4 100644 --- a/libs/server/Resp/Vector/RespServerSessionVectors.cs +++ b/libs/server/Resp/Vector/RespServerSessionVectors.cs @@ -1189,8 +1189,7 @@ private bool NetworkVINFO(ref TGarnetApi storageApi) _ => throw new GarnetException($"Invalid VectorDistanceMetricType: {distanceMetricType}"), }; - WriteArrayLength(14); - WriteSimpleString(CmdStrings.quant_type); + WriteDirect(CmdStrings.RESP_Array14_quant_type); WriteLargeBulkString(quantTypeSpan); WriteSimpleString(CmdStrings.distance_metric); WriteLargeBulkString(distanceMetricTypeSpan); From caecd1981ca6fe601183e7f38fac1ac2dc884bf6 Mon Sep 17 00:00:00 2001 From: Kevin Montrose Date: Fri, 8 May 2026 17:59:29 -0400 Subject: [PATCH 16/16] use Release for analyzers when not debugging them --- libs/server/Garnet.server.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/server/Garnet.server.csproj b/libs/server/Garnet.server.csproj index 4a137173748..820bfa87a3b 100644 --- a/libs/server/Garnet.server.csproj +++ b/libs/server/Garnet.server.csproj @@ -8,7 +8,7 @@ - +