Skip to content

Azure Managed Redis

Each Microtec ERP environment has a dedicated Azure Managed Redis instance. Redis is used for distributed caching (reducing database load on frequently read data) and as the storage backend for ASP.NET Core DataProtection key ring sharing across container replicas.

Azure Managed Redis — Not Azure Cache for Redis

Microtec ERP uses Azure Managed Redis (the newer in-memory data grid product), which is a completely different product from the legacy Azure Cache for Redis. The SKU family (Balanced_B0, Balanced_B1, etc.) and some endpoint patterns differ from the old Basic C0/Standard C1/Premium P1 SKUs.


Instance Inventory

EnvironmentRedis HostnamePortSSLResource Group
devmic-erp-be-dev-redis.{region}.redis.azure.net10000Yesmic-erp-be-dev-utils-rg
stagemic-erp-be-stage-redis.uksouth.redis.azure.net10000Yesmic-erp-be-stage-utils-rg
preprodmic-erp-be-preprod-redis.{region}.redis.azure.net10000Yesmic-erp-be-preprod-utils-rg
uatmic-erp-be-uat-redis.{region}.redis.azure.net10000Yesmic-erp-be-uat-utils-rg
productionmic-erp-be-production-redis.{region}.redis.azure.net10000Yesmic-erp-be-production-utils-rg

Stage Redis Hostname

The stage Redis instance uses the .uksouth.redis.azure.net suffix because it was provisioned in UK South. All Azure Managed Redis instances use port 10000 (not 6380 which is the legacy Azure Cache for Redis SSL port). Do not change the connection string format for stage.


Connection String Format

All backend services use the StackExchange.Redis connection string format:

{hostname}:{port},password={password},ssl=True,abortConnect=False,connectTimeout=10000,syncTimeout=5000

Per-Environment Examples

# dev
mic-erp-be-dev-redis.{region}.redis.azure.net:10000,password={from-kv},ssl=True,abortConnect=False

# stage
mic-erp-be-stage-redis.uksouth.redis.azure.net:10000,password={from-kv},ssl=True,abortConnect=False

# production
mic-erp-be-production-redis.{region}.redis.azure.net:10000,password={from-kv},ssl=True,abortConnect=False

The password for each environment is stored in Key Vault:

EnvironmentKey VaultSecret Name
devmic-erp-be-dev-skvRedisConfiguration--Password
stagemic-erp-stg-kvRedisConfiguration--Password
preprodmic-erp-be-preprod-skvRedisConfiguration--Password
uatmic-erp-uat-kvRedisConfiguration--Password

The full connection string (including host, port, and password) is assembled in appsettings.json at runtime:

json
{
  "Redis": {
    "Configuration": "mic-erp-be-dev-redis.{region}.redis.azure.net:10000,password={Redis__Password},ssl=True,abortConnect=False"
  }
}

SKU and Capacity

Azure Managed Redis uses a different SKU family from the legacy Azure Cache for Redis:

EnvironmentSKUNotes
devBalanced_B0Smallest non-prod tier
stageBalanced_B1Default tier
preprodBalanced_B1Default tier
uatBalanced_B1Default tier
productionBalanced_B1Default tier (scale up as needed)

Balanced Tier

The Balanced_B tier is part of the Azure Managed Redis in-memory data grid SKU family. It is completely different from the legacy Basic C/Standard C/Premium P tiers of Azure Cache for Redis. Do not mix up the two products when reading Azure documentation or Bicep reference templates.


Usage in Backend Services

1. Distributed Caching

Backend services cache frequently-read, rarely-changed data to avoid repeated database queries:

csharp
// Shared.Infrastructure/Caching/RedisCacheService.cs
public async Task<T?> GetOrSetAsync<T>(
    string key,
    Func<Task<T>> factory,
    TimeSpan? expiry = null)
{
    var cached = await _db.StringGetAsync(key);
    if (cached.HasValue)
        return JsonSerializer.Deserialize<T>(cached!);

    var value = await factory();
    await _db.StringSetAsync(
        key,
        JsonSerializer.Serialize(value),
        expiry ?? TimeSpan.FromMinutes(5)
    );
    return value;
}

Typical cache keys and TTLs:

