Reliability Analyzers
Reliability Analyzers
Reliability analyzers detect inefficient exception handling patterns and unsafe access patterns that can harm performance and reliability in NPipeline pipelines.
NP9202: Inefficient Exception Handling
ID: NP9202
Severity: Warning
Category: Reliability & Error Handling
This analyzer detects inefficient exception handling patterns that can harm performance and reliability in NPipeline pipelines. The analyzer identifies overly broad exception catches, missing specific exception handling, and performance-impacting exception usage in hot paths.
Why This Matters
Inefficient exception handling causes:
- Performance Degradation: Exception throwing and catching is expensive
- Masked Errors: Overly broad catches hide real issues
- Poor Debugging: Generic exception handling obscures root causes
- Resource Leaks: Improper exception handling can prevent cleanup
Problematic Patterns
// PROBLEM: Catching Exception instead of specific exceptions
public async Task ProcessAsync(Input input, CancellationToken cancellationToken)
{
try
{
await ProcessItemAsync(input, cancellationToken);
}
catch (Exception ex) // NP9202: Too broad exception handling
{
_logger.LogError(ex, "Processing failed");
throw;
}
}
// PROBLEM: Using exceptions for control flow
public Item GetItem(string id)
{
if (!_cache.TryGetValue(id, out var item))
{
throw new KeyNotFoundException($"Item {id} not found"); // NP9202: Exception for control flow
}
return item;
}
// PROBLEM: Exception handling in hot paths
public async Task ProcessBatchAsync(IEnumerable<Item> items, CancellationToken cancellationToken)
{
foreach (var item in items)
{
try
{
await ProcessItemAsync(item, cancellationToken);
}
catch (Exception ex) // NP9202: Exception handling in performance-critical loop
{
_logger.LogError(ex, "Failed to process item {ItemId}", item.Id);
}
}
}
Solution: Use Specific Exception Handling and Alternative Patterns
// CORRECT: Catch specific exceptions
public async Task ProcessAsync(Input input, CancellationToken cancellationToken)
{
try
{
await ProcessItemAsync(input, cancellationToken);
}
catch (InvalidOperationException ex)
{
_logger.LogError(ex, "Invalid operation during processing");
throw;
}
catch (TimeoutException ex)
{
_logger.LogError(ex, "Processing timeout");
throw new ProcessingTimeoutException("Item processing timed out", ex);
}
}
// CORRECT: Use result pattern instead of exceptions for control flow
public Result<Item> TryGetItem(string id)
{
if (!_cache.TryGetValue(id, out var item))
{
return Result<Item>.Failure($"Item {id} not found");
}
return Result<Item>.Success(item);
}
// CORRECT: Filter invalid items before processing
public async Task ProcessBatchAsync(IEnumerable<Item> items, CancellationToken cancellationToken)
{
var validItems = new List<Item>();
var failedItems = new List<(Item Item, Exception Error)>();
// Validate first to avoid exceptions in hot path
foreach (var item in items)
{
if (IsValid(item))
{
validItems.Add(item);
}
else
{
failedItems.Add((item, new ValidationException("Invalid item")));
}
}
// Process valid items
foreach (var item in validItems)
{
try
{
await ProcessItemAsync(item, cancellationToken);
}
catch (Exception ex)
{
failedItems.Add((item, ex));
}
}
if (failedItems.Any())
{
_logger.LogWarning("Failed to process {Count} items", failedItems.Count);
}
}
Exception Handling Guidelines
| Scenario | Recommended Approach | Benefit |
|---|---|---|
| Expected failures | Result pattern or TryXXX methods | No exception overhead |
| Validation failures | Input validation before processing | Prevent exceptions |
| External dependencies | Specific exception types | Precise error handling |
| Hot paths | Avoid exceptions for control flow | Better performance |
| Cleanup operations | finally blocks or using statements | Guaranteed cleanup |
Best Practices for Reliability
- Catch specific exceptions - Avoid catching Exception when possible
- Use result patterns - For expected failures that don't need exceptions
- Validate inputs early - Prevent exceptions before they occur
- Avoid exceptions in hot paths - Use alternative control flow mechanisms
- Always clean up resources - Use finally blocks or using statements
Configuration
Adjust analyzer severity in .editorconfig:
# Treat inefficient exception handling as warnings
dotnet_diagnostic.NP9202.severity = warning