IDEA Technology · R&D
VendAD
CMS quảng cáo số cho máy bán hàng
- Vai trò
- Phụ trách end-to-end
- Liên kết
- GitHub ↗
- Công nghệ
- 50Thiết bị đồng thời trong bài load test k6
- <1sThời gian phản hồi p95 dưới tải đó
- <3sTừ lúc duyệt tới mọi màn hình, trước đó ~10 phút
Tổng quan
Nền tảng quảng cáo cho máy bán hàng tự động do tôi phụ trách end-to-end: portal ASP.NET Core MVC, backend và database, ứng dụng phát nội dung Flutter cho Android TV, và hạ tầng triển khai giữ cả đội màn hình luôn đồng bộ.
Những gì tôi làm
- Phụ trách toàn bộ nền tảng: portal ASP.NET Core MVC, backend, database, ứng dụng Flutter cho Android TV và triển khai.
- Các background workflow với EF Core, PostgreSQL/SQL Server, MediatR, RabbitMQ, Redis và SignalR cho xử lý media, trạng thái thiết bị và báo cáo phát.
- Triển khai bằng Docker và GitHub Actions lên Render, load test bằng k6: 50 thiết bị đồng thời với p95 dưới một giây.
Thiết kế hệ thống
Cache invalidation hướng sự kiện
Khi nội dung được duyệt hoặc sửa, một message RabbitMQ được publish. Background worker consume và chỉ invalidate đúng các Redis key liên quan, nên mọi màn hình nhận thay đổi trong vài giây mà không cần polling.
Redis distributed lock với Lua
SETNX kết hợp Lua script tuần tự hóa các cập nhật nội dung và rebuild cache đồng thời giữa nhiều instance, có lease TTL để worker bị crash không bao giờ làm kẹt cả hệ thống.
Một codebase, hai database
EF Core với migration không phụ thuộc provider cho phép cùng hệ thống chạy trên PostgreSQL hoặc SQL Server bằng cấu hình, được kiểm chứng bằng Testcontainers cho cả hai trong CI.
Pipeline kiểm tra video
File upload đi qua bước kiểm tra header bằng ffprobe để loại sớm, rồi decode đầy đủ bằng FFmpeg theo chính sách codec, độ phân giải, bitrate và thời lượng trước khi lên màn hình.
Bài toán đã giải
Vấn đềMàn hình ở nhiều địa điểm hiển thị nội dung cũ tới 10 phút sau khi admin cập nhật.
Giải phápMỗi thay đổi nội dung phát ra sự kiện để background worker invalidate đúng Redis key. Độ trễ giảm từ khoảng 10 phút xuống dưới 3 giây.
Vấn đềNhiều admin sửa cùng lúc làm cache lệch, hai màn hình có thể phát hai phiên bản khác nhau của cùng một chiến dịch.
Giải phápCác thao tác ghi quan trọng được tuần tự hóa bằng Redis lock dùng Lua, có lease và tự hết hạn, cùng retry queue cho các lần acquire thất bại.
Vấn đềVideo sai codec lọt tới thiết bị và lỗi khi phát.
Giải phápKiểm tra hai bước: ffprobe để loại ngay, decode đầy đủ bằng FFmpeg cho an toàn, kèm thông báo lỗi chỉ rõ cách encode lại.
Hình ảnh



Bài học
- Thiết kế hướng sự kiện tách việc phát hành nội dung khỏi cache invalidation, giúp hệ thống bình tĩnh khi traffic tăng đột biến.
- Lock SETNX đơn giản có thể deadlock khi worker chết giữa chừng. Lua script và lease TTL là bắt buộc.
- Testcontainers cho cả PostgreSQL và SQL Server xóa sổ cả nhóm lỗi "máy tôi chạy được".