Cache Key PatternTTLDescription
tenant:{id}:branches10 minBranch list for tenant
tenant:{id}:currencies1 hourCurrency settings
accounts:{tenantId}:coa5 minChart of accounts
user:{id}:permissions5 minERP permission codes (mirrors Keycloak mapper cache)
dropdown:{entity}:{tenantId}5 minDropdown list data

2. ASP.NET Core DataProtection

All backend services share a single key ring stored in Redis to ensure encrypted cookies and tokens issued by one replica can be decrypted by any other:

csharp
// Program.cs — DataProtection setup
builder.Services.AddDataProtection()
    .SetApplicationName("microtec-erp")
    .PersistKeysToStackExchangeRedis(
        ConnectionMultiplexer.Connect(redisConfig),
        "DataProtection-Keys"
    )
    .ProtectKeysWithAzureKeyVault(
        new Uri($"https://{kvName}.vault.azure.net/keys/DataProtection"),
        new DefaultAzureCredential()
    );

DataProtection Key Sharing is Critical

If Redis is unavailable at startup, DataProtection key loading fails and the service cannot start. Monitor Redis connectivity as a top-priority health signal. All services across all replicas MUST use the same Redis instance for DataProtection to function correctly.

3. Distributed Lock (Redlock)

The Workflow service uses Redis for distributed locks to prevent concurrent execution of the same workflow step across replicas:

csharp
// WorkflowLockService.cs
public async Task<IDisposable?> AcquireLockAsync(string resourceKey, TimeSpan expiry)
{
    var lockKey = $"lock:workflow:{resourceKey}";
    var token   = Guid.NewGuid().ToString();

    bool acquired = await _db.StringSetAsync(
        lockKey, token, expiry,
        When.NotExists
    );

    return acquired ? new RedisMutex(_db, lockKey, token) : null;
}

Bicep Configuration

bicep
// redis.bicep
resource redis 'Microsoft.Cache/redisEnterprise@2024-02-01' = {
  name: '${resourcePrefix}-redis'
  location: location
  sku: {
    name: redisSku      // e.g. 'Balanced_B0', 'Balanced_B1'
    capacity: 1
  }
  properties: {
    minimumTlsVersion: '1.2'
  }
}

resource redisDatabase 'Microsoft.Cache/redisEnterprise/databases@2024-02-01' = {
  name: 'default'
  parent: redis
  properties: {
    evictionPolicy: 'AllKeysLRU'
    clusteringPolicy: 'NonClustered'
    port: 10000
  }
}

Monitoring

Key metrics to monitor via Azure Monitor:

MetricWarning ThresholdCritical Threshold
UsedMemoryPercentage> 70%> 85%
CacheHits (rate)< 80%< 60%
CacheMisses (rate)
ConnectedClients> 80> 120
Evictions> 10/min> 100/min

High eviction rates indicate the Redis instance needs a larger SKU.


Flushing Cache (Development)

Caution in Shared Environments

Flushing Redis in stage/preprod clears DataProtection keys, which invalidates all active user sessions. Only flush dev Redis without coordination.

bash
# Connect to Redis CLI (requires redis-cli installed)
redis-cli -h mic-erp-be-dev-redis.{region}.redis.azure.net \
          -p 10000 \
          --tls \
          -a '{password}' \
          FLUSHDB

# Or flush only cache keys (preserve DataProtection keys)
redis-cli ... --scan --pattern 'tenant:*' | xargs redis-cli ... DEL
redis-cli ... --scan --pattern 'dropdown:*' | xargs redis-cli ... DEL

Troubleshooting

SymptomLikely CauseResolution
Service fails to start: "DataProtection key load failed"Redis unreachableCheck NSG rules; verify Redis connection string in Key Vault
All users logged out suddenlyDataProtection keys evicted from RedisRedis memory full (maxmemory-policy: allkeys-lru evicted DP keys); upgrade SKU
High cache miss rateTTL too short or key pattern mismatchReview cache key patterns; check for cache key prefix mismatches across services
Connection timeoutWrong port — Azure Managed Redis uses port 10000 (not 6380)Verify connection string uses port 10000

Internal Documentation — Microtec Platform Team