-
Notifications
You must be signed in to change notification settings - Fork 557
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
bc21fc4
commit adabc24
Showing
16 changed files
with
487 additions
and
415 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,187 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT license. | ||
|
||
using System; | ||
using System.Diagnostics; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
using Garnet.common; | ||
using Microsoft.Extensions.Logging; | ||
using Tsavorite.core; | ||
|
||
namespace Garnet.server | ||
{ | ||
/// <summary> | ||
/// Server session for RESP protocol - ETag associated commands are in this file | ||
/// </summary> | ||
internal sealed unsafe partial class RespServerSession : ServerSessionBase | ||
{ | ||
/// <summary> | ||
/// GETWITHETAG key | ||
/// Given a key get the value and it's ETag | ||
/// </summary> | ||
private bool NetworkGETWITHETAG<TGarnetApi>(ref TGarnetApi storageApi) | ||
where TGarnetApi : IGarnetApi | ||
{ | ||
Debug.Assert(parseState.Count == 1); | ||
|
||
var key = parseState.GetArgSliceByRef(0).SpanByte; | ||
var input = new RawStringInput(RespCommand.GETWITHETAG, ref parseState, startIdx: 1); | ||
var output = new SpanByteAndMemory(dcurr, (int)(dend - dcurr)); | ||
var status = storageApi.GET(ref key, ref input, ref output); | ||
|
||
switch (status) | ||
{ | ||
case GarnetStatus.NOTFOUND: | ||
Debug.Assert(output.IsSpanByte); | ||
while (!RespWriteUtils.WriteDirect(CmdStrings.RESP_ERRNOTFOUND, ref dcurr, dend)) | ||
SendAndReset(); | ||
break; | ||
default: | ||
if (!output.IsSpanByte) | ||
SendAndReset(output.Memory, output.Length); | ||
else | ||
dcurr += output.Length; | ||
break; | ||
} | ||
|
||
return true; | ||
} | ||
|
||
/// <summary> | ||
/// GETIFNOTMATCH key etag | ||
/// Given a key and an etag, return the value and it's etag. | ||
/// </summary> | ||
private bool NetworkGETIFNOTMATCH<TGarnetApi>(ref TGarnetApi storageApi) | ||
where TGarnetApi : IGarnetApi | ||
{ | ||
Debug.Assert(parseState.Count == 2); | ||
|
||
var key = parseState.GetArgSliceByRef(0).SpanByte; | ||
var input = new RawStringInput(RespCommand.GETIFNOTMATCH, ref parseState, startIdx: 1); | ||
var output = new SpanByteAndMemory(dcurr, (int)(dend - dcurr)); | ||
var status = storageApi.GET(ref key, ref input, ref output); | ||
|
||
switch (status) | ||
{ | ||
case GarnetStatus.NOTFOUND: | ||
Debug.Assert(output.IsSpanByte); | ||
while (!RespWriteUtils.WriteDirect(CmdStrings.RESP_ERRNOTFOUND, ref dcurr, dend)) | ||
SendAndReset(); | ||
break; | ||
default: | ||
if (!output.IsSpanByte) | ||
SendAndReset(output.Memory, output.Length); | ||
else | ||
dcurr += output.Length; | ||
break; | ||
} | ||
|
||
return true; | ||
} | ||
|
||
/// <summary> | ||
/// SETIFMATCH key val etag EX|PX expiry | ||
/// Sets a key value pair only if the already existing etag matches the etag sent as a part of the request. | ||
/// </summary> | ||
/// <typeparam name="TGarnetApi"></typeparam> | ||
/// <param name="storageApi"></param> | ||
/// <returns></returns> | ||
private bool NetworkSETIFMATCH<TGarnetApi>(ref TGarnetApi storageApi) | ||
where TGarnetApi : IGarnetApi | ||
{ | ||
if (parseState.Count < 3 || parseState.Count > 5) | ||
{ | ||
return AbortWithWrongNumberOfArguments(nameof(RespCommand.SETIFMATCH)); | ||
} | ||
|
||
// SETIFMATCH Args: KEY VAL ETAG -> [ ((EX || PX) expiration)] | ||
int expiry = 0; | ||
ReadOnlySpan<byte> errorMessage = default; | ||
var expOption = ExpirationOption.None; | ||
|
||
var tokenIdx = 3; | ||
Span<byte> nextOpt = default; | ||
var optUpperCased = false; | ||
while (tokenIdx < parseState.Count || optUpperCased) | ||
{ | ||
if (!optUpperCased) | ||
{ | ||
nextOpt = parseState.GetArgSliceByRef(tokenIdx++).Span; | ||
} | ||
|
||
if (nextOpt.SequenceEqual(CmdStrings.EX)) | ||
{ | ||
// Validate expiry | ||
if (!parseState.TryGetInt(tokenIdx++, out expiry)) | ||
{ | ||
errorMessage = CmdStrings.RESP_ERR_GENERIC_VALUE_IS_NOT_INTEGER; | ||
break; | ||
} | ||
|
||
if (expOption != ExpirationOption.None) | ||
{ | ||
errorMessage = CmdStrings.RESP_ERR_GENERIC_SYNTAX_ERROR; | ||
break; | ||
} | ||
|
||
expOption = ExpirationOption.EX; | ||
if (expiry <= 0) | ||
{ | ||
errorMessage = CmdStrings.RESP_ERR_GENERIC_INVALIDEXP_IN_SET; | ||
break; | ||
} | ||
} | ||
else if (nextOpt.SequenceEqual(CmdStrings.PX)) | ||
{ | ||
// Validate expiry | ||
if (!parseState.TryGetInt(tokenIdx++, out expiry)) | ||
{ | ||
errorMessage = CmdStrings.RESP_ERR_GENERIC_VALUE_IS_NOT_INTEGER; | ||
break; | ||
} | ||
|
||
if (expOption != ExpirationOption.None) | ||
{ | ||
errorMessage = CmdStrings.RESP_ERR_GENERIC_SYNTAX_ERROR; | ||
break; | ||
} | ||
|
||
expOption = ExpirationOption.PX; | ||
if (expiry <= 0) | ||
{ | ||
errorMessage = CmdStrings.RESP_ERR_GENERIC_INVALIDEXP_IN_SET; | ||
break; | ||
} | ||
} | ||
else | ||
{ | ||
if (!optUpperCased) | ||
{ | ||
AsciiUtils.ToUpperInPlace(nextOpt); | ||
optUpperCased = true; | ||
continue; | ||
} | ||
|
||
errorMessage = CmdStrings.RESP_ERR_GENERIC_UNK_CMD; | ||
break; | ||
} | ||
|
||
optUpperCased = false; | ||
} | ||
|
||
if (!errorMessage.IsEmpty) | ||
{ | ||
while (!RespWriteUtils.WriteError(errorMessage, ref dcurr, dend)) | ||
SendAndReset(); | ||
return true; | ||
} | ||
|
||
SpanByte key = parseState.GetArgSliceByRef(0).SpanByte; | ||
|
||
NetworkSET_Conditional(RespCommand.SETIFMATCH, expiry, ref key, getValue: true, highPrecision: expOption == ExpirationOption.PX, withEtag: true, ref storageApi); | ||
|
||
return true; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.