Appearance
Redis Cache
Azure Cache for Redis is used across all Microtec environments for distributed caching, session data, and ASP.NET Core DataProtection key ring storage. All Redis connections use SSL on port 10000.
Redis Usage Patterns
| Use Case | Description | Key Pattern |
|---|---|---|
| Session caching | User session data (JWT claims cache) | session:{userId}:{tenantId} |
| DataProtection keys | ASP.NET Core key ring (cookie encryption) | DataProtection-Keys |
| Distributed cache | Frequently-read reference data (dropdowns, lookups) | cache:{tenantId}:{entity}:{id} |
| Output caching | HTTP response caching for read-heavy endpoints | oc:{route-hash} |
| Idempotency keys | Prevent duplicate command processing | idempotency:{requestId} |
Per-Environment Redis Instances
| Environment | Hostname | Port | SKU |
|---|---|---|---|
| dev | mic-erp-be-dev-redis.redis.cache.windows.net | 10000 | Basic C0 |
| stage | mic-erp-be-stage-redis.uksouth.redis.azure.net | 10000 | Standard C1 |
| preprod | mic-erp-be-preprod-redis.uksouth.redis.azure.net | 10000 | Standard C1 |
| uat | mic-erp-be-uat-redis.uksouth.redis.azure.net | 10000 | Standard C1 |
| production | mic-erp-be-prod-redis.uksouth.redis.azure.net | 10000 | Premium P1 |
Production Premium SKU
The production Redis instance uses the Premium P1 SKU which provides:
- Redis Cluster for higher throughput
- Geo-replication (secondary replica in West Europe)
- VNet injection (instead of private endpoint)
- Redis persistence (RDB snapshots every hour)
Connection String
Format
{hostname}:{port},password={password},ssl=True,abortConnect=False,connectTimeout=5000,syncTimeout=5000Stage example:
mic-erp-be-stage-redis.uksouth.redis.azure.net:10000,password=<from-kv>,ssl=True,abortConnect=FalseSSL=True and Port 10000 Are Mandatory
Azure Cache for Redis enforces SSL. Non-SSL port 6379 is disabled on all instances. Always specify:
ssl=Truein the connection string- Port
10000(not 6379)
Without these, connections will fail silently or with a cryptic timeout error.
Key Vault Secret
KV Secret name: RedisConfiguration--Password
App config key: RedisConfiguration:Password
Full connection string is assembled in the shared Microtec.Web.Core package:
"{host}:{port},password={KV password},ssl=True,abortConnect=False".NET Integration
StackExchange.Redis Registration
csharp
// Microtec.Web.Core / Extensions/CacheExtensions.cs
public static IServiceCollection AddMicrotecCache(
this IServiceCollection services,
IConfiguration configuration)
{
var redisConnectionString = configuration["RedisConfiguration:ConnectionString"]!;
services.AddStackExchangeRedisCache(options =>
{
options.Configuration = redisConnectionString;
options.InstanceName = "MicrotecERP:";
});
services.AddSingleton<IConnectionMultiplexer>(
ConnectionMultiplexer.Connect(redisConnectionString));
return services;
}DataProtection Key Ring
ASP.NET Core DataProtection keys are persisted to Redis so all Container App replicas share the same key ring (required for cookie decryption across replicas):
csharp
services.AddDataProtection()
.PersistKeysToStackExchangeRedis(
ConnectionMultiplexer.Connect(redisConnectionString),
"DataProtection-Keys")
.SetApplicationName("MicrotecERP")
.SetDefaultKeyLifetime(TimeSpan.FromDays(90));DataProtection Keys Are Machine-Specific by Default
Without the Redis key ring persistence, each Container App replica generates its own DataProtection key. Users whose session cookie was encrypted by replica A cannot be decrypted by replica B, causing continuous logouts. Always use the shared Redis key ring in environments with more than one replica.
Known gotcha: DataProtection keys in Redis are encrypted with a machine-specific key. If you flush Redis and the original keys are gone, existing session cookies cannot be decrypted. Users must re-authenticate. This is expected behaviour during a Redis flush.
Cache Key Strategies
Tenant Isolation
All application-level cache keys are prefixed with the tenant ID to prevent cross-tenant data leakage:
csharp
public class TenantAwareCacheService : ICacheService
{
private readonly IDistributedCache _cache;
private readonly ITenantProvider _tenantProvider;
public async Task<T?> GetAsync<T>(string key, CancellationToken ct = default)
{
var tenantKey = $"{_tenantProvider.TenantId}:{key}";
var bytes = await _cache.GetAsync(tenantKey, ct);
return bytes is null ? default : JsonSerializer.Deserialize<T>(bytes);
}
}TTL Conventions
| Data Type | TTL | Rationale |
|---|---|---|
| User session | 8 hours | Align with business day working session |
| Reference data (dropdowns) | 30 minutes | Refresh frequently enough for config changes |
| Report results | 5 minutes | Balance freshness vs DB load |
| Idempotency keys | 24 hours | Cover duplicate submission window |
| DataProtection keys | 90 days | Set by SetDefaultKeyLifetime |
Monitoring Redis
Azure Portal Metrics
Key metrics to watch in Azure Monitor for each Redis instance:
| Metric | Healthy Range | Alert Threshold |
|---|---|---|
| Server Load | < 60% | > 80% for 5 min |
| Used Memory | < 70% | > 85% |
| Cache Hits | > 80% hit rate | < 50% (cache not effective) |
| Connected Clients | < 500 | > 900 (SKU limit approaching) |
| Evicted Keys | 0 | > 100/min (memory pressure) |
CLI Diagnostics
bash
# Connection test (from inside private VNet / jump box)
redis-cli \
-h mic-erp-be-stage-redis.uksouth.redis.azure.net \
-p 10000 \
-a "${REDIS_PWD}" \
--tls PING
# Key count by prefix
redis-cli --tls -h ... -p 10000 -a ... KEYS "MicrotecERP:*" | wc -l
# Memory info
redis-cli --tls -h ... -p 10000 -a ... INFO memory
# Check DataProtection keys
redis-cli --tls -h ... -p 10000 -a ... KEYS "DataProtection-Keys*"Redis Flush (Emergency Procedure)
Impact of FLUSHALL
Flushing Redis clears:
- All session caches → all users are logged out
- DataProtection keys → all existing cookies are invalidated (logouts)
- All distributed cache → increased DB load on next requests
- Idempotency keys → duplicate requests may be processed
Only flush Redis when directed by an on-call incident commander during a P1 incident. Notify the support team before executing.
bash
REDIS_PWD=$(az keyvault secret show \
--vault-name mic-erp-stg-kv \
--name "RedisConfiguration--Password" \
--query value -o tsv)
redis-cli \
-h mic-erp-be-stage-redis.uksouth.redis.azure.net \
-p 10000 \
-a "${REDIS_PWD}" \
--tls FLUSHALL ASYNCUse FLUSHALL ASYNC (not FLUSHALL) to avoid blocking the Redis server during the flush operation.