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

Support OBJECT REFCOUNT #2087

Merged
merged 8 commits into from
Apr 13, 2022
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
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
1 change: 1 addition & 0 deletions docs/ReleaseNotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
- Adds: Support for `SINTERCARD` with `.SetIntersectionLength()`/`.SetIntersectionLengthAsync()` ([#2078 by Avital-Fine](https://github.com/StackExchange/StackExchange.Redis/pull/2078))
- Adds: Support for `LPOS` with `.ListPosition()`/`.ListPositionAsync()` and `.ListPositions()`/`.ListPositionsAsync()` ([#2080 by slorello89](https://github.com/StackExchange/StackExchange.Redis/pull/2080))
- Fix: For streams, properly hash `XACK`, `XCLAIM`, and `XPENDING` in cluster scenarios to eliminate `MOVED` retries ([#2085 by nielsderdaele](https://github.com/StackExchange/StackExchange.Redis/pull/2085))
- Adds: Support for `OBJECT REFCOUNT` with `.KeyRefCount()`/`.KeyRefCountAsync()` ([#2087 by Avital-Fine](https://github.com/StackExchange/StackExchange.Redis/pull/2087))

## 2.5.61

Expand Down
9 changes: 9 additions & 0 deletions src/StackExchange.Redis/Interfaces/IDatabase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,15 @@ public interface IDatabase : IRedis, IDatabaseAsync
/// <remarks>https://redis.io/commands/randomkey</remarks>
RedisKey KeyRandom(CommandFlags flags = CommandFlags.None);

/// <summary>
/// Returns the reference count of the object stored at <paramref name="key"/>.
/// </summary>
/// <param name="key">The key to get a reference count for.</param>
/// <param name="flags">The flags to use for this operation.</param>
/// <returns>The number of references (0 if the key does not exist).</returns>
/// <remarks>https://redis.io/commands/object-refcount</remarks>
long? KeyRefCount(RedisKey key, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Renames <paramref name="key"/> to <paramref name="newKey"/>.
/// It returns an error when the source and destination names are the same, or when key does not exist.
Expand Down
9 changes: 9 additions & 0 deletions src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,15 @@ public interface IDatabaseAsync : IRedisAsync
/// <remarks>https://redis.io/commands/randomkey</remarks>
Task<RedisKey> KeyRandomAsync(CommandFlags flags = CommandFlags.None);

/// <summary>
/// Returns the reference count of the object stored at <paramref name="key"/>.
/// </summary>
/// <param name="key">The key to get a reference count for.</param>
/// <param name="flags">The flags to use for this operation.</param>
/// <returns>The number of references (0 if the key does not exist).</returns>
/// <remarks>https://redis.io/commands/object-refcount</remarks>
Task<long?> KeyRefCountAsync(RedisKey key, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Renames <paramref name="key"/> to <paramref name="newKey"/>.
/// It returns an error when the source and destination names are the same, or when key does not exist.
Expand Down
3 changes: 3 additions & 0 deletions src/StackExchange.Redis/KeyspaceIsolation/DatabaseWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,9 @@ public bool KeyPersist(RedisKey key, CommandFlags flags = CommandFlags.None) =>
public RedisKey KeyRandom(CommandFlags flags = CommandFlags.None) =>
throw new NotSupportedException("RANDOMKEY is not supported when a key-prefix is specified");

public long? KeyRefCount(RedisKey key, CommandFlags flags = CommandFlags.None) =>
Inner.KeyRefCount(ToInner(key), flags);

public bool KeyRename(RedisKey key, RedisKey newKey, When when = When.Always, CommandFlags flags = CommandFlags.None) =>
Inner.KeyRename(ToInner(key), ToInner(newKey), when, flags);

Expand Down
3 changes: 3 additions & 0 deletions src/StackExchange.Redis/KeyspaceIsolation/WrapperBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,9 @@ public Task<bool> KeyPersistAsync(RedisKey key, CommandFlags flags = CommandFlag
public Task<RedisKey> KeyRandomAsync(CommandFlags flags = CommandFlags.None) =>
throw new NotSupportedException("RANDOMKEY is not supported when a key-prefix is specified");

public Task<long?> KeyRefCountAsync(RedisKey key, CommandFlags flags = CommandFlags.None) =>
Inner.KeyRefCountAsync(ToInner(key), flags);

public Task<bool> KeyRenameAsync(RedisKey key, RedisKey newKey, When when = When.Always, CommandFlags flags = CommandFlags.None) =>
Inner.KeyRenameAsync(ToInner(key), ToInner(newKey), when, flags);

Expand Down
2 changes: 2 additions & 0 deletions src/StackExchange.Redis/PublicAPI.Shipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,7 @@ StackExchange.Redis.IDatabase.KeyMigrate(StackExchange.Redis.RedisKey key, Syste
StackExchange.Redis.IDatabase.KeyMove(StackExchange.Redis.RedisKey key, int database, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> bool
StackExchange.Redis.IDatabase.KeyPersist(StackExchange.Redis.RedisKey key, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> bool
StackExchange.Redis.IDatabase.KeyRandom(StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.RedisKey
StackExchange.Redis.IDatabase.KeyRefCount(StackExchange.Redis.RedisKey key, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long?
StackExchange.Redis.IDatabase.KeyRename(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisKey newKey, StackExchange.Redis.When when = StackExchange.Redis.When.Always, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> bool
StackExchange.Redis.IDatabase.KeyRestore(StackExchange.Redis.RedisKey key, byte[]! value, System.TimeSpan? expiry = null, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> void
StackExchange.Redis.IDatabase.KeyTimeToLive(StackExchange.Redis.RedisKey key, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.TimeSpan?
Expand Down Expand Up @@ -731,6 +732,7 @@ StackExchange.Redis.IDatabaseAsync.KeyMigrateAsync(StackExchange.Redis.RedisKey
StackExchange.Redis.IDatabaseAsync.KeyMoveAsync(StackExchange.Redis.RedisKey key, int database, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task<bool>!
StackExchange.Redis.IDatabaseAsync.KeyPersistAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task<bool>!
StackExchange.Redis.IDatabaseAsync.KeyRandomAsync(StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task<StackExchange.Redis.RedisKey>!
StackExchange.Redis.IDatabaseAsync.KeyRefCountAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task<long?>!
StackExchange.Redis.IDatabaseAsync.KeyRenameAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisKey newKey, StackExchange.Redis.When when = StackExchange.Redis.When.Always, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task<bool>!
StackExchange.Redis.IDatabaseAsync.KeyRestoreAsync(StackExchange.Redis.RedisKey key, byte[]! value, System.TimeSpan? expiry = null, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task!
StackExchange.Redis.IDatabaseAsync.KeyTimeToLiveAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task<System.TimeSpan?>!
Expand Down
12 changes: 12 additions & 0 deletions src/StackExchange.Redis/RedisDatabase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -827,6 +827,18 @@ public Task<RedisKey> KeyRandomAsync(CommandFlags flags = CommandFlags.None)
return ExecuteAsync(msg, ResultProcessor.RedisKey);
}

public long? KeyRefCount(RedisKey key, CommandFlags flags = CommandFlags.None)
{
var msg = Message.Create(Database, flags, RedisCommand.OBJECT, RedisLiterals.REFCOUNT, key);
return ExecuteSync(msg, ResultProcessor.NullableInt64);
}

public Task<long?> KeyRefCountAsync(RedisKey key, CommandFlags flags = CommandFlags.None)
{
var msg = Message.Create(Database, flags, RedisCommand.OBJECT, RedisLiterals.REFCOUNT, key);
return ExecuteAsync(msg, ResultProcessor.NullableInt64);
}

public bool KeyRename(RedisKey key, RedisKey newKey, When when = When.Always, CommandFlags flags = CommandFlags.None)
{
WhenAlwaysOrNotExists(when);
Expand Down
3 changes: 2 additions & 1 deletion src/StackExchange.Redis/RedisLiterals.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,12 +92,13 @@ public static readonly RedisValue
PX = "PX",
PXAT = "PXAT",
RANK = "RANK",
RIGHT = "RIGHT",
REFCOUNT = "REFCOUNT",
REPLACE = "REPLACE",
RESET = "RESET",
RESETSTAT = "RESETSTAT",
REV = "REV",
REWRITE = "REWRITE",
RIGHT = "RIGHT",
SAVE = "SAVE",
SEGFAULT = "SEGFAULT",
SET = "SET",
Expand Down
7 changes: 7 additions & 0 deletions tests/StackExchange.Redis.Tests/DatabaseWrapperTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,13 @@ public void KeyRandom()
Assert.Throws<NotSupportedException>(() => wrapper.KeyRandom());
}

[Fact]
public void KeyRefCount()
{
wrapper.KeyRefCount("key", CommandFlags.None);
mock.Verify(_ => _.KeyRefCount("prefix:key", CommandFlags.None));
}

[Fact]
public void KeyRename()
{
Expand Down
17 changes: 17 additions & 0 deletions tests/StackExchange.Redis.Tests/Keys.cs
Original file line number Diff line number Diff line change
Expand Up @@ -264,5 +264,22 @@ public async Task TouchIdleTimeAsync()
Assert.True(idleTime1 < idleTime);
}
}

[Fact]
public async Task KeyRefCount()
{
using var muxer = Create();
var key = Me();
var db = muxer.GetDatabase();
db.KeyDelete(key, CommandFlags.FireAndForget);
db.StringSet(key, "new value", flags: CommandFlags.FireAndForget);

Assert.Equal(1, db.KeyRefCount(key));
Assert.Equal(1, await db.KeyRefCountAsync(key));

var keyNotExists = key + "no-exist";
Assert.Null(db.KeyRefCount(keyNotExists));
Assert.Null(await db.KeyRefCountAsync(keyNotExists));
}
}
}
7 changes: 7 additions & 0 deletions tests/StackExchange.Redis.Tests/WrapperBaseTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,13 @@ public Task KeyRandomAsync()
return Assert.ThrowsAsync<NotSupportedException>(() => wrapper.KeyRandomAsync());
}

[Fact]
public void KeyRefCountAsync()
{
wrapper.KeyRefCountAsync("key", CommandFlags.None);
mock.Verify(_ => _.KeyRefCountAsync("prefix:key", CommandFlags.None));
}

[Fact]
public void KeyRenameAsync()
{
Expand Down