Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions assets/css/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,12 @@ section.prose {
@apply bg-slate-900 rounded-lg;
}

.prose pre.mermaid {
background-color: white !important;
border-radius: 0.5rem;
padding: 1rem;
}

.prose pre > code {
@apply bg-none font-monogeist;
}
Expand Down
147 changes: 147 additions & 0 deletions content/develop/clients/dotnet/error-handling.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
---
title: Error handling
description: Learn how to handle errors when using NRedisStack.
linkTitle: Error handling
weight: 60
---

NRedisStack uses **exceptions** to signal errors. Code examples in the documentation often omit error handling for brevity, but it is essential in production code. This page explains how NRedisStack's error handling works and how to apply common error handling patterns.

For an overview of error types and handling strategies, see [Error handling]({{< relref "/develop/clients/error-handling" >}}).
See also [Production usage]({{< relref "/develop/clients/dotnet/produsage" >}})
for more information on connection management, timeouts, and other aspects of
app reliability.

## Exception types

NRedisStack throws exceptions to signal errors. Common exception types include:

| Exception | When it occurs | Recoverable | Recommended action |
|---|---|---|---|
| `RedisConnectionException` | Connection refused or lost | ✅ | Retry with backoff or fall back |
| `RedisTimeoutException` | Operation exceeded timeout | ✅ | Retry with backoff |
| `RedisCommandException` | Invalid command or arguments | ❌ | Fix the command or arguments |
| `RedisServerException` | Invalid operation on server | ❌ | Fix the operation or data |

See [Categories of errors]({{< relref "/develop/clients/error-handling#categories-of-errors" >}})
for a more detailed discussion of these errors and their causes.

## Applying error handling patterns

The [Error handling]({{< relref "/develop/clients/error-handling" >}}) overview
describes four main patterns. The sections below show how to implement them in
NRedisStack:

### Pattern 1: Fail fast

Catch specific exceptions that represent unrecoverable errors and re-throw them (see
[Pattern 1: Fail fast]({{< relref "/develop/clients/error-handling#pattern-1-fail-fast" >}})
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure why, but this anchor goes to a location well above the actually section.

for a full description):

```csharp
using NRedisStack;
using StackExchange.Redis;

var muxer = ConnectionMultiplexer.Connect("localhost:6379");
var db = muxer.GetDatabase();

try {
var result = db.StringGet("key");
} catch (RedisCommandException) {
// This indicates a bug in the code, such as using
// StringGet on a list key.
throw;
}
```

### Pattern 2: Graceful degradation

Catch specific errors and fall back to an alternative, where possible (see
[Pattern 2: Graceful degradation]({{< relref "/develop/clients/error-handling#pattern-2-graceful-degradation" >}})
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one too. Maybe the diagrams are throwing something off?

for a full description):

```csharp
try {
var cachedValue = db.StringGet(key);
return cachedValue.ToString();
} catch (RedisConnectionException) {
logger.LogWarning("Cache unavailable, using database");

// Fallback to database
return database.Get(key);
}
```

### Pattern 3: Retry with backoff

Retry on temporary errors such as timeouts (see
[Pattern 3: Retry with backoff]({{< relref "/develop/clients/error-handling#pattern-3-retry-with-backoff" >}})
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same.

for a full description):

```csharp
const int maxRetries = 3;
int retryDelay = 100;

for (int attempt = 0; attempt < maxRetries; attempt++) {
try {
return db.StringGet(key).ToString();
} catch (RedisTimeoutException) {
if (attempt < maxRetries - 1) {
Thread.Sleep(retryDelay);
retryDelay *= 2; // Exponential backoff
} else {
throw;
}
}
}
```

See also [Timeouts]({{< relref "/develop/clients/dotnet/produsage#timeouts" >}})
for more information on configuring timeouts in NRedisStack.

### Pattern 4: Log and continue

Log non-critical errors and continue (see
[Pattern 4: Log and continue]({{< relref "/develop/clients/error-handling#pattern-4-log-and-continue" >}})
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't make this problem happen in staging or locally, so I'm not sure if I've accidentally fixed it or if I just haven't had the right circumstances to see it. I'll keep an eye open for this, though - it would be great to keep using the Mermaid diagrams, so if they introduce any faults then I'll definitely try to fix them.

for a full description):

```csharp
try {
db.StringSet(key, value, TimeSpan.FromSeconds(3600));
} catch (RedisConnectionException) {
logger.LogWarning($"Failed to cache {key}, continuing without cache");
// Application continues normally
}
```

## Async error handling

Error handling works the usual way with `async`/`await`, as shown in the
example below:

```csharp
using NRedisStack;
using StackExchange.Redis;

var muxer = ConnectionMultiplexer.Connect("localhost:6379");
var db = muxer.GetDatabase();

async Task<string> GetWithFallbackAsync(string key) {
try {
var result = await db.StringGetAsync(key);
if (result.HasValue) {
return result.ToString();
}
} catch (RedisConnectionException) {
logger.LogWarning("Cache unavailable");
return await database.GetAsync(key);
}

return await database.GetAsync(key);
}
```

## See also

- [Error handling]({{< relref "/develop/clients/error-handling" >}})
- [Production usage]({{< relref "/develop/clients/dotnet/produsage" >}})
3 changes: 3 additions & 0 deletions content/develop/clients/dotnet/produsage.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ the most common Redis exceptions:
[stream entry]({{< relref "/develop/data-types/streams#entry-ids" >}})
using an invalid ID).

See [Error handling]({{< relref "/develop/clients/dotnet/error-handling" >}})
for more information on handling exceptions.

### Retries

During the initial `ConnectionMultiplexer.Connect()` call, `NRedisStack` will
Expand Down
Loading