diff --git a/Directory.Build.props b/Directory.Build.props
index 273acae25..58334de25 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -10,7 +10,7 @@
true
$(MSBuildThisFileDirectory)Shared.ruleset
NETSDK1069
- $(NoWarn);NU5105;NU1507;SER001;SER002;SER003;SER004;SER005
+ $(NoWarn);NU5105;NU1507;SER001;SER002;SER003;SER004;SER005;SER006
https://stackexchange.github.io/StackExchange.Redis/ReleaseNotes
https://stackexchange.github.io/StackExchange.Redis/
MIT
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 9767a0ab1..f1f070b85 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -32,6 +32,7 @@
+
diff --git a/docs/ActiveActive.md b/docs/ActiveActive.md
new file mode 100644
index 000000000..b58edd656
--- /dev/null
+++ b/docs/ActiveActive.md
@@ -0,0 +1,269 @@
+# Active:Active
+
+## Overview
+
+The Active:Active feature provides automatic failover and intelligent routing across multiple Redis deployments. The library
+automatically selects the best available endpoint based on:
+
+1. **Availability** - Connected endpoints are always preferred over disconnected ones
+2. **Weight** - User-defined preference values (higher is better)
+3. **Latency** - Measured response times (lower is better)
+
+This enables scenarios such as:
+- Multi-datacenter deployments with automatic failover
+- Geographic routing to the nearest Redis instance
+- Graceful degradation during maintenance or outages
+- Load distribution across multiple Redis clusters
+
+## Basic Usage
+
+### Connecting to Multiple Groups
+
+To create an Active:Active connection, use `ConnectionMultiplexer.ConnectGroupAsync()` with an array of `ConnectionGroupMember` instances:
+
+```csharp
+using StackExchange.Redis;
+
+// Define your Redis endpoints
+ConnectionGroupMember[] members = [
+ new("us-east.redis.example.com:6379", name: "US East"),
+ new("us-west.redis.example.com:6379", name: "US West"),
+ new("eu-central.redis.example.com:6379", name: "EU Central")
+];
+
+// Connect to all members
+await using var conn = await ConnectionMultiplexer.ConnectGroupAsync(members);
+
+// Use the connection normally
+var db = conn.GetDatabase();
+await db.StringSetAsync("mykey", "myvalue");
+var value = await db.StringGetAsync("mykey");
+```
+
+### Using ConfigurationOptions
+
+You can also use `ConfigurationOptions` for more advanced configuration:
+
+```csharp
+var eastConfig = new ConfigurationOptions
+{
+ EndPoints = { "us-east-1.redis.example.com:6379", "us-east-2.redis.example.com:6379" },
+ Password = "your-password",
+ Ssl = true,
+};
+
+var westConfig = new ConfigurationOptions
+{
+ EndPoints = { "us-west-1.redis.example.com:6379", "us-west-2.redis.example.com:6379" },
+ Password = "another-different-password",
+ Ssl = true,
+};
+
+ConnectionGroupMember[] members = [
+ new(eastConfig, name: "US East"),
+ new(westConfig, name: "US West")
+];
+
+await using var conn = await ConnectionMultiplexer.ConnectGroupAsync(members);
+```
+
+## Configuring Weights
+
+Weights allow you to express preference for specific endpoints. Higher weights are preferred when multiple endpoints are available:
+
+```csharp
+ConnectionGroupMember[] members = [
+ new("local-dc.redis.example.com:6379") { Weight = 10 }, // Strongly preferred
+ new("nearby-dc.redis.example.com:6379") { Weight = 5 }, // Moderately preferred
+ new("remote-dc.redis.example.com:6379") { Weight = 1 } // Fallback option
+];
+
+await using var conn = await ConnectionMultiplexer.ConnectGroupAsync(members);
+```
+
+Weights can be adjusted dynamically:
+
+```csharp
+// Adjust weight based on runtime conditions
+members[0].Weight = 1; // Reduce preference for local DC
+members[2].Weight = 10; // Increase preference for remote DC
+```
+
+## Working with IDatabase
+
+The `IDatabase` interface works transparently with Active:Active connections. All operations are automatically routed to the currently selected endpoint:
+
+```csharp
+var db = conn.GetDatabase();
+
+// String operations
+await db.StringSetAsync("user:1:name", "Alice");
+var name = await db.StringGetAsync("user:1:name");
+
+// Hash operations
+await db.HashSetAsync("user:1", new HashEntry[] {
+ new("name", "Alice"),
+ new("email", "alice@example.com")
+});
+
+// List operations
+await db.ListRightPushAsync("queue:tasks", "task1");
+var task = await db.ListLeftPopAsync("queue:tasks");
+
+// Set operations
+await db.SetAddAsync("tags", new RedisValue[] { "redis", "cache", "database" });
+var members = await db.SetMembersAsync("tags");
+
+// Sorted set operations
+await db.SortedSetAddAsync("leaderboard", "player1", 100);
+var rank = await db.SortedSetRankAsync("leaderboard", "player1");
+
+// Transactions
+var tran = db.CreateTransaction();
+var t1 = tran.StringSetAsync("key1", "value1");
+var t2 = tran.StringSetAsync("key2", "value2");
+if (await tran.ExecuteAsync())
+{
+ await t1;
+ await t2;
+}
+
+// Batches
+var batch = db.CreateBatch();
+var b1 = batch.StringSetAsync("key1", "value1");
+var b2 = batch.StringSetAsync("key2", "value2");
+batch.Execute();
+await Task.WhenAll(b1, b2);
+```
+
+## Working with ISubscriber
+
+Pub/Sub operations work across all connected endpoints. When you subscribe to a channel, the subscription is established against *all* endpoints (for immediate pickup
+during failover events), and received messages are filtered in the library so only the messages for the *active* endpoint are observed. Message publishing
+occurs only to the *active* endpoint. The effect of this is that pub/sub works transparently as though
+you were only talking to the *active* endpoint:
+
+```csharp
+var subscriber = conn.GetSubscriber();
+
+// Subscribe to a channel
+await subscriber.SubscribeAsync(RedisChannel.Literal("notifications"), (channel, message) =>
+{
+ Console.WriteLine($"Received: {message}");
+});
+
+// Publish to a channel
+await subscriber.PublishAsync(RedisChannel.Literal("notifications"), "Hello, World!");
+
+// Pattern-based subscriptions
+await subscriber.SubscribeAsync(RedisChannel.Pattern("events:*"), (channel, message) =>
+{
+ Console.WriteLine($"Event on {channel}: {message}");
+});
+
+// Unsubscribe
+await subscriber.UnsubscribeAsync(RedisChannel.Literal("notifications"));
+```
+
+**Note:** When the active endpoint changes (due to failover), subscriptions are automatically re-established on the new endpoint.
+
+## Monitoring Connection Changes
+
+You can monitor when the active connection changes using the `ConnectionChanged` event:
+
+```csharp
+conn.ConnectionChanged += (sender, args) =>
+{
+ Console.WriteLine($"Connection changed: {args.Type}");
+ Console.WriteLine($"Previous: {args.PreviousGroup?.Name ?? "(none)"}");
+ Console.WriteLine($"Current: {args.Group.Name}");
+};
+```
+
+## Monitoring Member Status
+
+Each `ConnectionGroupMember` provides status information:
+
+```csharp
+foreach (var member in conn.GetMembers())
+{
+ Console.WriteLine($"{member.Name}:");
+ Console.WriteLine($" Connected: {member.IsConnected}");
+ Console.WriteLine($" Weight: {member.Weight}");
+ Console.WriteLine($" Latency: {member.Latency}");
+}
+```
+
+These are the same instances that were passed into `ConnectGroupAsync`.
+
+## Dynamic Member Management
+
+You can add or remove members dynamically using the `IConnectionGroup` interface:
+
+```csharp
+// Cast to IConnectionGroup to access dynamic member management
+var group = (IConnectionGroup)conn;
+
+// Add a new member at runtime
+var newMember = new ConnectionGroupMember("new-dc.redis.example.com:6379", name: "New Datacenter")
+{
+ Weight = 5
+};
+await group.AddAsync(newMember);
+Console.WriteLine($"Added {newMember.Name} to the group");
+
+// Remove a member
+var memberToRemove = members[2]; // Reference to an existing member
+if (group.Remove(memberToRemove))
+{
+ Console.WriteLine($"Removed {memberToRemove.Name} from the group");
+}
+else
+{
+ Console.WriteLine($"Failed to remove {memberToRemove.Name} - member not found");
+}
+
+// Check current members
+var currentMembers = group.GetMembers();
+Console.WriteLine($"Current member count: {currentMembers.Length}");
+foreach (var member in currentMembers)
+{
+ Console.WriteLine($" - {member.Name} (Weight: {member.Weight}, Connected: {member.IsConnected})");
+}
+```
+
+### Adding Members During Maintenance
+
+Add a new datacenter before removing an old one for zero-downtime migrations:
+
+```csharp
+var group = (IConnectionGroup)conn;
+
+// Add the new datacenter
+var newDC = new ConnectionGroupMember("new-location.redis.example.com:6379", name: "New Location")
+{
+ Weight = 10 // High weight to prefer the new location
+};
+await group.AddAsync(newDC);
+
+// Wait for the new member to be fully connected and healthy
+await Task.Delay(TimeSpan.FromSeconds(5));
+
+if (newDC.IsConnected)
+{
+ Console.WriteLine("New datacenter is online and healthy");
+
+ // Reduce weight of old datacenter
+ var oldDC = members[0];
+ oldDC.Weight = 1;
+
+ // Wait for traffic to shift
+ await Task.Delay(TimeSpan.FromSeconds(10));
+
+ // Remove the old datacenter
+ if (group.Remove(oldDC))
+ {
+ Console.WriteLine("Old datacenter removed successfully");
+ }
+}
+```
diff --git a/docs/exp/SER004.md b/docs/exp/SER004.md
new file mode 100644
index 000000000..ee5c3017b
--- /dev/null
+++ b/docs/exp/SER004.md
@@ -0,0 +1,17 @@
+# RESPite
+
+RESPite is an experimental library that provides high-performance low-level RESP (Redis, etc) parsing and serialization.
+It is used as the IO core for StackExchange.Redis v3+. You should not (yet) use it directly unless you have a very
+good reason to do so.
+
+To suppress this message, add the following to your `csproj` file:
+
+```xml
+$(NoWarn);SER004
+```
+
+or more granularly / locally in C#:
+
+``` c#
+#pragma warning disable SER004
+```
\ No newline at end of file
diff --git a/docs/exp/SER005.md b/docs/exp/SER005.md
new file mode 100644
index 000000000..f0d29b742
--- /dev/null
+++ b/docs/exp/SER005.md
@@ -0,0 +1,23 @@
+# Unit Testing
+
+Unit testing is great! Yay, do more of that!
+
+This type is provided for external unit testing, in particular by people using modules or server features
+not directly implemented by SE.Redis - for example to verify messsage parsing or formatting without
+talking to a RESP server.
+
+These types are considered slightly more... *mercurial*. We encourage you to use them, but *occasionally*
+(not just for fun) you might need to update your test code if we tweak something. This should not impact
+"real" library usage.
+
+To suppress this message, add the following to your `csproj` file:
+
+```xml
+$(NoWarn);SER005
+```
+
+or more granularly / locally in C#:
+
+``` c#
+#pragma warning disable SER005
+```
\ No newline at end of file
diff --git a/docs/exp/SER006.md b/docs/exp/SER006.md
new file mode 100644
index 000000000..96feb90d8
--- /dev/null
+++ b/docs/exp/SER006.md
@@ -0,0 +1,15 @@
+# Active:Active
+
+This feature is typically used to provide geo-redundant services; please see [full docs](/ActiveActive).
+
+To suppress this message, add the following to your `csproj` file:
+
+```xml
+$(NoWarn);SER006
+```
+
+or more granularly / locally in C#:
+
+``` c#
+#pragma warning disable SER006
+```
\ No newline at end of file
diff --git a/docs/index.md b/docs/index.md
index 0a2e6c721..144a5d7fe 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -35,6 +35,7 @@ Documentation
- [Basic Usage](Basics) - getting started and basic usage
- [Async Timeouts](AsyncTimeouts) - async timeouts and cancellation
- [Configuration](Configuration) - options available when connecting to redis
+- [Active:Active](ActiveActive) - connecting to multiple Redis endpoints for high availability
- [Pipelines and Multiplexers](PipelinesMultiplexers) - what is a multiplexer?
- [Keys, Values and Channels](KeysValues) - discusses the data-types used on the API
- [Transactions](Transactions) - how atomic transactions work in redis
diff --git a/src/RESPite/Shared/Experiments.cs b/src/RESPite/Shared/Experiments.cs
index b4b9fcee1..9fb13d3ff 100644
--- a/src/RESPite/Shared/Experiments.cs
+++ b/src/RESPite/Shared/Experiments.cs
@@ -13,6 +13,7 @@ internal static class Experiments
public const string Server_8_6 = "SER003";
public const string Respite = "SER004";
public const string UnitTesting = "SER005";
+ public const string ActiveActive = "SER006";
// ReSharper restore InconsistentNaming
}
}
diff --git a/src/StackExchange.Redis/ChannelMessageQueue.cs b/src/StackExchange.Redis/ChannelMessageQueue.cs
index f7bd9a4a2..950234f99 100644
--- a/src/StackExchange.Redis/ChannelMessageQueue.cs
+++ b/src/StackExchange.Redis/ChannelMessageQueue.cs
@@ -1,6 +1,7 @@
using System;
using System.Buffers.Text;
using System.Collections.Generic;
+using System.Diagnostics;
using System.Threading;
using System.Threading.Channels;
using System.Threading.Tasks;
@@ -35,7 +36,7 @@ public sealed class ChannelMessageQueue : IAsyncEnumerable
///
public Task Completion => _queue.Reader.Completion;
- internal ChannelMessageQueue(in RedisChannel redisChannel, RedisSubscriber parent)
+ internal ChannelMessageQueue(in RedisChannel redisChannel, RedisSubscriber? parent)
{
Channel = redisChannel;
_parent = parent;
@@ -49,8 +50,22 @@ internal ChannelMessageQueue(in RedisChannel redisChannel, RedisSubscriber paren
private void Write(in RedisChannel channel, in RedisValue value)
{
- var writer = _queue.Writer;
- writer.TryWrite(new ChannelMessage(this, channel, value));
+ try
+ {
+ _queue.Writer.TryWrite(new ChannelMessage(this, channel, value));
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("pub/sub ChannelWrite.TryWrite failed: " + ex.Message);
+ }
+ }
+
+ internal void SynchronizedWrite(in RedisChannel channel, in RedisValue value)
+ {
+ lock (this)
+ {
+ Write(channel, value);
+ }
}
///
@@ -327,4 +342,7 @@ public async IAsyncEnumerator GetAsyncEnumerator(CancellationTok
}
}
#endif
+
+ internal ValueTask WaitToReadAsync(CancellationToken cancellationToken = default)
+ => _queue.Reader.WaitToReadAsync(cancellationToken);
}
diff --git a/src/StackExchange.Redis/ConfigurationOptions.cs b/src/StackExchange.Redis/ConfigurationOptions.cs
index 641fccc95..e370051fa 100644
--- a/src/StackExchange.Redis/ConfigurationOptions.cs
+++ b/src/StackExchange.Redis/ConfigurationOptions.cs
@@ -40,6 +40,12 @@ public static int ParseInt32(string key, string value, int minValue = int.MinVal
return tmp;
}
+ public static float ParseSingle(string key, string value)
+ {
+ if (!Format.TryParseDouble(value, out double tmp)) throw new ArgumentOutOfRangeException(key, $"Keyword '{key}' requires a numeric value; the value '{value}' is not recognised.");
+ return (float)tmp;
+ }
+
internal static bool ParseBoolean(string key, string value)
{
if (!Format.TryParseBoolean(value, out bool tmp)) throw new ArgumentOutOfRangeException(key, $"Keyword '{key}' requires a boolean value; the value '{value}' is not recognised.");
@@ -944,9 +950,9 @@ public string ToString(bool includePassword)
};
}
- private static void Append(StringBuilder sb, object value)
+ private static void Append(StringBuilder sb, object? value)
{
- if (value == null) return;
+ if (value is null) return;
string s = Format.ToString(value);
if (!string.IsNullOrWhiteSpace(s))
{
@@ -957,7 +963,8 @@ private static void Append(StringBuilder sb, object value)
private static void Append(StringBuilder sb, string prefix, object? value)
{
- string? s = value?.ToString();
+ if (value is null) return;
+ string? s = value.ToString();
if (!string.IsNullOrWhiteSpace(s))
{
if (sb.Length != 0) sb.Append(',');
diff --git a/src/StackExchange.Redis/ConnectionMultiplexer.cs b/src/StackExchange.Redis/ConnectionMultiplexer.cs
index a5995046e..7137f4d26 100644
--- a/src/StackExchange.Redis/ConnectionMultiplexer.cs
+++ b/src/StackExchange.Redis/ConnectionMultiplexer.cs
@@ -1038,7 +1038,7 @@ public void UnRoot(int token)
}
}
- private void OnHeartbeat()
+ internal void OnHeartbeat()
{
try
{
@@ -1131,7 +1131,7 @@ public IDatabase GetDatabase(int db = -1, object? asyncState = null)
}
// DB zero is stored separately, since 0-only is a massively common use-case
- private const int MaxCachedDatabaseInstance = 16; // 17 items - [0,16]
+ internal const int MaxCachedDatabaseInstance = 16; // 17 items - [0,16]
// Side note: "databases 16" is the default in redis.conf; happy to store one extra to get nice alignment etc
private IDatabase? dbCacheZero;
private IDatabase[]? dbCacheLow;
@@ -1284,6 +1284,8 @@ public long OperationCount
}
}
+ internal uint LatencyTicks { get; private set; } = uint.MaxValue;
+
// note that the RedisChannel->byte[] converter is always direct, so this is not an alloc
// (we deal with channels far less frequently, so pay the encoding cost up-front)
internal byte[] ChannelPrefix => ((byte[]?)RawConfig.ChannelPrefix) ?? [];
@@ -2363,5 +2365,29 @@ private Task[] QuitAllServers()
long? IInternalConnectionMultiplexer.GetConnectionId(EndPoint endpoint, ConnectionType type)
=> GetServerEndPoint(endpoint)?.GetBridge(type)?.ConnectionId;
+
+ internal uint UpdateLatency()
+ {
+ var snapshot = GetServerSnapshot();
+ uint max = uint.MaxValue;
+ foreach (var server in snapshot)
+ {
+ if (server.IsConnected)
+ {
+ var latency = server.LatencyTicks;
+ if (max is uint.MaxValue || latency > max)
+ {
+ max = latency;
+ }
+ }
+ }
+
+ if (max != uint.MaxValue)
+ {
+ LatencyTicks = max;
+ }
+
+ return LatencyTicks;
+ }
}
}
diff --git a/src/StackExchange.Redis/Interfaces/IConnectionGroup.cs b/src/StackExchange.Redis/Interfaces/IConnectionGroup.cs
new file mode 100644
index 000000000..9d2477257
--- /dev/null
+++ b/src/StackExchange.Redis/Interfaces/IConnectionGroup.cs
@@ -0,0 +1,110 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.IO;
+using System.Text;
+using System.Threading.Tasks;
+using RESPite;
+
+// ReSharper disable once CheckNamespace
+namespace StackExchange.Redis;
+
+///
+/// A group of connections to redis servers, that manages connections to multiple
+/// servers, routing traffic based on the availability of the servers and their
+/// relative .
+///
+[Experimental(Experiments.ActiveActive, UrlFormat = Experiments.UrlFormat)]
+public interface IConnectionGroup : IConnectionMultiplexer
+{
+ ///
+ /// A change occured to one of the connection groups.
+ ///
+ event EventHandler? ConnectionChanged;
+
+ ///
+ /// Adds a new member to the group.
+ ///
+ Task AddAsync(ConnectionGroupMember group, TextWriter? log = null);
+
+ ///
+ /// Removes a member from the group.
+ ///
+ bool Remove(ConnectionGroupMember group);
+
+ ///
+ /// Get the members of the group.
+ ///
+ ReadOnlySpan GetMembers();
+}
+
+///
+/// Represents a change to a connection group.
+///
+[Experimental(Experiments.ActiveActive, UrlFormat = Experiments.UrlFormat)]
+public class GroupConnectionChangedEventArgs(GroupConnectionChangedEventArgs.ChangeType type, ConnectionGroupMember group, ConnectionGroupMember? previousGroup = null) : EventArgs, ICompletable
+{
+ ///
+ /// The group relating to the change. For , this is the new group.
+ ///
+ public ConnectionGroupMember Group => group;
+
+ ///
+ /// The previous group relating to the change, if applicable.
+ ///
+ public ConnectionGroupMember? PreviousGroup => previousGroup;
+
+ ///
+ /// The type of change that occurred.
+ ///
+ public ChangeType Type => type;
+
+ private EventHandler? _handler;
+ private object? _sender;
+
+ ///
+ /// The type of change that occurred.
+ ///
+ public enum ChangeType
+ {
+ ///
+ /// Unused.
+ ///
+ Unknown = 0,
+
+ ///
+ /// A new connection group was added.
+ ///
+ Added = 1,
+
+ ///
+ /// A connection group was removed.
+ ///
+ Removed = 2,
+
+ ///
+ /// A connection group became disconnected.
+ ///
+ Disconnected = 3,
+
+ ///
+ /// A connection group became reconnected.
+ ///
+ Reconnected = 4,
+
+ ///
+ /// The active connection group changed, changing how traffic is routed.
+ ///
+ ActiveChanged = 5,
+ }
+
+ internal void CompleteAsWorker(EventHandler handler, object sender)
+ {
+ _handler = handler;
+ _sender = sender;
+ ConnectionMultiplexer.CompleteAsWorker(this);
+ }
+
+ void ICompletable.AppendStormLog(StringBuilder sb) { }
+
+ bool ICompletable.TryComplete(bool isAsync) => ConnectionMultiplexer.TryCompleteHandler(_handler, _sender!, this, isAsync);
+}
diff --git a/src/StackExchange.Redis/MultiGroupDatabase.Async.cs b/src/StackExchange.Redis/MultiGroupDatabase.Async.cs
new file mode 100644
index 000000000..03706f355
--- /dev/null
+++ b/src/StackExchange.Redis/MultiGroupDatabase.Async.cs
@@ -0,0 +1,66 @@
+using System;
+using System.Net;
+using System.Threading.Tasks;
+
+namespace StackExchange.Redis;
+
+internal sealed partial class MultiGroupDatabase
+{
+ // Async methods - Core operations
+ public Task DebugObjectAsync(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().DebugObjectAsync(key, flags);
+
+ public Task IdentifyEndpointAsync(RedisKey key = default, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().IdentifyEndpointAsync(key, flags);
+
+ public Task KeyMigrateAsync(RedisKey key, EndPoint toServer, int toDatabase = 0, int timeoutMilliseconds = 0, MigrateOptions migrateOptions = MigrateOptions.None, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().KeyMigrateAsync(key, toServer, toDatabase, timeoutMilliseconds, migrateOptions, flags);
+
+ public Task PingAsync(CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().PingAsync(flags);
+
+ public Task PublishAsync(RedisChannel channel, RedisValue message, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().PublishAsync(channel, message, flags);
+
+ public Task ExecuteAsync(string command, params object[] args)
+ => GetActiveDatabase().ExecuteAsync(command, args);
+
+ public Task ExecuteAsync(string command, System.Collections.Generic.ICollection? args, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().ExecuteAsync(command, args, flags);
+
+ public Task ScriptEvaluateAsync(string script, RedisKey[]? keys = null, RedisValue[]? values = null, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().ScriptEvaluateAsync(script, keys, values, flags);
+
+ public Task ScriptEvaluateAsync(byte[] hash, RedisKey[]? keys = null, RedisValue[]? values = null, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().ScriptEvaluateAsync(hash, keys, values, flags);
+
+ public Task ScriptEvaluateAsync(LuaScript script, object? parameters = null, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().ScriptEvaluateAsync(script, parameters, flags);
+
+ public Task ScriptEvaluateAsync(LoadedLuaScript script, object? parameters = null, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().ScriptEvaluateAsync(script, parameters, flags);
+
+ public Task ScriptEvaluateReadOnlyAsync(string script, RedisKey[]? keys = null, RedisValue[]? values = null, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().ScriptEvaluateReadOnlyAsync(script, keys, values, flags);
+
+ public Task ScriptEvaluateReadOnlyAsync(byte[] hash, RedisKey[]? keys = null, RedisValue[]? values = null, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().ScriptEvaluateReadOnlyAsync(hash, keys, values, flags);
+
+ public Task LockExtendAsync(RedisKey key, RedisValue value, TimeSpan expiry, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().LockExtendAsync(key, value, expiry, flags);
+
+ public Task LockQueryAsync(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().LockQueryAsync(key, flags);
+
+ public Task LockReleaseAsync(RedisKey key, RedisValue value, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().LockReleaseAsync(key, value, flags);
+
+ public Task LockTakeAsync(RedisKey key, RedisValue value, TimeSpan expiry, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().LockTakeAsync(key, value, expiry, flags);
+
+ public Task SortAsync(RedisKey key, long skip = 0, long take = -1, Order order = Order.Ascending, SortType sortType = SortType.Numeric, RedisValue by = default, RedisValue[]? get = null, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortAsync(key, skip, take, order, sortType, by, get, flags);
+
+ public Task SortAndStoreAsync(RedisKey destination, RedisKey key, long skip = 0, long take = -1, Order order = Order.Ascending, SortType sortType = SortType.Numeric, RedisValue by = default, RedisValue[]? get = null, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortAndStoreAsync(destination, key, skip, take, order, sortType, by, get, flags);
+}
diff --git a/src/StackExchange.Redis/MultiGroupDatabase.Geo.Async.cs b/src/StackExchange.Redis/MultiGroupDatabase.Geo.Async.cs
new file mode 100644
index 000000000..422ff59fc
--- /dev/null
+++ b/src/StackExchange.Redis/MultiGroupDatabase.Geo.Async.cs
@@ -0,0 +1,52 @@
+using System.Threading.Tasks;
+
+namespace StackExchange.Redis;
+
+internal sealed partial class MultiGroupDatabase
+{
+ // Geo Async
+ public Task GeoAddAsync(RedisKey key, double longitude, double latitude, RedisValue member, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().GeoAddAsync(key, longitude, latitude, member, flags);
+
+ public Task GeoAddAsync(RedisKey key, GeoEntry value, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().GeoAddAsync(key, value, flags);
+
+ public Task GeoAddAsync(RedisKey key, GeoEntry[] values, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().GeoAddAsync(key, values, flags);
+
+ public Task GeoRemoveAsync(RedisKey key, RedisValue member, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().GeoRemoveAsync(key, member, flags);
+
+ public Task GeoDistanceAsync(RedisKey key, RedisValue member1, RedisValue member2, GeoUnit unit = GeoUnit.Meters, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().GeoDistanceAsync(key, member1, member2, unit, flags);
+
+ public Task GeoHashAsync(RedisKey key, RedisValue[] members, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().GeoHashAsync(key, members, flags);
+
+ public Task GeoHashAsync(RedisKey key, RedisValue member, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().GeoHashAsync(key, member, flags);
+
+ public Task GeoPositionAsync(RedisKey key, RedisValue[] members, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().GeoPositionAsync(key, members, flags);
+
+ public Task GeoPositionAsync(RedisKey key, RedisValue member, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().GeoPositionAsync(key, member, flags);
+
+ public Task GeoRadiusAsync(RedisKey key, RedisValue member, double radius, GeoUnit unit = GeoUnit.Meters, int count = -1, Order? order = null, GeoRadiusOptions options = GeoRadiusOptions.Default, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().GeoRadiusAsync(key, member, radius, unit, count, order, options, flags);
+
+ public Task GeoRadiusAsync(RedisKey key, double longitude, double latitude, double radius, GeoUnit unit = GeoUnit.Meters, int count = -1, Order? order = null, GeoRadiusOptions options = GeoRadiusOptions.Default, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().GeoRadiusAsync(key, longitude, latitude, radius, unit, count, order, options, flags);
+
+ public Task GeoSearchAsync(RedisKey key, RedisValue member, GeoSearchShape shape, int count = -1, bool demandClosest = true, Order? order = null, GeoRadiusOptions options = GeoRadiusOptions.Default, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().GeoSearchAsync(key, member, shape, count, demandClosest, order, options, flags);
+
+ public Task GeoSearchAsync(RedisKey key, double longitude, double latitude, GeoSearchShape shape, int count = -1, bool demandClosest = true, Order? order = null, GeoRadiusOptions options = GeoRadiusOptions.Default, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().GeoSearchAsync(key, longitude, latitude, shape, count, demandClosest, order, options, flags);
+
+ public Task GeoSearchAndStoreAsync(RedisKey sourceKey, RedisKey destinationKey, RedisValue member, GeoSearchShape shape, int count = -1, bool demandClosest = true, Order? order = null, bool storeDistances = false, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().GeoSearchAndStoreAsync(sourceKey, destinationKey, member, shape, count, demandClosest, order, storeDistances, flags);
+
+ public Task GeoSearchAndStoreAsync(RedisKey sourceKey, RedisKey destinationKey, double longitude, double latitude, GeoSearchShape shape, int count = -1, bool demandClosest = true, Order? order = null, bool storeDistances = false, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().GeoSearchAndStoreAsync(sourceKey, destinationKey, longitude, latitude, shape, count, demandClosest, order, storeDistances, flags);
+}
diff --git a/src/StackExchange.Redis/MultiGroupDatabase.Geo.cs b/src/StackExchange.Redis/MultiGroupDatabase.Geo.cs
new file mode 100644
index 000000000..c74982c83
--- /dev/null
+++ b/src/StackExchange.Redis/MultiGroupDatabase.Geo.cs
@@ -0,0 +1,50 @@
+namespace StackExchange.Redis;
+
+internal sealed partial class MultiGroupDatabase
+{
+ // Geo operations
+ public bool GeoAdd(RedisKey key, double longitude, double latitude, RedisValue member, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().GeoAdd(key, longitude, latitude, member, flags);
+
+ public bool GeoAdd(RedisKey key, GeoEntry value, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().GeoAdd(key, value, flags);
+
+ public long GeoAdd(RedisKey key, GeoEntry[] values, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().GeoAdd(key, values, flags);
+
+ public bool GeoRemove(RedisKey key, RedisValue member, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().GeoRemove(key, member, flags);
+
+ public double? GeoDistance(RedisKey key, RedisValue member1, RedisValue member2, GeoUnit unit = GeoUnit.Meters, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().GeoDistance(key, member1, member2, unit, flags);
+
+ public string?[] GeoHash(RedisKey key, RedisValue[] members, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().GeoHash(key, members, flags);
+
+ public string? GeoHash(RedisKey key, RedisValue member, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().GeoHash(key, member, flags);
+
+ public GeoPosition?[] GeoPosition(RedisKey key, RedisValue[] members, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().GeoPosition(key, members, flags);
+
+ public GeoPosition? GeoPosition(RedisKey key, RedisValue member, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().GeoPosition(key, member, flags);
+
+ public GeoRadiusResult[] GeoRadius(RedisKey key, RedisValue member, double radius, GeoUnit unit = GeoUnit.Meters, int count = -1, Order? order = null, GeoRadiusOptions options = GeoRadiusOptions.Default, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().GeoRadius(key, member, radius, unit, count, order, options, flags);
+
+ public GeoRadiusResult[] GeoRadius(RedisKey key, double longitude, double latitude, double radius, GeoUnit unit = GeoUnit.Meters, int count = -1, Order? order = null, GeoRadiusOptions options = GeoRadiusOptions.Default, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().GeoRadius(key, longitude, latitude, radius, unit, count, order, options, flags);
+
+ public GeoRadiusResult[] GeoSearch(RedisKey key, RedisValue member, GeoSearchShape shape, int count = -1, bool demandClosest = true, Order? order = null, GeoRadiusOptions options = GeoRadiusOptions.Default, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().GeoSearch(key, member, shape, count, demandClosest, order, options, flags);
+
+ public GeoRadiusResult[] GeoSearch(RedisKey key, double longitude, double latitude, GeoSearchShape shape, int count = -1, bool demandClosest = true, Order? order = null, GeoRadiusOptions options = GeoRadiusOptions.Default, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().GeoSearch(key, longitude, latitude, shape, count, demandClosest, order, options, flags);
+
+ public long GeoSearchAndStore(RedisKey sourceKey, RedisKey destinationKey, RedisValue member, GeoSearchShape shape, int count = -1, bool demandClosest = true, Order? order = null, bool storeDistances = false, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().GeoSearchAndStore(sourceKey, destinationKey, member, shape, count, demandClosest, order, storeDistances, flags);
+
+ public long GeoSearchAndStore(RedisKey sourceKey, RedisKey destinationKey, double longitude, double latitude, GeoSearchShape shape, int count = -1, bool demandClosest = true, Order? order = null, bool storeDistances = false, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().GeoSearchAndStore(sourceKey, destinationKey, longitude, latitude, shape, count, demandClosest, order, storeDistances, flags);
+}
diff --git a/src/StackExchange.Redis/MultiGroupDatabase.Hashes.Async.cs b/src/StackExchange.Redis/MultiGroupDatabase.Hashes.Async.cs
new file mode 100644
index 000000000..15e8111e8
--- /dev/null
+++ b/src/StackExchange.Redis/MultiGroupDatabase.Hashes.Async.cs
@@ -0,0 +1,128 @@
+using System;
+using System.Threading.Tasks;
+
+namespace StackExchange.Redis;
+
+internal sealed partial class MultiGroupDatabase
+{
+ // Hash Async
+ public Task HashDecrementAsync(RedisKey key, RedisValue hashField, long value = 1, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashDecrementAsync(key, hashField, value, flags);
+
+ public Task HashDecrementAsync(RedisKey key, RedisValue hashField, double value, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashDecrementAsync(key, hashField, value, flags);
+
+ public Task HashDeleteAsync(RedisKey key, RedisValue hashField, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashDeleteAsync(key, hashField, flags);
+
+ public Task HashDeleteAsync(RedisKey key, RedisValue[] hashFields, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashDeleteAsync(key, hashFields, flags);
+
+ public Task HashExistsAsync(RedisKey key, RedisValue hashField, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashExistsAsync(key, hashField, flags);
+
+ public Task HashFieldExpireAsync(RedisKey key, RedisValue[] hashFields, TimeSpan expiry, ExpireWhen when = ExpireWhen.Always, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashFieldExpireAsync(key, hashFields, expiry, when, flags);
+
+ public Task HashFieldExpireAsync(RedisKey key, RedisValue[] hashFields, DateTime expiry, ExpireWhen when = ExpireWhen.Always, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashFieldExpireAsync(key, hashFields, expiry, when, flags);
+
+ public Task HashFieldGetExpireDateTimeAsync(RedisKey key, RedisValue[] hashFields, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashFieldGetExpireDateTimeAsync(key, hashFields, flags);
+
+ public Task HashFieldPersistAsync(RedisKey key, RedisValue[] hashFields, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashFieldPersistAsync(key, hashFields, flags);
+
+ public Task HashFieldGetTimeToLiveAsync(RedisKey key, RedisValue[] hashFields, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashFieldGetTimeToLiveAsync(key, hashFields, flags);
+
+ public Task HashGetAsync(RedisKey key, RedisValue hashField, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashGetAsync(key, hashField, flags);
+
+ public Task?> HashGetLeaseAsync(RedisKey key, RedisValue hashField, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashGetLeaseAsync(key, hashField, flags);
+
+ public Task HashGetAsync(RedisKey key, RedisValue[] hashFields, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashGetAsync(key, hashFields, flags);
+
+ public Task HashFieldGetAndDeleteAsync(RedisKey key, RedisValue hashField, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashFieldGetAndDeleteAsync(key, hashField, flags);
+
+ public Task?> HashFieldGetLeaseAndDeleteAsync(RedisKey key, RedisValue hashField, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashFieldGetLeaseAndDeleteAsync(key, hashField, flags);
+
+ public Task HashFieldGetAndDeleteAsync(RedisKey key, RedisValue[] hashFields, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashFieldGetAndDeleteAsync(key, hashFields, flags);
+
+ public Task HashFieldGetAndSetExpiryAsync(RedisKey key, RedisValue hashField, TimeSpan? expiry = null, bool persist = false, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashFieldGetAndSetExpiryAsync(key, hashField, expiry, persist, flags);
+
+ public Task HashFieldGetAndSetExpiryAsync(RedisKey key, RedisValue hashField, DateTime expiry, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashFieldGetAndSetExpiryAsync(key, hashField, expiry, flags);
+
+ public Task?> HashFieldGetLeaseAndSetExpiryAsync(RedisKey key, RedisValue hashField, TimeSpan? expiry = null, bool persist = false, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashFieldGetLeaseAndSetExpiryAsync(key, hashField, expiry, persist, flags);
+
+ public Task?> HashFieldGetLeaseAndSetExpiryAsync(RedisKey key, RedisValue hashField, DateTime expiry, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashFieldGetLeaseAndSetExpiryAsync(key, hashField, expiry, flags);
+
+ public Task HashFieldGetAndSetExpiryAsync(RedisKey key, RedisValue[] hashFields, TimeSpan? expiry = null, bool persist = false, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashFieldGetAndSetExpiryAsync(key, hashFields, expiry, persist, flags);
+
+ public Task HashFieldGetAndSetExpiryAsync(RedisKey key, RedisValue[] hashFields, DateTime expiry, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashFieldGetAndSetExpiryAsync(key, hashFields, expiry, flags);
+
+ public Task HashFieldSetAndSetExpiryAsync(RedisKey key, RedisValue field, RedisValue value, TimeSpan? expiry = null, bool keepTtl = false, When when = When.Always, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashFieldSetAndSetExpiryAsync(key, field, value, expiry, keepTtl, when, flags);
+
+ public Task HashFieldSetAndSetExpiryAsync(RedisKey key, RedisValue field, RedisValue value, DateTime expiry, When when = When.Always, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashFieldSetAndSetExpiryAsync(key, field, value, expiry, when, flags);
+
+ public Task HashFieldSetAndSetExpiryAsync(RedisKey key, HashEntry[] hashFields, TimeSpan? expiry = null, bool keepTtl = false, When when = When.Always, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashFieldSetAndSetExpiryAsync(key, hashFields, expiry, keepTtl, when, flags);
+
+ public Task HashFieldSetAndSetExpiryAsync(RedisKey key, HashEntry[] hashFields, DateTime expiry, When when = When.Always, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashFieldSetAndSetExpiryAsync(key, hashFields, expiry, when, flags);
+
+ public Task HashGetAllAsync(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashGetAllAsync(key, flags);
+
+ public Task HashIncrementAsync(RedisKey key, RedisValue hashField, long value = 1, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashIncrementAsync(key, hashField, value, flags);
+
+ public Task HashIncrementAsync(RedisKey key, RedisValue hashField, double value, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashIncrementAsync(key, hashField, value, flags);
+
+ public Task HashKeysAsync(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashKeysAsync(key, flags);
+
+ public Task HashLengthAsync(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashLengthAsync(key, flags);
+
+ public Task HashRandomFieldAsync(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashRandomFieldAsync(key, flags);
+
+ public Task HashRandomFieldsAsync(RedisKey key, long count, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashRandomFieldsAsync(key, count, flags);
+
+ public Task HashRandomFieldsWithValuesAsync(RedisKey key, long count, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashRandomFieldsWithValuesAsync(key, count, flags);
+
+ public System.Collections.Generic.IAsyncEnumerable HashScanAsync(RedisKey key, RedisValue pattern = default, int pageSize = RedisBase.CursorUtils.DefaultLibraryPageSize, long cursor = RedisBase.CursorUtils.Origin, int pageOffset = 0, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashScanAsync(key, pattern, pageSize, cursor, pageOffset, flags);
+
+ public System.Collections.Generic.IAsyncEnumerable HashScanNoValuesAsync(RedisKey key, RedisValue pattern = default, int pageSize = RedisBase.CursorUtils.DefaultLibraryPageSize, long cursor = RedisBase.CursorUtils.Origin, int pageOffset = 0, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashScanNoValuesAsync(key, pattern, pageSize, cursor, pageOffset, flags);
+
+ public Task HashSetAsync(RedisKey key, RedisValue hashField, RedisValue value, When when = When.Always, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashSetAsync(key, hashField, value, when, flags);
+
+ public Task HashSetAsync(RedisKey key, HashEntry[] hashFields, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashSetAsync(key, hashFields, flags);
+
+ public Task HashStringLengthAsync(RedisKey key, RedisValue hashField, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashStringLengthAsync(key, hashField, flags);
+
+ public Task HashValuesAsync(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashValuesAsync(key, flags);
+}
diff --git a/src/StackExchange.Redis/MultiGroupDatabase.Hashes.cs b/src/StackExchange.Redis/MultiGroupDatabase.Hashes.cs
new file mode 100644
index 000000000..312a4dcb0
--- /dev/null
+++ b/src/StackExchange.Redis/MultiGroupDatabase.Hashes.cs
@@ -0,0 +1,130 @@
+using System;
+
+namespace StackExchange.Redis;
+
+internal sealed partial class MultiGroupDatabase
+{
+ // Hash operations
+ public long HashDecrement(RedisKey key, RedisValue hashField, long value = 1, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashDecrement(key, hashField, value, flags);
+
+ public double HashDecrement(RedisKey key, RedisValue hashField, double value, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashDecrement(key, hashField, value, flags);
+
+ public bool HashDelete(RedisKey key, RedisValue hashField, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashDelete(key, hashField, flags);
+
+ public long HashDelete(RedisKey key, RedisValue[] hashFields, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashDelete(key, hashFields, flags);
+
+ public bool HashExists(RedisKey key, RedisValue hashField, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashExists(key, hashField, flags);
+
+ public ExpireResult[] HashFieldExpire(RedisKey key, RedisValue[] hashFields, TimeSpan expiry, ExpireWhen when = ExpireWhen.Always, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashFieldExpire(key, hashFields, expiry, when, flags);
+
+ public ExpireResult[] HashFieldExpire(RedisKey key, RedisValue[] hashFields, DateTime expiry, ExpireWhen when = ExpireWhen.Always, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashFieldExpire(key, hashFields, expiry, when, flags);
+
+ public long[] HashFieldGetExpireDateTime(RedisKey key, RedisValue[] hashFields, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashFieldGetExpireDateTime(key, hashFields, flags);
+
+ public PersistResult[] HashFieldPersist(RedisKey key, RedisValue[] hashFields, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashFieldPersist(key, hashFields, flags);
+
+ public long[] HashFieldGetTimeToLive(RedisKey key, RedisValue[] hashFields, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashFieldGetTimeToLive(key, hashFields, flags);
+
+ public RedisValue HashGet(RedisKey key, RedisValue hashField, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashGet(key, hashField, flags);
+
+ public Lease? HashGetLease(RedisKey key, RedisValue hashField, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashGetLease(key, hashField, flags);
+
+ public RedisValue[] HashGet(RedisKey key, RedisValue[] hashFields, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashGet(key, hashFields, flags);
+
+ public RedisValue HashFieldGetAndDelete(RedisKey key, RedisValue hashField, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashFieldGetAndDelete(key, hashField, flags);
+
+ public Lease? HashFieldGetLeaseAndDelete(RedisKey key, RedisValue hashField, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashFieldGetLeaseAndDelete(key, hashField, flags);
+
+ public RedisValue[] HashFieldGetAndDelete(RedisKey key, RedisValue[] hashFields, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashFieldGetAndDelete(key, hashFields, flags);
+
+ public RedisValue HashFieldGetAndSetExpiry(RedisKey key, RedisValue hashField, TimeSpan? expiry = null, bool persist = false, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashFieldGetAndSetExpiry(key, hashField, expiry, persist, flags);
+
+ public RedisValue HashFieldGetAndSetExpiry(RedisKey key, RedisValue hashField, DateTime expiry, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashFieldGetAndSetExpiry(key, hashField, expiry, flags);
+
+ public Lease? HashFieldGetLeaseAndSetExpiry(RedisKey key, RedisValue hashField, TimeSpan? expiry = null, bool persist = false, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashFieldGetLeaseAndSetExpiry(key, hashField, expiry, persist, flags);
+
+ public Lease? HashFieldGetLeaseAndSetExpiry(RedisKey key, RedisValue hashField, DateTime expiry, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashFieldGetLeaseAndSetExpiry(key, hashField, expiry, flags);
+
+ public RedisValue[] HashFieldGetAndSetExpiry(RedisKey key, RedisValue[] hashFields, TimeSpan? expiry = null, bool persist = false, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashFieldGetAndSetExpiry(key, hashFields, expiry, persist, flags);
+
+ public RedisValue[] HashFieldGetAndSetExpiry(RedisKey key, RedisValue[] hashFields, DateTime expiry, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashFieldGetAndSetExpiry(key, hashFields, expiry, flags);
+
+ public RedisValue HashFieldSetAndSetExpiry(RedisKey key, RedisValue field, RedisValue value, TimeSpan? expiry = null, bool keepTtl = false, When when = When.Always, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashFieldSetAndSetExpiry(key, field, value, expiry, keepTtl, when, flags);
+
+ public RedisValue HashFieldSetAndSetExpiry(RedisKey key, RedisValue field, RedisValue value, DateTime expiry, When when = When.Always, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashFieldSetAndSetExpiry(key, field, value, expiry, when, flags);
+
+ public RedisValue HashFieldSetAndSetExpiry(RedisKey key, HashEntry[] hashFields, TimeSpan? expiry = null, bool keepTtl = false, When when = When.Always, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashFieldSetAndSetExpiry(key, hashFields, expiry, keepTtl, when, flags);
+
+ public RedisValue HashFieldSetAndSetExpiry(RedisKey key, HashEntry[] hashFields, DateTime expiry, When when = When.Always, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashFieldSetAndSetExpiry(key, hashFields, expiry, when, flags);
+
+ public HashEntry[] HashGetAll(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashGetAll(key, flags);
+
+ public long HashIncrement(RedisKey key, RedisValue hashField, long value = 1, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashIncrement(key, hashField, value, flags);
+
+ public double HashIncrement(RedisKey key, RedisValue hashField, double value, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashIncrement(key, hashField, value, flags);
+
+ public RedisValue[] HashKeys(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashKeys(key, flags);
+
+ public long HashLength(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashLength(key, flags);
+
+ public RedisValue HashRandomField(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashRandomField(key, flags);
+
+ public RedisValue[] HashRandomFields(RedisKey key, long count, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashRandomFields(key, count, flags);
+
+ public HashEntry[] HashRandomFieldsWithValues(RedisKey key, long count, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashRandomFieldsWithValues(key, count, flags);
+
+ public System.Collections.Generic.IEnumerable HashScan(RedisKey key, RedisValue pattern, int pageSize, CommandFlags flags)
+ => GetActiveDatabase().HashScan(key, pattern, pageSize, flags);
+
+ public System.Collections.Generic.IEnumerable HashScan(RedisKey key, RedisValue pattern = default, int pageSize = RedisBase.CursorUtils.DefaultLibraryPageSize, long cursor = RedisBase.CursorUtils.Origin, int pageOffset = 0, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashScan(key, pattern, pageSize, cursor, pageOffset, flags);
+
+ public System.Collections.Generic.IEnumerable HashScanNoValues(RedisKey key, RedisValue pattern = default, int pageSize = RedisBase.CursorUtils.DefaultLibraryPageSize, long cursor = RedisBase.CursorUtils.Origin, int pageOffset = 0, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashScanNoValues(key, pattern, pageSize, cursor, pageOffset, flags);
+
+ public bool HashSet(RedisKey key, RedisValue hashField, RedisValue value, When when = When.Always, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashSet(key, hashField, value, when, flags);
+
+ public void HashSet(RedisKey key, HashEntry[] hashFields, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashSet(key, hashFields, flags);
+
+ public long HashStringLength(RedisKey key, RedisValue hashField, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashStringLength(key, hashField, flags);
+
+ public RedisValue[] HashValues(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HashValues(key, flags);
+}
diff --git a/src/StackExchange.Redis/MultiGroupDatabase.HyperLogLog.Async.cs b/src/StackExchange.Redis/MultiGroupDatabase.HyperLogLog.Async.cs
new file mode 100644
index 000000000..d57166885
--- /dev/null
+++ b/src/StackExchange.Redis/MultiGroupDatabase.HyperLogLog.Async.cs
@@ -0,0 +1,25 @@
+using System.Threading.Tasks;
+
+namespace StackExchange.Redis;
+
+internal sealed partial class MultiGroupDatabase
+{
+ // HyperLogLog Async
+ public Task HyperLogLogAddAsync(RedisKey key, RedisValue value, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HyperLogLogAddAsync(key, value, flags);
+
+ public Task HyperLogLogAddAsync(RedisKey key, RedisValue[] values, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HyperLogLogAddAsync(key, values, flags);
+
+ public Task HyperLogLogLengthAsync(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HyperLogLogLengthAsync(key, flags);
+
+ public Task HyperLogLogLengthAsync(RedisKey[] keys, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HyperLogLogLengthAsync(keys, flags);
+
+ public Task HyperLogLogMergeAsync(RedisKey destination, RedisKey first, RedisKey second, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HyperLogLogMergeAsync(destination, first, second, flags);
+
+ public Task HyperLogLogMergeAsync(RedisKey destination, RedisKey[] sourceKeys, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HyperLogLogMergeAsync(destination, sourceKeys, flags);
+}
diff --git a/src/StackExchange.Redis/MultiGroupDatabase.HyperLogLog.cs b/src/StackExchange.Redis/MultiGroupDatabase.HyperLogLog.cs
new file mode 100644
index 000000000..3312b4fc0
--- /dev/null
+++ b/src/StackExchange.Redis/MultiGroupDatabase.HyperLogLog.cs
@@ -0,0 +1,23 @@
+namespace StackExchange.Redis;
+
+internal sealed partial class MultiGroupDatabase
+{
+ // HyperLogLog operations
+ public bool HyperLogLogAdd(RedisKey key, RedisValue value, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HyperLogLogAdd(key, value, flags);
+
+ public bool HyperLogLogAdd(RedisKey key, RedisValue[] values, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HyperLogLogAdd(key, values, flags);
+
+ public long HyperLogLogLength(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HyperLogLogLength(key, flags);
+
+ public long HyperLogLogLength(RedisKey[] keys, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HyperLogLogLength(keys, flags);
+
+ public void HyperLogLogMerge(RedisKey destination, RedisKey first, RedisKey second, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HyperLogLogMerge(destination, first, second, flags);
+
+ public void HyperLogLogMerge(RedisKey destination, RedisKey[] sourceKeys, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().HyperLogLogMerge(destination, sourceKeys, flags);
+}
diff --git a/src/StackExchange.Redis/MultiGroupDatabase.Keys.Async.cs b/src/StackExchange.Redis/MultiGroupDatabase.Keys.Async.cs
new file mode 100644
index 000000000..76c7798be
--- /dev/null
+++ b/src/StackExchange.Redis/MultiGroupDatabase.Keys.Async.cs
@@ -0,0 +1,80 @@
+using System;
+using System.Threading.Tasks;
+
+namespace StackExchange.Redis;
+
+internal sealed partial class MultiGroupDatabase
+{
+ // Key Async
+ public Task KeyCopyAsync(RedisKey sourceKey, RedisKey destinationKey, int destinationDatabase = -1, bool replace = false, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().KeyCopyAsync(sourceKey, destinationKey, destinationDatabase, replace, flags);
+
+ public Task KeyDeleteAsync(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().KeyDeleteAsync(key, flags);
+
+ public Task KeyDeleteAsync(RedisKey[] keys, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().KeyDeleteAsync(keys, flags);
+
+ public Task KeyDumpAsync(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().KeyDumpAsync(key, flags);
+
+ public Task KeyEncodingAsync(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().KeyEncodingAsync(key, flags);
+
+ public Task KeyExistsAsync(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().KeyExistsAsync(key, flags);
+
+ public Task KeyExistsAsync(RedisKey[] keys, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().KeyExistsAsync(keys, flags);
+
+ public Task KeyExpireAsync(RedisKey key, TimeSpan? expiry, CommandFlags flags)
+ => GetActiveDatabase().KeyExpireAsync(key, expiry, flags);
+
+ public Task KeyExpireAsync(RedisKey key, TimeSpan? expiry, ExpireWhen when = ExpireWhen.Always, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().KeyExpireAsync(key, expiry, when, flags);
+
+ public Task KeyExpireAsync(RedisKey key, DateTime? expiry, CommandFlags flags)
+ => GetActiveDatabase().KeyExpireAsync(key, expiry, flags);
+
+ public Task KeyExpireAsync(RedisKey key, DateTime? expiry, ExpireWhen when = ExpireWhen.Always, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().KeyExpireAsync(key, expiry, when, flags);
+
+ public Task KeyExpireTimeAsync(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().KeyExpireTimeAsync(key, flags);
+
+ public Task KeyFrequencyAsync(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().KeyFrequencyAsync(key, flags);
+
+ public Task KeyIdleTimeAsync(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().KeyIdleTimeAsync(key, flags);
+
+ public Task KeyMoveAsync(RedisKey key, int database, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().KeyMoveAsync(key, database, flags);
+
+ public Task KeyPersistAsync(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().KeyPersistAsync(key, flags);
+
+ public Task KeyRandomAsync(CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().KeyRandomAsync(flags);
+
+ public Task KeyRefCountAsync(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().KeyRefCountAsync(key, flags);
+
+ public Task KeyRenameAsync(RedisKey key, RedisKey newKey, When when = When.Always, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().KeyRenameAsync(key, newKey, when, flags);
+
+ public Task KeyRestoreAsync(RedisKey key, byte[] value, TimeSpan? expiry = null, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().KeyRestoreAsync(key, value, expiry, flags);
+
+ public Task KeyTimeToLiveAsync(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().KeyTimeToLiveAsync(key, flags);
+
+ public Task KeyTouchAsync(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().KeyTouchAsync(key, flags);
+
+ public Task KeyTouchAsync(RedisKey[] keys, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().KeyTouchAsync(keys, flags);
+
+ public Task KeyTypeAsync(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().KeyTypeAsync(key, flags);
+}
diff --git a/src/StackExchange.Redis/MultiGroupDatabase.Keys.cs b/src/StackExchange.Redis/MultiGroupDatabase.Keys.cs
new file mode 100644
index 000000000..2ee6bdce0
--- /dev/null
+++ b/src/StackExchange.Redis/MultiGroupDatabase.Keys.cs
@@ -0,0 +1,79 @@
+using System;
+
+namespace StackExchange.Redis;
+
+internal sealed partial class MultiGroupDatabase
+{
+ // Key operations
+ public bool KeyCopy(RedisKey sourceKey, RedisKey destinationKey, int destinationDatabase = -1, bool replace = false, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().KeyCopy(sourceKey, destinationKey, destinationDatabase, replace, flags);
+
+ public bool KeyDelete(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().KeyDelete(key, flags);
+
+ public long KeyDelete(RedisKey[] keys, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().KeyDelete(keys, flags);
+
+ public byte[]? KeyDump(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().KeyDump(key, flags);
+
+ public string? KeyEncoding(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().KeyEncoding(key, flags);
+
+ public bool KeyExists(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().KeyExists(key, flags);
+
+ public long KeyExists(RedisKey[] keys, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().KeyExists(keys, flags);
+
+ public bool KeyExpire(RedisKey key, TimeSpan? expiry, CommandFlags flags)
+ => GetActiveDatabase().KeyExpire(key, expiry, flags);
+
+ public bool KeyExpire(RedisKey key, TimeSpan? expiry, ExpireWhen when = ExpireWhen.Always, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().KeyExpire(key, expiry, when, flags);
+
+ public bool KeyExpire(RedisKey key, DateTime? expiry, CommandFlags flags)
+ => GetActiveDatabase().KeyExpire(key, expiry, flags);
+
+ public bool KeyExpire(RedisKey key, DateTime? expiry, ExpireWhen when = ExpireWhen.Always, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().KeyExpire(key, expiry, when, flags);
+
+ public DateTime? KeyExpireTime(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().KeyExpireTime(key, flags);
+
+ public long? KeyFrequency(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().KeyFrequency(key, flags);
+
+ public TimeSpan? KeyIdleTime(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().KeyIdleTime(key, flags);
+
+ public bool KeyMove(RedisKey key, int database, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().KeyMove(key, database, flags);
+
+ public bool KeyPersist(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().KeyPersist(key, flags);
+
+ public RedisKey KeyRandom(CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().KeyRandom(flags);
+
+ public long? KeyRefCount(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().KeyRefCount(key, flags);
+
+ public bool KeyRename(RedisKey key, RedisKey newKey, When when = When.Always, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().KeyRename(key, newKey, when, flags);
+
+ public void KeyRestore(RedisKey key, byte[] value, TimeSpan? expiry = null, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().KeyRestore(key, value, expiry, flags);
+
+ public TimeSpan? KeyTimeToLive(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().KeyTimeToLive(key, flags);
+
+ public bool KeyTouch(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().KeyTouch(key, flags);
+
+ public long KeyTouch(RedisKey[] keys, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().KeyTouch(keys, flags);
+
+ public RedisType KeyType(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().KeyType(key, flags);
+}
diff --git a/src/StackExchange.Redis/MultiGroupDatabase.Lists.Async.cs b/src/StackExchange.Redis/MultiGroupDatabase.Lists.Async.cs
new file mode 100644
index 000000000..d284dfee9
--- /dev/null
+++ b/src/StackExchange.Redis/MultiGroupDatabase.Lists.Async.cs
@@ -0,0 +1,79 @@
+using System.Threading.Tasks;
+
+namespace StackExchange.Redis;
+
+internal sealed partial class MultiGroupDatabase
+{
+ // List Async operations
+ public Task ListGetByIndexAsync(RedisKey key, long index, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().ListGetByIndexAsync(key, index, flags);
+
+ public Task ListInsertAfterAsync(RedisKey key, RedisValue pivot, RedisValue value, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().ListInsertAfterAsync(key, pivot, value, flags);
+
+ public Task ListInsertBeforeAsync(RedisKey key, RedisValue pivot, RedisValue value, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().ListInsertBeforeAsync(key, pivot, value, flags);
+
+ public Task ListLeftPopAsync(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().ListLeftPopAsync(key, flags);
+
+ public Task ListLeftPopAsync(RedisKey key, long count, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().ListLeftPopAsync(key, count, flags);
+
+ public Task ListLeftPopAsync(RedisKey[] keys, long count, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().ListLeftPopAsync(keys, count, flags);
+
+ public Task ListLeftPushAsync(RedisKey key, RedisValue value, When when = When.Always, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().ListLeftPushAsync(key, value, when, flags);
+
+ public Task ListLeftPushAsync(RedisKey key, RedisValue[] values, When when = When.Always, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().ListLeftPushAsync(key, values, when, flags);
+
+ public Task ListLeftPushAsync(RedisKey key, RedisValue[] values, CommandFlags flags)
+ => GetActiveDatabase().ListLeftPushAsync(key, values, flags);
+
+ public Task ListLengthAsync(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().ListLengthAsync(key, flags);
+
+ public Task ListMoveAsync(RedisKey sourceKey, RedisKey destinationKey, ListSide sourceSide, ListSide destinationSide, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().ListMoveAsync(sourceKey, destinationKey, sourceSide, destinationSide, flags);
+
+ public Task ListPositionAsync(RedisKey key, RedisValue element, long rank = 1, long maxLength = 0, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().ListPositionAsync(key, element, rank, maxLength, flags);
+
+ public Task ListPositionsAsync(RedisKey key, RedisValue element, long count, long rank = 1, long maxLength = 0, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().ListPositionsAsync(key, element, count, rank, maxLength, flags);
+
+ public Task ListRangeAsync(RedisKey key, long start = 0, long stop = -1, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().ListRangeAsync(key, start, stop, flags);
+
+ public Task ListRemoveAsync(RedisKey key, RedisValue value, long count = 0, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().ListRemoveAsync(key, value, count, flags);
+
+ public Task ListRightPopAsync(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().ListRightPopAsync(key, flags);
+
+ public Task ListRightPopAsync(RedisKey key, long count, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().ListRightPopAsync(key, count, flags);
+
+ public Task ListRightPopAsync(RedisKey[] keys, long count, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().ListRightPopAsync(keys, count, flags);
+
+ public Task ListRightPopLeftPushAsync(RedisKey source, RedisKey destination, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().ListRightPopLeftPushAsync(source, destination, flags);
+
+ public Task ListRightPushAsync(RedisKey key, RedisValue value, When when = When.Always, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().ListRightPushAsync(key, value, when, flags);
+
+ public Task ListRightPushAsync(RedisKey key, RedisValue[] values, When when = When.Always, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().ListRightPushAsync(key, values, when, flags);
+
+ public Task ListRightPushAsync(RedisKey key, RedisValue[] values, CommandFlags flags)
+ => GetActiveDatabase().ListRightPushAsync(key, values, flags);
+
+ public Task ListSetByIndexAsync(RedisKey key, long index, RedisValue value, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().ListSetByIndexAsync(key, index, value, flags);
+
+ public Task ListTrimAsync(RedisKey key, long start, long stop, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().ListTrimAsync(key, start, stop, flags);
+}
diff --git a/src/StackExchange.Redis/MultiGroupDatabase.Lists.cs b/src/StackExchange.Redis/MultiGroupDatabase.Lists.cs
new file mode 100644
index 000000000..58ffc7a1b
--- /dev/null
+++ b/src/StackExchange.Redis/MultiGroupDatabase.Lists.cs
@@ -0,0 +1,77 @@
+namespace StackExchange.Redis;
+
+internal sealed partial class MultiGroupDatabase
+{
+ // List operations
+ public RedisValue ListGetByIndex(RedisKey key, long index, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().ListGetByIndex(key, index, flags);
+
+ public long ListInsertAfter(RedisKey key, RedisValue pivot, RedisValue value, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().ListInsertAfter(key, pivot, value, flags);
+
+ public long ListInsertBefore(RedisKey key, RedisValue pivot, RedisValue value, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().ListInsertBefore(key, pivot, value, flags);
+
+ public RedisValue ListLeftPop(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().ListLeftPop(key, flags);
+
+ public RedisValue[] ListLeftPop(RedisKey key, long count, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().ListLeftPop(key, count, flags);
+
+ public ListPopResult ListLeftPop(RedisKey[] keys, long count, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().ListLeftPop(keys, count, flags);
+
+ public long ListLeftPush(RedisKey key, RedisValue value, When when = When.Always, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().ListLeftPush(key, value, when, flags);
+
+ public long ListLeftPush(RedisKey key, RedisValue[] values, When when = When.Always, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().ListLeftPush(key, values, when, flags);
+
+ public long ListLeftPush(RedisKey key, RedisValue[] values, CommandFlags flags)
+ => GetActiveDatabase().ListLeftPush(key, values, flags);
+
+ public long ListLength(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().ListLength(key, flags);
+
+ public RedisValue ListMove(RedisKey sourceKey, RedisKey destinationKey, ListSide sourceSide, ListSide destinationSide, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().ListMove(sourceKey, destinationKey, sourceSide, destinationSide, flags);
+
+ public long ListPosition(RedisKey key, RedisValue element, long rank = 1, long maxLength = 0, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().ListPosition(key, element, rank, maxLength, flags);
+
+ public long[] ListPositions(RedisKey key, RedisValue element, long count, long rank = 1, long maxLength = 0, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().ListPositions(key, element, count, rank, maxLength, flags);
+
+ public RedisValue[] ListRange(RedisKey key, long start = 0, long stop = -1, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().ListRange(key, start, stop, flags);
+
+ public long ListRemove(RedisKey key, RedisValue value, long count = 0, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().ListRemove(key, value, count, flags);
+
+ public RedisValue ListRightPop(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().ListRightPop(key, flags);
+
+ public RedisValue[] ListRightPop(RedisKey key, long count, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().ListRightPop(key, count, flags);
+
+ public ListPopResult ListRightPop(RedisKey[] keys, long count, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().ListRightPop(keys, count, flags);
+
+ public RedisValue ListRightPopLeftPush(RedisKey source, RedisKey destination, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().ListRightPopLeftPush(source, destination, flags);
+
+ public long ListRightPush(RedisKey key, RedisValue value, When when = When.Always, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().ListRightPush(key, value, when, flags);
+
+ public long ListRightPush(RedisKey key, RedisValue[] values, When when = When.Always, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().ListRightPush(key, values, when, flags);
+
+ public long ListRightPush(RedisKey key, RedisValue[] values, CommandFlags flags)
+ => GetActiveDatabase().ListRightPush(key, values, flags);
+
+ public void ListSetByIndex(RedisKey key, long index, RedisValue value, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().ListSetByIndex(key, index, value, flags);
+
+ public void ListTrim(RedisKey key, long start, long stop, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().ListTrim(key, start, stop, flags);
+}
diff --git a/src/StackExchange.Redis/MultiGroupDatabase.Sets.Async.cs b/src/StackExchange.Redis/MultiGroupDatabase.Sets.Async.cs
new file mode 100644
index 000000000..7d8eddf54
--- /dev/null
+++ b/src/StackExchange.Redis/MultiGroupDatabase.Sets.Async.cs
@@ -0,0 +1,64 @@
+using System.Threading.Tasks;
+
+namespace StackExchange.Redis;
+
+internal sealed partial class MultiGroupDatabase
+{
+ // Set Async operations
+ public Task SetAddAsync(RedisKey key, RedisValue value, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SetAddAsync(key, value, flags);
+
+ public Task SetAddAsync(RedisKey key, RedisValue[] values, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SetAddAsync(key, values, flags);
+
+ public Task SetCombineAsync(SetOperation operation, RedisKey first, RedisKey second, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SetCombineAsync(operation, first, second, flags);
+
+ public Task SetCombineAsync(SetOperation operation, RedisKey[] keys, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SetCombineAsync(operation, keys, flags);
+
+ public Task SetCombineAndStoreAsync(SetOperation operation, RedisKey destination, RedisKey first, RedisKey second, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SetCombineAndStoreAsync(operation, destination, first, second, flags);
+
+ public Task SetCombineAndStoreAsync(SetOperation operation, RedisKey destination, RedisKey[] keys, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SetCombineAndStoreAsync(operation, destination, keys, flags);
+
+ public Task SetContainsAsync(RedisKey key, RedisValue value, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SetContainsAsync(key, value, flags);
+
+ public Task SetContainsAsync(RedisKey key, RedisValue[] values, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SetContainsAsync(key, values, flags);
+
+ public Task SetIntersectionLengthAsync(RedisKey[] keys, long limit = 0, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SetIntersectionLengthAsync(keys, limit, flags);
+
+ public Task SetLengthAsync(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SetLengthAsync(key, flags);
+
+ public Task SetMembersAsync(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SetMembersAsync(key, flags);
+
+ public Task SetMoveAsync(RedisKey source, RedisKey destination, RedisValue value, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SetMoveAsync(source, destination, value, flags);
+
+ public Task SetPopAsync(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SetPopAsync(key, flags);
+
+ public Task SetPopAsync(RedisKey key, long count, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SetPopAsync(key, count, flags);
+
+ public Task SetRandomMemberAsync(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SetRandomMemberAsync(key, flags);
+
+ public Task SetRandomMembersAsync(RedisKey key, long count, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SetRandomMembersAsync(key, count, flags);
+
+ public Task SetRemoveAsync(RedisKey key, RedisValue value, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SetRemoveAsync(key, value, flags);
+
+ public Task SetRemoveAsync(RedisKey key, RedisValue[] values, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SetRemoveAsync(key, values, flags);
+
+ public System.Collections.Generic.IAsyncEnumerable SetScanAsync(RedisKey key, RedisValue pattern = default, int pageSize = RedisBase.CursorUtils.DefaultLibraryPageSize, long cursor = RedisBase.CursorUtils.Origin, int pageOffset = 0, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SetScanAsync(key, pattern, pageSize, cursor, pageOffset, flags);
+}
diff --git a/src/StackExchange.Redis/MultiGroupDatabase.Sets.cs b/src/StackExchange.Redis/MultiGroupDatabase.Sets.cs
new file mode 100644
index 000000000..f04df3eeb
--- /dev/null
+++ b/src/StackExchange.Redis/MultiGroupDatabase.Sets.cs
@@ -0,0 +1,65 @@
+namespace StackExchange.Redis;
+
+internal sealed partial class MultiGroupDatabase
+{
+ // Set operations
+ public bool SetAdd(RedisKey key, RedisValue value, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SetAdd(key, value, flags);
+
+ public long SetAdd(RedisKey key, RedisValue[] values, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SetAdd(key, values, flags);
+
+ public RedisValue[] SetCombine(SetOperation operation, RedisKey first, RedisKey second, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SetCombine(operation, first, second, flags);
+
+ public RedisValue[] SetCombine(SetOperation operation, RedisKey[] keys, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SetCombine(operation, keys, flags);
+
+ public long SetCombineAndStore(SetOperation operation, RedisKey destination, RedisKey first, RedisKey second, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SetCombineAndStore(operation, destination, first, second, flags);
+
+ public long SetCombineAndStore(SetOperation operation, RedisKey destination, RedisKey[] keys, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SetCombineAndStore(operation, destination, keys, flags);
+
+ public bool SetContains(RedisKey key, RedisValue value, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SetContains(key, value, flags);
+
+ public bool[] SetContains(RedisKey key, RedisValue[] values, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SetContains(key, values, flags);
+
+ public long SetIntersectionLength(RedisKey[] keys, long limit = 0, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SetIntersectionLength(keys, limit, flags);
+
+ public long SetLength(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SetLength(key, flags);
+
+ public RedisValue[] SetMembers(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SetMembers(key, flags);
+
+ public bool SetMove(RedisKey source, RedisKey destination, RedisValue value, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SetMove(source, destination, value, flags);
+
+ public RedisValue SetPop(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SetPop(key, flags);
+
+ public RedisValue[] SetPop(RedisKey key, long count, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SetPop(key, count, flags);
+
+ public RedisValue SetRandomMember(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SetRandomMember(key, flags);
+
+ public RedisValue[] SetRandomMembers(RedisKey key, long count, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SetRandomMembers(key, count, flags);
+
+ public bool SetRemove(RedisKey key, RedisValue value, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SetRemove(key, value, flags);
+
+ public long SetRemove(RedisKey key, RedisValue[] values, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SetRemove(key, values, flags);
+
+ public System.Collections.Generic.IEnumerable SetScan(RedisKey key, RedisValue pattern, int pageSize, CommandFlags flags)
+ => GetActiveDatabase().SetScan(key, pattern, pageSize, flags);
+
+ public System.Collections.Generic.IEnumerable SetScan(RedisKey key, RedisValue pattern = default, int pageSize = RedisBase.CursorUtils.DefaultLibraryPageSize, long cursor = RedisBase.CursorUtils.Origin, int pageOffset = 0, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SetScan(key, pattern, pageSize, cursor, pageOffset, flags);
+}
diff --git a/src/StackExchange.Redis/MultiGroupDatabase.SortedSets.Async.cs b/src/StackExchange.Redis/MultiGroupDatabase.SortedSets.Async.cs
new file mode 100644
index 000000000..ae4c21186
--- /dev/null
+++ b/src/StackExchange.Redis/MultiGroupDatabase.SortedSets.Async.cs
@@ -0,0 +1,124 @@
+using System.Threading.Tasks;
+
+namespace StackExchange.Redis;
+
+internal sealed partial class MultiGroupDatabase
+{
+ // SortedSet Async operations
+ public Task SortedSetAddAsync(RedisKey key, RedisValue member, double score, CommandFlags flags)
+ => GetActiveDatabase().SortedSetAddAsync(key, member, score, flags);
+
+ public Task SortedSetAddAsync(RedisKey key, RedisValue member, double score, When when, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetAddAsync(key, member, score, when, flags);
+
+ public Task SortedSetAddAsync(RedisKey key, RedisValue member, double score, SortedSetWhen when = SortedSetWhen.Always, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetAddAsync(key, member, score, when, flags);
+
+ public Task SortedSetAddAsync(RedisKey key, SortedSetEntry[] values, CommandFlags flags)
+ => GetActiveDatabase().SortedSetAddAsync(key, values, flags);
+
+ public Task SortedSetAddAsync(RedisKey key, SortedSetEntry[] values, When when, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetAddAsync(key, values, when, flags);
+
+ public Task SortedSetAddAsync(RedisKey key, SortedSetEntry[] values, SortedSetWhen when = SortedSetWhen.Always, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetAddAsync(key, values, when, flags);
+
+ public Task SortedSetCombineAsync(SetOperation operation, RedisKey[] keys, double[]? weights = null, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetCombineAsync(operation, keys, weights, aggregate, flags);
+
+ public Task SortedSetCombineWithScoresAsync(SetOperation operation, RedisKey[] keys, double[]? weights = null, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetCombineWithScoresAsync(operation, keys, weights, aggregate, flags);
+
+ public Task SortedSetCombineAndStoreAsync(SetOperation operation, RedisKey destination, RedisKey first, RedisKey second, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetCombineAndStoreAsync(operation, destination, first, second, aggregate, flags);
+
+ public Task SortedSetCombineAndStoreAsync(SetOperation operation, RedisKey destination, RedisKey[] keys, double[]? weights = null, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetCombineAndStoreAsync(operation, destination, keys, weights, aggregate, flags);
+
+ public Task SortedSetDecrementAsync(RedisKey key, RedisValue member, double value, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetDecrementAsync(key, member, value, flags);
+
+ public Task SortedSetIncrementAsync(RedisKey key, RedisValue member, double value, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetIncrementAsync(key, member, value, flags);
+
+ public Task SortedSetIntersectionLengthAsync(RedisKey[] keys, long limit = 0, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetIntersectionLengthAsync(keys, limit, flags);
+
+ public Task SortedSetLengthAsync(RedisKey key, double min = double.NegativeInfinity, double max = double.PositiveInfinity, Exclude exclude = Exclude.None, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetLengthAsync(key, min, max, exclude, flags);
+
+ public Task SortedSetLengthByValueAsync(RedisKey key, RedisValue min, RedisValue max, Exclude exclude = Exclude.None, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetLengthByValueAsync(key, min, max, exclude, flags);
+
+ public Task SortedSetRandomMemberAsync(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetRandomMemberAsync(key, flags);
+
+ public Task SortedSetRandomMembersAsync(RedisKey key, long count, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetRandomMembersAsync(key, count, flags);
+
+ public Task SortedSetRandomMembersWithScoresAsync(RedisKey key, long count, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetRandomMembersWithScoresAsync(key, count, flags);
+
+ public Task SortedSetRangeByRankAsync(RedisKey key, long start = 0, long stop = -1, Order order = Order.Ascending, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetRangeByRankAsync(key, start, stop, order, flags);
+
+ public Task SortedSetRangeAndStoreAsync(RedisKey sourceKey, RedisKey destinationKey, RedisValue start, RedisValue stop, SortedSetOrder sortedSetOrder = SortedSetOrder.ByRank, Exclude exclude = Exclude.None, Order order = Order.Ascending, long skip = 0, long? take = null, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetRangeAndStoreAsync(sourceKey, destinationKey, start, stop, sortedSetOrder, exclude, order, skip, take, flags);
+
+ public Task SortedSetRangeByRankWithScoresAsync(RedisKey key, long start = 0, long stop = -1, Order order = Order.Ascending, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetRangeByRankWithScoresAsync(key, start, stop, order, flags);
+
+ public Task SortedSetRangeByScoreAsync(RedisKey key, double start = double.NegativeInfinity, double stop = double.PositiveInfinity, Exclude exclude = Exclude.None, Order order = Order.Ascending, long skip = 0, long take = -1, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetRangeByScoreAsync(key, start, stop, exclude, order, skip, take, flags);
+
+ public Task SortedSetRangeByScoreWithScoresAsync(RedisKey key, double start = double.NegativeInfinity, double stop = double.PositiveInfinity, Exclude exclude = Exclude.None, Order order = Order.Ascending, long skip = 0, long take = -1, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetRangeByScoreWithScoresAsync(key, start, stop, exclude, order, skip, take, flags);
+
+ public Task SortedSetRangeByValueAsync(RedisKey key, RedisValue min, RedisValue max, Exclude exclude, long skip, long take = -1, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetRangeByValueAsync(key, min, max, exclude, skip, take, flags);
+
+ public Task SortedSetRangeByValueAsync(RedisKey key, RedisValue min = default, RedisValue max = default, Exclude exclude = Exclude.None, Order order = Order.Ascending, long skip = 0, long take = -1, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetRangeByValueAsync(key, min, max, exclude, order, skip, take, flags);
+
+ public Task SortedSetRankAsync(RedisKey key, RedisValue member, Order order = Order.Ascending, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetRankAsync(key, member, order, flags);
+
+ public Task SortedSetRemoveAsync(RedisKey key, RedisValue member, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetRemoveAsync(key, member, flags);
+
+ public Task SortedSetRemoveAsync(RedisKey key, RedisValue[] members, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetRemoveAsync(key, members, flags);
+
+ public Task SortedSetRemoveRangeByRankAsync(RedisKey key, long start, long stop, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetRemoveRangeByRankAsync(key, start, stop, flags);
+
+ public Task SortedSetRemoveRangeByScoreAsync(RedisKey key, double start, double stop, Exclude exclude = Exclude.None, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetRemoveRangeByScoreAsync(key, start, stop, exclude, flags);
+
+ public Task SortedSetRemoveRangeByValueAsync(RedisKey key, RedisValue min, RedisValue max, Exclude exclude = Exclude.None, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetRemoveRangeByValueAsync(key, min, max, exclude, flags);
+
+ public System.Collections.Generic.IAsyncEnumerable SortedSetScanAsync(RedisKey key, RedisValue pattern = default, int pageSize = RedisBase.CursorUtils.DefaultLibraryPageSize, long cursor = RedisBase.CursorUtils.Origin, int pageOffset = 0, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetScanAsync(key, pattern, pageSize, cursor, pageOffset, flags);
+
+ public Task SortedSetScoreAsync(RedisKey key, RedisValue member, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetScoreAsync(key, member, flags);
+
+ public Task SortedSetScoresAsync(RedisKey key, RedisValue[] members, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetScoresAsync(key, members, flags);
+
+ public Task SortedSetPopAsync(RedisKey key, Order order = Order.Ascending, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetPopAsync(key, order, flags);
+
+ public Task SortedSetPopAsync(RedisKey key, long count, Order order = Order.Ascending, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetPopAsync(key, count, order, flags);
+
+ public Task SortedSetPopAsync(RedisKey[] keys, long count, Order order = Order.Ascending, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetPopAsync(keys, count, order, flags);
+
+ public Task SortedSetUpdateAsync(RedisKey key, RedisValue member, double score, SortedSetWhen when = SortedSetWhen.Always, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetUpdateAsync(key, member, score, when, flags);
+
+ public Task SortedSetUpdateAsync(RedisKey key, SortedSetEntry[] values, SortedSetWhen when = SortedSetWhen.Always, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetUpdateAsync(key, values, when, flags);
+}
diff --git a/src/StackExchange.Redis/MultiGroupDatabase.SortedSets.cs b/src/StackExchange.Redis/MultiGroupDatabase.SortedSets.cs
new file mode 100644
index 000000000..90814054f
--- /dev/null
+++ b/src/StackExchange.Redis/MultiGroupDatabase.SortedSets.cs
@@ -0,0 +1,127 @@
+using System;
+
+namespace StackExchange.Redis;
+
+internal sealed partial class MultiGroupDatabase
+{
+ // SortedSet operations
+ public bool SortedSetAdd(RedisKey key, RedisValue member, double score, CommandFlags flags)
+ => GetActiveDatabase().SortedSetAdd(key, member, score, flags);
+
+ public bool SortedSetAdd(RedisKey key, RedisValue member, double score, When when, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetAdd(key, member, score, when, flags);
+
+ public bool SortedSetAdd(RedisKey key, RedisValue member, double score, SortedSetWhen when = SortedSetWhen.Always, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetAdd(key, member, score, when, flags);
+
+ public long SortedSetAdd(RedisKey key, SortedSetEntry[] values, CommandFlags flags)
+ => GetActiveDatabase().SortedSetAdd(key, values, flags);
+
+ public long SortedSetAdd(RedisKey key, SortedSetEntry[] values, When when, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetAdd(key, values, when, flags);
+
+ public long SortedSetAdd(RedisKey key, SortedSetEntry[] values, SortedSetWhen when = SortedSetWhen.Always, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetAdd(key, values, when, flags);
+
+ public RedisValue[] SortedSetCombine(SetOperation operation, RedisKey[] keys, double[]? weights = null, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetCombine(operation, keys, weights, aggregate, flags);
+
+ public SortedSetEntry[] SortedSetCombineWithScores(SetOperation operation, RedisKey[] keys, double[]? weights = null, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetCombineWithScores(operation, keys, weights, aggregate, flags);
+
+ public long SortedSetCombineAndStore(SetOperation operation, RedisKey destination, RedisKey first, RedisKey second, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetCombineAndStore(operation, destination, first, second, aggregate, flags);
+
+ public long SortedSetCombineAndStore(SetOperation operation, RedisKey destination, RedisKey[] keys, double[]? weights = null, Aggregate aggregate = Aggregate.Sum, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetCombineAndStore(operation, destination, keys, weights, aggregate, flags);
+
+ public double SortedSetDecrement(RedisKey key, RedisValue member, double value, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetDecrement(key, member, value, flags);
+
+ public double SortedSetIncrement(RedisKey key, RedisValue member, double value, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetIncrement(key, member, value, flags);
+
+ public long SortedSetIntersectionLength(RedisKey[] keys, long limit = 0, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetIntersectionLength(keys, limit, flags);
+
+ public long SortedSetLength(RedisKey key, double min = double.NegativeInfinity, double max = double.PositiveInfinity, Exclude exclude = Exclude.None, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetLength(key, min, max, exclude, flags);
+
+ public long SortedSetLengthByValue(RedisKey key, RedisValue min, RedisValue max, Exclude exclude = Exclude.None, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetLengthByValue(key, min, max, exclude, flags);
+
+ public RedisValue SortedSetRandomMember(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetRandomMember(key, flags);
+
+ public RedisValue[] SortedSetRandomMembers(RedisKey key, long count, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetRandomMembers(key, count, flags);
+
+ public SortedSetEntry[] SortedSetRandomMembersWithScores(RedisKey key, long count, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetRandomMembersWithScores(key, count, flags);
+
+ public RedisValue[] SortedSetRangeByRank(RedisKey key, long start = 0, long stop = -1, Order order = Order.Ascending, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetRangeByRank(key, start, stop, order, flags);
+
+ public long SortedSetRangeAndStore(RedisKey sourceKey, RedisKey destinationKey, RedisValue start, RedisValue stop, SortedSetOrder sortedSetOrder = SortedSetOrder.ByRank, Exclude exclude = Exclude.None, Order order = Order.Ascending, long skip = 0, long? take = null, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetRangeAndStore(sourceKey, destinationKey, start, stop, sortedSetOrder, exclude, order, skip, take, flags);
+
+ public SortedSetEntry[] SortedSetRangeByRankWithScores(RedisKey key, long start = 0, long stop = -1, Order order = Order.Ascending, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetRangeByRankWithScores(key, start, stop, order, flags);
+
+ public RedisValue[] SortedSetRangeByScore(RedisKey key, double start = double.NegativeInfinity, double stop = double.PositiveInfinity, Exclude exclude = Exclude.None, Order order = Order.Ascending, long skip = 0, long take = -1, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetRangeByScore(key, start, stop, exclude, order, skip, take, flags);
+
+ public SortedSetEntry[] SortedSetRangeByScoreWithScores(RedisKey key, double start = double.NegativeInfinity, double stop = double.PositiveInfinity, Exclude exclude = Exclude.None, Order order = Order.Ascending, long skip = 0, long take = -1, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetRangeByScoreWithScores(key, start, stop, exclude, order, skip, take, flags);
+
+ public RedisValue[] SortedSetRangeByValue(RedisKey key, RedisValue min, RedisValue max, Exclude exclude, long skip, long take = -1, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetRangeByValue(key, min, max, exclude, skip, take, flags);
+
+ public RedisValue[] SortedSetRangeByValue(RedisKey key, RedisValue min = default, RedisValue max = default, Exclude exclude = Exclude.None, Order order = Order.Ascending, long skip = 0, long take = -1, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetRangeByValue(key, min, max, exclude, order, skip, take, flags);
+
+ public long? SortedSetRank(RedisKey key, RedisValue member, Order order = Order.Ascending, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetRank(key, member, order, flags);
+
+ public bool SortedSetRemove(RedisKey key, RedisValue member, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetRemove(key, member, flags);
+
+ public long SortedSetRemove(RedisKey key, RedisValue[] members, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetRemove(key, members, flags);
+
+ public long SortedSetRemoveRangeByRank(RedisKey key, long start, long stop, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetRemoveRangeByRank(key, start, stop, flags);
+
+ public long SortedSetRemoveRangeByScore(RedisKey key, double start, double stop, Exclude exclude = Exclude.None, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetRemoveRangeByScore(key, start, stop, exclude, flags);
+
+ public long SortedSetRemoveRangeByValue(RedisKey key, RedisValue min, RedisValue max, Exclude exclude = Exclude.None, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetRemoveRangeByValue(key, min, max, exclude, flags);
+
+ public System.Collections.Generic.IEnumerable SortedSetScan(RedisKey key, RedisValue pattern, int pageSize, CommandFlags flags)
+ => GetActiveDatabase().SortedSetScan(key, pattern, pageSize, flags);
+
+ public System.Collections.Generic.IEnumerable SortedSetScan(RedisKey key, RedisValue pattern = default, int pageSize = RedisBase.CursorUtils.DefaultLibraryPageSize, long cursor = RedisBase.CursorUtils.Origin, int pageOffset = 0, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetScan(key, pattern, pageSize, cursor, pageOffset, flags);
+
+ public double? SortedSetScore(RedisKey key, RedisValue member, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetScore(key, member, flags);
+
+ public double?[] SortedSetScores(RedisKey key, RedisValue[] members, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetScores(key, members, flags);
+
+ public SortedSetEntry? SortedSetPop(RedisKey key, Order order = Order.Ascending, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetPop(key, order, flags);
+
+ public SortedSetEntry[] SortedSetPop(RedisKey key, long count, Order order = Order.Ascending, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetPop(key, count, order, flags);
+
+ public SortedSetPopResult SortedSetPop(RedisKey[] keys, long count, Order order = Order.Ascending, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetPop(keys, count, order, flags);
+
+ public bool SortedSetUpdate(RedisKey key, RedisValue member, double score, SortedSetWhen when = SortedSetWhen.Always, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetUpdate(key, member, score, when, flags);
+
+ public long SortedSetUpdate(RedisKey key, SortedSetEntry[] values, SortedSetWhen when = SortedSetWhen.Always, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().SortedSetUpdate(key, values, when, flags);
+}
diff --git a/src/StackExchange.Redis/MultiGroupDatabase.Streams.Async.cs b/src/StackExchange.Redis/MultiGroupDatabase.Streams.Async.cs
new file mode 100644
index 000000000..89950e554
--- /dev/null
+++ b/src/StackExchange.Redis/MultiGroupDatabase.Streams.Async.cs
@@ -0,0 +1,131 @@
+using System;
+using System.Threading.Tasks;
+
+namespace StackExchange.Redis;
+
+internal sealed partial class MultiGroupDatabase
+{
+ // Stream Async operations
+ public Task StreamAutoClaimIdsOnlyAsync(RedisKey key, RedisValue consumerGroup, RedisValue claimingConsumer, long minIdleTimeInMs, RedisValue startAtId, int? count = null, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamAutoClaimIdsOnlyAsync(key, consumerGroup, claimingConsumer, minIdleTimeInMs, startAtId, count, flags);
+
+ public Task StreamAutoClaimAsync(RedisKey key, RedisValue consumerGroup, RedisValue claimingConsumer, long minIdleTimeInMs, RedisValue startAtId, int? count = null, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamAutoClaimAsync(key, consumerGroup, claimingConsumer, minIdleTimeInMs, startAtId, count, flags);
+
+ public Task StreamAddAsync(RedisKey key, RedisValue streamField, RedisValue streamValue, RedisValue? messageId = null, int? maxLength = null, bool useApproximateMaxLength = false, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamAddAsync(key, streamField, streamValue, messageId, maxLength, useApproximateMaxLength, flags);
+
+ public Task StreamAddAsync(RedisKey key, RedisValue streamField, RedisValue streamValue, RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StreamTrimMode trimMode = StreamTrimMode.KeepReferences, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamAddAsync(key, streamField, streamValue, messageId, maxLength, useApproximateMaxLength, limit, trimMode, flags);
+
+ public Task StreamAddAsync(RedisKey key, NameValueEntry[] streamPairs, RedisValue? messageId = null, int? maxLength = null, bool useApproximateMaxLength = false, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamAddAsync(key, streamPairs, messageId, maxLength, useApproximateMaxLength, flags);
+
+ public Task StreamAddAsync(RedisKey key, NameValueEntry[] streamPairs, RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StreamTrimMode trimMode = StreamTrimMode.KeepReferences, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamAddAsync(key, streamPairs, messageId, maxLength, useApproximateMaxLength, limit, trimMode, flags);
+
+ public Task StreamAddAsync(RedisKey key, RedisValue streamField, RedisValue streamValue, StreamIdempotentId idempotentId, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StreamTrimMode trimMode = StreamTrimMode.KeepReferences, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamAddAsync(key, streamField, streamValue, idempotentId, maxLength, useApproximateMaxLength, limit, trimMode, flags);
+
+ public Task StreamAddAsync(RedisKey key, NameValueEntry[] streamPairs, StreamIdempotentId idempotentId, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StreamTrimMode trimMode = StreamTrimMode.KeepReferences, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamAddAsync(key, streamPairs, idempotentId, maxLength, useApproximateMaxLength, limit, trimMode, flags);
+
+ public Task StreamClaimAsync(RedisKey key, RedisValue consumerGroup, RedisValue claimingConsumer, long minIdleTimeInMs, RedisValue[] messageIds, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamClaimAsync(key, consumerGroup, claimingConsumer, minIdleTimeInMs, messageIds, flags);
+
+ public Task StreamClaimIdsOnlyAsync(RedisKey key, RedisValue consumerGroup, RedisValue claimingConsumer, long minIdleTimeInMs, RedisValue[] messageIds, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamClaimIdsOnlyAsync(key, consumerGroup, claimingConsumer, minIdleTimeInMs, messageIds, flags);
+
+ public Task StreamConfigureAsync(RedisKey key, StreamConfiguration configuration, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamConfigureAsync(key, configuration, flags);
+
+ public Task StreamConsumerGroupSetPositionAsync(RedisKey key, RedisValue groupName, RedisValue position, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamConsumerGroupSetPositionAsync(key, groupName, position, flags);
+
+ public Task StreamConsumerInfoAsync(RedisKey key, RedisValue groupName, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamConsumerInfoAsync(key, groupName, flags);
+
+ public Task StreamCreateConsumerGroupAsync(RedisKey key, RedisValue groupName, RedisValue? position, CommandFlags flags)
+ => GetActiveDatabase().StreamCreateConsumerGroupAsync(key, groupName, position, flags);
+
+ public Task StreamCreateConsumerGroupAsync(RedisKey key, RedisValue groupName, RedisValue? position = null, bool createStream = true, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamCreateConsumerGroupAsync(key, groupName, position, createStream, flags);
+
+ public Task StreamDeleteAsync(RedisKey key, RedisValue[] messageIds, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamDeleteAsync(key, messageIds, flags);
+
+ public Task StreamDeleteAsync(RedisKey key, RedisValue[] messageIds, StreamTrimMode mode, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamDeleteAsync(key, messageIds, mode, flags);
+
+ public Task StreamDeleteConsumerAsync(RedisKey key, RedisValue groupName, RedisValue consumerName, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamDeleteConsumerAsync(key, groupName, consumerName, flags);
+
+ public Task StreamDeleteConsumerGroupAsync(RedisKey key, RedisValue groupName, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamDeleteConsumerGroupAsync(key, groupName, flags);
+
+ public Task StreamGroupInfoAsync(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamGroupInfoAsync(key, flags);
+
+ public Task StreamInfoAsync(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamInfoAsync(key, flags);
+
+ public Task StreamLengthAsync(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamLengthAsync(key, flags);
+
+ public Task StreamPendingAsync(RedisKey key, RedisValue groupName, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamPendingAsync(key, groupName, flags);
+
+ public Task StreamPendingMessagesAsync(RedisKey key, RedisValue groupName, int count, RedisValue consumerName, RedisValue? minId = null, RedisValue? maxId = null, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamPendingMessagesAsync(key, groupName, count, consumerName, minId, maxId, flags);
+
+ public Task StreamPendingMessagesAsync(RedisKey key, RedisValue groupName, int count, RedisValue consumerName, RedisValue? minId = null, RedisValue? maxId = null, long? idle = null, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamPendingMessagesAsync(key, groupName, count, consumerName, minId, maxId, idle, flags);
+
+ public Task StreamRangeAsync(RedisKey key, RedisValue? minId = null, RedisValue? maxId = null, int? count = null, Order messageOrder = Order.Ascending, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamRangeAsync(key, minId, maxId, count, messageOrder, flags);
+
+ public Task StreamReadAsync(RedisKey key, RedisValue position, int? count = null, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamReadAsync(key, position, count, flags);
+
+ public Task StreamReadAsync(StreamPosition[] streamPositions, int? countPerStream = null, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamReadAsync(streamPositions, countPerStream, flags);
+
+ public Task StreamReadGroupAsync(RedisKey key, RedisValue groupName, RedisValue consumerName, RedisValue? position, int? count, CommandFlags flags)
+ => GetActiveDatabase().StreamReadGroupAsync(key, groupName, consumerName, position, count, flags);
+
+ public Task StreamReadGroupAsync(RedisKey key, RedisValue groupName, RedisValue consumerName, RedisValue? position = null, int? count = null, bool noAck = false, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamReadGroupAsync(key, groupName, consumerName, position, count, noAck, flags);
+
+ public Task StreamReadGroupAsync(RedisKey key, RedisValue groupName, RedisValue consumerName, RedisValue? position = null, int? count = null, bool noAck = false, TimeSpan? blockingTimeout = null, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamReadGroupAsync(key, groupName, consumerName, position, count, noAck, blockingTimeout, flags);
+
+ public Task StreamReadGroupAsync(StreamPosition[] streamPositions, RedisValue groupName, RedisValue consumerName, int? countPerStream, CommandFlags flags)
+ => GetActiveDatabase().StreamReadGroupAsync(streamPositions, groupName, consumerName, countPerStream, flags);
+
+ public Task StreamReadGroupAsync(StreamPosition[] streamPositions, RedisValue groupName, RedisValue consumerName, int? countPerStream = null, bool noAck = false, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamReadGroupAsync(streamPositions, groupName, consumerName, countPerStream, noAck, flags);
+
+ public Task StreamReadGroupAsync(StreamPosition[] streamPositions, RedisValue groupName, RedisValue consumerName, int? countPerStream = null, bool noAck = false, TimeSpan? blockingTimeout = null, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamReadGroupAsync(streamPositions, groupName, consumerName, countPerStream, noAck, blockingTimeout, flags);
+
+ public Task StreamTrimAsync(RedisKey key, int maxLength, bool useApproximateMaxLength = false, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamTrimAsync(key, maxLength, useApproximateMaxLength, flags);
+
+ public Task StreamTrimAsync(RedisKey key, long maxLength, bool useApproximateMaxLength = false, long? limit = null, StreamTrimMode trimMode = StreamTrimMode.KeepReferences, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamTrimAsync(key, maxLength, useApproximateMaxLength, limit, trimMode, flags);
+
+ public Task StreamTrimByMinIdAsync(RedisKey key, RedisValue minId, bool useApproximateMaxLength = false, long? limit = null, StreamTrimMode trimMode = StreamTrimMode.KeepReferences, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamTrimByMinIdAsync(key, minId, useApproximateMaxLength, limit, trimMode, flags);
+
+ public Task StreamAcknowledgeAsync(RedisKey key, RedisValue groupName, RedisValue messageId, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamAcknowledgeAsync(key, groupName, messageId, flags);
+
+ public Task StreamAcknowledgeAsync(RedisKey key, RedisValue groupName, RedisValue[] messageIds, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamAcknowledgeAsync(key, groupName, messageIds, flags);
+
+ public Task StreamAcknowledgeAndDeleteAsync(RedisKey key, RedisValue groupName, StreamTrimMode mode, RedisValue messageId, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamAcknowledgeAndDeleteAsync(key, groupName, mode, messageId, flags);
+
+ public Task StreamAcknowledgeAndDeleteAsync(RedisKey key, RedisValue groupName, StreamTrimMode mode, RedisValue[] messageIds, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamAcknowledgeAndDeleteAsync(key, groupName, mode, messageIds, flags);
+}
diff --git a/src/StackExchange.Redis/MultiGroupDatabase.Streams.cs b/src/StackExchange.Redis/MultiGroupDatabase.Streams.cs
new file mode 100644
index 000000000..e29a59cf9
--- /dev/null
+++ b/src/StackExchange.Redis/MultiGroupDatabase.Streams.cs
@@ -0,0 +1,130 @@
+using System;
+
+namespace StackExchange.Redis;
+
+internal sealed partial class MultiGroupDatabase
+{
+ // Stream operations
+ public StreamAutoClaimIdsOnlyResult StreamAutoClaimIdsOnly(RedisKey key, RedisValue consumerGroup, RedisValue claimingConsumer, long minIdleTimeInMs, RedisValue startAtId, int? count = null, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamAutoClaimIdsOnly(key, consumerGroup, claimingConsumer, minIdleTimeInMs, startAtId, count, flags);
+
+ public StreamAutoClaimResult StreamAutoClaim(RedisKey key, RedisValue consumerGroup, RedisValue claimingConsumer, long minIdleTimeInMs, RedisValue startAtId, int? count = null, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamAutoClaim(key, consumerGroup, claimingConsumer, minIdleTimeInMs, startAtId, count, flags);
+
+ public RedisValue StreamAdd(RedisKey key, RedisValue streamField, RedisValue streamValue, RedisValue? messageId = null, int? maxLength = null, bool useApproximateMaxLength = false, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamAdd(key, streamField, streamValue, messageId, maxLength, useApproximateMaxLength, flags);
+
+ public RedisValue StreamAdd(RedisKey key, RedisValue streamField, RedisValue streamValue, RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StreamTrimMode trimMode = StreamTrimMode.KeepReferences, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamAdd(key, streamField, streamValue, messageId, maxLength, useApproximateMaxLength, limit, trimMode, flags);
+
+ public RedisValue StreamAdd(RedisKey key, NameValueEntry[] streamPairs, RedisValue? messageId = null, int? maxLength = null, bool useApproximateMaxLength = false, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamAdd(key, streamPairs, messageId, maxLength, useApproximateMaxLength, flags);
+
+ public RedisValue StreamAdd(RedisKey key, NameValueEntry[] streamPairs, RedisValue? messageId = null, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StreamTrimMode trimMode = StreamTrimMode.KeepReferences, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamAdd(key, streamPairs, messageId, maxLength, useApproximateMaxLength, limit, trimMode, flags);
+
+ public RedisValue StreamAdd(RedisKey key, RedisValue streamField, RedisValue streamValue, StreamIdempotentId idempotentId, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StreamTrimMode trimMode = StreamTrimMode.KeepReferences, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamAdd(key, streamField, streamValue, idempotentId, maxLength, useApproximateMaxLength, limit, trimMode, flags);
+
+ public RedisValue StreamAdd(RedisKey key, NameValueEntry[] streamPairs, StreamIdempotentId idempotentId, long? maxLength = null, bool useApproximateMaxLength = false, long? limit = null, StreamTrimMode trimMode = StreamTrimMode.KeepReferences, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamAdd(key, streamPairs, idempotentId, maxLength, useApproximateMaxLength, limit, trimMode, flags);
+
+ public StreamEntry[] StreamClaim(RedisKey key, RedisValue consumerGroup, RedisValue claimingConsumer, long minIdleTimeInMs, RedisValue[] messageIds, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamClaim(key, consumerGroup, claimingConsumer, minIdleTimeInMs, messageIds, flags);
+
+ public RedisValue[] StreamClaimIdsOnly(RedisKey key, RedisValue consumerGroup, RedisValue claimingConsumer, long minIdleTimeInMs, RedisValue[] messageIds, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamClaimIdsOnly(key, consumerGroup, claimingConsumer, minIdleTimeInMs, messageIds, flags);
+
+ public void StreamConfigure(RedisKey key, StreamConfiguration configuration, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamConfigure(key, configuration, flags);
+
+ public bool StreamConsumerGroupSetPosition(RedisKey key, RedisValue groupName, RedisValue position, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamConsumerGroupSetPosition(key, groupName, position, flags);
+
+ public StreamConsumerInfo[] StreamConsumerInfo(RedisKey key, RedisValue groupName, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamConsumerInfo(key, groupName, flags);
+
+ public bool StreamCreateConsumerGroup(RedisKey key, RedisValue groupName, RedisValue? position, CommandFlags flags)
+ => GetActiveDatabase().StreamCreateConsumerGroup(key, groupName, position, flags);
+
+ public bool StreamCreateConsumerGroup(RedisKey key, RedisValue groupName, RedisValue? position = null, bool createStream = true, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamCreateConsumerGroup(key, groupName, position, createStream, flags);
+
+ public long StreamDelete(RedisKey key, RedisValue[] messageIds, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamDelete(key, messageIds, flags);
+
+ public long StreamDeleteConsumer(RedisKey key, RedisValue groupName, RedisValue consumerName, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamDeleteConsumer(key, groupName, consumerName, flags);
+
+ public bool StreamDeleteConsumerGroup(RedisKey key, RedisValue groupName, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamDeleteConsumerGroup(key, groupName, flags);
+
+ public StreamGroupInfo[] StreamGroupInfo(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamGroupInfo(key, flags);
+
+ public StreamInfo StreamInfo(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamInfo(key, flags);
+
+ public long StreamLength(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamLength(key, flags);
+
+ public StreamPendingInfo StreamPending(RedisKey key, RedisValue groupName, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamPending(key, groupName, flags);
+
+ public StreamPendingMessageInfo[] StreamPendingMessages(RedisKey key, RedisValue groupName, int count, RedisValue consumerName, RedisValue? minId = null, RedisValue? maxId = null, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamPendingMessages(key, groupName, count, consumerName, minId, maxId, flags);
+
+ public StreamEntry[] StreamRange(RedisKey key, RedisValue? minId = null, RedisValue? maxId = null, int? count = null, Order messageOrder = Order.Ascending, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamRange(key, minId, maxId, count, messageOrder, flags);
+
+ public StreamEntry[] StreamRead(RedisKey key, RedisValue position, int? count = null, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamRead(key, position, count, flags);
+
+ public RedisStream[] StreamRead(StreamPosition[] streamPositions, int? countPerStream = null, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamRead(streamPositions, countPerStream, flags);
+
+ public StreamEntry[] StreamReadGroup(RedisKey key, RedisValue groupName, RedisValue consumerName, RedisValue? position, int? count, CommandFlags flags)
+ => GetActiveDatabase().StreamReadGroup(key, groupName, consumerName, position, count, flags);
+
+ public StreamEntry[] StreamReadGroup(RedisKey key, RedisValue groupName, RedisValue consumerName, RedisValue? position = null, int? count = null, bool noAck = false, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamReadGroup(key, groupName, consumerName, position, count, noAck, flags);
+
+ public RedisStream[] StreamReadGroup(StreamPosition[] streamPositions, RedisValue groupName, RedisValue consumerName, int? countPerStream, CommandFlags flags)
+ => GetActiveDatabase().StreamReadGroup(streamPositions, groupName, consumerName, countPerStream, flags);
+
+ public RedisStream[] StreamReadGroup(StreamPosition[] streamPositions, RedisValue groupName, RedisValue consumerName, int? countPerStream = null, bool noAck = false, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamReadGroup(streamPositions, groupName, consumerName, countPerStream, noAck, flags);
+
+ public long StreamTrim(RedisKey key, int maxLength, bool useApproximateMaxLength = false, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamTrim(key, maxLength, useApproximateMaxLength, flags);
+
+ public long StreamAcknowledge(RedisKey key, RedisValue groupName, RedisValue messageId, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamAcknowledge(key, groupName, messageId, flags);
+
+ public long StreamAcknowledge(RedisKey key, RedisValue groupName, RedisValue[] messageIds, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamAcknowledge(key, groupName, messageIds, flags);
+
+ public StreamTrimResult StreamAcknowledgeAndDelete(RedisKey key, RedisValue groupName, StreamTrimMode mode, RedisValue messageId, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamAcknowledgeAndDelete(key, groupName, mode, messageId, flags);
+
+ public StreamTrimResult[] StreamAcknowledgeAndDelete(RedisKey key, RedisValue groupName, StreamTrimMode mode, RedisValue[] messageIds, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamAcknowledgeAndDelete(key, groupName, mode, messageIds, flags);
+
+ public StreamTrimResult[] StreamDelete(RedisKey key, RedisValue[] messageIds, StreamTrimMode mode, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamDelete(key, messageIds, mode, flags);
+
+ public StreamPendingMessageInfo[] StreamPendingMessages(RedisKey key, RedisValue groupName, int count, RedisValue consumerName, RedisValue? minId = null, RedisValue? maxId = null, long? idle = null, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamPendingMessages(key, groupName, count, consumerName, minId, maxId, idle, flags);
+
+ public StreamEntry[] StreamReadGroup(RedisKey key, RedisValue groupName, RedisValue consumerName, RedisValue? position = null, int? count = null, bool noAck = false, TimeSpan? blockingTimeout = null, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamReadGroup(key, groupName, consumerName, position, count, noAck, blockingTimeout, flags);
+
+ public RedisStream[] StreamReadGroup(StreamPosition[] streamPositions, RedisValue groupName, RedisValue consumerName, int? countPerStream = null, bool noAck = false, TimeSpan? blockingTimeout = null, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamReadGroup(streamPositions, groupName, consumerName, countPerStream, noAck, blockingTimeout, flags);
+
+ public long StreamTrim(RedisKey key, long maxLength, bool useApproximateMaxLength = false, long? limit = null, StreamTrimMode trimMode = StreamTrimMode.KeepReferences, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamTrim(key, maxLength, useApproximateMaxLength, limit, trimMode, flags);
+
+ public long StreamTrimByMinId(RedisKey key, RedisValue minId, bool useApproximateMaxLength = false, long? limit = null, StreamTrimMode trimMode = StreamTrimMode.KeepReferences, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StreamTrimByMinId(key, minId, useApproximateMaxLength, limit, trimMode, flags);
+}
diff --git a/src/StackExchange.Redis/MultiGroupDatabase.Strings.Async.cs b/src/StackExchange.Redis/MultiGroupDatabase.Strings.Async.cs
new file mode 100644
index 000000000..9dba49a82
--- /dev/null
+++ b/src/StackExchange.Redis/MultiGroupDatabase.Strings.Async.cs
@@ -0,0 +1,119 @@
+using System;
+using System.Threading.Tasks;
+
+namespace StackExchange.Redis;
+
+internal sealed partial class MultiGroupDatabase
+{
+ // String Async operations
+ public Task StringAppendAsync(RedisKey key, RedisValue value, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StringAppendAsync(key, value, flags);
+
+ public Task StringBitCountAsync(RedisKey key, long start, long end, CommandFlags flags)
+ => GetActiveDatabase().StringBitCountAsync(key, start, end, flags);
+
+ public Task StringBitCountAsync(RedisKey key, long start = 0, long end = -1, StringIndexType indexType = StringIndexType.Byte, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StringBitCountAsync(key, start, end, indexType, flags);
+
+ public Task StringBitOperationAsync(Bitwise operation, RedisKey destination, RedisKey first, RedisKey second = default, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StringBitOperationAsync(operation, destination, first, second, flags);
+
+ public Task StringBitOperationAsync(Bitwise operation, RedisKey destination, RedisKey[] keys, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StringBitOperationAsync(operation, destination, keys, flags);
+
+ public Task StringBitPositionAsync(RedisKey key, bool bit, long start, long end, CommandFlags flags)
+ => GetActiveDatabase().StringBitPositionAsync(key, bit, start, end, flags);
+
+ public Task StringBitPositionAsync(RedisKey key, bool bit, long start = 0, long end = -1, StringIndexType indexType = StringIndexType.Byte, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StringBitPositionAsync(key, bit, start, end, indexType, flags);
+
+ public Task StringDecrementAsync(RedisKey key, long value = 1, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StringDecrementAsync(key, value, flags);
+
+ public Task StringDecrementAsync(RedisKey key, double value, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StringDecrementAsync(key, value, flags);
+
+ public Task StringDeleteAsync(RedisKey key, ValueCondition when, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StringDeleteAsync(key, when, flags);
+
+ public Task StringDigestAsync(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StringDigestAsync(key, flags);
+
+ public Task StringGetAsync(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StringGetAsync(key, flags);
+
+ public Task StringGetAsync(RedisKey[] keys, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StringGetAsync(keys, flags);
+
+ public Task?> StringGetLeaseAsync(RedisKey key, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StringGetLeaseAsync(key, flags);
+
+ public Task StringGetBitAsync(RedisKey key, long offset, CommandFlags flags = CommandFlags.None)
+ => GetActiveDatabase().StringGetBitAsync(key, offset, flags);
+
+ public Task