Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Prepare applications for Azure deployment by generating infrastructure code, Dockerfiles, and config files.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/services/app-service/templates/recipes/redis/source/python.md
1# Redis Recipe — Python — REFERENCE ONLY23## Redis Client with Token Refresh45### Requirements67Add to `requirements.txt`:89```10redis>=5.011azure-identity12```1314### Cache Module1516Create `cache.py`:1718```python19import os20import time21import threading22import redis23from azure.identity import DefaultAzureCredential2425# Redis ACL auth requires both username and token as password26_REDIS_USERNAME = "default"27_TOKEN_SCOPE = "https://redis.azure.com/.default"28_TOKEN_REFRESH_MARGIN = 300 # Refresh 5 minutes before expiry2930_credential = DefaultAzureCredential()31_lock = threading.RLock() # reentrant — get_cache() calls _get_token() while holding the lock32_token_cache: dict = {}333435def _get_token():36"""Return a valid Entra ID token, refreshing if within expiry margin."""37with _lock:38now = time.time()39if not _token_cache or now >= _token_cache["expires_on"] - _TOKEN_REFRESH_MARGIN:40tok = _credential.get_token(_TOKEN_SCOPE)41_token_cache["token"] = tok.token42_token_cache["expires_on"] = tok.expires_on43return _token_cache["token"]444546def create_redis_client() -> redis.Redis:47"""Create a Redis client. Token is refreshed on each call via _get_token()."""48return redis.Redis(49host=os.environ["REDIS_HOST"],50port=int(os.environ.get("REDIS_PORT", 6380)),51ssl=True,52username=_REDIS_USERNAME,53password=_get_token(),54decode_responses=True,55)565758def get_cache() -> redis.Redis:59"""Lazy/refreshing accessor — call this from request handlers instead of holding a module-level client.60Recreates the client when the cached token is within the refresh margin and closes the prior one."""61global _client62with _lock:63now = time.time()64if _client is None or now >= _token_cache.get("expires_on", 0) - _TOKEN_REFRESH_MARGIN:65old = _client66_client = create_redis_client()67if old is not None:68try:69old.close()70except Exception:71pass72return _client737475_client: redis.Redis | None = None76```7778> ⚠️ Entra ID tokens expire in ~1 hour. The `_get_token()` helper refreshes proactively 5 minutes before expiry, and `get_cache()` recreates the underlying client so in-flight pool connections pick up the new token. Always call `get_cache()` from request handlers — never cache the client at module scope across the token lifetime.7980### Usage8182```python83from cache import get_cache8485def get_cached(key: str):86cache = get_cache()87value = cache.get(key)88if value is None:89value = "computed-value"90cache.setex(key, 300, value) # TTL 5 minutes91return value92```9394## Files to Modify9596| File | Action |97|------|--------|98| `cache.py` | Create — Redis client with token refresh |99| `main.py` | Modify — use cache client |100| `requirements.txt` | Modify — add redis, azure-identity |101