Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Native ETag Support #675

Open
wants to merge 85 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 65 commits
Commits
Show all changes
85 commits
Select commit Hold shift + click to select a range
ce7d671
init etag impl
hamdaankhalidmsft Sep 18, 2024
74619d0
WIP tests
hamdaankhalidmsft Sep 19, 2024
2dbab85
complete test coverage and bugfixes
hamdaankhalidmsft Sep 24, 2024
683ead2
add edge case handling for etag override set
hamdaankhalidmsft Sep 24, 2024
7078ec3
Add documentation
hamdaankhalidmsft Sep 25, 2024
0e766f5
fix etag clearing
hamdaankhalidmsft Sep 25, 2024
1f7226d
Format
hamdaankhalidmsft Sep 25, 2024
c7cb0b5
fix build warnings
hamdaankhalidmsft Sep 25, 2024
3631d20
Update test
hamdaankhalidmsft Sep 25, 2024
a079c0f
Update tests for badrish reqs
hamdaankhalidmsft Sep 30, 2024
41bdb41
Update mainstore ops fixing rename bug and remove unnecessary GetForE…
hamdaankhalidmsft Oct 1, 2024
f6e2fb3
add read nil test
hamdaankhalidmsft Oct 1, 2024
e89477d
fmt
hamdaankhalidmsft Oct 1, 2024
ae5dd43
Update docs and add more tests for edgecase
hamdaankhalidmsft Oct 1, 2024
98e4ec3
fix renamenx and etag compat
hamdaankhalidmsft Oct 1, 2024
da48417
Add more etag edge case tests
hamdaankhalidmsft Oct 1, 2024
d581562
remove shifting in network buffer in setifmatch
hamdaankhalidmsft Oct 2, 2024
355bef9
Fix named parameters for readability
hamdaankhalidmsft Oct 2, 2024
8b18e43
use constants for etag size
hamdaankhalidmsft Oct 2, 2024
0c340c8
update read nil to be a faster
hamdaankhalidmsft Oct 2, 2024
141c870
format
hamdaankhalidmsft Oct 2, 2024
a7756c8
pr feedback
hamdaankhalidmsft Oct 8, 2024
b6ffd7a
Merge branch 'main' into hkhalid/etag-impl
hamdaankhalidmsft Oct 8, 2024
8014242
remove redundant ternary
hamdaankhalidmsft Oct 8, 2024
abc6d8f
update tests for transaction
hamdaankhalidmsft Oct 9, 2024
802ff73
Add test to make sure etag data works with recovery scenarios
hamdaankhalidmsft Oct 10, 2024
d11a1e8
Merge branch 'main' into hkhalid/etag-impl
hamdaankhalidmsft Oct 11, 2024
8bda79a
WIP
hamdaankhalidmsft Oct 24, 2024
1309833
Merge branch 'main' into hkhalid/etag-impl
hamdaankhalidmsft Nov 15, 2024
8f09c7e
WIP
hamdaankhalidmsft Nov 16, 2024
ca4caaf
Merge branch 'main' into hkhalid/etag-impl
hamdaankhalidmsft Dec 12, 2024
8861f75
unit tests working
hamdaankhalidmsft Dec 16, 2024
d7beeb6
Merge branch 'main' into hkhalid/etag-impl
hamdaankhalidmsft Dec 16, 2024
c832f71
fmt
hamdaankhalidmsft Dec 16, 2024
e7a90c8
Fix boundary condition for bitmanager
hamdaankhalidmsft Dec 16, 2024
56e305c
fix rename edgecase
hamdaankhalidmsft Dec 16, 2024
fd062d8
Merge branch 'main' into hkhalid/etag-impl
hamdaankhalid Dec 16, 2024
ddf63ca
Add custom txn, proc, and cmd handling
hamdaankhalidmsft Dec 17, 2024
2e2c6b1
Merge branch 'hkhalid/etag-impl' of https://github.com/hamdaankhalid/…
hamdaankhalidmsft Dec 17, 2024
4e327b4
update docs
hamdaankhalidmsft Dec 17, 2024
1d05e69
Finish website and nits
hamdaankhalidmsft Dec 17, 2024
c3b2dbd
nit
hamdaankhalidmsft Dec 17, 2024
5bd4e7c
big ole refactor
hamdaankhalidmsft Dec 19, 2024
7f5170d
Beautiful patter matching done
hamdaankhalidmsft Dec 19, 2024
5c2337d
SET working, todo remaining tests fix
hamdaankhalidmsft Dec 20, 2024
e750564
Fix all tests
hamdaankhalidmsft Dec 20, 2024
003a660
Got everythign working
hamdaankhalidmsft Dec 20, 2024
4d38f77
reuse code smartly
hamdaankhalidmsft Dec 20, 2024
fc2d0a0
update documentation
hamdaankhalidmsft Dec 20, 2024
8fa17c7
Finish documentation
hamdaankhalidmsft Dec 20, 2024
6d76b5d
wip
hamdaankhalidmsft Dec 23, 2024
9cb6983
branchless programming in hot path
hamdaankhalidmsft Dec 23, 2024
d764aed
branchless programming in more hoptpath
hamdaankhalidmsft Dec 23, 2024
ffbce09
More branchlessness
hamdaankhalidmsft Dec 25, 2024
7ad5694
reduce branching for set_conditional
hamdaankhalidmsft Dec 25, 2024
337cab0
fmt
hamdaankhalidmsft Dec 26, 2024
32977fb
Merge branch 'main' into hkhalid/etag-impl
hamdaankhalidmsft Dec 26, 2024
70f09b3
experiment with calling the accessor only once
hamdaankhalidmsft Dec 26, 2024
63e80f8
more branchless
hamdaankhalidmsft Dec 26, 2024
1e4e256
reduce branchless because branchless has more instructions
hamdaankhalidmsft Dec 26, 2024
73cebec
reduce branching
hamdaankhalidmsft Dec 26, 2024
764b85f
remoove reinterpret casting more
hamdaankhalidmsft Dec 26, 2024
5809459
fix read
hamdaankhalidmsft Dec 27, 2024
9bc570b
precompute offsets
hamdaankhalidmsft Dec 28, 2024
458e2e8
Fix bugs
hamdaankhalidmsft Dec 28, 2024
ff459e9
wippy
hamdaankhalidmsft Dec 29, 2024
110368f
Try adding switch case
hamdaankhalidmsft Dec 30, 2024
04fab8c
remove redundant metadatasize calc
hamdaankhalidmsft Dec 30, 2024
24505c6
add flag for set to parser
hamdaankhalidmsft Dec 30, 2024
fa5941b
fix bugs and add comments
hamdaankhalidmsft Jan 1, 2025
dab924c
Fix ACL and Txn unit test
hamdaankhalidmsft Jan 6, 2025
5200298
remove leftover
hamdaankhalidmsft Jan 6, 2025
5671d3d
reduce extra comparison
hamdaankhalidmsft Jan 6, 2025
44be2fc
Merge branch 'main' into hkhalid/etag-impl
hamdaankhalidmsft Jan 6, 2025
38c266f
leave input header alone
hamdaankhalidmsft Jan 7, 2025
e67cf9a
wip
hamdaankhalidmsft Jan 7, 2025
40d9f6d
wip
hamdaankhalidmsft Jan 7, 2025
c5ed4a3
reduce common path logic
hamdaankhalidmsft Jan 7, 2025
fb4403a
WIP
hamdaankhalidmsft Jan 7, 2025
de65bcf
WIP try rearranging at server session level
hamdaankhalidmsft Jan 7, 2025
eee23e0
reduce branching in rmw
hamdaankhalidmsft Jan 8, 2025
de179ce
add back bdn benchmarks
hamdaankhalidmsft Jan 8, 2025
bc21fc4
go back to using branching again
hamdaankhalidmsft Jan 8, 2025
adabc24
Big ole refactor
hamdaankhalidmsft Jan 9, 2025
9de234d
Badrish PR feedback
hamdaankhalidmsft Jan 9, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions libs/common/RespReadUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Buffers.Text;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
Expand Down Expand Up @@ -458,6 +459,47 @@ public static bool TryRead64Int(out long number, ref byte* ptr, byte* end, out b
return true;
}

