Understanding the Azure Redis Migration

Microsoft recently announced the retirement of Basic, Standard, and Premium tiers of Azure Cache for Redis, with a hard deadline of September 30, 2028. This affects many production workloads, and planning your migration strategy now will save headaches later.

Key Dates to Remember

  • October 1, 2026: No new instances of Azure Cache for Redis can be created
  • September 30, 2028: Migration deadline
  • October 1, 2028: All remaining instances will be disabled

Pre-Migration Assessment

Inventory Your Redis Instances

# List all Redis caches across subscriptions
az redis list --query "[].{name:name, tier:sku.name, capacity:sku.capacity, location:location}" --output table

# Get detailed configuration for specific instance
az redis show --name <redis-name> --resource-group <rg-name>

Analyze Current Usage Patterns

# Export Redis metrics for capacity planning
az monitor metrics list \
  --resource <redis-resource-id> \
  --metric-names "connectedclients" "usedmemory" "serverLoad" "operationsPerSecond" \
  --start-time 2024-01-01T00:00:00Z \
  --end-time 2024-01-31T23:59:59Z \
  --interval PT1H \
  --output json > redis-metrics.json

Migration Strategy

1. Create Azure Managed Redis Instance

# Create new Managed Redis instance
az redis enterprise create \
  --name <managed-redis-name> \
  --resource-group <rg-name> \
  --location <location> \
  --sku Enterprise_E10 \
  --capacity 2

# Create database within the instance
az redis enterprise database create \
  --cluster-name <managed-redis-name> \
  --resource-group <rg-name> \
  --client-protocol Encrypted \
  --clustering-policy EnterpriseCluster \
  --eviction-policy NoEviction \
  --modules name=RedisJSON,name=RedisTimeSeries

2. Data Migration Approaches

Option A: Redis MIGRATE Command (Small Datasets)

import redis
import time

# Connect to source and target
source = redis.Redis(
    host='source-redis.redis.cache.windows.net',
    port=6379,
    password='source_password',
    ssl=True
)

target = redis.Redis(
    host='target-redis.region.redisenterprise.cache.azure.net',
    port=10000,
    password='target_password',
    ssl=True
)

# Migrate keys
for key in source.scan_iter(count=100):
    ttl = source.ttl(key)
    value = source.dump(key)
    
    if value:
        target.restore(key, ttl if ttl > 0 else 0, value)
        
    # Rate limiting to avoid overwhelming the system
    time.sleep(0.001)

Option B: Redis Replication (Large Datasets)

# Export data from source
redis-cli -h source-redis.redis.cache.windows.net \
  -p 6379 \
  -a <password> \
  --rdb dump.rdb \
  --scan

# Import to target using redis-cli
redis-cli -h target-redis.region.redisenterprise.cache.azure.net \
  -p 10000 \
  -a <password> \
  --pipe < dump.rdb

3. Application Configuration Updates

# Update your application configuration
# Old configuration
redis:
  host: old-redis.redis.cache.windows.net
  port: 6379
  ssl: true
  password: ${REDIS_PASSWORD}

# New configuration
redis:
  host: new-redis.region.redisenterprise.cache.azure.net
  port: 10000
  ssl: true
  password: ${MANAGED_REDIS_PASSWORD}
  # Additional Managed Redis specific settings
  cluster_enabled: true
  read_replicas:
    - host: replica1.region.redisenterprise.cache.azure.net
    - host: replica2.region.redisenterprise.cache.azure.net

Testing and Validation

Performance Testing Script

import redis
import time
import statistics

def benchmark_redis_instance(connection_params, test_name):
    r = redis.Redis(**connection_params)
    
    # Test SET operations
    set_times = []
    for i in range(1000):
        start = time.time()
        r.set(f'test_key_{i}', 'x' * 1024)  # 1KB payload
        set_times.append(time.time() - start)
    
    # Test GET operations
    get_times = []
    for i in range(1000):
        start = time.time()
        r.get(f'test_key_{i}')
        get_times.append(time.time() - start)
    
    print(f"\
{test_name} Results:")
    print(f"SET - Avg: {statistics.mean(set_times)*1000:.2f}ms, "
          f"P99: {sorted(set_times)[int(len(set_times)*0.99)]*1000:.2f}ms")
    print(f"GET - Avg: {statistics.mean(get_times)*1000:.2f}ms, "
          f"P99: {sorted(get_times)[int(len(get_times)*0.99)]*1000:.2f}ms")

# Benchmark both instances
benchmark_redis_instance(old_redis_config, "Azure Cache for Redis")
benchmark_redis_instance(new_redis_config, "Azure Managed Redis")

Cutover Strategy

Blue-Green Deployment Approach

# Step 1: Deploy application with dual-write capability
# Application writes to both old and new Redis

# Step 2: Verify data consistency
redis-cli -h old-redis.redis.cache.windows.net --scan --pattern '*' | wc -l
redis-cli -h new-redis.redisenterprise.cache.azure.net --scan --pattern '*' | wc -l

# Step 3: Switch reads to new Redis (canary deployment)
# Monitor error rates and latencies

# Step 4: Complete cutover
# Update DNS/load balancer to point to new Redis

Post-Migration Monitoring

# Set up alerts for the new Managed Redis instance
az monitor metrics alert create \
  --name high-memory-usage \
  --resource <managed-redis-id> \
  --condition "avg usedmemorypercentage > 80" \
  --window-size 5m \
  --evaluation-frequency 1m

az monitor metrics alert create \
  --name high-cpu-usage \
  --resource <managed-redis-id> \
  --condition "avg cpuusagepercentage > 75" \
  --window-size 5m \
  --evaluation-frequency 1m

Rollback Plan

Always maintain your rollback capability:

# Maintain bi-directional sync during transition
class DualRedisClient:
    def __init__(self, primary_config, secondary_config):
        self.primary = redis.Redis(**primary_config)
        self.secondary = redis.Redis(**secondary_config)
        
    def set(self, key, value, **kwargs):
        # Write to both, read from primary
        result = self.primary.set(key, value, **kwargs)
        try:
            self.secondary.set(key, value, **kwargs)
        except Exception as e:
            # Log but don't fail
            logger.warning(f"Secondary write failed: {e}")
        return result
        
    def get(self, key):
        return self.primary.get(key)

Key Takeaways

  1. Start Early: Don’t wait until 2027 to begin migration planning
  2. Test Thoroughly: Azure Managed Redis has different performance characteristics
  3. Plan for Downtime: While minimal downtime is possible, plan maintenance windows
  4. Monitor Costs: Managed Redis pricing model differs from Cache for Redis
  5. Update Documentation: Ensure runbooks and disaster recovery procedures are updated

The migration from Azure Cache for Redis to Azure Managed Redis is mandatory but also an opportunity to leverage improved performance, better scalability, and access to the latest Redis features. Plan your migration strategy now to ensure a smooth transition well before the 2028 deadline."