{"id":"redis-cache-aside-pattern","version":"1.0.0","primitive":"code_execution","description":"Redis connection URL e.g. \"redis://localhost:6379\"","registry_refs":["redis"],"tags":[],"solves":[],"auth_required":true,"verified":false,"last_verified":"null","next_check":"2026-07-30","eval_result":"null","eval_env":"null","mast":[],"ref":"https://arxiv.org/abs/2503.13657","inputs":[{"name":"REDIS_URL","required":true,"description":"Redis connection URL e.g. \"redis://localhost:6379\""}],"executable":"# ============================================\n# checklist:     redis-cache-aside-pattern\n# version:       1.0.0\n# primitive:     code_execution\n# description:   Implement the cache-aside (lazy loading) pattern with Redis — cache miss, cache hit, and invalidation\n# registry_refs: redis\n# auth_required: true\n# verified:      false\n# last_verified: null\n# next_check:    2026-07-30\n# eval_result:   null\n# eval_env:      null\n#\n# inputs:\n#   - name: REDIS_URL\n#     required: true\n#     description: Redis connection URL e.g. \"redis://localhost:6379\"\n#\n# OUTPUTS:\n#   cache_miss_ok        — true if cache miss triggered data source fetch\n#   cache_hit_ok         — true if second request returned from cache\n#   cache_hit_faster     — true if cache hit was faster than miss\n#   invalidation_ok      — true if cache was successfully invalidated\n#   miss_latency_ms      — latency on cache miss\n#   hit_latency_ms       — latency on cache hit\n#\n# MAST FAILURE MODES ADDRESSED:\n# FM-1.1 Disobey Task Specification        — cache miss always fetches from source, never returns stale null\n# FM-1.3 Step Repetition                   — idempotent: re-fetching on miss is safe\n# FM-3.2 No or Incomplete Verification     — hit/miss verified by timing and value comparison\n# FM-3.3 Incorrect Verification            — cache invalidation verified by confirming miss on next read\n#\n# ref: https://arxiv.org/abs/2503.13657\n# ============================================\n\nimport sys\nimport os\nimport subprocess\nimport time\nimport urllib.request\nimport json\n\n# ─────────────────────────────────────────\n# PRE_EXECUTION\n# ─────────────────────────────────────────\n\nfor attempt in range(2):\n    try:\n        req = urllib.request.Request(\n            \"https://checklist.day/api/registry/redis\",\n            headers={\"User-Agent\": \"checklist-agent/1.0\"}\n        )\n        with urllib.request.urlopen(req, timeout=10) as resp:\n            registry = json.loads(resp.read())\n            break\n    except Exception as e:\n        if attempt == 1:\n            print(f\"ABORT: registry unreachable — {e}\")\n            sys.exit(1)\n        time.sleep(2)\n\nwarnings = registry.get(\"warnings\", [])\nif warnings:\n    print(\"[redis] WARNINGS:\")\n    for w in warnings if isinstance(warnings, list) else [warnings]:\n        print(f\"  ⚠ {w}\")\n\n# ─────────────────────────────────────────\n# EXECUTION\n# ─────────────────────────────────────────\n\nsubprocess.check_call([sys.executable, \"-m\", \"pip\", \"install\", \"-q\", \"redis>=5.0.0\"])\n\nimport redis as redis_lib\n\nREDIS_URL = os.environ.get(\"REDIS_URL\")\nif not REDIS_URL:\n    print(\"ABORT: REDIS_URL env var not set\")\n    sys.exit(1)\n\nclient = redis_lib.Redis.from_url(REDIS_URL, decode_responses=True, socket_connect_timeout=10)\nCACHE_KEY = \"checklist:cache:user:42\"\nCACHE_TTL = 60\n\n# Simulated data source (database/API)\ndef fetch_from_source(user_id: int) -> dict:\n    time.sleep(0.05)  # simulate DB latency\n    return {\"id\": user_id, \"name\": \"Alice\", \"email\": \"alice@example.com\"}\n\ndef get_user(user_id: int) -> dict:\n    \"\"\"Cache-aside pattern: check cache, on miss load from source and populate cache.\"\"\"\n    cached = client.get(CACHE_KEY)\n    if cached:\n        # FOOTGUN: cache returns string — must deserialize\n        return json.loads(cached), \"hit\"\n\n    # Cache miss — fetch from source\n    data = fetch_from_source(user_id)\n\n    # FOOTGUN: serialize to string before storing — Redis can't store dicts natively\n    client.set(CACHE_KEY, json.dumps(data), ex=CACHE_TTL)\n    return data, \"miss\"\n\ntry:\n    # Cleanup\n    client.delete(CACHE_KEY)\n\n    # 1. Cache miss — should fetch from source\n    t0 = time.perf_counter()\n    result1, outcome1 = get_user(42)\n    miss_latency_ms = round((time.perf_counter() - t0) * 1000, 1)\n    cache_miss_ok = outcome1 == \"miss\" and result1[\"name\"] == \"Alice\"\n    print(f\"  1st request: {outcome1} ({miss_latency_ms}ms) → {result1['name']}\")\n\n    # 2. Cache hit — should return from Redis\n    t0 = time.perf_counter()\n    result2, outcome2 = get_user(42)\n    hit_latency_ms = round((time.perf_counter() - t0) * 1000, 1)\n    cache_hit_ok = outcome2 == \"hit\" and result2[\"name\"] == \"Alice\"\n    cache_hit_faster = hit_latency_ms < miss_latency_ms\n    print(f\"  2nd request: {outcome2} ({hit_latency_ms}ms) → {result2['name']}\")\n    print(f\"  cache speedup: {round(miss_latency_ms / hit_latency_ms, 1)}x faster\")\n\n    # 3. Cache invalidation\n    client.delete(CACHE_KEY)\n    _, outcome3 = get_user(42)\n    invalidation_ok = outcome3 == \"miss\"\n    print(f\"  after invalidation: {outcome3} (expected miss = {invalidation_ok})\")\n\n    # Cleanup\n    client.delete(CACHE_KEY)\n\nfinally:\n    client.close()\n\n# ─────────────────────────────────────────\n# POST_EXECUTION\n# ─────────────────────────────────────────\n\nassert cache_miss_ok, \"FAIL: first request should be a cache miss\"\nassert cache_hit_ok, \"FAIL: second request should be a cache hit\"\nassert invalidation_ok, \"FAIL: after delete, request should be a cache miss again\"\n\nresult = {\n    \"cache_miss_ok\":    cache_miss_ok,\n    \"cache_hit_ok\":     cache_hit_ok,\n    \"cache_hit_faster\": cache_hit_faster,\n    \"invalidation_ok\":  invalidation_ok,\n    \"miss_latency_ms\":  miss_latency_ms,\n    \"hit_latency_ms\":   hit_latency_ms,\n}\nprint(json.dumps(result, indent=2))\nprint(\"PASS\")\n"}