/// <summary>
/// Given a buffer check if the value is nil ($-1\r\n)
/// If the value is nil it advances the buffer forward
/// </summary>
/// <param name="ptr">The starting position in the RESP string. Will be advanced if parsing is successful.</param>
/// <param name="end">The current end of the RESP string.</param>
/// <param name="unexpectedToken"></param>
/// <returns>True if value is nil on the buffer, false if the value on buffer is not nil</returns>
public static bool ReadNil(ref byte* ptr, byte* end, out byte? unexpectedToken)
{
unexpectedToken = null;
if (end - ptr < 5)
{
return false;
}

ReadOnlySpan<byte> expectedNilRepr = "$-1\r\n"u8;

if (*(uint*)ptr != MemoryMarshal.Read<uint>(expectedNilRepr.Slice(0, 4)) || *(ptr + 4) != expectedNilRepr[4])
{
ReadOnlySpan<byte> ptrNext5Bytes = new ReadOnlySpan<byte>(ptr, 5);
for (int i = 0; i < 5; i++)
{
// first place where the sequence differs we have found the unexpected token
if (expectedNilRepr[i] != ptrNext5Bytes[i])
{
// move the pointer to the unexpected token
ptr += i;
unexpectedToken = ptrNext5Bytes[i];
return false;
}
}
// If the sequence is not equal we shouldn't even reach this because atleast one byte should have mismatched
Debug.Assert(false);
return false;
}

ptr += 5;
return true;
}

