1. Bối cảnh nghiệp vụ: flash-sale và bài toán quy mô, đồng thời, tồn kho
Flash-sale là chương trình bán hàng giảm giá mạnh trong khoảng thời gian rất ngắn, thường vài phút tới vài giờ, với số lượng hàng giới hạn. Đặc trưng của nó là một lượng người dùng khổng lồ đổ vào cùng lúc để tranh mua một lượng hàng ít ỏi. Đây là cơn ác mộng kinh điển của kỹ thuật: quy mô lớn, đồng thời cực cao, và tồn kho là tài nguyên khan hiếm phải tranh chấp. Với người kiểm thử, flash-sale là phép thử gắt gao nhất cho tính đúng đắn của checkout, vì mọi lỗi tiềm ẩn về đồng thời vốn im lặng trong tải thấp sẽ bùng lên khi hàng nghìn request cùng chạm vào một dòng tồn kho trong cùng một mili-giây.
Bài viết này tiếp cận flash-sale từ hai góc kết hợp. Thứ nhất là dùng AI để sinh dữ liệu tổng hợp ở quy mô lớn, tạo ra hàng chục nghìn hồ sơ người mua với giỏ hàng, thẻ thanh toán, địa chỉ đa dạng và hợp lệ về mặt cấu trúc, đủ để mô phỏng đám đông thật. Thứ hai và quan trọng hơn là định nghĩa oracle bất biến nghiệp vụ mà hệ thống phải giữ đúng ở mọi thứ tự thực thi: tồn kho không âm, không bán vượt, giá và khuyến mãi tính đúng, đơn hàng idempotent. AI giúp ta tạo tải và dữ liệu; con người định nghĩa đâu là đúng. Đó là ranh giới xuyên suốt bài.
2. Oracle bất biến: tồn kho không âm, không oversell, giá đúng, idempotent
Trước khi bàn tới cách tạo tải, ta phải chốt oracle vì oracle quyết định test có ý nghĩa hay không. Với checkout flash-sale, có sáu bất biến cốt lõi. Một, tồn kho không bao giờ âm: dù bao nhiêu người tranh mua, số tồn của mỗi SKU luôn lớn hơn hoặc bằng không. Hai, không bán vượt: tổng số lượng đã bán không bao giờ vượt tồn ban đầu. Ba, giá và khuyến mãi tính đúng tới từng đơn vị tiền nhỏ nhất. Bốn, đơn hàng idempotent: một thao tác đặt hàng lặp lại do double-submit hay retry mạng chỉ tạo đúng một đơn. Năm, thanh toán và tồn kho nhất quán: không bao giờ có tình huống đã trừ tiền mà không có hàng, hoặc đã giữ hàng mà không thu tiền. Sáu, hoàn tiền hoàn tồn kho đúng.
Điểm mấu chốt của các bất biến này là chúng phải đúng ở mọi thứ tự thực thi có thể xảy ra, không chỉ ở đường hạnh phúc tuần tự. Đây chính là lý do test tuần tự đơn giản luôn xanh mà vẫn giấu lỗi: nó chỉ chạy một thứ tự duy nhất trong vô số thứ tự khả dĩ. Một bất biến đúng khi chạy tuần tự có thể vỡ tan khi hai request xen kẽ nhau đúng vào khoảnh khắc nhạy cảm. Vì vậy oracle không chỉ là danh sách điều kiện, mà phải đi kèm cam kết rằng ta sẽ kiểm chúng dưới điều kiện đồng thời thật, tức nhiều request chạy song song tranh chấp cùng tài nguyên.
// invariants.ts — oracle bất biến nghiệp vụ, kiểm sau mỗi kịch bản tải
import { db } from './db';
export async function assertInventoryInvariants(sku: string, initialStock: number) {
const stock = await db.stock(sku);
const sold = await db.soldCount(sku);
// 1 · Tồn không bao giờ âm
if (stock < 0) throw new Error('INVARIANT: tồn âm = ' + stock);
// 2 · Không oversell: đã bán + còn lại = ban đầu, và đã bán <= ban đầu
if (sold > initialStock) throw new Error('INVARIANT: oversell ' + sold + '/' + initialStock);
if (stock + sold !== initialStock)
throw new Error('INVARIANT: mất cân bằng tồn ' + (stock + sold) + ' != ' + initialStock);
}
export async function assertOrderIdempotency(idemKey: string) {
const orders = await db.ordersByIdemKey(idemKey);
// 4 · Idempotency (冪等性): 1 key → đúng 1 đơn
if (orders.length !== 1) throw new Error('INVARIANT: idemKey tạo ' + orders.length + ' đơn');
}3. Dữ liệu tổng hợp do AI sinh ở quy mô lớn
Để mô phỏng đám đông flash-sale một cách chân thực, ta cần hàng chục nghìn hồ sơ người mua đa dạng: tên, email, địa chỉ giao ở nhiều tỉnh thành, thẻ thanh toán thuộc nhiều loại, và giỏ hàng với tổ hợp sản phẩm khác nhau. Sinh tay thì bất khả thi, còn dữ liệu quá đơn điệu thì không lộ được lỗi. AI giúp sinh dữ liệu tổng hợp phong phú và hợp lệ về cấu trúc, nhưng phải tuân thủ hai nguyên tắc bắt buộc: không bao giờ dùng dữ liệu thật của khách hàng, và mọi thông tin nhạy cảm như số thẻ phải là giá trị test hợp lệ về định dạng nhưng không phải thẻ thật, ví dụ số thẻ test tiêu chuẩn của cổng thanh toán.
// synth-data.ts — AI sinh hồ sơ đa dạng nhưng có RÀNG BUỘC (không PII thật, thẻ test)
import { z } from 'zod';
// Schema ràng buộc — AI phải sinh khớp; ta validate lại, không tin mù output AI
export const BuyerSchema = z.object({
name: z.string().min(2).max(60),
email: z.string().email(),
province: z.enum(['HN', 'HCM', 'DN', 'CT', 'HP']),
card: z.enum(['4242424242424242', '5555555555554444']), // THẺ TEST, không thật
cart: z.array(z.object({ sku: z.string(), qty: z.number().int().min(1).max(3) })).min(1),
});
export type Buyer = z.infer<typeof BuyerSchema>;
// Sinh N hồ sơ; MỌI bản ghi phải qua validate schema trước khi dùng
export function validateSynthetic(rows: unknown[]): Buyer[] {
return rows.map((r, i) => {
const p = BuyerSchema.safeParse(r);
if (!p.success) throw new Error('SYNTH row ' + i + ' không hợp lệ: ' + p.error.message);
return p.data;
});
}Điểm cần nhấn mạnh là ta không bao giờ tin mù đầu ra của AI. Mọi bản ghi tổng hợp đều phải đi qua một schema ràng buộc và được validate lại trước khi đưa vào test. Lý do là AI có thể sinh ra dữ liệu lệch phân phối, trùng lặp, hoặc vi phạm ràng buộc nghiệp vụ mà ta không lường trước, ví dụ số lượng đặt hàng vượt giới hạn hợp lý. Schema đóng vai trò cổng chất lượng cho chính dữ liệu test. Ngoài validate, ta còn nên chủ động yêu cầu AI sinh cả các ca biên như tên rất dài, ký tự unicode hiếm, địa chỉ thiếu trường, vì chính những ca biên này mới hay lộ lỗi validation của checkout.
4. Kiểm thử đồng thời và race condition trên tồn kho
Race condition là lỗi phát sinh khi kết quả phụ thuộc vào thứ tự xen kẽ của các thao tác đồng thời. Trong flash-sale, race nguy hiểm nhất nằm ở việc trừ tồn kho. Nếu code làm theo kiểu đọc tồn rồi kiểm rồi ghi mà không có khoá hay thao tác nguyên tử, thì hai request có thể cùng đọc thấy còn một món, cùng kết luận là bán được, rồi cùng tạo đơn, dẫn tới bán vượt. Đây là loại lỗi mà test tuần tự không bao giờ bắt được vì nó chỉ xuất hiện khi hai luồng thực sự chạy song song và xen kẽ vào đúng khoảnh khắc. Để lộ nó, test phải phát nhiều request đồng thời tranh chấp cùng một SKU cuối cùng.
💬 Bình luận (0)
Chưa có bình luận nào. Hãy là người đầu tiên!