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 FREQ #2105

Merged
merged 6 commits into from
Apr 19, 2022
Merged
Show file tree
Hide file tree
Changes from all 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 @@ -26,6 +26,7 @@
- Adds: Support for `LMPOP` with `.ListLeftPop()`/`.ListLeftPopAsync()` and `.ListRightPop()`/`.ListRightPopAsync()` ([#2094 by slorello89](https://github.com/StackExchange/StackExchange.Redis/pull/2094))
- Adds: Support for `ZMPOP` with `.SortedSetPop()`/`.SortedSetPopAsync()` ([#2094 by slorello89](https://github.com/StackExchange/StackExchange.Redis/pull/2094))
- Adds: Support for `XAUTOCLAIM` with `.StreamAutoClaim()`/.`StreamAutoClaimAsync()` and `.StreamAutoClaimIdsOnly()`/.`StreamAutoClaimIdsOnlyAsync()` ([#2095 by ttingen](https://github.com/StackExchange/StackExchange.Redis/pull/2095))
- Adds: Support for `OBJECT FREQ` with `.KeyFrequency()`/`.KeyFrequencyAsync()` ([#2105 by Avital-Fine](https://github.com/StackExchange/StackExchange.Redis/pull/2105))

## 2.5.61

Expand Down
11 changes: 11 additions & 0 deletions src/StackExchange.Redis/Interfaces/IDatabase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -754,6 +754,17 @@ public interface IDatabase : IRedis, IDatabaseAsync
/// </remarks>
DateTime? KeyExpireTime(RedisKey key, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Returns the logarithmic access frequency counter of the object stored at <paramref name="key"/>.
/// The command is only available when the <c>maxmemory-policy</c> configuration directive is set to
/// one of <see href="https://redis.io/docs/manual/eviction/#the-new-lfu-mode">the LFU policies</see>.
/// </summary>
/// <param name="key">The key to get a frequency count for.</param>
/// <param name="flags">The flags to use for this operation.</param>
/// <returns>The number of logarithmic access frequency counter, (<see langword="null"/> if the key does not exist).</returns>
/// <remarks><seealso href="https://redis.io/commands/object-freq"/></remarks>
long? KeyFrequency(RedisKey key, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Returns the time since the object stored at the specified key is idle (not requested by read or write operations).
/// </summary>
Expand Down
11 changes: 11 additions & 0 deletions src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs
Original file line number Diff line number Diff line change
Expand Up @@ -730,6 +730,17 @@ public interface IDatabaseAsync : IRedisAsync
/// </remarks>
Task<DateTime?> KeyExpireTimeAsync(RedisKey key, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Returns the logarithmic access frequency counter of the object stored at <paramref name="key"/>.
/// The command is only available when the <c>maxmemory-policy</c> configuration directive is set to
/// one of <see href="https://redis.io/docs/manual/eviction/#the-new-lfu-mode">the LFU policies</see>.
/// </summary>
/// <param name="key">The key to get a frequency count for.</param>
/// <param name="flags">The flags to use for this operation.</param>
/// <returns>The number of logarithmic access frequency counter, (<see langword="null"/> if the key does not exist).</returns>
/// <remarks><seealso href="https://redis.io/commands/object-freq"/></remarks>
Task<long?> KeyFrequencyAsync(RedisKey key, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Returns the time since the object stored at the specified key is idle (not requested by read or write operations).
/// </summary>
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 @@ -182,6 +182,9 @@ public bool KeyExpire(RedisKey key, TimeSpan? expiry, ExpireWhen when, CommandFl
public DateTime? KeyExpireTime(RedisKey key, CommandFlags flags = CommandFlags.None) =>
Inner.KeyExpireTime(ToInner(key), flags);

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

public TimeSpan? KeyIdleTime(RedisKey key, CommandFlags flags = CommandFlags.None) =>
Inner.KeyIdleTime(ToInner(key), 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 @@ -193,6 +193,9 @@ public Task<bool> KeyExpireAsync(RedisKey key, TimeSpan? expiry, ExpireWhen when
public Task<DateTime?> KeyExpireTimeAsync(RedisKey key, CommandFlags flags = CommandFlags.None) =>
Inner.KeyExpireTimeAsync(ToInner(key), flags);

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

public Task<TimeSpan?> KeyIdleTimeAsync(RedisKey key, CommandFlags flags = CommandFlags.None) =>
Inner.KeyIdleTimeAsync(ToInner(key), 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 @@ -555,6 +555,7 @@ StackExchange.Redis.IDatabase.KeyExpire(StackExchange.Redis.RedisKey key, System
StackExchange.Redis.IDatabase.KeyExpire(StackExchange.Redis.RedisKey key, System.TimeSpan? expiry, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> bool
StackExchange.Redis.IDatabase.KeyExpire(StackExchange.Redis.RedisKey key, System.TimeSpan? expiry, StackExchange.Redis.ExpireWhen when, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> bool
StackExchange.Redis.IDatabase.KeyExpireTime(StackExchange.Redis.RedisKey key, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.DateTime?
StackExchange.Redis.IDatabase.KeyFrequency(StackExchange.Redis.RedisKey key, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long?
StackExchange.Redis.IDatabase.KeyIdleTime(StackExchange.Redis.RedisKey key, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.TimeSpan?
StackExchange.Redis.IDatabase.KeyMigrate(StackExchange.Redis.RedisKey key, System.Net.EndPoint! toServer, int toDatabase = 0, int timeoutMilliseconds = 0, StackExchange.Redis.MigrateOptions migrateOptions = StackExchange.Redis.MigrateOptions.None, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> void
StackExchange.Redis.IDatabase.KeyMove(StackExchange.Redis.RedisKey key, int database, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> bool
Expand Down Expand Up @@ -773,6 +774,7 @@ StackExchange.Redis.IDatabaseAsync.KeyExpireAsync(StackExchange.Redis.RedisKey k
StackExchange.Redis.IDatabaseAsync.KeyExpireAsync(StackExchange.Redis.RedisKey key, System.TimeSpan? expiry, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task<bool>!
StackExchange.Redis.IDatabaseAsync.KeyExpireAsync(StackExchange.Redis.RedisKey key, System.TimeSpan? expiry, StackExchange.Redis.ExpireWhen when, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task<bool>!
StackExchange.Redis.IDatabaseAsync.KeyExpireTimeAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task<System.DateTime?>!
StackExchange.Redis.IDatabaseAsync.KeyFrequencyAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task<long?>!
StackExchange.Redis.IDatabaseAsync.KeyIdleTimeAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task<System.TimeSpan?>!
StackExchange.Redis.IDatabaseAsync.KeyMigrateAsync(StackExchange.Redis.RedisKey key, System.Net.EndPoint! toServer, int toDatabase = 0, int timeoutMilliseconds = 0, StackExchange.Redis.MigrateOptions migrateOptions = StackExchange.Redis.MigrateOptions.None, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task!
StackExchange.Redis.IDatabaseAsync.KeyMoveAsync(StackExchange.Redis.RedisKey key, int database, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task<bool>!
Expand Down
13 changes: 13 additions & 0 deletions src/StackExchange.Redis/RedisDatabase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -877,11 +877,24 @@ public Task<bool> KeyExpireAsync(RedisKey key, DateTime? expire, ExpireWhen when
return ExecuteAsync(msg, ResultProcessor.NullableDateTimeFromMilliseconds);
}

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

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

public TimeSpan? KeyIdleTime(RedisKey key, CommandFlags flags = CommandFlags.None)
{
var msg = Message.Create(Database, flags, RedisCommand.OBJECT, RedisLiterals.IDLETIME, key);
return ExecuteSync(msg, ResultProcessor.TimeSpanFromSeconds);
}

public Task<TimeSpan?> KeyIdleTimeAsync(RedisKey key, CommandFlags flags = CommandFlags.None)
{
var msg = Message.Create(Database, flags, RedisCommand.OBJECT, RedisLiterals.IDLETIME, key);
Expand Down
1 change: 1 addition & 0 deletions src/StackExchange.Redis/RedisLiterals.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ public static readonly RedisValue
EXAT = "EXAT",
EXISTS = "EXISTS",
FLUSH = "FLUSH",
FREQ = "FREQ",
GET = "GET",
GETNAME = "GETNAME",
GT = "GT",
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 @@ -319,6 +319,13 @@ public void KeyExpireTime()
mock.Verify(_ => _.KeyExpireTime("prefix:key", CommandFlags.None));
}

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

[Fact]
public void KeyMigrate()
{
Expand Down
43 changes: 43 additions & 0 deletions tests/StackExchange.Redis.Tests/Keys.cs
Original file line number Diff line number Diff line change
Expand Up @@ -296,4 +296,47 @@ public async Task KeyRefCount()
Assert.Null(db.KeyRefCount(keyNotExists));
Assert.Null(await db.KeyRefCountAsync(keyNotExists));
}

[Fact]
public async Task KeyFrequency()
{
using var conn = Create(allowAdmin: true, require: RedisFeatures.v4_0_0);

var key = Me();
var db = conn.GetDatabase();
var server = GetServer(conn);

var serverConfig = server.ConfigGet("maxmemory-policy");
var maxMemoryPolicy = serverConfig.Length == 1 ? serverConfig[0].Value : "";
Log($"maxmemory-policy detected as {maxMemoryPolicy}");
var isLfu = maxMemoryPolicy.Contains("lfu");

db.KeyDelete(key, CommandFlags.FireAndForget);
db.StringSet(key, "new value", flags: CommandFlags.FireAndForget);
db.StringGet(key);

if (isLfu)
{
var count = db.KeyFrequency(key);
Assert.True(count > 0);

count = await db.KeyFrequencyAsync(key);
Assert.True(count > 0);

// Key not exists
db.KeyDelete(key, CommandFlags.FireAndForget);
var res = db.KeyFrequency(key);
Assert.Null(res);

res = await db.KeyFrequencyAsync(key);
Assert.Null(res);
}
else
{
var ex = Assert.Throws<RedisServerException>(() => db.KeyFrequency(key));
Assert.Contains("An LFU maxmemory policy is not selected", ex.Message);
ex = await Assert.ThrowsAsync<RedisServerException>(() => db.KeyFrequencyAsync(key));
Assert.Contains("An LFU maxmemory policy is not selected", ex.Message);
}
}
}
7 changes: 7 additions & 0 deletions tests/StackExchange.Redis.Tests/WrapperBaseTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,13 @@ public void KeyExpireTimeAsync()
mock.Verify(_ => _.KeyExpireTimeAsync("prefix:key", CommandFlags.None));
}

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

[Fact]
public void KeyMigrateAsync()
{
Expand Down