/// <summary>
/// Tries to read a RESP array length header from the given ASCII-encoded RESP string
/// and, if successful, moves the given ptr to the end of the length header.
Expand Down
135 changes: 135 additions & 0 deletions libs/resources/RespCommandsDocs.json
Original file line number Diff line number Diff line change
Expand Up @@ -4443,6 +4443,45 @@
"DisplayText": "newkey",
"Type": "Key",
"KeySpecIndex": 1
},
{
"TypeDiscriminator": "RespCommandBasicArgument",
"Name": "WITHETAG",
"DisplayText": "WITHETAG",
"Type": "PureToken",
"ArgumentFlags": "Optional",
"Token": "WITHETAG"
}
]
},
{
"Command": "RENAMENX",
"Name": "RENAMENX",
"Summary": "Renames a key and overwrites the destination if the newkey does not exist.",
"Group": "Generic",
"Complexity": "O(1)",
"Arguments": [
{
"TypeDiscriminator": "RespCommandKeyArgument",
"Name": "KEY",
"DisplayText": "key",
"Type": "Key",
"KeySpecIndex": 0
},
{
"TypeDiscriminator": "RespCommandKeyArgument",
"Name": "NEWKEY",
"DisplayText": "newkey",
"Type": "Key",
"KeySpecIndex": 1
},
{
"TypeDiscriminator": "RespCommandBasicArgument",
"Name": "WITHETAG",
"DisplayText": "WITHETAG",
"Type": "PureToken",
"ArgumentFlags": "Optional",
"Token": "WITHETAG"
}
]
},
Expand Down Expand Up @@ -4873,6 +4912,14 @@
"Token": "GET",
"ArgumentFlags": "Optional"
},
{
"TypeDiscriminator": "RespCommandBasicArgument",
"Name": "WITHETAG",
"DisplayText": "WITHETAG",
"Type": "PureToken",
"ArgumentFlags": "Optional",
"Token": "WITHETAG"
},
{
"TypeDiscriminator": "RespCommandContainerArgument",
"Name": "EXPIRATION",
Expand Down Expand Up @@ -4918,6 +4965,94 @@
}
]
},
{
"Command": "GETIFNOTMATCH",
"Name": "GETIFNOTMATCH",
"Summary": "Gets the ETag and value if the key\u0027s current etag does not match the given etag.",
"Group": "String",
"Complexity": "O(1)",
"Arguments": [
{
"TypeDiscriminator": "RespCommandKeyArgument",
"Name": "KEY",
"DisplayText": "key",
"Type": "Key",
"KeySpecIndex": 0
},
{
"TypeDiscriminator": "RespCommandBasicArgument",
"Name": "ETAG",
"DisplayText": "etag",
"Type": "Integer"
}
]
},
{
"Command": "GETWITHETAG",
"Name": "GETWITHETAG",
"Summary": "Gets the ETag and value for the key",
"Group": "String",
"Complexity": "O(1)",
"Arguments": [
{
"TypeDiscriminator": "RespCommandKeyArgument",
"Name": "KEY",
"DisplayText": "key",
"Type": "Key",
"KeySpecIndex": 0
}
]
},
{
"Command": "SETIFMATCH",
"Name": "SETIFMATCH",
"Summary": "Sets the string value of a key, ignoring its type, if the key\u0027s current etag matches the given etag.",
"Group": "String",
"Complexity": "O(1)",
"Arguments": [
{
"TypeDiscriminator": "RespCommandKeyArgument",
"Name": "KEY",
"DisplayText": "key",
"Type": "Key",
"KeySpecIndex": 0
},
{
"TypeDiscriminator": "RespCommandBasicArgument",
"Name": "VALUE",
"DisplayText": "value",
"Type": "String"
},
{
"TypeDiscriminator": "RespCommandBasicArgument",
"Name": "ETAG",
"DisplayText": "etag",
"Type": "Integer"
},
{
"TypeDiscriminator": "RespCommandContainerArgument",
"Name": "EXPIRATION",
"Type": "OneOf",
"ArgumentFlags": "Optional",
"Arguments": [
{
"TypeDiscriminator": "RespCommandBasicArgument",
"Name": "SECONDS",
"DisplayText": "seconds",
"Type": "Integer",
"Token": "EX"
},
{
"TypeDiscriminator": "RespCommandBasicArgument",
"Name": "MILLISECONDS",
"DisplayText": "milliseconds",
"Type": "Integer",
"Token": "PX"
}
]
}
]
},
{
"Command": "SETBIT",
"Name": "SETBIT",
Expand Down
59 changes: 57 additions & 2 deletions libs/resources/RespCommandsInfo.json
Original file line number Diff line number Diff line change
Expand Up @@ -1496,6 +1496,20 @@
}
]
},
{
"Command": "GETIFNOTMATCH",
"Name": "GETIFNOTMATCH",
"IsInternal": false,
"Arity": 3,
"Flags": "NONE",
"FirstKey": 1,
"LastKey": 1,
"Step": 1,
"AclCategories": "Fast, String, Read",
"Tips": null,
"KeySpecifications": null,
"SubCommands": null
},
{
"Command": "GETRANGE",
"Name": "GETRANGE",
Expand Down Expand Up @@ -1546,6 +1560,20 @@
}
]
},
{
"Command": "GETWITHETAG",
"Name": "GETWITHETAG",
"IsInternal": false,
"Arity": 2,
"Flags": "NONE",
"FirstKey": 1,
"LastKey": 1,
"Step": 1,
"AclCategories": "Fast, String, Read",
"Tips": null,
"KeySpecifications": null,
"SubCommands": null
},
{
"Command": "HDEL",
"Name": "HDEL",
Expand Down Expand Up @@ -2981,7 +3009,7 @@
{
"Command": "RENAME",
"Name": "RENAME",
"Arity": 3,
"Arity": -3,
"Flags": "Write",
"FirstKey": 1,
"LastKey": 2,
Expand Down Expand Up @@ -3019,7 +3047,7 @@
{
"Command": "RENAMENX",
"Name": "RENAMENX",
"Arity": 3,
"Arity": -3,
"Flags": "Fast, Write",
"FirstKey": 1,
"LastKey": 2,
Expand Down Expand Up @@ -3475,6 +3503,33 @@
}
]
},
{
"Command": "SETEXNX",
"Name": "SETEXNX",
"Arity": -3,
"Flags": "NONE",
"FirstKey": 1,
"LastKey": 1,
"Step": 1,
"AclCategories": "Fast, String, Write, Transaction",
"Tips": null,
"KeySpecifications": null,
"SubCommands": null
},
{
"Command": "SETIFMATCH",
"Name": "SETIFMATCH",
"IsInternal": false,
"Arity": -4,
"Flags": "NONE",
"FirstKey": 1,
"LastKey": 1,
"Step": 1,
"AclCategories": "Fast, String, Write",
"Tips": null,
"KeySpecifications": null,
"SubCommands": null
},
{
"Command": "SETRANGE",
"Name": "SETRANGE",
Expand Down
3 changes: 3 additions & 0 deletions libs/server/AOF/AofProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,9 @@ static unsafe void StoreRMW(BasicContext<SpanByte, SpanByte, RawStringInput, Spa
// input
storeInput.DeserializeFrom(curr);

if (storeInput.header.CheckWithEtagFlag())
EtagOffsetManagementContext.SetEtagOffsetBasedOnInputHeader(ref storeInput.etagOffsetManagementContext);

var pbOutput = stackalloc byte[32];
var output = new SpanByteAndMemory(pbOutput, 32);

Expand Down
8 changes: 4 additions & 4 deletions libs/server/API/GarnetApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -176,12 +176,12 @@ public GarnetStatus APPEND(ArgSlice key, ArgSlice value, ref ArgSlice output)

#region RENAME
/// <inheritdoc />
public GarnetStatus RENAME(ArgSlice oldKey, ArgSlice newKey, StoreType storeType = StoreType.All)
=> storageSession.RENAME(oldKey, newKey, storeType);
public GarnetStatus RENAME(ArgSlice oldKey, ArgSlice newKey, bool withEtag, StoreType storeType = StoreType.All)
=> storageSession.RENAME(oldKey, newKey, storeType, withEtag);

/// <inheritdoc />
public GarnetStatus RENAMENX(ArgSlice oldKey, ArgSlice newKey, out int result, StoreType storeType = StoreType.All)
=> storageSession.RENAMENX(oldKey, newKey, storeType, out result);
public GarnetStatus RENAMENX(ArgSlice oldKey, ArgSlice newKey, out int result, bool withEtag, StoreType storeType = StoreType.All)
=> storageSession.RENAMENX(oldKey, newKey, storeType, out result, withEtag);
#endregion

#region EXISTS
Expand Down
2 changes: 1 addition & 1 deletion libs/server/API/GarnetStatus.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,6 @@ public enum GarnetStatus : byte
/// <summary>
/// Wrong type
/// </summary>
WRONGTYPE
WRONGTYPE,
}
}
4 changes: 2 additions & 2 deletions libs/server/API/IGarnetApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ public interface IGarnetApi : IGarnetReadApi, IGarnetAdvancedApi
/// <param name="newKey"></param>
/// <param name="storeType"></param>
/// <returns></returns>
GarnetStatus RENAME(ArgSlice oldKey, ArgSlice newKey, StoreType storeType = StoreType.All);
GarnetStatus RENAME(ArgSlice oldKey, ArgSlice newKey, bool withEtag, StoreType storeType = StoreType.All);

