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

Feature: Support for hash field expiration #2716

Merged
merged 16 commits into from
Aug 17, 2024
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 @@ -8,6 +8,7 @@ Current package versions:

## Unreleased

- Add support for hash field expiration (see [#2715](https://github.com/StackExchange/StackExchange.Redis/issues/2715)) ([#2716 by atakavci](https://github.com/StackExchange/StackExchange.Redis/pull/2716]))

## 2.8.0

Expand Down
27 changes: 27 additions & 0 deletions src/StackExchange.Redis/Enums/ExpireResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
namespace StackExchange.Redis;

/// <summary>
/// Specifies the result of operation to set expire time.
/// </summary>
public enum ExpireResult
{
/// <summary>
/// Field deleted because the specified expiration time is due.
/// </summary>
Due = 2,

/// <summary>
/// Expiration time/duration updated successfully.
/// </summary>
Success = 1,

/// <summary>
/// Expiration not set because of a specified NX | XX | GT | LT condition not met.
/// </summary>
ConditionNotMet = 0,

/// <summary>
/// No such field.
/// </summary>
NoSuchField = -2,
}
22 changes: 22 additions & 0 deletions src/StackExchange.Redis/Enums/PersistResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
namespace StackExchange.Redis;

/// <summary>
/// Specifies the result of operation to remove the expire time.
/// </summary>
public enum PersistResult
{
/// <summary>
/// Expiration removed successfully.
/// </summary>
Success = 1,

/// <summary>
/// Expiration not removed because of a specified NX | XX | GT | LT condition not met.
/// </summary>
ConditionNotMet = -1,

/// <summary>
/// No such field.
/// </summary>
NoSuchField = -2,
}
16 changes: 16 additions & 0 deletions src/StackExchange.Redis/Enums/RedisCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ internal enum RedisCommand
HDEL,
HELLO,
HEXISTS,
HEXPIRE,
HEXPIREAT,
HEXPIRETIME,
HGET,
HGETALL,
HINCRBY,
Expand All @@ -74,6 +77,11 @@ internal enum RedisCommand
HLEN,
HMGET,
HMSET,
HPERSIST,
HPEXPIRE,
HPEXPIREAT,
HPEXPIRETIME,
HPTTL,
HRANDFIELD,
HSCAN,
HSET,
Expand Down Expand Up @@ -279,9 +287,14 @@ internal static bool IsPrimaryOnly(this RedisCommand command)
case RedisCommand.GETEX:
case RedisCommand.GETSET:
case RedisCommand.HDEL:
case RedisCommand.HEXPIRE:
case RedisCommand.HEXPIREAT:
case RedisCommand.HINCRBY:
case RedisCommand.HINCRBYFLOAT:
case RedisCommand.HMSET:
case RedisCommand.HPERSIST:
case RedisCommand.HPEXPIRE:
case RedisCommand.HPEXPIREAT:
case RedisCommand.HSET:
case RedisCommand.HSETNX:
case RedisCommand.INCR:
Expand Down Expand Up @@ -378,11 +391,14 @@ internal static bool IsPrimaryOnly(this RedisCommand command)
case RedisCommand.GETRANGE:
case RedisCommand.HELLO:
case RedisCommand.HEXISTS:
case RedisCommand.HEXPIRETIME:
case RedisCommand.HGET:
case RedisCommand.HGETALL:
case RedisCommand.HKEYS:
case RedisCommand.HLEN:
case RedisCommand.HMGET:
case RedisCommand.HPEXPIRETIME:
case RedisCommand.HPTTL:
case RedisCommand.HRANDFIELD:
case RedisCommand.HSCAN:
case RedisCommand.HSTRLEN:
Expand Down
159 changes: 159 additions & 0 deletions src/StackExchange.Redis/Interfaces/IDatabase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,165 @@ public interface IDatabase : IRedis, IDatabaseAsync
/// <remarks><seealso href="https://redis.io/commands/hexists"/></remarks>
bool HashExists(RedisKey key, RedisValue hashField, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Set the remaining time to live in milliseconds for the given set of fields of hash
/// After the timeout has expired, the field of the hash will automatically be deleted.
/// </summary>
/// <param name="key">The key of the hash.</param>
/// <param name="hashFields">The fields in the hash to set expire time.</param>
/// <param name="expiry">The timeout to set.</param>
/// <param name="when">under which condition the expiration will be set using <see cref="ExpireWhen"/>.</param>
/// <param name="flags">The flags to use for this operation.</param>
/// <returns>
/// Empty array if the key does not exist. Otherwise returns an array where each item is the result of operation for given fields:
/// <list type="table">
/// <listheader>
/// <term>Result</term>
/// <description>Description</description>
/// </listheader>
/// <item>
/// <term>2</term>
/// <description>Field deleted because the specified expiration time is due.</description>
/// </item>
/// <item>
/// <term>1</term>
/// <description>Expiration time set/updated.</description>
/// </item>
/// <item>
/// <term>0</term>
/// <description>Expiration time is not set/update (a specified ExpireWhen condition is not met).</description>
/// </item>
/// <item>
/// <term>-1</term>
/// <description>No such field exists.</description>
/// </item>
/// </list>
/// </returns>
ExpireResult[] HashFieldExpire(RedisKey key, RedisValue[] hashFields, TimeSpan expiry, ExpireWhen when = ExpireWhen.Always, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Set the time out on a field of the given set of fields of hash.
/// After the timeout has expired, the field of the hash will automatically be deleted.
/// </summary>
/// <param name="key">The key of the hash.</param>
/// <param name="hashFields">The fields in the hash to set expire time.</param>
/// <param name="expiry">The exact date to expiry to set.</param>
/// <param name="when">under which condition the expiration will be set using <see cref="ExpireWhen"/>.</param>
/// <param name="flags">The flags to use for this operation.</param>
/// <returns>
/// Empty array if the key does not exist. Otherwise returns an array where each item is the result of operation for given fields:
/// <list type="table">
/// <listheader>
/// <term>Result</term>
/// <description>Description</description>
/// </listheader>
/// <item>
/// <term>2</term>
/// <description>Field deleted because the specified expiration time is due.</description>
/// </item>
/// <item>
/// <term>1</term>
/// <description>Expiration time set/updated.</description>
/// </item>
/// <item>
/// <term>0</term>
/// <description>Expiration time is not set/update (a specified ExpireWhen condition is not met).</description>
/// </item>
/// <item>
/// <term>-1</term>
/// <description>No such field exists.</description>
/// </item>
/// </list>
/// </returns>
ExpireResult[] HashFieldExpire(RedisKey key, RedisValue[] hashFields, DateTime expiry, ExpireWhen when = ExpireWhen.Always, CommandFlags flags = CommandFlags.None);

/// <summary>
/// For each specified field, it gets the expiration time as a Unix timestamp in milliseconds (milliseconds since the Unix epoch).
/// </summary>
/// <param name="key">The key of the hash.</param>
/// <param name="hashFields">The fields in the hash to get expire time.</param>
/// <param name="flags">The flags to use for this operation.</param>
/// <returns>
/// Empty array if the key does not exist. Otherwise returns the result of operation for given fields:
/// <list type="table">
/// <listheader>
/// <term>Result</term>
/// <description>Description</description>
/// </listheader>
/// <item>
/// <term>&gt; 0</term>
/// <description>Expiration time, as a Unix timestamp in milliseconds.</description>
/// </item>
/// <item>
/// <term>-1</term>
/// <description>Field has no associated expiration time.</description>
/// </item>
/// <item>
/// <term>-2</term>
/// <description>No such field exists.</description>
/// </item>
/// </list>
/// </returns>
long[] HashFieldGetExpireDateTime(RedisKey key, RedisValue[] hashFields, CommandFlags flags = CommandFlags.None);

/// <summary>
/// For each specified field, it removes the expiration time.
/// </summary>
/// <param name="key">The key of the hash.</param>
/// <param name="hashFields">The fields in the hash to remove expire time.</param>
/// <param name="flags">The flags to use for this operation.</param>
/// <returns>
/// Empty array if the key does not exist. Otherwise returns the result of operation for given fields:
/// <list type="table">
/// <listheader>
/// <term>Result</term>
/// <description>Description</description>
/// </listheader>
/// <item>
/// <term>1</term>
/// <description>Expiration time was removed.</description>
/// </item>
/// <item>
/// <term>-1</term>
/// <description>Field has no associated expiration time.</description>
/// </item>
/// <item>
/// <term>-2</term>
/// <description>No such field exists.</description>
/// </item>
/// </list>
/// </returns>
PersistResult[] HashFieldPersist(RedisKey key, RedisValue[] hashFields, CommandFlags flags = CommandFlags.None);

/// <summary>
/// For each specified field, it gets the remaining time to live in milliseconds.
/// </summary>
/// <param name="key">The key of the hash.</param>
/// <param name="hashFields">The fields in the hash to get expire time.</param>
/// <param name="flags">The flags to use for this operation.</param>
/// <returns>
/// Empty array if the key does not exist. Otherwise returns the result of operation for given fields:
/// <list type="table">
/// <listheader>
/// <term>Result</term>
/// <description>Description</description>
/// </listheader>
/// <item>
/// <term>&gt; 0</term>
/// <description>Time to live, in milliseconds.</description>
/// </item>
/// <item>
/// <term>-1</term>
/// <description>Field has no associated expiration time.</description>
/// </item>
/// <item>
/// <term>-2</term>
/// <description>No such field exists.</description>
/// </item>
/// </list>
/// </returns>
long[] HashFieldGetTimeToLive(RedisKey key, RedisValue[] hashFields, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Returns the value associated with field in the hash stored at key.
/// </summary>
Expand Down
15 changes: 15 additions & 0 deletions src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,21 @@ public interface IDatabaseAsync : IRedisAsync
/// <inheritdoc cref="IDatabase.HashExists(RedisKey, RedisValue, CommandFlags)"/>
Task<bool> HashExistsAsync(RedisKey key, RedisValue hashField, CommandFlags flags = CommandFlags.None);

/// <inheritdoc cref="IDatabase.HashFieldExpire(RedisKey, RedisValue[], TimeSpan, ExpireWhen, CommandFlags)"/>
Task<ExpireResult[]> HashFieldExpireAsync(RedisKey key, RedisValue[] hashFields, TimeSpan expiry, ExpireWhen when = ExpireWhen.Always, CommandFlags flags = CommandFlags.None);

/// <inheritdoc cref="IDatabase.HashFieldExpire(RedisKey, RedisValue[], DateTime, ExpireWhen, CommandFlags)"/>
Task<ExpireResult[]> HashFieldExpireAsync(RedisKey key, RedisValue[] hashFields, DateTime expiry, ExpireWhen when = ExpireWhen.Always, CommandFlags flags = CommandFlags.None);

/// <inheritdoc cref="IDatabase.HashFieldGetExpireDateTime(RedisKey, RedisValue[], CommandFlags)"/>
Task<long[]> HashFieldGetExpireDateTimeAsync(RedisKey key, RedisValue[] hashFields, CommandFlags flags = CommandFlags.None);
NickCraver marked this conversation as resolved.
Show resolved Hide resolved

/// <inheritdoc cref="HashFieldPersistAsync(RedisKey, RedisValue[], CommandFlags)"/>
Task<PersistResult[]> HashFieldPersistAsync(RedisKey key, RedisValue[] hashFields, CommandFlags flags = CommandFlags.None);

/// <inheritdoc cref="IDatabase.HashFieldGetTimeToLive(RedisKey, RedisValue[], CommandFlags)"/>
Task<long[]> HashFieldGetTimeToLiveAsync(RedisKey key, RedisValue[] hashFields, CommandFlags flags = CommandFlags.None);

/// <inheritdoc cref="IDatabase.HashGet(RedisKey, RedisValue, CommandFlags)"/>
Task<RedisValue> HashGetAsync(RedisKey key, RedisValue hashField, CommandFlags flags = CommandFlags.None);

Expand Down
15 changes: 15 additions & 0 deletions src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixed.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,21 @@ public Task<bool> HashDeleteAsync(RedisKey key, RedisValue hashField, CommandFla
public Task<bool> HashExistsAsync(RedisKey key, RedisValue hashField, CommandFlags flags = CommandFlags.None) =>
Inner.HashExistsAsync(ToInner(key), hashField, flags);

public Task<ExpireResult[]> HashFieldExpireAsync(RedisKey key, RedisValue[] hashFields, TimeSpan expiry, ExpireWhen when = ExpireWhen.Always, CommandFlags flags = CommandFlags.None) =>
Inner.HashFieldExpireAsync(ToInner(key), hashFields, expiry, when, flags);

public Task<ExpireResult[]> HashFieldExpireAsync(RedisKey key, RedisValue[] hashFields, DateTime expiry, ExpireWhen when = ExpireWhen.Always, CommandFlags flags = CommandFlags.None) =>
Inner.HashFieldExpireAsync(ToInner(key), hashFields, expiry, when, flags);

public Task<long[]> HashFieldGetExpireDateTimeAsync(RedisKey key, RedisValue[] hashFields, CommandFlags flags) =>
Inner.HashFieldGetExpireDateTimeAsync(ToInner(key), hashFields, flags);

public Task<PersistResult[]> HashFieldPersistAsync(RedisKey key, RedisValue[] hashFields, CommandFlags flags) =>
Inner.HashFieldPersistAsync(ToInner(key), hashFields, flags);

public Task<long[]> HashFieldGetTimeToLiveAsync(RedisKey key, RedisValue[] hashFields, CommandFlags flags) =>
Inner.HashFieldGetTimeToLiveAsync(ToInner(key), hashFields, flags);

public Task<HashEntry[]> HashGetAllAsync(RedisKey key, CommandFlags flags = CommandFlags.None) =>
Inner.HashGetAllAsync(ToInner(key), flags);

Expand Down
15 changes: 15 additions & 0 deletions src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixedDatabase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,21 @@ public bool HashDelete(RedisKey key, RedisValue hashField, CommandFlags flags =
public bool HashExists(RedisKey key, RedisValue hashField, CommandFlags flags = CommandFlags.None) =>
Inner.HashExists(ToInner(key), hashField, flags);

public ExpireResult[] HashFieldExpire(RedisKey key, RedisValue[] hashFields, TimeSpan expiry, ExpireWhen when = ExpireWhen.Always, CommandFlags flags = CommandFlags.None) =>
Inner.HashFieldExpire(ToInner(key), hashFields, expiry, when, flags);

public ExpireResult[] HashFieldExpire(RedisKey key, RedisValue[] hashFields, DateTime expiry, ExpireWhen when = ExpireWhen.Always, CommandFlags flags = CommandFlags.None) =>
Inner.HashFieldExpire(ToInner(key), hashFields, expiry, when, flags);

public long[] HashFieldGetExpireDateTime(RedisKey key, RedisValue[] hashFields, CommandFlags flags) =>
Inner.HashFieldGetExpireDateTime(ToInner(key), hashFields, flags);

public PersistResult[] HashFieldPersist(RedisKey key, RedisValue[] hashFields, CommandFlags flags) =>
Inner.HashFieldPersist(ToInner(key), hashFields, flags);

public long[] HashFieldGetTimeToLive(RedisKey key, RedisValue[] hashFields, CommandFlags flags) =>
Inner.HashFieldGetTimeToLive(ToInner(key), hashFields, flags);

public HashEntry[] HashGetAll(RedisKey key, CommandFlags flags = CommandFlags.None) =>
Inner.HashGetAll(ToInner(key), flags);

Expand Down
Loading
Loading