Skip to content

Commit

Permalink
support for hash field expiration
Browse files Browse the repository at this point in the history
HPEXPIRETIME, HPTTL, HPERSIST, HGETF, HSETF
  • Loading branch information
atakavci committed May 17, 2024
1 parent 9b0baec commit 1ae4d4a
Show file tree
Hide file tree
Showing 11 changed files with 1,236 additions and 13 deletions.
63 changes: 63 additions & 0 deletions src/StackExchange.Redis/Enums/HashFieldFlags.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using System;
using System.Collections.Generic;
using System.Linq;

namespace StackExchange.Redis;

/// <summary>
/// Specifies the options to the HSETF command when to create hash/fields and the return data
/// </summary>
[Flags]
public enum HashFieldFlags
{

/// <summary>
/// No options specified.
/// </summary>
None = 0,
/// <summary>
/// When DC (“Don’t Create”) is specified: if key does not exist: do nothing (don’t create key)
/// </summary>
DC = 1,
/// <summary>
/// When DCF (“Don’t Create Fields”) is specified: for each specified field: if the field already exists: set the field's value and expiration time; ignore fields that do not exist
/// </summary>
DCF = 2,
/// <summary>
/// When DOF (“Don’t Overwrite Fields”) is specified: for each specified field: if such field does not exist: create field and set its value and expiration time; ignore fields that already exists
/// </summary>
DOF = 4,
/// <summary>
/// When GETNEW is specified: returns the new value of given fields
/// </summary>
GETNEW = 8,
/// <summary>
/// When GETOLD is specified: returns the old value of given fields
/// </summary>
GETOLD = 16,
}

internal static class HashFieldFlagsExtensions
{
internal static bool isNone(this HashFieldFlags flags) =>
flags == HashFieldFlags.None;
internal static bool isDC(this HashFieldFlags flags) =>
flags.HasFlag(HashFieldFlags.DC);

internal static bool isDCF(this HashFieldFlags flags) =>
flags.HasFlag(HashFieldFlags.DCF);

internal static bool isDOF(this HashFieldFlags flags) =>
flags.HasFlag(HashFieldFlags.DOF);

internal static bool isGETNEW(this HashFieldFlags flags) =>
flags.HasFlag(HashFieldFlags.GETNEW);

internal static bool isGETOLD(this HashFieldFlags flags) =>
flags.HasFlag(HashFieldFlags.GETOLD);

internal static List<RedisValue> ToRedisValueList(this HashFieldFlags flags) =>
flags.isNone() ? new List<RedisValue>() : flags.ToString().Split(',').Select(v => (RedisValue)v).ToList();

}

12 changes: 12 additions & 0 deletions src/StackExchange.Redis/Enums/RedisCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,19 +68,25 @@ internal enum RedisCommand
HEXISTS,
HEXPIRE,
HEXPIREAT,
HEXPIRETIME,
HGET,
HGETALL,
HGETF,
HINCRBY,
HINCRBYFLOAT,
HKEYS,
HLEN,
HMGET,
HMSET,
HPERSIST,
HPEXPIRE,
HPEXPIREAT,
HPEXPIRETIME,
HPTTL,
HRANDFIELD,
HSCAN,
HSET,
HSETF,
HSETNX,
HSTRLEN,
HVALS,
Expand Down Expand Up @@ -285,12 +291,15 @@ internal static bool IsPrimaryOnly(this RedisCommand command)
case RedisCommand.HDEL:
case RedisCommand.HEXPIRE:
case RedisCommand.HEXPIREAT:
case RedisCommand.HGETF:
case RedisCommand.HINCRBY:
case RedisCommand.HINCRBYFLOAT:
case RedisCommand.HMSET:
case RedisCommand.HPERSIST:
case RedisCommand.HPEXPIRE:
case RedisCommand.HPEXPIREAT:
case RedisCommand.HSET:
case RedisCommand.HSETF:
case RedisCommand.HSETNX:
case RedisCommand.INCR:
case RedisCommand.INCRBY:
Expand Down Expand Up @@ -386,11 +395,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
194 changes: 193 additions & 1 deletion src/StackExchange.Redis/Interfaces/IDatabase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,84 @@ public interface IDatabase : IRedis, IDatabaseAsync
/// </returns>
ExpireResult[]? HashFieldExpire(RedisKey key, RedisValue[] hashFields, DateTime expiry, ExpireWhen when = ExpireWhen.Always, CommandFlags flags = CommandFlags.None);

