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..86cc5b993b3 --- /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 compilationStartContext => + { + 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); + + compilationStartContext.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/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); + } + } + } +} 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/analyzer/Garnet.analyzers/UseLargeOrConstantsForRespWritesAnalyzer.cs b/analyzer/Garnet.analyzers/UseLargeOrConstantsForRespWritesAnalyzer.cs new file mode 100644 index 00000000000..753c77b06a8 --- /dev/null +++ b/analyzer/Garnet.analyzers/UseLargeOrConstantsForRespWritesAnalyzer.cs @@ -0,0 +1,464 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +using System.Collections.Generic; +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; +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("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, CmdStringsConstantTooLarge]; + + /// + 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 = BuildWriteLargeLookup(respServerSessionType); + + compilationStartContext.RegisterSyntaxNodeAction( + syntaxNodeContext => + { + AnalyzeCallerForVariableSizeViolations(syntaxNodeContext, cmdStringType, variableLengthTypes, suggestionLookup, syntaxNodeContext.CancellationToken); + }, + SyntaxKind.InvocationExpression + ); + + var knownConstants = BuildKnownConstantsLookup(cmdStringType, readOnlySpanByteType, compilationStartContext.CancellationToken); + + compilationStartContext.RegisterSyntaxNodeAction( + syntaxNodeAction => + { + AnalyzeCallerForNonCmdStringsConstants(syntaxNodeAction, cmdStringType, respServerSessionType, knownConstants, syntaxNodeAction.CancellationToken); + }, + SyntaxKind.InvocationExpression + ); + + compilationStartContext.RegisterSyntaxNodeAction( + syntaxNodeAction => + { + AnalyzeDeclarationForTooLargeCmdStringConstants(syntaxNodeAction, cmdStringType, knownConstants); + }, + SyntaxKind.ClassDeclaration + ); + } + } + ); + + // Build a map of WriteXXX -> WriteLargeXXX methods on RespServerSession + static Dictionary BuildWriteLargeLookup(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; + } + + // Build a map of literals ("foo" and "bar"u8) to the corresponding properties on CmdStrings + static Dictionary BuildKnownConstantsLookup(INamedTypeSymbol cmdStringsType, INamedTypeSymbol readOnlySpanByteType, CancellationToken cancellation) + { + var lookup = new Dictionary(); + + foreach (var prop in cmdStringsType.GetMembers().OfType()) + { + if (prop.GetMethod is null || prop.SetMethod is not null) + { + continue; + } + + if (!SymbolEqualityComparer.Default.Equals(prop.Type, readOnlySpanByteType)) + { + 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 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, 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") || 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, cancellation).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); + } + + /// + /// 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 && literal.Kind() is SyntaxKind.StringLiteralExpression or SyntaxKind.Utf8StringLiteralExpression) + { + 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); + } + + /// + /// 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/common/RespWriteUtils.cs b/libs/common/RespWriteUtils.cs index a953eb82637..df4f1155e7a 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); @@ -761,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 /// @@ -821,13 +852,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/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/Custom/CustomRespCommands.cs b/libs/server/Custom/CustomRespCommands.cs index 3b97d3c5e5a..dff0f223bf6 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 { @@ -45,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(CmdStrings.ERR_transaction_failed); } LatencyMetrics?.Stop(LatencyMetricsType.TX_PROC_LAT); @@ -72,16 +70,14 @@ 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 { if (output.MemoryOwner != null) SendAndReset(output.MemoryOwner, output.Length); else - while (!RespWriteUtils.TryWriteError($"ERR Command failed.", ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.ERR_command_failed); } } @@ -106,8 +102,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 +114,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 { @@ -157,15 +151,13 @@ 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) SendAndReset(output.SpanByteAndMemory.Memory, output.SpanByteAndMemory.Length); else - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); break; } } @@ -180,16 +172,14 @@ 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); 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/Garnet.server.csproj b/libs/server/Garnet.server.csproj index de66b80f463..820bfa87a3b 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/LuaCommands.cs b/libs/server/Lua/LuaCommands.cs index 07d6dcd41ab..b92dcc4b401 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 { @@ -199,8 +197,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 +214,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 +261,7 @@ private bool NetworkScriptFlush() } } - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); return true; } @@ -324,8 +319,7 @@ private bool NetworkScriptLoad() } } - while (!RespWriteUtils.TryWriteBulkString(digest, ref dcurr, dend)) - SendAndReset(); + WriteLargeBulkString(digest); } return true; @@ -340,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; } @@ -364,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(ex); // Exceptions shouldn't happen, so if they did the runner is probably in a bad state return false; 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..be83ba24f0d 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,27 @@ public void SendAndReset() BufferEnd = origin + scratchSpace.Length; curHead = origin + len; } + +#pragma warning disable GARNET0001 // These methods manually manage a buffer, so must directly use RespWriteUtils + + /// + 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(); + } + } +#pragma warning restore GARNET0001 } private static (int Start, ulong[] ByteMask) NoScriptDetails = InitializeNoScriptDetails(); @@ -468,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; } @@ -1156,8 +1194,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 +1540,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 +1552,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 +1574,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 +1679,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; } @@ -1944,8 +1974,10 @@ 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 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 +2005,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 +2033,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 +2065,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 +2139,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 +2186,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 +2215,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 +2300,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 +2384,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 +2460,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 +2537,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 +2564,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"); @@ -2599,6 +2631,7 @@ static unsafe bool TryWriteTableToArray(LuaRunner runner, bool canSend, ref TRes errConstStrIndex = -1; return fitInBuffer; } +#pragma warning restore GARNET0001 } /// diff --git a/libs/server/Metrics/Info/InfoCommand.cs b/libs/server/Metrics/Info/InfoCommand.cs index 0722fdd3b9e..23bdadc56f8 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(); + WriteLargeError($"ERR Invalid section {invalidSection}. Try INFO HELP"); return true; } @@ -61,8 +60,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 { @@ -71,12 +69,11 @@ 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 { - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_EMPTY, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_EMPTY); } } return true; @@ -86,12 +83,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(); + WriteLargeBulkString(sectionInfo); } } } diff --git a/libs/server/Metrics/Latency/RespLatencyCommands.cs b/libs/server/Metrics/Latency/RespLatencyCommands.cs index d786fd8e083..b9db7b5edd9 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(); + WriteLargeSimpleString(command); } return true; @@ -65,15 +63,13 @@ private bool NetworkLatencyHistogram() if (invalid) { - while (!RespWriteUtils.TryWriteError($"ERR Invalid event {invalidEvent}. Try LATENCY HELP", ref dcurr, dend)) - SendAndReset(); + WriteLargeError($"ERR Invalid event {invalidEvent}. Try LATENCY HELP"); } else { var garnetLatencyMetrics = storeWrapper.monitor?.GlobalMetrics.globalLatencyMetrics; string response = garnetLatencyMetrics != null ? garnetLatencyMetrics.GetRespHistograms(events) : "*0\r\n"; - while (!RespWriteUtils.TryWriteAsciiDirect(response, ref dcurr, dend)) - SendAndReset(); + WriteLargeAsciiDirect(response); } return true; @@ -111,8 +107,7 @@ private bool NetworkLatencyReset() if (invalid) { - while (!RespWriteUtils.TryWriteError($"ERR Invalid type {invalidEvent}", ref dcurr, dend)) - SendAndReset(); + WriteLargeError($"ERR Invalid type {invalidEvent}"); } else { @@ -122,8 +117,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..634f0270bbc 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(); + WriteLargeSimpleString(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); + WriteEnumAsBulkString(entry.Command); } 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); + WriteEnumAsBulkString(entry.Command); for (int i = 0; i < sps.Count; i++) { - while (!RespWriteUtils.TryWriteAsciiBulkString(sps.GetString(i), ref dcurr, dend)) - SendAndReset(); + WriteLargeBulkString(sps.GetArgSliceByRef(i).Span); } } - while (!RespWriteUtils.TryWriteAsciiBulkString(entry.ClientIpPort, ref dcurr, dend)) - SendAndReset(); - while (!RespWriteUtils.TryWriteAsciiBulkString(entry.ClientName, ref dcurr, dend)) - SendAndReset(); + WriteLargeAsciiBulkString(entry.ClientIpPort); + WriteLargeAsciiBulkString(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..e6666843444 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; @@ -20,8 +19,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; @@ -31,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; } @@ -64,13 +60,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(); + WriteLargeBulkString(userHandle.Value.User.DescribeUser()); } return true; @@ -93,13 +87,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(); + WriteLargeBulkString(user.Key); } return true; @@ -121,12 +113,11 @@ private bool NetworkAclCat() return true; var categories = ACLParser.ListCategories(); - RespWriteUtils.TryWriteArrayLength(categories.Count, ref dcurr, dend); + WriteArrayLength(categories.Count); foreach (var category in categories) { - while (!RespWriteUtils.TryWriteAsciiBulkString(category, ref dcurr, dend)) - SendAndReset(); + WriteLargeBulkString(category); } return true; @@ -198,14 +189,12 @@ private bool NetworkAclSetUser() catch (ACLException exception) { // Abort command execution - while (!RespWriteUtils.TryWriteError($"ERR {exception.Message}", ref dcurr, dend)) - SendAndReset(); + WriteError(exception); return true; } - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); return true; } @@ -246,15 +235,13 @@ private bool NetworkAclDelUser() logger?.LogDebug("ACLException: {message}", exception.Message); // Abort command execution - while (!RespWriteUtils.TryWriteError($"ERR {exception.Message}", ref dcurr, dend)) - SendAndReset(); + WriteError(exception); return true; } // Return the number of successful deletes - while (!RespWriteUtils.TryWriteInt32(successfulDeletes, ref dcurr, dend)) - SendAndReset(); + WriteInt32(successfulDeletes); return true; } @@ -279,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; } @@ -313,13 +300,11 @@ 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) { - while (!RespWriteUtils.TryWriteError($"ERR {exception.Message}", ref dcurr, dend)) - SendAndReset(); + WriteError(exception); } return true; @@ -355,14 +340,12 @@ private bool NetworkAclSave() catch (Exception ex) { logger?.LogError(ex, "ACL SAVE faulted"); - while (!RespWriteUtils.TryWriteError($"ERR {ex.Message}", ref dcurr, dend)) - SendAndReset(); + WriteError(ex); return true; } - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); return true; } @@ -396,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; } @@ -432,8 +415,7 @@ private bool NetworkAclGetUser() catch (ACLException exception) { // Abort command execution - while (!RespWriteUtils.TryWriteError($"ERR {exception.Message}", ref dcurr, dend)) - SendAndReset(); + WriteError(exception); return true; } @@ -444,34 +426,37 @@ private bool NetworkAclGetUser() } else { - WriteMapLength(3); - - while (!RespWriteUtils.TryWriteAsciiBulkString("flags", ref dcurr, dend)) - SendAndReset(); - - WriteSetLength(1); + if(respProtocolVersion == 3) + { + WriteDirect(CmdStrings.RESP_Map3_flags_Set1); + } + else + { + WriteDirect(CmdStrings.RESP_Array6_flags_Array1); + } - while (!RespWriteUtils.TryWriteAsciiBulkString(user.IsEnabled ? "on" : "off", ref dcurr, dend)) - SendAndReset(); + if (user.IsEnabled) + { + WriteBulkString(CmdStrings.on); + } + else + { + WriteBulkString(CmdStrings.off); + } var passwords = user.Passwords; - while (!RespWriteUtils.TryWriteAsciiBulkString("passwords", ref dcurr, dend)) - SendAndReset(); + WriteBulkString(CmdStrings.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(); + WriteLargeBulkString($"#{password.ToString()}"); } - while (!RespWriteUtils.TryWriteAsciiBulkString("commands", ref dcurr, dend)) - SendAndReset(); + WriteBulkString(CmdStrings.commands); - while (!RespWriteUtils.TryWriteAsciiBulkString(user.GetEnabledCommandsDescription(), ref dcurr, dend)) - SendAndReset(); + WriteLargeBulkString(user.GetEnabledCommandsDescription()); } return true; diff --git a/libs/server/Resp/AdminCommands.cs b/libs/server/Resp/AdminCommands.cs index f2e9b0f47a7..84fae95b070 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; } @@ -510,13 +507,11 @@ 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 { - while (!RespWriteUtils.TryWriteError(errorMsg, ref dcurr, dend)) - SendAndReset(); + WriteLargeError(errorMsg); } return true; @@ -555,7 +550,7 @@ private bool NetworkModuleLoad(CustomCommandManager customCommandManager) { if (!errorMsg.IsEmpty) { - WriteError(errorMsg); + WriteLargeError(errorMsg); } return true; @@ -571,14 +566,13 @@ 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); } } if (!errorMsg.IsEmpty) { - WriteError(errorMsg); + WriteLargeError(errorMsg); } return true; @@ -608,8 +602,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(CmdStrings.AOF_file_committed); return true; } @@ -631,8 +624,7 @@ private bool NetworkFORCEGC() } GC.Collect(generation, GCCollectionMode.Forced, true); - while (!RespWriteUtils.TryWriteSimpleString("GC completed"u8, ref dcurr, dend)) - SendAndReset(); + WriteSimpleString(CmdStrings.GC_completed); return true; } @@ -655,12 +647,10 @@ 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)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_HCOLLECT_ALREADY_IN_PROGRESS); break; } @@ -685,12 +675,10 @@ 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)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_ZCOLLECT_ALREADY_IN_PROGRESS); break; } @@ -734,7 +722,7 @@ private bool NetworkDebug() nameof(RespCommand.DEBUG)); } - WriteError(parseState.GetString(1)); + WriteLargeError(parseState.GetArgSliceByRef(1).Span); return true; } @@ -771,12 +759,12 @@ private bool NetworkDebug() WriteArrayLength(help.Length); foreach (var line in help) { - WriteSimpleString(line); + WriteLargeSimpleString(line); } 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; } @@ -789,17 +777,7 @@ private bool NetworkROLE() if (!storeWrapper.serverOptions.EnableCluster) { - while (!RespWriteUtils.TryWriteArrayLength(3, ref dcurr, dend)) - SendAndReset(); - - while (!RespWriteUtils.TryWriteAsciiBulkString("master", ref dcurr, dend)) - SendAndReset(); - - while (!RespWriteUtils.TryWriteInt32(0, ref dcurr, dend)) - SendAndReset(); - - while (!RespWriteUtils.TryWriteEmptyArray(ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_Array3_master_0_EmptyArray); } else { @@ -807,51 +785,33 @@ private bool NetworkROLE() { var (replication_offset, replicaInfo) = storeWrapper.clusterProvider.GetPrimaryInfo(); - while (!RespWriteUtils.TryWriteArrayLength(3, ref dcurr, dend)) - SendAndReset(); - - while (!RespWriteUtils.TryWriteAsciiBulkString("master", ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_Array3_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); + WriteLargeAsciiBulkString(replice.address); + WriteInt32(replice.port); + WriteInt64(replice.replication_offset); } } else { var role = storeWrapper.clusterProvider.GetReplicaInfo(); - while (!RespWriteUtils.TryWriteArrayLength(5, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_Array5_slave); - while (!RespWriteUtils.TryWriteAsciiBulkString("slave", ref dcurr, dend)) - SendAndReset(); + WriteLargeAsciiBulkString(role.address); - while (!RespWriteUtils.TryWriteAsciiBulkString(role.address, ref dcurr, dend)) - SendAndReset(); + WriteInt32(role.port); - while (!RespWriteUtils.TryWriteInt32(role.port, ref dcurr, dend)) - SendAndReset(); + WriteLargeAsciiBulkString(role.replication_state); - while (!RespWriteUtils.TryWriteAsciiBulkString(role.replication_state, ref dcurr, dend)) - SendAndReset(); - - while (!RespWriteUtils.TryWriteInt64(role.replication_offset, ref dcurr, dend)) - SendAndReset(); + WriteInt64(role.replication_offset); } } @@ -886,13 +846,11 @@ 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 { - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); } return true; @@ -929,14 +887,9 @@ 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(); - - while (!RespWriteUtils.TryWriteArrayItem(recordsExpired, ref dcurr, dend)) - SendAndReset(); - - while (!RespWriteUtils.TryWriteArrayItem(recordsScanned, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(2); + WriteArrayItem(recordsExpired); + WriteArrayItem(recordsScanned); return true; } @@ -966,8 +919,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,13 +960,11 @@ private bool NetworkBGSAVE() if (success) { - while (!RespWriteUtils.TryWriteSimpleString("Background saving started"u8, ref dcurr, dend)) - SendAndReset(); + WriteSimpleString(CmdStrings.Background_saving_started); } 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; @@ -1025,23 +975,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 7ba2d976413..34ea9a63da5 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,15 +127,13 @@ 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 { // 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; @@ -183,13 +177,11 @@ private bool NetworkSWAPDB() if (storeWrapper.TrySwapDatabases(index1, index2)) { - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); } else { - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_SWAPDB_UNSUPPORTED, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_SWAPDB_UNSUPPORTED); } return true; @@ -203,8 +195,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 +216,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(); + WriteLargeBulkString(item); } } else { - while (!RespWriteUtils.TryWriteEmptyArray(ref dcurr, dend)) - SendAndReset(); + WriteEmptyArray(); } return true; @@ -280,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; } } @@ -297,22 +284,15 @@ private bool NetworkSCAN(ref TGarnetApi storageApi) // Prepare values for output if (keys.Count == 0) { - while (!RespWriteUtils.TryWriteArrayLength(2, ref dcurr, dend)) - SendAndReset(); - - // Number of keys "0" - while (!RespWriteUtils.TryWriteInt32AsBulkString(0, ref dcurr, dend)) - SendAndReset(); - - // Empty array - while (!RespWriteUtils.TryWriteEmptyArray(ref dcurr, dend)) - SendAndReset(); + // 2 element array + // - Number of keys = "0" + // - Empty array + WriteDirect(CmdStrings.RESP_Array2_0String_EmptyArray); } 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 +314,11 @@ private bool NetworkTYPE(ref TGarnetApi storageApi) if (status == GarnetStatus.OK) { - while (!RespWriteUtils.TryWriteSimpleString(typeName, ref dcurr, dend)) - SendAndReset(); + WriteLargeSimpleString(typeName); } else { - while (!RespWriteUtils.TryWriteSimpleString("none"u8, ref dcurr, dend)) - SendAndReset(); + WriteSimpleString(CmdStrings.none); } return true; @@ -355,17 +333,15 @@ 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++) { - WriteDirectLargeRespString(keys[i]); + WriteLargeBulkString(keys[i]); } } @@ -383,7 +359,7 @@ private bool NetworkArrayPING() } var message = parseState.GetArgSliceByRef(0).ReadOnlySpan; - WriteDirectLargeRespString(message); + WriteLargeBulkString(message); return true; } diff --git a/libs/server/Resp/AsyncProcessor.cs b/libs/server/Resp/AsyncProcessor.cs index a88fe0a534d..2166f5cb1c2 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 @@ -50,8 +49,7 @@ void NetworkGETPending(ref TGarnetApi storageApi) { unsafe { - while (!RespWriteUtils.TryWriteError($"ASYNC {asyncStarted}", ref dcurr, dend)) - SendAndReset(); + WriteLargeError($"ASYNC {asyncStarted}"); } if (++asyncStarted == 1) // first async operation on the session, create the IO continuation processor @@ -104,12 +102,8 @@ 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(); - while (!RespWriteUtils.TryWriteBulkString(CmdStrings.async, ref dcurr, dend)) - SendAndReset(); - while (!RespWriteUtils.TryWriteInt32AsBulkString((int)completedOutputs.Current.Context, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_Push3_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..efe0cadfb39 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(); + WriteLargeError($"ERR Unsupported option {parseState.GetString(1)}"); return true; } } @@ -288,8 +287,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 +331,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(); + WriteLargeIntegerFromBytes(outputBuffer.Slice(0, output.Length)); return true; } @@ -369,8 +366,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 +401,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 +424,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; } @@ -559,8 +553,7 @@ private bool NetworkSETEXNX(ref TGarnetApi storageApi) if (!errorMessage.IsEmpty) { - while (!RespWriteUtils.TryWriteError(errorMessage, ref dcurr, dend)) - SendAndReset(); + WriteLargeError(errorMessage); return true; } @@ -606,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; } @@ -627,8 +619,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 +645,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 +657,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 +732,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(); + WriteLargeIntegerFromBytes(outputBuffer.Slice(0, output.Length)); break; case OperationError.NAN_OR_INFINITY: case OperationError.INVALID_TYPE: @@ -782,8 +770,7 @@ private bool NetworkIncrementByFloat(ref TGarnetApi storageApi) switch (status) { case GarnetStatus.OK: - while (!RespWriteUtils.TryWriteBulkString(output.ReadOnlySpan, ref dcurr, dend)) - SendAndReset(); + WriteLargeBulkString(output.ReadOnlySpan); break; case GarnetStatus.WRONGTYPE: default: @@ -812,8 +799,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(); + WriteLargeIntegerFromBytes(outputBuffer.Slice(0, output.Length)); return true; } @@ -825,13 +811,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 +828,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 +837,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; } @@ -872,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; } @@ -912,8 +893,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 +905,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 +930,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; } @@ -1017,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(); + WriteLargeError(errorMsg); } else { @@ -1047,8 +1023,7 @@ private bool NetworkCOMMAND_COUNT() var commandCount = customCommandManagerSession.GetCustomCommandInfoCount() + respCommandCount; - while (!RespWriteUtils.TryWriteInt32(commandCount, ref dcurr, dend)) - SendAndReset(); + WriteInt32(commandCount); return true; } @@ -1193,13 +1168,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(); + WriteLargeBulkString(key.Span); } return true; @@ -1230,24 +1203,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(); + WriteLargeBulkString(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(); + WriteLargeBulkString(Encoding.ASCII.GetBytes(flag)); } } @@ -1262,7 +1231,7 @@ private bool NetworkECHO() } var message = parseState.GetArgSliceByRef(0).ReadOnlySpan; - WriteDirectLargeRespString(message); + WriteLargeBulkString(message); return true; } @@ -1337,8 +1306,7 @@ private bool NetworkHELLO() if (errorMsg != default) { - while (!RespWriteUtils.TryWriteError(errorMsg, ref dcurr, dend)) - SendAndReset(); + WriteLargeError(errorMsg); return true; } @@ -1359,8 +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"; - while (!RespWriteUtils.TryWriteAsciiDirect(response, ref dcurr, dend)) - SendAndReset(); + WriteLargeAsciiDirect(response); return true; } @@ -1390,28 +1357,24 @@ 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(CmdStrings.ERR_Client_sent_AUTH_no_pass); return true; } // 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 { 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; @@ -1454,8 +1417,7 @@ private bool NetworkMemoryUsage(ref TGarnetApi storageApi) if (status == GarnetStatus.OK) { - while (!RespWriteUtils.TryWriteInt32((int)memoryUsage, ref dcurr, dend)) - SendAndReset(); + WriteInt32((int)memoryUsage); } else { @@ -1511,14 +1473,12 @@ private bool NetworkASYNC() } else { - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_SYNTAX_ERROR, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_SYNTAX_ERROR); return true; } - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); return true; } @@ -1584,23 +1544,17 @@ 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(); + WriteLargeAsciiBulkString(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(); + WriteLargeAsciiBulkString(helloResult[i].Item2.ToString()); } } - while (!RespWriteUtils.TryWriteAsciiBulkString("modules", ref dcurr, dend)) - SendAndReset(); - while (!RespWriteUtils.TryWriteEmptyArray(ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_modules_EmptyArray); } /// @@ -1659,8 +1613,7 @@ void FlushDb(RespCommand cmd) if (syntaxError) { - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_SYNTAX_ERROR, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_SYNTAX_ERROR); return; } @@ -1670,8 +1623,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..9d7391e7177 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; } @@ -208,8 +207,7 @@ private bool NetworkSetETagConditional(RespCommand cmd, ref TGarnetA if (!errorMessage.IsEmpty) { - while (!RespWriteUtils.TryWriteError(errorMessage, ref dcurr, dend)) - SendAndReset(); + WriteLargeError(errorMessage); return true; } diff --git a/libs/server/Resp/Bitmap/BitmapCommands.cs b/libs/server/Resp/Bitmap/BitmapCommands.cs index 20600939332..23c7c2a659b 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(); + WriteLargeDirect(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..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; } @@ -226,16 +226,14 @@ private bool NetworkCLIENTKILL() { _ = session.TryKill(); - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); return true; } } } - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_NO_SUCH_CLIENT, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_NO_SUCH_CLIENT); return true; } @@ -401,8 +399,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 +502,7 @@ private bool NetworkCLIENTGETNAME() } else { - while (!RespWriteUtils.TryWriteAsciiBulkString(this.clientName, ref dcurr, dend)) - SendAndReset(); + WriteLargeAsciiBulkString(this.clientName); } return true; @@ -529,8 +525,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 +555,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 +599,7 @@ private bool NetworkCLIENTUNBLOCK() if (session is null) { - while (!RespWriteUtils.TryWriteInt32(0, ref dcurr, dend)) - SendAndReset(); + WriteInt32(0); return true; } @@ -616,26 +609,22 @@ 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 { - 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/CmdStrings.cs b/libs/server/Resp/CmdStrings.cs index e8c5ba5fb9e..0bb10a4d032 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 /// @@ -531,5 +536,64 @@ 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; + 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/HyperLogLog/HyperLogLogCommands.cs b/libs/server/Resp/HyperLogLog/HyperLogLogCommands.cs index 3ccff292178..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; } @@ -49,13 +48,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; } @@ -81,13 +78,11 @@ 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 { - while (!RespWriteUtils.TryWriteInt64(cardinality, ref dcurr, dend)) - SendAndReset(); + WriteInt64(cardinality); } return true; @@ -112,15 +107,13 @@ 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; } 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..1c13902fa89 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(CmdStrings.ERR_RESTORE_currently_only_supports); 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(CmdStrings.ERR_DUMP_payload_version_or_checksum_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(CmdStrings.ERR_DUMP_payload_version_or_checksum_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(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)) { - while (!RespWriteUtils.TryWriteError("ERR DUMP payload length format is invalid", ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.ERR_DUMP_payload_length_format_invalid); return true; } @@ -113,13 +108,11 @@ 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; } - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_BUSSYKEY, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_BUSSYKEY); return true; } @@ -149,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(CmdStrings.ERR_DUMP_payload_length_is_invalid); return true; } @@ -207,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 @@ -249,12 +241,10 @@ 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)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_GENERIC_NOSUCHKEY); break; } return true; @@ -292,13 +282,11 @@ 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 { - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_GENERIC_NOSUCHKEY, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_GENERIC_NOSUCHKEY); } return true; @@ -363,8 +351,7 @@ private bool NetworkEXISTS(ref TGarnetApi storageApi) exists++; } - while (!RespWriteUtils.TryWriteInt32(exists, ref dcurr, dend)) - SendAndReset(); + WriteInt32(exists); return true; } @@ -431,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(CmdStrings.ERR_NX_and_XX_GT_or_LT_not_compatible); } } } @@ -456,13 +440,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 +469,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 +508,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 +543,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/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/HashCommands.cs b/libs/server/Resp/Objects/HashCommands.cs index bfcc4986cf5..50620c68834 100644 --- a/libs/server/Resp/Objects/HashCommands.cs +++ b/libs/server/Resp/Objects/HashCommands.cs @@ -53,19 +53,16 @@ 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) { - 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; } @@ -107,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; } @@ -147,12 +143,10 @@ 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)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -192,15 +186,13 @@ 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(); break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -280,8 +272,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 { @@ -289,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; } @@ -325,16 +315,13 @@ 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)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -368,16 +355,13 @@ 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)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } return true; @@ -410,16 +394,13 @@ 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)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } return true; @@ -451,16 +432,13 @@ 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)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } return true; @@ -511,12 +489,10 @@ 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)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } return true; @@ -564,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); @@ -649,16 +624,13 @@ 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: - 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: @@ -739,16 +711,13 @@ 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: - 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: @@ -801,16 +770,13 @@ 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: - 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..1cdd14d7d2c 100644 --- a/libs/server/Resp/Objects/ListCommands.cs +++ b/libs/server/Resp/Objects/ListCommands.cs @@ -48,14 +48,12 @@ 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 { // Write result to output - while (!RespWriteUtils.TryWriteInt32(output.result1, ref dcurr, dend)) - SendAndReset(); + WriteInt32(output.result1); } return true; } @@ -119,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; } @@ -173,8 +170,7 @@ private unsafe bool ListPosition(ref TGarnetApi storageApi) if (count) { - while (!RespWriteUtils.TryWriteEmptyArray(ref dcurr, dend)) - SendAndReset(); + WriteEmptyArray(); } else { @@ -182,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; } @@ -258,19 +253,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(); + WriteLargeBulkString(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(); + WriteLargeBulkString(element.Span); } break; @@ -278,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; } @@ -313,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; } @@ -331,14 +319,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(); + WriteLargeBulkString(new Span(result.Key)); - while (!RespWriteUtils.TryWriteBulkString(new Span(result.Item), ref dcurr, dend)) - SendAndReset(); + WriteLargeBulkString(new Span(result.Item)); } return true; @@ -422,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; } @@ -440,8 +423,7 @@ private bool ListBlockingMove(ArgSlice srcKey, ArgSlice dstKey, } else { - while (!RespWriteUtils.TryWriteBulkString(new Span(result.Item), ref dcurr, dend)) - SendAndReset(); + WriteLargeBulkString(result.Item); } return true; @@ -474,17 +456,14 @@ 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)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; default: // Process output - while (!RespWriteUtils.TryWriteInt32(output.result1, ref dcurr, dend)) - SendAndReset(); + WriteInt32(output.result1); break; } @@ -514,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; } @@ -528,14 +506,12 @@ 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 // no need to process output, just send OK - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); break; } @@ -565,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; } @@ -586,12 +561,10 @@ 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)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } return true; @@ -619,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; } @@ -645,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; } @@ -685,16 +656,13 @@ 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)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -723,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; } @@ -741,16 +708,13 @@ 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)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -789,8 +753,7 @@ private bool ListMove(ref TGarnetApi storageApi) case GarnetStatus.OK: if (node != null) { - while (!RespWriteUtils.TryWriteBulkString(node, ref dcurr, dend)) - SendAndReset(); + WriteLargeBulkString(node); } else { @@ -799,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; } @@ -832,8 +794,7 @@ private bool ListRightPopLeftPush(ref TGarnetApi storageApi) case GarnetStatus.OK: if (node != null) { - while (!RespWriteUtils.TryWriteBulkString(node, ref dcurr, dend)) - SendAndReset(); + WriteLargeBulkString(node); } else { @@ -842,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; } @@ -910,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; } @@ -999,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; } @@ -1016,20 +972,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(); + WriteLargeBulkString(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(); + WriteLargeBulkString(element); } return true; diff --git a/libs/server/Resp/Objects/ObjectStoreUtils.cs b/libs/server/Resp/Objects/ObjectStoreUtils.cs index 08169de31d0..6568cb04b9a 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 { @@ -46,10 +45,8 @@ 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 - while (!RespWriteUtils.TryWriteError(errorMessage, ref dcurr, dend)) - SendAndReset(); + WriteLargeError(errorMessage); return true; } diff --git a/libs/server/Resp/Objects/SetCommands.cs b/libs/server/Resp/Objects/SetCommands.cs index 7d7438ec465..39a6a38b7b0 100644 --- a/libs/server/Resp/Objects/SetCommands.cs +++ b/libs/server/Resp/Objects/SetCommands.cs @@ -42,13 +42,11 @@ 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 - while (!RespWriteUtils.TryWriteInt32(output.result1, ref dcurr, dend)) - SendAndReset(); + WriteInt32(output.result1); break; } @@ -89,20 +87,17 @@ private bool SetIntersect(ref TGarnetApi storageApi) foreach (var item in result) { - while (!RespWriteUtils.TryWriteBulkString(item, ref dcurr, dend)) - SendAndReset(); + WriteLargeBulkString(item); } } else { - while (!RespWriteUtils.TryWriteArrayLength(resultCount, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(resultCount); } break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -137,12 +132,10 @@ 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)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -203,12 +196,10 @@ 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)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -247,13 +238,11 @@ private bool SetUnion(ref TGarnetApi storageApi) foreach (var item in result) { - while (!RespWriteUtils.TryWriteBulkString(item, ref dcurr, dend)) - SendAndReset(); + WriteLargeBulkString(item); } break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -288,12 +277,10 @@ 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)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -330,16 +317,13 @@ 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)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -374,16 +358,13 @@ 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)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -427,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; } @@ -478,26 +458,22 @@ 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); } } break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -533,8 +509,7 @@ private unsafe bool SetPop(ref TGarnetApi storageApi) if (countParameter == 0) { - while (!RespWriteUtils.TryWriteEmptyArray(ref dcurr, dend)) - SendAndReset(); + WriteEmptyArray(); return true; } @@ -559,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; } @@ -596,16 +570,13 @@ 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)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -645,8 +616,7 @@ private unsafe bool SetRandomMember(ref TGarnetApi storageApi) if (countParameter == 0) { - while (!RespWriteUtils.TryWriteEmptyArray(ref dcurr, dend)) - SendAndReset(); + WriteEmptyArray(); return true; } @@ -673,16 +643,14 @@ private unsafe bool SetRandomMember(ref TGarnetApi storageApi) case GarnetStatus.NOTFOUND: if (parseState.Count == 2) { - while (!RespWriteUtils.TryWriteEmptyArray(ref dcurr, dend)) - SendAndReset(); + WriteEmptyArray(); break; } WriteNull(); break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -723,14 +691,12 @@ private bool SetDiff(ref TGarnetApi storageApi) foreach (var item in output) { - while (!RespWriteUtils.TryWriteBulkString(item, ref dcurr, dend)) - SendAndReset(); + WriteLargeBulkString(item); } } break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -758,12 +724,10 @@ 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)) - 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 965f8d3645c..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 @@ -77,16 +75,12 @@ 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)) - 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 37cc6715a99..4ad13825504 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); @@ -79,16 +78,13 @@ 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)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } return true; @@ -121,16 +117,13 @@ 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)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -197,12 +190,10 @@ 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)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -229,12 +220,10 @@ 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)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -279,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,15 +311,13 @@ 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(); break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -392,12 +378,10 @@ 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)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -487,23 +471,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(); + WriteLargeBulkString(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); + WriteLargeBulkString(member.ReadOnlySpan); if (respProtocolVersion >= 3) { @@ -511,16 +490,14 @@ private unsafe bool SortedSetMPop(ref TGarnetApi storageApi) } else { - while (!RespWriteUtils.TryWriteBulkString(score.ReadOnlySpan, ref dcurr, dend)) - SendAndReset(); + WriteLargeBulkString(score.ReadOnlySpan); } } } break; case GarnetStatus.WRONGTYPE: - while (!RespWriteUtils.TryWriteError(CmdStrings.RESP_ERR_WRONG_TYPE, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -560,12 +537,10 @@ 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)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -618,22 +593,18 @@ 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; 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)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } @@ -676,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; } @@ -744,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; } @@ -796,12 +765,10 @@ 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)) - SendAndReset(); + WriteError(CmdStrings.RESP_ERR_WRONG_TYPE); break; } return true; @@ -833,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; } @@ -882,8 +848,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 { @@ -891,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; @@ -939,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; } @@ -953,32 +916,26 @@ 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) { - 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(); + WriteLargeBulkString(element); if (includeWithScores) { @@ -1028,12 +985,10 @@ 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: - while (!RespWriteUtils.TryWriteInt32(count, ref dcurr, dend)) - SendAndReset(); + WriteInt32(count); break; } @@ -1124,14 +1079,12 @@ 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) { - while (!RespWriteUtils.TryWriteEmptyArray(ref dcurr, dend)) - SendAndReset(); + WriteEmptyArray(); break; } @@ -1139,19 +1092,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(); + WriteLargeBulkString(element); if (includeWithScores) { @@ -1223,12 +1173,10 @@ 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: - while (!RespWriteUtils.TryWriteInt32(count, ref dcurr, dend)) - SendAndReset(); + WriteInt32(count); break; } @@ -1311,12 +1259,10 @@ 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: - while (!RespWriteUtils.TryWriteInt32(count, ref dcurr, dend)) - SendAndReset(); + WriteInt32(count); break; } @@ -1413,14 +1359,12 @@ 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) { - while (!RespWriteUtils.TryWriteEmptyArray(ref dcurr, dend)) - SendAndReset(); + WriteEmptyArray(); break; } @@ -1428,19 +1372,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(); + WriteLargeBulkString(element); if (includeWithScores) { @@ -1537,12 +1478,10 @@ 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: - while (!RespWriteUtils.TryWriteInt32(count, ref dcurr, dend)) - SendAndReset(); + WriteInt32(count); break; } @@ -1579,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; } @@ -1597,14 +1534,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(); + WriteLargeBulkString(result.Key); - while (!RespWriteUtils.TryWriteBulkString(result.Item, ref dcurr, dend)) - SendAndReset(); + WriteLargeBulkString(result.Item); WriteDoubleNumeric(result.Score); } @@ -1692,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; } @@ -1711,21 +1643,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(); + WriteLargeBulkString(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); + WriteLargeBulkString(result.Items[i]); WriteDoubleNumeric(result.Scores[i]); } @@ -1810,16 +1737,13 @@ 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: - 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: @@ -1903,16 +1827,13 @@ 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: - 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: @@ -1972,16 +1893,13 @@ 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: - 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..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); @@ -183,8 +182,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(); @@ -194,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; } @@ -296,13 +293,11 @@ 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: - 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..411fd1c9bdf 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(); + WriteLargeError(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 d353b7b5a7a..87ba20a95d0 100644 --- a/libs/server/Resp/PubSubCommands.cs +++ b/libs/server/Resp/PubSubCommands.cs @@ -24,14 +24,18 @@ public override unsafe void Publish(ArgSlice key, ArgSlice value) { networkSender.EnterAndGetResponseObject(out dcurr, out dend); - WritePushLength(3); - - while (!RespWriteUtils.TryWriteBulkString("message"u8, ref dcurr, dend)) - SendAndReset(); + if (respProtocolVersion == 3) + { + WriteDirect(CmdStrings.RESP_Push3_messages); + } + else + { + WriteDirect(CmdStrings.RESP_Array3_messages); + } // Write key and value to the network - WriteDirectLargeRespString(key.ReadOnlySpan); - WriteDirectLargeRespString(value.ReadOnlySpan); + WriteLargeBulkString(key.ReadOnlySpan); + WriteLargeBulkString(value.ReadOnlySpan); // Flush the publish message for this subscriber if (dcurr > networkSender.GetResponseObjectHead()) @@ -54,15 +58,19 @@ public override unsafe void PatternPublish(ArgSlice pattern, ArgSlice key, ArgSl { networkSender.EnterAndGetResponseObject(out dcurr, out dend); - WritePushLength(4); - - while (!RespWriteUtils.TryWriteBulkString("pmessage"u8, ref dcurr, dend)) - SendAndReset(); + if(respProtocolVersion == 3) + { + WriteDirect(CmdStrings.RESP_Push4_pmessages); + } + else + { + WriteDirect(CmdStrings.RESP_Array4_pmessages); + } // Write pattern, key, and value to the network - WriteDirectLargeRespString(pattern.ReadOnlySpan); - WriteDirectLargeRespString(key.ReadOnlySpan); - WriteDirectLargeRespString(value.ReadOnlySpan); + WriteLargeBulkString(pattern.ReadOnlySpan); + WriteLargeBulkString(key.ReadOnlySpan); + WriteLargeBulkString(value.ReadOnlySpan); if (dcurr > networkSender.GetResponseObjectHead()) Send(networkSender.GetResponseObjectHead()); @@ -120,8 +128,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,26 +168,21 @@ 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(); + WriteLargeBulkString(header); - while (!RespWriteUtils.TryWriteBulkString(key.ReadOnlySpan, ref dcurr, dend)) - SendAndReset(); + WriteLargeBulkString(key.ReadOnlySpan); if (subscribeBroker.Subscribe(key, this)) numActiveChannels++; - while (!RespWriteUtils.TryWriteInt32(numActiveChannels, ref dcurr, dend)) - SendAndReset(); + WriteInt32(numActiveChannels); } if (disabledBroker) { - while (!RespWriteUtils.TryWriteError("ERR SUBSCRIBE is disabled, enable it with --pubsub option."u8, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.ERR_SUBSCRIBE_is_disabled); return true; } @@ -204,25 +206,18 @@ private bool NetworkPSUBSCRIBE() if (disabledBroker) continue; - while (!RespWriteUtils.TryWriteArrayLength(3, ref dcurr, dend)) - SendAndReset(); - - while (!RespWriteUtils.TryWriteBulkString("psubscribe"u8, ref dcurr, dend)) - SendAndReset(); - while (!RespWriteUtils.TryWriteBulkString(key.ReadOnlySpan, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_Array3_psubscribe); + WriteLargeBulkString(key.ReadOnlySpan); if (subscribeBroker.PatternSubscribe(key, this)) numActiveChannels++; - while (!RespWriteUtils.TryWriteInt32(numActiveChannels, ref dcurr, dend)) - SendAndReset(); + WriteInt32(numActiveChannels); } if (disabledBroker) { - while (!RespWriteUtils.TryWriteError("ERR SUBSCRIBE is disabled, enable it with --pubsub option."u8, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.ERR_SUBSCRIBE_is_disabled); return true; } @@ -244,31 +239,26 @@ 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(); - - while (!RespWriteUtils.TryWriteBulkString(channel.ReadOnlySpan, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_Array3_unsubscribe); + WriteLargeBulkString(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(); - - WriteNull(); - - while (!RespWriteUtils.TryWriteInt32(numActiveChannels, ref dcurr, dend)) - SendAndReset(); + if(respProtocolVersion == 3) + { + WriteDirect(CmdStrings.RESP_Array3_unsubscribe_null3); + } + else + { + WriteDirect(CmdStrings.RESP_Array3_unsubscribe_null2); + } + + WriteInt32(numActiveChannels); } if (numActiveChannels == 0) @@ -283,25 +273,19 @@ 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(); + WriteDirect(CmdStrings.RESP_Array3_unsubscribe); + WriteLargeBulkString(key.ReadOnlySpan); if (subscribeBroker.Unsubscribe(new ByteArrayWrapper(key), this)) numActiveChannels--; - while (!RespWriteUtils.TryWriteInt32(numActiveChannels, ref dcurr, dend)) - SendAndReset(); + WriteInt32(numActiveChannels); } } if (subscribeBroker == null) { - while (!RespWriteUtils.TryWriteError("ERR UNSUBSCRIBE is disabled, enable it with --pubsub option."u8, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.ERR_UNSUBSCRIBE_is_disabled); } if (numActiveChannels == 0) @@ -324,33 +308,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(); + WriteDirect(CmdStrings.RESP_Array3_punsubscribe); - while (!RespWriteUtils.TryWriteBulkString(channel.ReadOnlySpan, ref dcurr, dend)) - SendAndReset(); + WriteLargeBulkString(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(); - - while (!RespWriteUtils.TryWriteBulkString("punsubscribe"u8, ref dcurr, dend)) - SendAndReset(); - - WriteNull(); - - while (!RespWriteUtils.TryWriteInt32(0, ref dcurr, dend)) - SendAndReset(); + if(respProtocolVersion == 3) + { + WriteDirect(CmdStrings.RESP_Array3_punsubscribe_null3_0); + } + else + { + WriteDirect(CmdStrings.RESP_Array3_punsubscribe_null2_0); + } } if (numActiveChannels == 0) @@ -365,25 +342,19 @@ 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(); + WriteDirect(CmdStrings.RESP_Array3_punsubscribe); + WriteLargeBulkString(key.ReadOnlySpan); if (subscribeBroker.PatternUnsubscribe(new ByteArrayWrapper(key), this)) numActiveChannels--; - while (!RespWriteUtils.TryWriteInt32(numActiveChannels, ref dcurr, dend)) - SendAndReset(); + WriteInt32(numActiveChannels); } } if (subscribeBroker == null) { - while (!RespWriteUtils.TryWriteError("ERR PUNSUBSCRIBE is disabled, enable it with --pubsub option."u8, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.ERR_PUNSUBSCRIBE_is_disabled); } if (numActiveChannels == 0) @@ -410,13 +381,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(); + WriteLargeBulkString(channel.ReadOnlySpan); } return true; } @@ -435,8 +404,7 @@ private bool NetworkPUBSUB_NUMPAT() var numPatSubs = subscribeBroker.NumPatternSubscriptions(); - while (!RespWriteUtils.TryWriteInt32(numPatSubs, ref dcurr, dend)) - SendAndReset(); + WriteInt32(numPatSubs); return true; } @@ -449,17 +417,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(); + WriteLargeBulkString(channel.ReadOnlySpan); + WriteInt32(subscribeBroker.NumSubscriptions(channel)); } return true; } diff --git a/libs/server/Resp/PurgeBPCommand.cs b/libs/server/Resp/PurgeBPCommand.cs index d0cde4b03f4..1ba1c6ebfb8 100644 --- a/libs/server/Resp/PurgeBPCommand.cs +++ b/libs/server/Resp/PurgeBPCommand.cs @@ -72,23 +72,20 @@ private bool NetworkPurgeBP() break; default: success = false; - while (!RespWriteUtils.TryWriteError($"ERR Could not purge {managerType}.", ref dcurr, dend)) - SendAndReset(); + WriteLargeError($"ERR Could not purge {managerType}."); break; } if (success) { GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true); - while (!RespWriteUtils.TryWriteSimpleString(managerType.ToReadOnlySpan(), ref dcurr, dend)) - SendAndReset(); + WriteEnumAsBulkString(managerType); } } catch (Exception ex) { logger?.LogError(ex, "PURGEBP {type}:{managerType}", managerType, managerType.ToString()); - while (!RespWriteUtils.TryWriteError($"ERR {ex.Message}", ref dcurr, dend)) - SendAndReset(); + WriteError(ex); return true; } @@ -96,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 f91c2bfcd45..f9cb5d9382a 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(); + WriteLargeError($"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(); + WriteLargeError($"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) @@ -1075,8 +1071,7 @@ bool NetworkCLIENTID() return AbortWithWrongNumberOfArguments("client|id"); } - while (!RespWriteUtils.TryWriteInt64(Id, ref dcurr, dend)) - SendAndReset(); + WriteInt64(Id); return true; } @@ -1163,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(); + WriteLargeError(string.Format(CmdStrings.GenericErrWrongNumArgs, cmdName)); return false; } @@ -1343,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 969c5059e89..5440b5a8163 100644 --- a/libs/server/Resp/RespServerSessionOutput.cs +++ b/libs/server/Resp/RespServerSessionOutput.cs @@ -2,7 +2,10 @@ // Licensed under the MIT license. using System; +using System.Buffers; +using System.Diagnostics; using System.Runtime.CompilerServices; +using System.Text; using Garnet.common; using Tsavorite.core; @@ -19,7 +22,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,54 +31,137 @@ private unsafe void ProcessOutput(SpanByteAndMemory output) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WriteAsciiBulkString(ReadOnlySpan message) + 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)] - private void WriteAsciiDirect(ReadOnlySpan message) + 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 { + var asStr = value.ToString(); + while (!RespWriteUtils.TryWriteAsciiBulkString(asStr, ref dcurr, dend)) + SendAndReset(); + } + + + [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)] - private void WriteArrayLength(int len) + 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) { while (!RespWriteUtils.TryWriteArrayLength(len, ref dcurr, dend)) SendAndReset(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WriteBulkString(scoped ReadOnlySpan message) + internal void WriteArrayItem(long recordsExpired) + { + while (!RespWriteUtils.TryWriteArrayItem(recordsExpired, ref dcurr, dend)) + SendAndReset(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void WriteBulkString(scoped ReadOnlySpan message) { while (!RespWriteUtils.TryWriteBulkString(message, ref dcurr, dend)) SendAndReset(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WriteDirectLargeRespString(ReadOnlySpan message) + internal void WriteLargeBulkString(ReadOnlySpan message) { while (!RespWriteUtils.TryWriteBulkStringLength(message, ref dcurr, dend)) SendAndReset(); - WriteDirectLarge(message); + WriteLargeDirect(message); while (!RespWriteUtils.TryWriteNewLine(ref dcurr, dend)) SendAndReset(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WriteDirect(scoped ReadOnlySpan span) + 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) { while (!RespWriteUtils.TryWriteDirect(span, ref dcurr, dend)) SendAndReset(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WriteDoubleNumeric(double value) + internal void WriteDoubleNumeric(double value) { if (respProtocolVersion >= 3) { @@ -90,14 +176,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) { @@ -111,57 +197,106 @@ 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 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; while (!RespWriteUtils.TryWriteError(errorString, ref dcurr, dend)) 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)] - 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 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(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WriteMapLength(int count) + internal void WriteMapLength(int count) { if (respProtocolVersion >= 3) { @@ -176,21 +311,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) { @@ -205,7 +340,7 @@ private void WriteNull() } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WriteNullArray() + internal void WriteNullArray() { if (respProtocolVersion >= 3) { @@ -220,7 +355,7 @@ private void WriteNullArray() } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WritePushLength(int count) + internal void WritePushLength(int count) { if (respProtocolVersion >= 3) { @@ -235,7 +370,7 @@ private void WritePushLength(int count) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void WriteSetLength(int count) + internal void WriteSetLength(int count) { if (respProtocolVersion >= 3) { @@ -250,29 +385,55 @@ 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(); } + [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)] - 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) { @@ -285,5 +446,28 @@ private void WriteVerbatimString(scoped ReadOnlySpan item, scoped ReadOnly 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 diff --git a/libs/server/Resp/Vector/RespServerSessionVectors.cs b/libs/server/Resp/Vector/RespServerSessionVectors.cs index cf3213dc822..f40e4230df4 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; } @@ -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,15 +786,13 @@ private bool NetworkVSIM(ref TGarnetApi storageApi) { // Vector Set does not exist - while (!RespWriteUtils.TryWriteEmptyArray(ref dcurr, dend)) - SendAndReset(); + WriteEmptyArray(); } else if (res == GarnetStatus.OK) { if (vectorRes == VectorManagerResult.MissingElement) { - while (!RespWriteUtils.TryWriteError("Element not in Vector Set"u8, ref dcurr, dend)) - SendAndReset(); + WriteError(CmdStrings.ERR_Element_not_in_Vector_Set); } else if (vectorRes == VectorManagerResult.OK) { @@ -838,8 +834,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 +884,7 @@ private bool NetworkVSIM(ref TGarnetApi storageApi) continue; } - while (!RespWriteUtils.TryWriteBulkString(elementData, ref dcurr, dend)) - SendAndReset(); + WriteLargeBulkString(elementData); if (withScores.Value) { @@ -911,8 +905,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(); + WriteLargeBulkString(attr); } else if (!remaininingAttributes.IsEmpty) { @@ -1009,8 +1002,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 +1020,7 @@ private bool NetworkVEMB(ref TGarnetApi storageApi) } else { - while (!RespWriteUtils.TryWriteEmptyArray(ref dcurr, dend)) - SendAndReset(); + WriteEmptyArray(); } return true; @@ -1053,8 +1044,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; } @@ -1076,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(CmdStrings.ERR_Key_not_found); } else if (res == GarnetStatus.WRONGTYPE) { @@ -1089,8 +1078,7 @@ private bool NetworkVDIM(ref TGarnetApi storageApi) } else { - while (!RespWriteUtils.TryWriteInt32(dimensions, ref dcurr, dend)) - SendAndReset(); + WriteInt32(dimensions); } return true; @@ -1140,7 +1128,7 @@ private bool NetworkVGETATTR(ref TGarnetApi storageApi) return AbortWithErrorMessage($"Unexpected GarnetStatus: {res}"); } - WriteSimpleString(attributesOutput.AsReadOnlySpan()); + WriteLargeBulkString(attributesOutput.AsReadOnlySpan()); return true; } finally @@ -1201,20 +1189,19 @@ private bool NetworkVINFO(ref TGarnetApi storageApi) _ => throw new GarnetException($"Invalid VectorDistanceMetricType: {distanceMetricType}"), }; - WriteArrayLength(14); - WriteSimpleString("quant-type"u8); - WriteSimpleString(quantTypeSpan); - WriteSimpleString("distance-metric"u8); - WriteSimpleString(distanceMetricTypeSpan); - WriteSimpleString("input-vector-dimensions"u8); + WriteDirect(CmdStrings.RESP_Array14_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; } @@ -1229,8 +1216,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 +1231,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 +1246,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 +1279,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 +1295,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; } @@ -1323,8 +1305,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(CmdStrings.ERR_Vector_Set_partially_deleted); return true; } @@ -1332,8 +1313,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(CmdStrings.RESP_ERR_WRONG_TYPE); return true; } diff --git a/libs/server/ServerConfig.cs b/libs/server/ServerConfig.cs index 02856cccd79..0cccce32c96 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(); + WriteLargeDirect(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,13 +229,11 @@ private unsafe bool NetworkCONFIG_SET() if (sbErrorMsg.Length == 0) { - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); } else { - while (!RespWriteUtils.TryWriteError(sbErrorMsg.ToString(), ref dcurr, dend)) - SendAndReset(); + WriteLargeError(sbErrorMsg.ToString()); } return true; 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(); diff --git a/libs/server/Transaction/TxnRespCommands.cs b/libs/server/Transaction/TxnRespCommands.cs index 6b31be3c015..d86a19f02ff 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; } @@ -31,8 +29,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; } @@ -48,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; } @@ -73,8 +69,7 @@ private bool NetworkEXEC() if (startTxn) { - while (!RespWriteUtils.TryWriteArrayLength(txnManager.operationCntTxn, ref dcurr, dend)) - SendAndReset(); + WriteArrayLength(txnManager.operationCntTxn); } else { @@ -85,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; } @@ -101,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; } @@ -126,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(); + WriteLargeError(err); txnManager.Abort(); return true; } @@ -159,8 +150,7 @@ private bool NetworkSKIP(RespCommand cmd) if (abort) { - while (!RespWriteUtils.TryWriteError(errMsg, ref dcurr, dend)) - SendAndReset(); + WriteLargeError(errMsg); txnManager.Abort(); return true; } @@ -168,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(); + WriteLargeError(System.Text.Encoding.ASCII.GetBytes(string.Format( + CmdStrings.GenericErrCommandDisallowedWithOption, RespCommand.DEBUG, "enable-debug-command"))); txnManager.Abort(); return true; @@ -178,8 +167,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 +182,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 +214,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 +246,7 @@ private bool NetworkUNWATCH() { txnManager.watchContainer.Reset(); } - while (!RespWriteUtils.TryWriteDirect(CmdStrings.RESP_OK, ref dcurr, dend)) - SendAndReset(); + WriteDirect(CmdStrings.RESP_OK); return true; } @@ -294,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; } @@ -303,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(); + WriteLargeError(string.Format(CmdStrings.GenericErrWrongNumArgsTxn, txId, expectedParams, count - 1)); } else TryTransactionProc((byte)txId, proc, 1);