Applied Case Studies: một bài toán đầy đủ
Áp dụng nội dung các chương trước vào một hệ thống hoàn chỉnh: data model, kiến trúc, cấu hình, kết quả đo, đánh đổi và quy trình tái hiện.
Mục lục chương
Bối cảnh case study
Một sàn E-commerce đa kênh với ba nhóm người dùng cùng truy vấn một data model.
Tình huống được xét là một tập đoàn E-commerce đa kênh (omnichannel) với hàng triệu giao dịch mỗi ngày. Hệ thống phân tích phải phục vụ cùng lúc ba bề mặt tiêu thụ. Mỗi bề mặt kết nối với Cube qua một cổng riêng nhưng cùng sử dụng một định nghĩa chỉ số. Chọn từng bề mặt để xem đối tượng sử dụng và nhu cầu tương ứng.
Bài toán và yêu cầu
Bốn yêu cầu bắt buộc, mỗi yêu cầu ứng với một tính năng của Cube.
Handbook đặt ra bốn yêu cầu: một định nghĩa duy nhất cho Weekly Active Users (WAU) và Net Revenue trên cả ba bề mặt; P95 dưới 0.25 giây trên các bảng dữ liệu từ hàng chục triệu dòng trở lên; multi-tenancy cho hơn 200 merchant; và AI Agent không được tự sinh SQL thô. Chọn một yêu cầu để xem tính năng Cube tương ứng.
Đọc sâuP95 nghĩa là gì?
P95 là phân vị thứ 95 của độ trễ: 95% truy vấn hoàn tất nhanh hơn ngưỡng này, 5% còn lại có thể chậm hơn. Mục tiêu được đặt theo P95 thay vì giá trị trung bình, vì giá trị trung bình che khuất các truy vấn chậm mà người dùng vẫn gặp phải.
Dữ liệu và data model
Bốn bảng thô, ba file cube, và một view chung cho BI và AI.
Data warehouse nguồn có bốn bảng: orders, customers, regions, events. Data model viết bằng YAML theo quy chuẩn v0.33: tên snake_case, cube đặt trong model/cubes/, view đặt trong model/views/. Measure net_revenue nằm trong orders, còn WAU được tính từ active_users của events theo tuần. Chọn từng dòng để xem nội dung khai báo.
Join từ orders sang customers được khai báo với relationship many_to_one. Nhờ khai báo này, Cube biết nhiều đơn hàng thuộc về một khách hàng, nên không tính trùng khi nối bảng (tránh fanout). View sales_data_mart chỉ công khai các trường cần cho báo cáo doanh thu, do đó AI Agent không phải tự chọn bảng.
Đọc sâuNhững điểm cần rà soát khi áp dụng thực tế
Model trong handbook đủ cho mục đích minh họa nhưng có một số điểm không nhất quán. Khi dùng model này làm điểm xuất phát cho dự án thực tế, cần kiểm tra:
1. Measure net_revenue lấy sql: profit. Lợi nhuận và doanh thu thuần là hai chỉ số khác nhau; cần xác nhận cột nguồn đúng với định nghĩa kinh doanh.
2. Dimension tenant_id lấy sql: customer_id. Trong case study, tenant là merchant (nhà bán hàng), không phải khách mua hàng. Nếu bảng orders có cột merchant, tenant nên trỏ vào cột đó.
3. View đi qua orders.customers.regions và lấy region_name, country_code, nhưng handbook không có file regions.yml. View chỉ compile được khi bổ sung cube regions.
4. Pre-aggregation tên merchant_monthly_sales nhưng granularity là day, partition_granularity là month. Tên gọi dễ gây hiểu nhầm là rollup theo tháng.
5. Yêu cầu nói về WAU, nhưng model chỉ có active_users đếm user_id không trùng. WAU thu được khi truy vấn active_users với time dimension event_time ở granularity week; handbook không khai báo measure WAU riêng.
Kiến trúc đề xuất
Ba bề mặt truy vấn qua Cube. Truy vấn khớp rollup được trả lời từ Cube Store; truy vấn không khớp được chuyển xuống data warehouse.
Mọi truy vấn đi qua view sales_data_mart và filter RLS theo tenant_id lấy từ JWT. Sau đó aggregate awareness xác định nguồn trả kết quả: nếu truy vấn khớp một pre-aggregation, Cube đọc bảng rollup trong Cube Store; nếu không khớp, Cube sinh SQL gửi xuống data warehouse (Postgres hoặc Snowflake). Dùng công tắc để xem đường đi của truy vấn, hoặc chọn từng tầng để xem chi tiết.
Đọc sâuNhánh “miss” và chế độ rollup-only
Mục 07.7 nêu rằng hệ thống bật CUBEJS_ROLLUP_ONLY=true. Ở chế độ rollup-only, truy vấn chỉ được phục vụ từ pre-aggregation, không được đẩy trực tiếp xuống data warehouse nguồn. Như vậy nhánh “Pre-agg miss → data warehouse” trong sơ đồ chỉ đúng khi chế độ này tắt. Khi bật, truy vấn không khớp rollup sẽ không đi xuống data warehouse; handbook không nói rõ case study chọn cách nào cho từng môi trường.
Triển khai
Ba cấu hình chính: pre-aggregation, RLS theo tenant từ JWT, và kết nối cho AI.
Thứ nhất, pre-aggregation merchant_monthly_sales ở 07.3 được chia partition theo tháng (partition_granularity) và làm mới mỗi 5 phút qua refresh_key. Theo handbook, cấu hình này giảm lượng dữ liệu cần quét từ hàng triệu dòng xuống vài nghìn dòng trong Cube Store.
Thứ hai, multi-tenancy dùng queryRewrite trong cube.js: hàm đọc tenant_id trong securityContext (giải mã từ JWT), sau đó thêm một filter vào mọi truy vấn. Chọn một JWT bên dưới để xem truy vấn sau khi được viết lại.
Thứ ba, AI Agent kết nối qua MCP. Agent dùng Meta API để khám phá view sales_data_mart trước khi gửi Semantic SQL hoặc GraphQL. Ba đoạn cấu hình liên quan được trình bày bên dưới.
Đọc sâuTăng độ an toàn cho queryRewrite
Code của handbook chỉ thêm filter khi có tenant_id. Nếu một request mang JWT hợp lệ nhưng thiếu claim này, truy vấn chạy không có filter và trả về dữ liệu của mọi merchant. Với yêu cầu cách ly giữa các merchant, cách an toàn hơn là từ chối truy vấn khi thiếu tenant_id:
queryRewrite: (query, { securityContext }) => {
if (!securityContext || !securityContext.tenant_id) {
throw new Error('Missing tenant_id in security context');
}
query.filters.push({
member: 'orders.tenant_id',
operator: 'equals',
values: [securityContext.tenant_id]
});
return query;
}Thực nghiệm và đánh giá
Bốn chỉ số trước và sau khi dùng Cube; mọi con số đều lấy từ case study.
Handbook đưa ra bảng so sánh bốn chỉ số. Dùng công tắc để chuyển giữa trạng thái trước và sau. Thanh ngang so sánh mỗi giá trị với giá trị “trước” của cùng chỉ số.
Handbook không mô tả cách đo: kích thước tập dữ liệu, thời gian quan sát, bộ câu hỏi dùng để chấm AI, hay ai chấm. Vì vậy, bảng này nên được xem là giả thuyết cần kiểm chứng, không phải kết quả có thể trích dẫn.
Kết quả, đánh đổi và bài học
Chấp nhận chi phí cho Cube Store để giảm chi phí quét trên data warehouse.
Cube Store phát sinh chi phí riêng: dung lượng ổ đĩa cho các file Parquet và RAM cho các worker node. Đổi lại, khi bật rollup-only, các truy vấn lặp lại từ người dùng và AI Agent được trả lời tại Cube Store, nhờ đó data warehouse chính không phải chịu concurrency lớn. Chọn từng mục để xem chi tiết đánh đổi.
Hướng dẫn tái hiện
Bốn bước chạy Cube Core và Postgres trên máy local bằng Docker Compose.
nickmancol/cube_bubble.io. Đây là repo mẫu tích hợp Cube với Bubble.io, không phải mã nguồn của case study này. Handbook không nêu rằng repo có sẵn data model ở 07.3; để thử model đó, cần tự đặt các file vào thư mục model/ và có dữ liệu tương ứng.Đánh dấu từng bước sau khi hoàn thành. Tên container và file SQL được giữ nguyên từ handbook; các tên này phụ thuộc vào repo mẫu và phiên bản Docker Compose, vì vậy cần kiểm tra lại bằng docker ps nếu lệnh báo lỗi.
Case study trong chương là tình huống giả định; các con số đều là số minh họa của handbook.