/// <summary>
/// For 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="hashField">The field in the hash to get expire time.</param>
/// <param name="flags">The flags to use for this operation.</param>
/// <returns>null if the key does not exist. Otherwise returns the result of operation for given field
/// expiration time: as a UNIX timestamp in milliseconds
/// -1: if field has no associated expiration time
/// -2: no such field
/// </returns>
long? HashFieldExpireTime(RedisKey key, RedisValue hashField, 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>null if the key does not exist. Otherwise returns the result of operation for given fields
/// expiration time: as a UNIX timestamp in milliseconds
/// -1: if field has no associated expiration time
/// -2: no such field
/// </returns>
long[]? HashFieldExpireTime(RedisKey key, RedisValue[] hashFields, CommandFlags flags = CommandFlags.None);

/// <summary>
/// For specified field, it removes the expiration time
/// </summary>
/// <param name="key">The key of the hash.</param>
/// <param name="hashField">The field in the hash to remove expire time.</param>
/// <param name="flags">The flags to use for this operation.</param>
/// <returns>null if the key does not exist. Otherwise returns the result of operation for given field
/// 1: if the expiration time was removed
/// -1: if field has no associated expiration time
/// -2: no such field
/// </returns>
long? HashFieldPersist(RedisKey key, RedisValue hashField, 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>null if the key does not exist. Otherwise returns the result of operation for given fields
/// 1: if the expiration time was removed
/// -1: if field has no associated expiration time
/// -2: no such field
/// </returns>
long[]? HashFieldPersist(RedisKey key, RedisValue[] hashFields, CommandFlags flags = CommandFlags.None);

/// <summary>
/// For specified field, it gets the remaining time to live in milliseconds
/// </summary>
/// <param name="key">The key of the hash.</param>
/// <param name="hashField">The field in the hash to get expire time.</param>
/// <param name="flags">The flags to use for this operation.</param>
/// <returns>null if the key does not exist. Otherwise returns the result of operation for given field
/// time to live: in milliseconds
/// -1: if field has no associated expiration time
/// -2: no such field
/// </returns>
long? HashFieldTimeToLive(RedisKey key, RedisValue hashField, 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>null if the key does not exist. Otherwise returns the result of operation for given fields
/// time to live: in milliseconds
/// -1: if field has no associated expiration time
/// -2: no such field
/// </returns>
long[]? HashFieldTimeToLive(RedisKey key, RedisValue[] hashFields, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Returns the value associated with field in the hash stored at key.
/// </summary>
Expand Down Expand Up @@ -426,6 +504,120 @@ public interface IDatabase : IRedis, IDatabaseAsync
/// <remarks><seealso href="https://redis.io/commands/hmget"/></remarks>
RedisValue[] HashGet(RedisKey key, RedisValue[] hashFields, CommandFlags flags = CommandFlags.None);

/// <summary>
/// For each specified field, it gets the value and sets the field's remaining time to live
/// </summary>
/// <param name="key">The key of the hash.</param>
/// <param name="hashFields">The fields in the hash for this operation.</param>
/// <param name="expireDuration">The time out to set in milliseconds</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>null if the key does not exist. Otherwise returns the result of operation for given fields
/// value: value of field
/// nil: if no such field exists
/// </returns>
RedisValue[]? HashGet(RedisKey key, RedisValue[] hashFields, TimeSpan expireDuration, ExpireWhen when = ExpireWhen.Always, CommandFlags flags = CommandFlags.None);

/// <summary>
/// For each specified field, it gets the value and sets the field's expiration timestamp
/// </summary>
/// <param name="key">The key of the hash.</param>
/// <param name="hashFields">The fields in the hash for this operation.</param>
/// <param name="expireTime">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>null if the key does not exist. Otherwise returns the result of operation for given fields
/// value: value of field
/// nil: if no such field exists
/// </returns>
RedisValue[]? HashGet(RedisKey key, RedisValue[] hashFields, DateTime expireTime, ExpireWhen when = ExpireWhen.Always, CommandFlags flags = CommandFlags.None);

/// <summary>
/// For each specified field, it gets the value and removes the field's expiration
/// </summary>
/// <param name="key">The key of the hash.</param>
/// <param name="hashFields">The fields in the hash for this operation.</param>
/// <param name="flags">The flags to use for this operation.</param>
/// <returns>null if the key does not exist. Otherwise returns the result of operation for given fields
/// value: value of field
/// nil: if no such field exists
/// </returns>
RedisValue[]? HashGetPersistFields(RedisKey key, RedisValue[] hashFields, CommandFlags flags = CommandFlags.None);

/// <summary>
/// For each specified field, it sets the value and optionally sets the fields expiration
/// Depending on HashFieldFlags it creates the hash key or its fields in case they dont exist
/// - When DC is not specified: if key does not exist: create key
/// - When DC (“Don’t Create”) is specified: if key does not exist: do nothing (don’t create key)
/// - When neither DCF nor DOF are specified: for each specified field: if such field does not exist: create field; set all fields' value and expiration time
/// - When DCF (“Don’t Create Fields”) is specified: for each specified field: if the field already exists: set the field's value and expiration time; ignore fields that do not exist
/// - When DOF (“Don’t Overwrite Fields”) is specified: for each specified field: if such field does not exist: create field and set its value and expiration time; ignore fields that already exists
/// </summary>
/// <param name="key">The key of the hash.</param>
/// <param name="hashFields">The fields in the hash for this operation.</param>
/// <param name="keepExpiry">Whether to maintain the existing expiration.</param>
/// <param name="fieldFlags"></param>
/// <param name="flags">The flags to use for this operation.</param>
/// <returns>null if the key does not exist. Otherwise returns the result of operation for given fields
/// return value depends on the HashFieldFlags (GETNEW/GETOLD)
/// if GETNEW is specified: the value of field after command execution
/// if GETOLD is specified: the value of field before command execution
/// if GETNEW or GETOLD is not specified: a + b where
/// a: 1 if the field's value was set or 0 if not (DCF/DOF met)
/// b: 2 if the field's expiration time was set/discarded or 0 if not (DCF/DOF met, NX/XX/GT/LT not met)
/// </returns>
RedisValue[]? HashSet(RedisKey key, HashEntry[] hashFields, bool keepExpiry, HashFieldFlags fieldFlags = HashFieldFlags.None, CommandFlags flags = CommandFlags.None);

/// <summary>
/// For each specified field, it sets the value and optionally sets the fields expiration
/// Depending on HashFieldFlags it creates the hash key or its fields in case they dont exist
/// - When DC is not specified: if key does not exist: create key
/// - When DC (“Don’t Create”) is specified: if key does not exist: do nothing (don’t create key)
/// - When neither DCF nor DOF are specified: for each specified field: if such field does not exist: create field; set all fields' value and expiration time
/// - When DCF (“Don’t Create Fields”) is specified: for each specified field: if the field already exists: set the field's value and expiration time; ignore fields that do not exist
/// - When DOF (“Don’t Overwrite Fields”) is specified: for each specified field: if such field does not exist: create field and set its value and expiration time; ignore fields that already exists
/// </summary>
/// <param name="key">The key of the hash.</param>
/// <param name="hashFields">The fields in the hash for this operation.</param>
/// <param name="expireDuration">The time out to set in milliseconds</param>
/// <param name="when">under which condition the expiration will be set using <see cref="ExpireWhen"/>.</param>
/// <param name="fieldFlags"></param>
/// <param name="flags">The flags to use for this operation.</param>
/// <returns>null if the key does not exist. Otherwise returns the result of operation for given fields
/// return value depends on the HashFieldFlags (GETNEW/GETOLD)
/// if GETNEW is specified: the value of field after command execution
/// if GETOLD is specified: the value of field before command execution
/// if GETNEW or GETOLD is not specified: a + b where
/// a: 1 if the field's value was set or 0 if not (DCF/DOF met)
/// b: 2 if the field's expiration time was set/discarded or 0 if not (DCF/DOF met, NX/XX/GT/LT not met)
/// </returns>
RedisValue[]? HashSet(RedisKey key, HashEntry[] hashFields, TimeSpan expireDuration, ExpireWhen when = ExpireWhen.Always, HashFieldFlags fieldFlags = HashFieldFlags.None, CommandFlags flags = CommandFlags.None);

/// <summary>
/// For each specified field, it sets the value and optionally sets the fields expiration
/// Depending on HashFieldFlags it creates the hash key or its fields in case they dont exist
/// - When DC is not specified: if key does not exist: create key
/// - When DC (“Don’t Create”) is specified: if key does not exist: do nothing (don’t create key)
/// - When neither DCF nor DOF are specified: for each specified field: if such field does not exist: create field; set all fields' value and expiration time
/// - When DCF (“Don’t Create Fields”) is specified: for each specified field: if the field already exists: set the field's value and expiration time; ignore fields that do not exist
/// - When DOF (“Don’t Overwrite Fields”) is specified: for each specified field: if such field does not exist: create field and set its value and expiration time; ignore fields that already exists
/// </summary>
/// <param name="key">The key of the hash.</param>
/// <param name="hashFields">The fields in the hash for this operation.</param>
/// <param name="expireTime">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="fieldFlags"></param>
/// <param name="flags">The flags to use for this operation.</param>
/// <returns>null if the key does not exist. Otherwise returns the result of operation for given fields
/// return value depends on the HashFieldFlags (GETNEW/GETOLD)
/// if GETNEW is specified: the value of field after command execution
/// if GETOLD is specified: the value of field before command execution
/// if GETNEW or GETOLD is not specified: a + b where
/// a: 1 if the field's value was set or 0 if not (DCF/DOF met)
/// b: 2 if the field's expiration time was set/discarded or 0 if not (DCF/DOF met, NX/XX/GT/LT not met)
/// </returns>
RedisValue[]? HashSet(RedisKey key, HashEntry[] hashFields, DateTime expireTime, ExpireWhen when = ExpireWhen.Always, HashFieldFlags fieldFlags = HashFieldFlags.None, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Returns all fields and values of the hash stored at key.
/// </summary>
Expand Down Expand Up @@ -1697,7 +1889,7 @@ public interface IDatabase : IRedis, IDatabaseAsync

/// <inheritdoc cref="SortedSetAdd(RedisKey, RedisValue, double, SortedSetWhen, CommandFlags)" />
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
bool SortedSetAdd(RedisKey key, RedisValue member, double score, When when, CommandFlags flags= CommandFlags.None);
bool SortedSetAdd(RedisKey key, RedisValue member, double score, When when, CommandFlags flags = CommandFlags.None);

/// <summary>
/// Adds the specified member with the specified score to the sorted set stored at key.
Expand Down
Loading

0 comments on commit 1ae4d4a

Please sign in to comment.