### Asynchronous String Get and Set with Multiplexing Source: https://stackexchange.github.io/StackExchange.Redis/PipelinesMultiplexers This example shows how to asynchronously retrieve a string value and, if it's null, compute and set it, leveraging the multiplexing capabilities of StackExchange.Redis for efficient concurrent access. ```csharp string value = await db.StringGetAsync(key); if (value == null) { value = await ComputeValueFromDatabase(...); db.StringSet(key, value, flags: CommandFlags.FireAndForget); } return value; ``` -------------------------------- ### Install StackExchange.Redis via NuGet Package Manager Console Source: https://stackexchange.github.io/StackExchange.Redis Use this command to install the StackExchange.Redis package using the nuget package manager console. ```powershell PM> Install-Package StackExchange.Redis ``` -------------------------------- ### Load Test Example Source: https://stackexchange.github.io/StackExchange.Redis/RespLogging A C# example demonstrating a load test scenario using StackExchange.Redis, involving writing and reading from a Redis set. This code is used to illustrate potential RESP stream issues. ```csharp // connect Console.WriteLine("Connecting..."); var options = ConfigurationOptions.Parse(ConnectionString); await using var muxer = await ConnectionMultiplexer.ConnectAsync(options); var db = muxer.GetDatabase(); // load RedisKey testKey = "marc_abc"; await db.KeyDeleteAsync(testKey); Console.WriteLine("Writing..."); for (int i = 0; i < 100; i++) { // sync every 50 iterations (pipeline the rest) var flags = (i % 50) == 0 ? CommandFlags.None : CommandFlags.FireAndForget; await db.SetAddAsync(testKey, Guid.NewGuid().ToString(), flags); } // fetch Console.WriteLine("Reading..."); int count = 0; for (int i = 0; i < 10; i++) { // this is deliberately not using SCARD // (to put load on the inbound) count += (await db.SetMembersAsync(testKey)).Length; } Console.WriteLine("all done"); ``` -------------------------------- ### Start, Capture, and Get Hot Keys Data Source: https://stackexchange.github.io/StackExchange.Redis/HotKeys This snippet demonstrates how to start a hot keys capture with a specified duration, perform some work, fetch the results, and optionally reset the capture. It requires connecting to Redis 8.6 or later. ```csharp // Get the server instance. IConnectionMultiplexer muxer = ... // connect to Redis 8.6 or later var server = muxer.GetServer(endpoint); // or muxer.GetServer(key) // Start the capture; you can specify a duration, or manually use the HotKeysStop[Async] method; specifying // a duration is recommended, so that the profiler will not be left running in the case of failure. // Optional parameters allow you to specify the metrics to capture, the sample ratio, and the key slots to include; // by default, all metrics are captured, every command is sampled, and all key slots are included. await server.HotKeysStartAsync(duration: TimeSpan.FromSeconds(30)); // Now either do some work ourselves, or await for some other activity to happen: await Task.Delay(TimeSpan.FromSeconds(35)); // whatever happens: happens // Fetch the results; note that this does not stop the capture, and you can fetch the results multiple times // either while it is running, or after it has completed - but only a single capture can be active at a time. var result = await server.HotKeysGetAsync(); // ...investigate the results... // Optional: discard the active capture data at the server, if any. await server.HotKeysResetAsync(); ``` -------------------------------- ### Create Consumer Group from Beginning Source: https://stackexchange.github.io/StackExchange.Redis/Streams Creates a consumer group and starts reading from the very first message in the stream. ```csharp // Begin reading from the first position in the stream. db.StreamCreateConsumerGroup("events_stream", "events_consumer_group", "0-0"); ``` -------------------------------- ### Subscribe to Sub-Key Changes for Hashes with Prefix Source: https://stackexchange.github.io/StackExchange.Redis/KeyspaceNotifications Subscribe to all sub-key changes for hashes with a specific prefix. This example demonstrates parsing the notification and iterating through affected fields. It also shows how to get the first field or use utility methods for collection conversion and span copying. ```csharp var channel = RedisChannel.SubKeySpacePrefix("user:", database: 0); sub.Subscribe(channel, (recvChannel, recvValue) => { if (KeyNotification.TryParse(recvChannel, recvValue, out var notification)) { Console.WriteLine($"Hash Key: {notification.GetKey()}"); Console.WriteLine($"Operation: {notification.Type}"); Console.WriteLine($"Kind: {notification.Kind}"); // Process all affected fields foreach (var field in notification.GetSubKeys()) { Console.WriteLine($"Field: {field}"); } // Or get just the first field for single-field operations var firstField = notification.GetSubKeys().FirstOrDefault(); // Utility methods available: // - Count() - get the number of fields // - First() / FirstOrDefault() - get the first field // - Single() / SingleOrDefault() - get the only field (throws if multiple) // - ToArray() / ToList() - convert to collection // - CopyTo(Span) - copy to a span (allocation-free) } }); ``` -------------------------------- ### Equivalent Connection String for Manual Configuration Source: https://stackexchange.github.io/StackExchange.Redis/Configuration This connection string format is equivalent to the C# ConfigurationOptions example, demonstrating how to specify endpoints, disabled commands, keep-alive, and version. ```config redis0:6379,redis1:6380,keepAlive=180,version=2.8.8,$CLIENT=,$CLUSTER=,$CONFIG=,$ECHO=,$INFO=,$PING= ``` -------------------------------- ### Pipelined Redis Gets with Task API Source: https://stackexchange.github.io/StackExchange.Redis/PipelinesMultiplexers This example shows how to pipeline two Redis StringGet operations using the Task API for asynchronous operations. It initiates both requests immediately and then waits for their results, significantly reducing latency compared to sequential calls. ```csharp var aPending = db.StringGetAsync("a"); var bPending = db.StringGetAsync("b"); var a = db.Wait(aPending); var b = db.Wait(bPending); ``` -------------------------------- ### Redis Transaction with WATCH and MULTI/EXEC Source: https://stackexchange.github.io/StackExchange.Redis/Transactions.html This example demonstrates a conceptual Redis transaction using WATCH, MULTI, and EXEC. It shows how to watch a key, check its existence, and conditionally update it within a transaction. ```redis WATCH {custKey} HEXISTS {custKey} "UniqueId" -- (check the reply, then either:) MULTI HSET {custKey} "UniqueId" {newId} EXEC -- (or, if we find there was already an unique-id:) UNWATCH ``` -------------------------------- ### Subscribing to Tenant-Specific Keyspace Notifications with Prefix Parsing Source: https://stackexchange.github.io/StackExchange.Redis/KeyspaceNotifications This example shows how to subscribe to keyspace notifications for a specific tenant using a prefixed channel. It utilizes `KeyNotification.TryParse` with the key prefix to filter notifications and automatically strip the prefix from the key. ```csharp var sub = conn.GetSubscriber(); // subscribe to the specific tenant as a prefix: var channel = RedisChannel.KeySpacePrefix("client1234:order/", db.Database); sub.SubscribeAsync(channel, (recvChannel, recvValue) => { // by including prefix in the TryParse, we filter out notifications that are not for this client // *and* the key is sliced internally to remove this prefix when reading if (KeyNotification.TryParse(keyPrefix, recvChannel, recvValue, out var notification)) { // if we get here, the key prefix was a match var key = notification.GetKey(); // "order/123" - note no prefix // ... } /* // for contrast only: this is *not* usually the recommended approach when using keyspace isolation if (KeyNotification.TryParse(recvChannel, recvValue, out var notification) && notification.KeyStartsWith(keyPrefix)) { var key = notification.GetKey(); // "client1234:order/123" - note prefix is included // ... } */ }); ``` -------------------------------- ### Setting and Getting Typed Primitive Values Source: https://stackexchange.github.io/StackExchange.Redis/KeysValues.html Demonstrates setting an integer value and retrieving it, showing the conversion from RedisValue to int. Explicit conversion is often required for safety. ```csharp db.StringSet("mykey", 123); // this is still a RedisKey and RedisValue ... int i = (int)db.StringGet("mykey"); ``` -------------------------------- ### Example TestConfig.json Override Source: https://stackexchange.github.io/StackExchange.Redis/Testing Use this JSON file to override default test server configurations, such as IP addresses and ports. If a server is not specified, related tests will be skipped. ```json { "RunLongRunning": true, "PrimaryServer": "192.168.0.42", "PrimaryPort": 12345 } ``` -------------------------------- ### Connect to Multiple Redis Servers Source: https://stackexchange.github.io/StackExchange.Redis/Basics Connects to a primary/replica Redis setup by specifying multiple server nodes. The library automatically identifies the primary server. ```csharp ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("server1:6379,server2:6379"); ``` -------------------------------- ### Basic Sequential Redis Gets Source: https://stackexchange.github.io/StackExchange.Redis/PipelinesMultiplexers This code demonstrates a basic, sequential retrieval of two string values from Redis. Each operation involves a separate network round trip, highlighting potential latency. ```csharp string a = db.StringGet("a"); string b = db.StringGet("b"); ``` -------------------------------- ### Accessing Server and Listing Keys Source: https://stackexchange.github.io/StackExchange.Redis/KeysScan Get a specific server from the connection and list all keys in database 0 that match a pattern. The `Keys` method handles `KEYS` vs `SCAN` internally and returns an `IEnumerable`. ```csharp // get the target server var server = conn.GetServer(someServer); // show all keys in database 0 that include "foo" in their name foreach(var key in server.Keys(pattern: "*foo*")) { Console.WriteLine(key); } ``` -------------------------------- ### Get Redis Database Instance Source: https://stackexchange.github.io/StackExchange.Redis/Basics Retrieves an IDatabase object for interacting with a Redis database. This object is a cheap pass-thru and does not need to be stored. ```csharp IDatabase db = redis.GetDatabase(); ``` -------------------------------- ### Getting a Random Key Source: https://stackexchange.github.io/StackExchange.Redis/KeysValues.html Retrieves a random key from the database. The returned RedisKey implicitly converts to string. ```csharp string someKey = db.KeyRandom(); ``` -------------------------------- ### Redis HashSet with When.NotExists Source: https://stackexchange.github.io/StackExchange.Redis/Transactions.html This C# example utilizes the When parameter with HashSet to perform an atomic HSETNX operation. It sets a hash field only if it does not already exist. ```csharp var newId = CreateNewId(); bool wasSet = db.HashSet(custKey, "UniqueID", newId, When.NotExists); ``` -------------------------------- ### Set and Get String Value Source: https://stackexchange.github.io/StackExchange.Redis/Basics Stores a string value in Redis using StringSet and retrieves it using StringGet. Demonstrates basic string operations. ```csharp string value = "abcdefg"; db.StringSet("mykey", value); ... string value = db.StringGet("mykey"); Console.WriteLine(value); // writes: "abcdefg" ``` -------------------------------- ### StackExchange.Redis Transaction with Constraints Source: https://stackexchange.github.io/StackExchange.Redis/Transactions.html This C# example shows how to use StackExchange.Redis transactions with constraints. It demonstrates adding a condition to ensure a hash field does not exist before executing a transaction. ```csharp var newId = CreateNewId(); var tran = db.CreateTransaction(); tran.AddCondition(Condition.HashNotExists(custKey, "UniqueID")); tran.HashSetAsync(custKey, "UniqueID", newId); bool committed = tran.Execute(); // ^^^ if true: it was applied; if false: it was rolled back ``` -------------------------------- ### Get a Specific Redis Server Instance Source: https://stackexchange.github.io/StackExchange.Redis/Basics Obtain an IServer instance for a specific Redis server using its hostname and port. This allows for issuing server-specific commands. ```csharp IServer server = redis.GetServer("localhost", 6379); ``` -------------------------------- ### StackExchange.Redis 1.* Timeout Exception with ThreadPool Stats Source: https://stackexchange.github.io/StackExchange.Redis/Timeouts.html This example displays the ThreadPool statistics found in a timeout exception message for StackExchange.Redis version 1.*. The format differs slightly from version 2.0+ but provides similar diagnostic information. ```text System.TimeoutException: Timeout performing GET MyKey, inst: 2, mgr: Inactive, queue: 6, qu: 0, qs: 6, qc: 0, wr: 0, wq: 0, in: 0, ar: 0, IOCP: (Busy=6,Free=994,Min=4,Max=1000), WORKER: (Busy=3,Free=997,Min=4,Max=1000) ``` -------------------------------- ### Semantic Search: Store and Retrieve Document Embeddings Source: https://stackexchange.github.io/StackExchange.Redis/VectorSets Example of storing document embeddings with associated metadata and then performing a similarity search for related documents. ```csharp // 1. Store document embeddings var embedding = await GetEmbeddingFromMLModel(document); var request = VectorSetAddRequest.Member( documentId, embedding.AsMemory(), attributesJson: $$"{"title":"{{document.Title}}","date":"{{document.Date}}"}"" ); await db.VectorSetAddAsync("documents", request); // 2. Search for similar documents var queryEmbedding = await GetEmbeddingFromMLModel(searchQuery); var query = VectorSetSimilaritySearchRequest.ByVector(queryEmbedding.AsMemory()); query.Count = 10; query.WithScores = true; query.WithAttributes = true; using var results = await db.VectorSetSimilaritySearchAsync("documents", query); ``` -------------------------------- ### Asynchronous String Set and Get Operations Source: https://stackexchange.github.io/StackExchange.Redis/Basics Perform asynchronous operations to set and retrieve string values from Redis. The `await` keyword is used to handle the completion of these asynchronous tasks. ```csharp string value = "abcdefg"; await db.StringSetAsync("mykey", value); ``` ```csharp string value = await db.StringGetAsync("mykey"); Console.WriteLine(value); // writes: "abcdefg" ``` -------------------------------- ### Load and Evaluate Lua Script with LoadedLuaScript Source: https://stackexchange.github.io/StackExchange.Redis/Scripting Load a Lua script onto the Redis server using LuaScript.Load to get a LoadedLuaScript object. This object uses EVALSHA for subsequent evaluations, improving efficiency. ```csharp const string Script = "redis.call('set', @key, @value)"; using (ConnectionMultiplexer conn = /* init code */) { var db = conn.GetDatabase(0); var server = conn.GetServer(/* appropriate parameters*/); var prepared = LuaScript.Prepare(Script); var loaded = prepared.Load(server); loaded.Evaluate(db, new { key = (RedisKey)"mykey", value = 123 }); } ``` -------------------------------- ### Setting Up Keyspace Isolation in a Multi-Tenant Scenario Source: https://stackexchange.github.io/StackExchange.Redis/KeyspaceNotifications This snippet demonstrates how to apply a key prefix to a database instance for a specific tenant in a multi-tenant scenario. This is useful when you need to isolate data for different clients within the same Redis instance. ```csharp byte[] keyPrefix = Encoding.UTF8.GetBytes("client1234:"); var db = conn.GetDatabase().WithKeyPrefix(keyPrefix); // we will later commit order data for example: await db.StringSetAsync("order/123", "ISBN 9789123684434"); ``` -------------------------------- ### Apply Runtime Settings to Configuration Source: https://stackexchange.github.io/StackExchange.Redis/Configuration Parses a base configuration string and applies runtime-specific settings like ClientName and AllowAdmin before connecting. ```csharp string configString = GetRedisConfiguration(); var options = ConfigurationOptions.Parse(configString); options.ClientName = GetAppName(); // only known at runtime options.AllowAdmin = true; conn = ConnectionMultiplexer.Connect(options); ``` -------------------------------- ### Get Array Length and Count Source: https://stackexchange.github.io/StackExchange.Redis/Arrays Use ArrayLengthAsync to get the notional length (highest index + 1) and ArrayCountAsync to get the number of populated cells. ```csharp await db.KeyDeleteAsync(key); await db.ArraySetAsync(key, 0, "a"); await db.ArraySetAsync(key, 10, "b"); RedisArrayIndex length = await db.ArrayLengthAsync(key); // 11 RedisArrayIndex count = await db.ArrayCountAsync(key); // 2 ``` -------------------------------- ### Configure Client Certificate with PFX Format Source: https://stackexchange.github.io/StackExchange.Redis/Authentication Supply a client certificate in PFX format for servers requiring client certificate authentication. Use the SetUserPfxCertificate method, optionally providing a password. ```csharp options.SetUserPfxCertificate( userCertificatePath: userCrtPath, password: filePassword // optional ); ``` -------------------------------- ### Memory Management with Leases Source: https://stackexchange.github.io/StackExchange.Redis/VectorSets Demonstrates proper memory management for vector operations using `Lease` by employing `using` statements or explicit `Dispose()` calls. ```csharp // Using statement (recommended) using var results = await db.VectorSetSimilaritySearchAsync(key, query); // Or explicit disposal var results = await db.VectorSetSimilaritySearchAsync(key, query); try { // Use results } finally { results?.Dispose(); } ``` -------------------------------- ### Read All Messages from Stream Source: https://stackexchange.github.io/StackExchange.Redis/Streams Reads all messages from a specified stream starting from ID "0-0". ```csharp var messages = db.StreamRead("events_stream", "0-0"); ``` -------------------------------- ### Subscribe to a Pub/Sub Channel (Sequential Processing) Source: https://stackexchange.github.io/StackExchange.Redis/PubSubOrder This snippet demonstrates how to subscribe to a channel and process messages sequentially. Messages are processed in the order they are received, ensuring thread-safety but potentially causing delays. ```csharp var channel = multiplexer.GetSubscriber().Subscribe("messages"); channel.OnMessage(message => { Console.WriteLine((string)message.Message); }); ``` -------------------------------- ### Get Random Vector Members Source: https://stackexchange.github.io/StackExchange.Redis/VectorSets Retrieves one or more random members from a vector set. Useful for sampling or testing. ```csharp // Get a single random member var member = await db.VectorSetRandomMemberAsync(key); // Get multiple random members var members = await db.VectorSetRandomMembersAsync(key, count: 5); ``` -------------------------------- ### Manual Configuration with ConfigurationOptions Source: https://stackexchange.github.io/StackExchange.Redis/Configuration Use ConfigurationOptions to manually set endpoints, command maps, keep-alive, default version, and password. This provides fine-grained control over connection settings. ```csharp ConfigurationOptions config = new ConfigurationOptions { EndPoints = { { "redis0", 6379 }, { "redis1", 6380 } }, CommandMap = CommandMap.Create(new HashSet { "INFO", "CONFIG", "CLUSTER", "PING", "ECHO", "CLIENT" }, available: false), KeepAlive = 180, DefaultVersion = new Version(2, 8, 8), Password = "changeme" }; ``` -------------------------------- ### Get Stream Information Source: https://stackexchange.github.io/StackExchange.Redis/Streams Retrieves information about a stream, including its length, first/last entries, and consumer group count. ```csharp var info = db.StreamInfo("events_stream"); Console.WriteLine(info.Length); Console.WriteLine(info.FirstEntry.Id); Console.WriteLine(info.LastEntry.Id); ``` -------------------------------- ### Get Pending Message Information Source: https://stackexchange.github.io/StackExchange.Redis/Streams Retrieves high-level information about pending messages in a consumer group, including counts and message IDs. ```csharp var pendingInfo = db.StreamPending("events_stream", "events_cg"); Console.WriteLine(pendingInfo.PendingMessageCount); Console.WriteLine(pendingInfo.LowestPendingMessageId); Console.WriteLine(pendingInfo.HighestPendingMessageId); Console.WriteLine($"Consumer count: {pendingInfo.Consumers.Length}."); Console.WriteLine(pendingInfo.Consumers.First().Name); Console.WriteLine(pendingInfo.Consumers.First().PendingMessageCount); ``` -------------------------------- ### Connect with Configuration Object Source: https://stackexchange.github.io/StackExchange.Redis/Configuration Connect to Redis using a ConfigurationOptions instance. This is useful when applying runtime-specific settings. ```csharp var conn = ConnectionMultiplexer.Connect(configuration); ``` -------------------------------- ### Retrieve Approximate Vector for a Member Source: https://stackexchange.github.io/StackExchange.Redis/VectorSets Get the approximate vector data for a specific member from a vector set. Ensure to dispose of the returned lease. ```csharp using var vectorLease = await db.VectorSetGetApproximateVectorAsync(key, "product-123"); if (vectorLease != null) { ReadOnlySpan vector = vectorLease.Value.Span; // Use the vector data } ``` -------------------------------- ### Get Redis Subscriber Instance Source: https://stackexchange.github.io/StackExchange.Redis/Basics Retrieves an ISubscriber object for interacting with Redis pub/sub features. This object is a cheap pass-thru and does not need to be stored. ```csharp ISubscriber sub = redis.GetSubscriber(); ``` -------------------------------- ### Get Vector Set Information Source: https://stackexchange.github.io/StackExchange.Redis/VectorSets Retrieves metadata about a vector set, such as its dimension, length, and quantization status. This is useful for understanding the set's properties. ```csharp var info = await db.VectorSetInfoAsync(key); if (info is not null) { Console.WriteLine($"Dimension: {info.Value.Dimension}"); Console.WriteLine($"Length: {info.Value.Length}"); Console.WriteLine($"Quantization: {info.Value.Quantization}"); } ``` -------------------------------- ### Get All Redis Server EndPoints Source: https://stackexchange.github.io/StackExchange.Redis/Basics Retrieve an array of all EndPoint objects representing the available Redis servers in the connection. This is useful for discovering server addresses. ```csharp EndPoint[] endpoints = redis.GetEndPoints(); ``` -------------------------------- ### Configure Client Certificate with PEM Format Source: https://stackexchange.github.io/StackExchange.Redis/Authentication Supply a client certificate in PEM format (separate .crt and .key files) for servers requiring client certificate authentication. Use the SetUserPemCertificate method. ```csharp options.SetUserPemCertificate( userCertificatePath: userCrtPath, userKeyPath: userKeyPath ); ``` -------------------------------- ### Get Specific Redis Database with AsyncState Source: https://stackexchange.github.io/StackExchange.Redis/Basics Retrieves an IDatabase object for a specific database number, optionally providing an async-state object for asynchronous operations. ```csharp int databaseNumber = ...; object asyncState = ...; IDatabase db = redis.GetDatabase(databaseNumber, asyncState); ``` -------------------------------- ### Subscribing to All Tenants' Keyspace Notifications Source: https://stackexchange.github.io/StackExchange.Redis/KeyspaceNotifications This snippet demonstrates how to subscribe to keyspace notifications for all tenants using a wildcard pattern. This approach requires manual parsing of the client identifier from the full key. ```csharp var channel = RedisChannel.KeySpacePattern("client*:order/*", db.Database); // with similar code, parsing the client from the key manually, using the full key length. ``` -------------------------------- ### Build and Run Tests on Windows Source: https://stackexchange.github.io/StackExchange.Redis/Testing Execute the build script on Windows to run tests. The '-BuildNumber local' argument is used for local builds. ```cmd .\build.cmd -BuildNumber local ``` -------------------------------- ### Read Messages from Multiple Streams Source: https://stackexchange.github.io/StackExchange.Redis/Streams Reads messages from multiple streams concurrently, starting from specified IDs. Supports limiting messages per stream. ```csharp var streams = db.StreamRead(new StreamPosition[] { new StreamPosition("events_stream", "0-0"), new StreamPosition("score_stream", "0-0") }); Console.WriteLine($"Stream = {streams.First().Key}"); Console.WriteLine($"Length = {streams.First().Entries.Length}"); ``` -------------------------------- ### Minimal Server ACL for StackExchange.Redis Source: https://stackexchange.github.io/StackExchange.Redis/Configuration A sample minimal Redis ACL configuration that grants necessary permissions for StackExchange.Redis to connect and operate. ```bash -@all +@pubsub +@read +echo +info ``` -------------------------------- ### Connect with Multiple Servers and Options Source: https://stackexchange.github.io/StackExchange.Redis/Configuration Connects to multiple Redis servers, specifying ports and enabling specific options like 'allowAdmin'. ```csharp var conn = ConnectionMultiplexer.Connect("redis0:6380,redis1:6380,allowAdmin=true"); ``` -------------------------------- ### Get Vector Members by Lexicographical Range Source: https://stackexchange.github.io/StackExchange.Redis/VectorSets Retrieves members from a vector set within a specified lexicographical range. Supports inclusive/exclusive boundaries and limiting the count. ```csharp // Get all members using var allMembers = await db.VectorSetRangeAsync(key); // ... access allMembers.Span, etc // Get members in a specific range using var rangeMembers = await db.VectorSetRangeAsync( key, start: "product-100", end: "product-200", count: 50 ); // ... access rangeMembers.Span, etc // Exclude boundaries using var members = await db.VectorSetRangeAsync( key, start: "product-100", end: "product-200", exclude: Exclude.Both ); // ... access members.Span, etc ``` -------------------------------- ### Connect to Localhost Source: https://stackexchange.github.io/StackExchange.Redis/Configuration Establishes a connection to a single Redis server on the local machine using the default port (6379). ```csharp var conn = ConnectionMultiplexer.Connect("localhost"); ``` -------------------------------- ### Set and Get Individual Array Cells Source: https://stackexchange.github.io/StackExchange.Redis/Arrays Use ArraySetAsync to write a value to a specific index and ArrayGetAsync to retrieve it. Returns true if the cell was newly filled. ```csharp var db = conn.GetDatabase(); RedisKey key = "events"; bool inserted = await db.ArraySetAsync(key, 0, "created"); RedisValue value = await db.ArrayGetAsync(key, 0); RedisValue missing = await db.ArrayGetAsync(key, 1); Console.WriteLine(inserted); // True when the cell did not previously have a value Console.WriteLine(value); // created Console.WriteLine(missing.IsNull); // True ``` -------------------------------- ### Prepare and Evaluate Lua Script with LuaScript Class Source: https://stackexchange.github.io/StackExchange.Redis/Scripting Use LuaScript.Prepare to create a script object that automatically rewrites @-prefixed variables for Redis. Parameters matching @-prefixed variables can be passed as an anonymous object. ```csharp const string Script = "redis.call('set', @key, @value)"; using (ConnectionMultiplexer conn = /* init code */) { var db = conn.GetDatabase(0); var prepared = LuaScript.Prepare(Script); db.ScriptEvaluate(prepared, new { key = (RedisKey)"mykey", value = 123 }); } ``` -------------------------------- ### Get Redis Array Metadata Source: https://stackexchange.github.io/StackExchange.Redis/Arrays Retrieves metadata about a Redis array, such as its count, length, and next insert index. Use `ArrayInfoAsync` for basic or full array information. ```csharp ArrayInfo info = await db.ArrayInfoAsync(key); ArrayInfo fullInfo = await db.ArrayInfoAsync(key, full: true); Console.WriteLine($"Count: {info.Count}"); Console.WriteLine($"Length: {info.Length}"); Console.WriteLine($"Next insert index: {info.NextInsertIndex}"); ``` -------------------------------- ### Cancel Keys Enumeration with CancellationToken Source: https://stackexchange.github.io/StackExchange.Redis/AsyncTimeouts Demonstrates how to cancel the enumeration of keys (using `SCAN`) from a Redis server by applying a `CancellationToken` via the `.WithCancellation(...)` method. ```csharp CancellationToken token = ...; // for example, from HttpContext.RequestAborted await foreach (var key in server.KeysAsync(pattern: "*foo*").WithCancellation(token)) { ... } ``` -------------------------------- ### Subscribe to a Pub/Sub Channel (Concurrent Processing) Source: https://stackexchange.github.io/StackExchange.Redis/PubSubOrder This snippet shows how to subscribe to a channel and process messages concurrently. This approach offers higher performance and scalability but does not guarantee message order and requires manual thread-safety management. ```csharp multiplexer.GetSubscriber().Subscribe("messages", (channel, message) => { Console.WriteLine((string)message); }); ``` -------------------------------- ### Connect to Local Redis Instance Source: https://stackexchange.github.io/StackExchange.Redis/Basics Connects to a Redis instance on the local machine using the default port. This ConnectionMultiplexer instance should be stored and reused. ```csharp using StackExchange.Redis; ... ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); // ^^^ store and re-use this!!! ``` -------------------------------- ### Enable Keyspace Notifications (Basic) Source: https://stackexchange.github.io/StackExchange.Redis/KeyspaceNotifications Configure your Redis server to enable basic keyspace notifications. This includes all event types (A), keyspace notifications (K), and keyevent notifications (E). ```conf notify-keyspace-events AKE ``` -------------------------------- ### Set Contiguous Range of Array Values Source: https://stackexchange.github.io/StackExchange.Redis/Arrays Efficiently writes a contiguous block of values to an array starting at a specified index. Returns the number of newly filled cells. ```csharp int inserted = await db.ArraySetAsync(key, 0, ["a", "b", "c"]); ``` -------------------------------- ### Configure Redis Connection with TLS and Custom Options Source: https://stackexchange.github.io/StackExchange.Redis/Authentication Manually configure TLS and other options using ConfigurationOptions. This is useful when the server certificate is not trusted by default or for advanced scenarios. ```csharp var options = ConfigurationOptions.Parse("myserver,ssl=true"); // or: var options = new ConfigurationOptions { Endpoints = { "myserver" }, Ssl = true }; // TODO configure var muxer = await ConnectionMultiplexer.ConnectAsync(options); ``` ```csharp options.TrustIssuer(caPath); ``` -------------------------------- ### Connect to Redis with Default Authentication Source: https://stackexchange.github.io/StackExchange.Redis/Authentication Use this for local transient servers when security is not a concern. It connects to the default user with no authentication or transport security. ```csharp var muxer = await ConnectionMultiplexer.ConnectAsync("myserver"); // or myserver:1241 to use a custom port ``` -------------------------------- ### Set and Get Binary Value Source: https://stackexchange.github.io/StackExchange.Redis/Basics Stores and retrieves raw binary data (byte arrays) in Redis using StringSet and StringGet. Redis supports binary data for keys and values. ```csharp byte[] key = ..., value = ...; db.StringSet(key, value); ... byte[] value = db.StringGet(key); ``` -------------------------------- ### Connect to Redis with Username and Password Source: https://stackexchange.github.io/StackExchange.Redis/Authentication Secure your connection using a username and password. If no user is specified, the 'default' user is assumed. Authentication tokens can also be used. ```csharp var muxer = await ConnectionMultiplexer.ConnectAsync("myserver,ssl=true,user=myuser,password=mypassword"); ``` -------------------------------- ### Perform Similarity Search with Parameters Source: https://stackexchange.github.io/StackExchange.Redis/VectorSets Perform a similarity search using a query vector, controlling search behavior with parameters like SearchExplorationFactor, Epsilon, and UseExactSearch. ```csharp var query = VectorSetSimilaritySearchRequest.ByVector(queryVector.AsMemory()); query.SearchExplorationFactor = 500; // Higher = more accurate, slower query.Epsilon = 0.1; // Only return similarity >= 0.9 query.UseExactSearch = true; // Use linear scan instead of HNSW await db.VectorSetSimilaritySearchAsync(key, query); ``` -------------------------------- ### Read Last Items from Redis Ring Buffer Source: https://stackexchange.github.io/StackExchange.Redis/Arrays Retrieves the last items from a Redis array, respecting the ring buffer's wrap-around behavior. Use `ArrayLastItemsAsync` to get the most recently retained values. ```csharp RedisValue[] last = await db.ArrayLastItemsAsync(key, count: 10); RedisValue[] lastReversed = await db.ArrayLastItemsAsync(key, count: 10, reverse: true); ``` -------------------------------- ### Subscribe to Server Maintenance Events Source: https://stackexchange.github.io/StackExchange.Redis/ServerMaintenanceEvent Attach an event handler to the ConnectionMultiplexer's ServerMaintenanceEvent to manage application behavior during Redis maintenance. This example shows how to react specifically to Azure Redis NodeMaintenanceStart events. ```csharp multiplexer.ServerMaintenanceEvent += (object sender, ServerMaintenanceEvent e) => { if (e is AzureMaintenanceEvent azureEvent && azureEvent.NotificationType == AzureNotificationType.NodeMaintenanceStart) { // Take whatever action is appropriate for your application to handle the maintenance operation gracefully. // This might mean writing a log entry, redirecting traffic away from the impacted Redis server, or // something entirely different. } }; ``` -------------------------------- ### Batch Vector Inserts using Pipelining Source: https://stackexchange.github.io/StackExchange.Redis/VectorSets Perform bulk inserts of vectors efficiently by creating a batch and executing operations concurrently. ```csharp var batch = db.CreateBatch(); var tasks = new List>(); foreach (var (member, vector) in vectorData) { var request = VectorSetAddRequest.Member(member, vector.AsMemory()); tasks.Add(batch.VectorSetAddAsync(key, request)); } batch.Execute(); await Task.WhenAll(tasks); ``` -------------------------------- ### Connect using Sentinel Mode Source: https://stackexchange.github.io/StackExchange.Redis/Configuration Connects to a sentinel server to discover and manage the primary server for a specified service name. The connection automatically updates if the primary changes. ```csharp var conn = ConnectionMultiplexer.Connect("localhost,serviceName=myprimary"); ``` -------------------------------- ### Subscribe to Keyspace Notifications by Prefix Source: https://stackexchange.github.io/StackExchange.Redis/KeyspaceNotifications Creates a RedisChannel to subscribe to all keys with a specific prefix in a given database. This method supports Redis Cluster. ```csharp // this will subscribe to __keyspace@0__:user:*, including supporting Redis Cluster var channel = RedisChannel.KeySpacePrefix(prefix: "user:"u8, database: 0); ``` -------------------------------- ### Configure Minimum Threads in runtimeconfig.json Source: https://stackexchange.github.io/StackExchange.Redis/Timeouts.html Specify the minimum number of I/O threads for the .NET runtime by setting the System.Threading.ThreadPool.MinThreads configuration option in the runtimeconfig.json file. ```json { "runtimeOptions": { "configProperties": { "System.Threading.ThreadPool.MinThreads": "4,4" } } } ``` -------------------------------- ### StackExchange.Redis 2.0+ Timeout Exception with ThreadPool Stats Source: https://stackexchange.github.io/StackExchange.Redis/Timeouts.html This example shows the ThreadPool statistics included in a timeout exception message for StackExchange.Redis version 2.0 and later. It helps diagnose issues related to busy IOCP and worker threads. ```text Timeout performing GET MyKey (1000ms), inst: 2, qs: 6, in: 0, mgr: 9 of 10 available, IOCP: (Busy=6,Free=994,Min=4,Max=1000), WORKER: (Busy=3,Free=997,Min=4,Max=1000) ``` -------------------------------- ### Parse Configuration String to Options Source: https://stackexchange.github.io/StackExchange.Redis/Configuration Converts a configuration string into a ConfigurationOptions object for programmatic manipulation. ```csharp ConfigurationOptions options = ConfigurationOptions.Parse(configString); ``` -------------------------------- ### Configure ASP.NET Minimum I/O Threads Source: https://stackexchange.github.io/StackExchange.Redis/Timeouts.html Set the minimum I/O threads for all ASP.NET applications on a machine by modifying the machine.config file. Ensure autoConfig is set to false when overriding thread settings. ```xml ``` -------------------------------- ### Enable RESP Stream Logging Source: https://stackexchange.github.io/StackExchange.Redis/RespLogging This C# snippet shows how to enable RESP stream logging by updating ConfigurationOptions with LoggingTunnel.LogToDirectory. Be aware of security concerns when logging production data. ```csharp // connect Console.WriteLine("Connecting..."); var options = ConfigurationOptions.Parse(ConnectionString); LoggingTunnel.LogToDirectory(options, @"C:\Code\RedisLog"); // <=== added! await using var muxer = await ConnectionMultiplexer.ConnectAsync(options); ... ``` -------------------------------- ### Set Array Values with Different Index Types Source: https://stackexchange.github.io/StackExchange.Redis/Arrays Demonstrates setting array values using both integer and RedisArrayIndex types for indexes. ```csharp await db.ArraySetAsync(key, 42, "answer"); await db.ArraySetAsync(key, new RedisArrayIndex(10_000_000UL), "large index"); ``` -------------------------------- ### Illustrative SQL Transaction (Pseudo-code) Source: https://stackexchange.github.io/StackExchange.Redis/Transactions.html This pseudo-code illustrates a conditional database transaction in a SQL-like environment. It shows how to assign a new ID only if one doesn't exist, ensuring thread safety. ```csharp var newId = CreateNewUniqueID(); // optimistic using(var tran = conn.BeginTran()) { var cust = GetCustomer(conn, custId, tran); var uniqueId = cust.UniqueID; if(uniqueId == null) { cust.UniqueId = newId; SaveCustomer(conn, cust, tran); } tran.Complete(); } ``` -------------------------------- ### Handling Non-Existent Keys Numerically Source: https://stackexchange.github.io/StackExchange.Redis/KeysValues.html Illustrates that Redis treats non-existent keys as zero when treated numerically. This behavior is consistent with StackExchange.Redis's nil response handling. ```csharp db.KeyDelete("abc"); int i = (int)db.StringGet("abc"); // this is ZERO ``` -------------------------------- ### Evaluate Lua Script with Keys and Values Source: https://stackexchange.github.io/StackExchange.Redis/KeysValues.html Use this method to execute a Lua script that requires both keys and values as input. The keys are passed in a `RedisKey[]` array and values in a `RedisValue[]` array. ```csharp var result = db.ScriptEvaluate(TransferScript, new RedisKey[] { from, to }, new RedisValue[] { quantity }); ``` -------------------------------- ### Set ASP.NET HttpRuntime Target Framework Source: https://stackexchange.github.io/StackExchange.Redis/ThreadTheft Ensure your web.config includes a httpRuntime element with a targetFramework of at least 4.5. This enables task-friendly SynchronizationContext behavior by default, mitigating thread theft. ```xml ``` -------------------------------- ### Key Event Notification (All Databases) Source: https://stackexchange.github.io/StackExchange.Redis/KeyspaceNotifications Maps to PSUBSCRIBE __keyevent@*__:set. Subscribes to specific key events across all databases. ```csharp RedisChannel.KeyEvent(KeyNotificationType.Set) ``` -------------------------------- ### Configuration for Twemproxy Source: https://stackexchange.github.io/StackExchange.Redis/Configuration Configure StackExchange.Redis to use Twemproxy by setting the Proxy option to Proxy.Twemproxy. This is useful when fronting Redis with Twemproxy for sharding and fault tolerance. ```csharp var options = new ConfigurationOptions { EndPoints = { "my-server" }, Proxy = Proxy.Twemproxy }; ``` -------------------------------- ### KeySpace Prefix Notification (All Databases) Source: https://stackexchange.github.io/StackExchange.Redis/KeyspaceNotifications Maps to PSUBSCRIBE __keyspace@*__:foo*. Subscribes to notifications for keys matching a pattern across all databases. ```csharp RedisChannel.KeySpacePrefix("foo") ``` -------------------------------- ### Subscribe to a Redis Channel with Callback Source: https://stackexchange.github.io/StackExchange.Redis/Basics Subscribe to a Redis channel and provide a callback function to process incoming messages. Exceptions within the callback are caught and discarded by default. ```csharp sub.Subscribe("messages", (channel, message) => { Console.WriteLine((string)message); }); ```