/// <summary>
/// Renames key to newkey if newkey does not yet exist. It returns an error when key does not exist.
Expand All @@ -141,7 +141,7 @@ public interface IGarnetApi : IGarnetReadApi, IGarnetAdvancedApi
/// <param name="result">The result of the operation.</param>
/// <param name="storeType">The type of store to perform the operation on.</param>
/// <returns></returns>
GarnetStatus RENAMENX(ArgSlice oldKey, ArgSlice newKey, out int result, StoreType storeType = StoreType.All);
GarnetStatus RENAMENX(ArgSlice oldKey, ArgSlice newKey, out int result, bool withEtag, StoreType storeType = StoreType.All);
#endregion

#region EXISTS
Expand Down
13 changes: 13 additions & 0 deletions libs/server/Constants.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.


namespace Garnet.server
{
internal static class Constants
{
public const int EtagSize = sizeof(long);

public const int BaseEtag = 0;
}
}
11 changes: 11 additions & 0 deletions libs/server/Custom/CustomFunctions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,17 @@ protected static unsafe void WriteBulkStringArray(ref MemoryResult<byte> output,
}
}

/// <summary>
/// Create output as bulk string, from given Span
/// </summary>
protected static unsafe void WriteBulkString(ref MemoryResult<byte> output, Span<byte> simpleString)
{
var _output = (output.MemoryOwner, output.Length);
WriteBulkString(ref _output, simpleString);
output.MemoryOwner = _output.MemoryOwner;
output.Length = _output.Length;
}

/// <summary>
/// Create output as bulk string, from given Span
/// </summary>
Expand Down
Loading
Loading