{
    "title": "Product Perf & Stock Cache",
    "description": "Hướng dẫn cách hệ thống truy xuất dữ liệu hiệu năng sản phẩm (KPIs) từ StarRocks và kiểm tra tồn kho realtime qua cơ chế parallel async fetch.",
    "diagram": "diagrams/stock-cache.svg",
    "sections": [
        {
            "title": "1. Tổng quan (Overview)",
            "type": "text",
            "content": "Đây là module quản trị Catalog và Tồn kho (Inventory). Hệ thống kết hợp giữa sức mạnh OLAP của <b>StarRocks</b> để tính toán KPIs siêu tốc trên hàng triệu rows, và Redis/httpx để lấy data tồn kho Realtime từ ERP mà không gây nghẽn mạng."
        },
        {
            "title": "2. Kiến trúc & Luồng dữ liệu (Data Flow)",
            "type": "text",
            "content": "Vì API tồn kho của Canifa ERP có giới hạn (Rate Limit) và chỉ nhận tối đa 200 SKU/lần, hệ thống phải triển khai cơ chế <b>Parallel Chunking Fetch</b>:<br><br><b>1. Expand (Bùng nổ SKU):</b> Khách hàng tra cứu mã Gốc (Ví dụ: `8TS24A001`). Hệ thống phải nổ mã này ra thành 15 SKU con (Đỏ-S, Đỏ-M, Xanh-L...).<br><b>2. Chunking (Chia nhỏ):</b> Cắt mảng 1000 SKUs thành các chunks nhỏ (mỗi chunk 50 SKU).<br><b>3. Async Scatter (Bắn đồng thời):</b> Dùng <code>httpx.AsyncClient</code> với <code>asyncio.gather()</code> bắn song song 20 requests lên ERP ERP.<br><b>4. Gather & Cache:</b> Gộp kết quả trả về, update vào Redis Cache với TTL 15 phút để các lượt check sau không phải gọi lại ERP."
        },
        {
            "title": "3. Core Logic: Product Performance (StarRocks)",
            "type": "text",
            "content": "Thay vì truy vấn Postgres chậm chạp, Dashboard sử dụng <b>StarRocks</b> làm OLAP engine để GROUP BY và SUM() doanh thu, số bán, tồn kho. Dữ liệu được nhóm theo <code>internal_ref_code</code> (Mã mẫu) để hiển thị cái nhìn tổng quan thay vì liệt kê từng màu/size lắt nhắt."
        },
        {
            "title": "4. API Hiệu năng & Tồn kho",
            "type": "api",
            "endpoints": [
                { "method": "GET", "path": "/api/products/overview", "description": "Lấy các chỉ số KPI tổng quát của toàn bộ kho hàng." },
                { "method": "GET", "path": "/api/products/list", "description": "Danh sách sản phẩm kèm hiệu số bán chạy và tìm kiếm thông minh." },
                { "method": "POST", "path": "/api/stock/check", "description": "Kiểm tra tồn kho realtime cho danh sách sản phẩm qua ERP." }
            ]
        },
        {
            "title": "5. Code Snippet Thực Tế (Parallel Fetching)",
            "type": "code",
            "language": "python",
            "code": "import asyncio\nimport httpx\n\nasync def fetch_stock_parallel(sku_list: list[str]):\n    \"\"\"Chia nhỏ list SKU và gọi API ERP song song\"\"\"\n    chunk_size = 50\n    chunks = [sku_list[i:i + chunk_size] for i in range(0, len(sku_list), chunk_size)]\n    \n    async with httpx.AsyncClient() as client:\n        tasks = [\n            client.post(\"https://erp.canifa.com/api/stock\", json={\"skus\": chunk})\n            for chunk in chunks\n        ]\n        \n        # Chạy đồng thời tất cả các HTTP requests\n        responses = await asyncio.gather(*tasks, return_exceptions=True)\n        \n        final_stock = {}\n        for res in responses:\n            if not isinstance(res, Exception) and res.status_code == 200:\n                final_stock.update(res.json().get(\"data\", {}))\n        return final_stock"
        },
        {
            "title": "6. Troubleshooting & Gotchas",
            "type": "text",
            "content": "<b>🚫 Lỗi Too Many Open Files (Socket Exhaustion):</b><br>Khi số lượng Task sinh ra quá lớn, HTTPX mở quá nhiều TCP connections dẫn đến lỗi OS. Giải pháp: Phải dùng <code>asyncio.Semaphore(10)</code> để giới hạn tối đa 10 requests chạy song song cùng lúc.<br><br><b>🚫 Cache Stampede (Bão Cache):</b><br>Khi Cache hết hạn (TTL expire), nếu có 100 users cùng check kho, cả 100 người sẽ cùng gọi lên ERP khiến server sập. Cần dùng cơ chế <b>Lock (Mutex)</b> để chỉ cho phép 1 user gọi ERP, 99 người còn lại chờ kết quả trả về."
        }
    ]
}
