Product Management
Đăng nhập
ESC

Nhập từ khóa để tìm kiếm

↑↓ Di chuyển
Enter Mở
ESC Đóng

Project 4: Performance Smoke với Postman

Mở đầu — vì sao bài này quan trọng

Hãy tưởng tượng bạn là QA của một sàn thương mại điện tử ở TP.HCM. Đội dev vừa merge một tính năng mới, pipeline chạy xanh, tất cả 300 test chức năng đều PASS. Bạn thở phào, deploy lên production lúc 2 giờ chiều. Ba mươi phút sau, đội chăm sóc khách hàng bắt đầu nhận điện thoại: "Đăng nhập lâu quá, quay mãi không vào được." Hoá ra một query mới trong endpoint /auth/login không có index, thời gian phản hồi nhảy từ 200ms lên 4 giây. Test chức năng của bạn vẫn PASS — vì về mặt logic, login vẫn trả đúng token. Chỉ có điều nó trả về... quá chậm.

Đây chính là khoảng trống mà performance smoke test lấp vào. Nó không phải là load test đầy đủ (chúng ta sẽ phân biệt rõ điều này), mà là một cổng kiểm tra nhanh, nhẹ, chạy ngay sau khi deploy để trả lời một câu hỏi duy nhất: "Các endpoint sống còn có đang phản hồi trong ngưỡng thời gian chấp nhận được không?" Nếu không, chặn deploy hoặc bật cảnh báo ngay lập tức.

Trong Project 4 này, bạn sẽ tự tay dựng một performance smoke gate cho mỗi lần deploy, tập trung vào 3 endpoint quan trọng nhất của một hệ thống e-commerce. Đây là kỹ năng cực kỳ giá trị vì nó biến Postman — công cụ bạn đã dùng suốt khoá học — thành một tuyến phòng thủ đầu tiên chống lại các sự cố hiệu năng, mà gần như không tốn thêm hạ tầng.

Khái niệm cốt lõi

Performance smoke test là gì và không là gì

"Smoke test" là thuật ngữ mượn từ ngành điện tử: cắm điện thiết bị mới, nếu không có khói bốc lên thì nó chưa cháy — đủ tốt để kiểm tra sâu hơn. Trong ngữ cảnh API, performance smoke test là một tập nhỏ request tới các endpoint sống còn, kiểm tra rằng thời gian phản hồi nằm dưới một ngưỡng (threshold) định trước.

Điều quan trọng cần khắc cốt ghi tâm: performance smoke KHÔNG phải load test. Nó không mô phỏng 1.000 người dùng đồng thời, không đo throughput, không tìm điểm gãy của hệ thống. Nó chỉ trả lời "một request đơn lẻ, trong điều kiện bình thường, có nhanh không?". Vì thế nó chạy trong vài giây, rẻ, và có thể nhét vào mọi pipeline deploy mà không làm chậm quy trình.

Ba chỉ số bạn quan tâm trong smoke test

Trong Postman, mỗi response mang theo một biến sẵn có là pm.response.responseTime — thời gian tính bằng mili-giây từ lúc gửi request đến lúc nhận đủ response. Đây là nguyên liệu chính. Từ nó, bạn xây dựng ba loại kiểm tra:

  • Ngưỡng cứng (hard threshold): responseTime phải nhỏ hơn một con số cố định, ví dụ 800ms. Vượt là FAIL.
  • Ngưỡng theo tầng (tiered): mỗi endpoint có ngưỡng riêng, vì login nặng về xác thực khác với đọc danh sách sản phẩm.
  • Cảnh báo mềm (warning): chưa FAIL nhưng đã gần chạm ngưỡng — hữu ích để phát hiện xu hướng xấu dần trước khi nó thành sự cố.

Vì sao "một phát bắn" là không đủ

Một cạm bẫy kinh điển: bạn gửi request đúng một lần, nó trả về 700ms, dưới ngưỡng 800ms, PASS. Nhưng con số đó có thể là ngẫu nhiên — lần đầu gặp cache lạnh (cold start), hoặc mạng nghẽn nhất thời. Để smoke test đáng tin, ta cần lấy nhiều mẫu rồi xét thống kê, thường là p95 (95th percentile — 95% request nhanh hơn con số này) thay vì chỉ nhìn một lần đo. Postman cho phép ta lặp request bằng biến collection và setNextRequest, hoặc đơn giản hơn là chạy collection với nhiều iteration qua Collection Runner / Newman.

Baseline — điểm neo để so sánh

Ngưỡng 800ms không nên rơi từ trên trời xuống. Bạn thiết lập nó bằng cách đo baseline: chạy smoke test trên môi trường ổn định vài chục lần, ghi lại phân phối thời gian, rồi đặt ngưỡng cao hơn p95 baseline khoảng 20–30% để có biên an toàn. Baseline biến smoke test từ "cảm tính" thành "dựa trên dữ liệu".

Tình huống thực tế

Tình huống 1: Sàn Tiki phát hiện regression trước khi khách hàng biết

Một đội QA giả định tại một sàn kiểu Tiki đặt smoke gate cho GET /products?limit=20. Baseline p95 của họ là 240ms, nên họ đặt ngưỡng FAIL ở 400ms và warning ở 320ms. Trong một sprint, dev thêm tính năng "gợi ý sản phẩm tương tự" và vô tình gọi thêm một service nội bộ ngay trong luồng list sản phẩm. Test chức năng xanh hết. Nhưng khi smoke gate chạy trong staging, p95 vọt lên 610ms — FAIL. Pipeline bị chặn, dev nhận thông báo trong 4 phút, phát hiện lời gọi service thừa và chuyển nó sang xử lý bất đồng bộ. Bài học: smoke gate bắt được regression hiệu năng ngay tại staging, nơi sửa lỗi rẻ hơn hàng chục lần so với sửa trên production.

Tình huống 2: Fintech VN và cái bẫy "cold start"

Một startup fintech ở Hà Nội dựng smoke test cho POST /auth/login. Lần chạy đầu tiên mỗi sáng luôn FAIL với responseTime ~1.800ms, khiến cả team hoảng loạn tưởng có sự cố. Điều tra ra: server chạy trên nền tảng serverless, request đầu tiên phải khởi động container (cold start). Họ sửa bằng cách thêm một warm-up request không tính điểm ở đầu collection, rồi mới bắt đầu đo từ request thứ hai. Ngưỡng được đặt trên các mẫu "ấm". Bài học: đo hiệu năng phải hiểu rõ đặc tính hạ tầng; một warm-up request đơn giản loại bỏ nhiễu do cold start và tránh báo động giả làm team mất niềm tin vào gate.

Tình huống 3: Grab-style checkout và ngưỡng theo tầng

Một đội mô phỏng luồng đặt hàng kiểu Grab đặt chung một ngưỡng 500ms cho cả ba endpoint: login, list products, và POST /orders. Vấn đề là POST /orders vốn nặng (kiểm tra tồn kho, tính phí ship, ghi giao dịch) nên p95 thật của nó là 700ms — hoàn toàn bình thường. Gate liên tục FAIL ở endpoint orders dù chẳng có gì sai, khiến team bắt đầu bỏ qua kết quả gate (hiện tượng "alert fatigue" — mệt mỏi vì cảnh báo). Họ sửa bằng cách đặt ngưỡng riêng: login 600ms, products 400ms, orders 900ms. Bài học: ngưỡng phải phản ánh bản chất từng endpoint; một ngưỡng chung "cho tiện" sẽ vừa bỏ sót vấn đề ở endpoint nhẹ, vừa báo động giả ở endpoint nặng.

Hướng dẫn từng bước

Chúng ta sẽ dựng smoke gate cho 3 endpoint: POST /auth/login, GET /products?limit=20, và POST /orders.

Bước 1: Tạo collection và cấu trúc request

Tạo một collection tên Perf Smoke - Ecommerce. Bên trong, thêm một folder warmup chứa một request gọi tới /health (hoặc login) không tính điểm, và một folder smoke chứa ba request chính. Dùng environment với các biến baseUrl, email, password để tách cấu hình khỏi request (như bạn đã học ở các bài về Environments và Variables).

Bước 2: Định nghĩa ngưỡng ở cấp collection

Đặt các biến ngưỡng ở collection-level để dễ chỉnh và tái sử dụng. Vào tab Variables của collection, thêm:

THRESH_LOGIN     = 600
THRESH_PRODUCTS  = 400
THRESH_ORDERS    = 900
WARN_RATIO       = 0.8

WARN_RATIO = 0.8 nghĩa là nếu response đạt trên 80% ngưỡng thì bật cảnh báo mềm.

Bước 3: Viết test script đo hiệu năng cho từng request

Trong tab Scripts → Post-response (trước đây gọi là Tests) của request login, viết:

const limit = Number(pm.collectionVariables.get("THRESH_LOGIN"));
const rt = pm.response.responseTime;
const warnAt = limit * Number(pm.collectionVariables.get("WARN_RATIO"));

// Kiểm tra chức năng cơ bản trước pm.test("Login trả về 200", () => pm.response.to.have.status(200));

// Cổng hiệu năng pm.test(Login < ${limit}ms (thực tế: ${rt}ms), () => { pm.expect(rt).to.be.below(limit); });

// Cảnh báo mềm - không FAIL nhưng log lại if (rt >= warnAt && rt < limit) { console.warn(⚠ Login chậm: ${rt}ms (ngưỡng cảnh báo ${warnAt}ms)); }

// Lưu mẫu để tính p95 ở request tổng kết let samples = pm.collectionVariables.get("login_samples"); samples = samples ? JSON.parse(samples) : []; samples.push(rt); pm.collectionVariables.set("login_samples", JSON.stringify(samples));

Làm tương tự cho products (dùng THRESH_PRODUCTS) và orders (dùng THRESH_ORDERS). Nhớ dùng biến từ response login để lấy token cho hai request sau (kỹ thuật chained request bạn đã học).

Bước 4: Lấy nhiều mẫu bằng iteration

Đừng đo một lần. Chạy collection với nhiều iteration để có phân phối. Với Collection Runner, đặt Iterations = 20. Mảng login_samples sẽ tích luỹ 20 giá trị.

Bước 5: Thêm request tổng kết tính p95

Thêm một request cuối cùng (có thể gọi /health) chỉ để chạy script tổng kết. Trong Post-response của nó:

function percentile(arr, p) {
  const sorted = [...arr].sort((a, b) => a - b);
  const idx = Math.ceil((p / 100) * sorted.length) - 1;
  return sorted[Math.max(0, idx)];
}

["login", "products", "orders"].forEach(name => { const raw = pm.collectionVariables.get(${name}_samples); if (!raw) return; const arr = JSON.parse(raw); const p95 = percentile(arr, 95); const max = Math.max(...arr); console.log(${name}: p95=${p95}ms, max=${max}ms, n=${arr.length}); pm.test(${name} p95 trong ngưỡng, () => { const limit = Number(pm.collectionVariables.get(THRESH_${name.toUpperCase()})); pm.expect(p95).to.be.below(limit); }); });

Bước 6: Biến nó thành cổng deploy

Chạy collection bằng Newman (bài về Newman đã hướng dẫn) để tích hợp vào pipeline. Newman trả về exit code khác 0 nếu có test FAIL, nên chỉ cần đặt lệnh Newman làm một step trong pipeline deploy — nếu smoke gate FAIL, deploy dừng. Đây chính là cơ chế "gate".

Lỗi thường gặp & mẹo

Lỗi 1 — Đo trên máy local rồi tưởng đó là hiệu năng thật. Chạy Newman từ laptop của bạn ở quán cà phê wifi yếu sẽ cho responseTime đầy nhiễu mạng. Hãy chạy smoke gate từ một runner ổn định, đặt gần server (cùng vùng), để responseTime phản ánh hiệu năng server chứ không phải chất lượng mạng của bạn.

Lỗi 2 — Quên rằng responseTime bao gồm cả thời gian mạng. Postman đo tổng thời gian round-trip, không tách riêng thời gian xử lý server. Nếu cần độ chính xác cao hơn, so sánh với header Server-Timing mà nhiều backend trả về, hoặc chấp nhận rằng smoke test đo trải nghiệm end-to-end (vốn cũng là điều khách hàng cảm nhận).

Lỗi 3 — Ngưỡng đặt tuỳ hứng. Đừng gõ "500ms nghe cho đẹp". Luôn đo baseline trước, lấy p95, cộng biên 20–30%. Ngưỡng không dựa trên dữ liệu sẽ hoặc quá lỏng (bỏ sót) hoặc quá chặt (báo động giả).

Lỗi 4 — Không xử lý cold start. Như tình huống fintech: luôn có warm-up request không tính điểm nếu backend chạy serverless hoặc mới khởi động.

Mẹo — Tách kết quả hiệu năng khỏi kết quả chức năng. Đặt tên test rõ ràng ("login < 600ms") để khi đọc report HTML/JUnit, người ta phân biệt được đây là FAIL do chậm hay FAIL do logic. Điều này giúp phân loại sự cố nhanh hơn nhiều.

Mẹo — Chạy smoke ngay sau deploy, không phải trước. Mục tiêu của gate là bắt regression do chính lần deploy này gây ra, nên nó phải chạy trên môi trường vừa được deploy.

Bài tập thực hành

  • Dựng collection cơ bản: Tạo Perf Smoke - Ecommerce với 3 request (login, products, orders) dùng một public API mock hoặc postman-echo.com. Thêm biến ngưỡng ở collection-level như hướng dẫn.
  • Đo baseline: Chạy collection 30 iteration bằng Collection Runner. Ghi lại p95 và max của từng endpoint bằng script tổng kết. Từ đó, tự đặt ngưỡng FAIL = p95 × 1.25.
  • Mô phỏng regression: Thêm một request tới một endpoint cố tình chậm (ví dụ https://postman-echo.com/delay/2 trả về sau 2 giây) và xác nhận smoke gate của bạn FAIL đúng như kỳ vọng.
  • Thêm cảnh báo mềm: Cải tiến script để in ra console cảnh báo khi responseTime vượt 80% ngưỡng nhưng chưa FAIL. Chạy lại và quan sát log.
  • Nâng cao: Viết lệnh Newman chạy collection với reporter JUnit, rồi kiểm tra exit code bằng echo $? trên terminal. Xác nhận exit code = 1 khi có FAIL và = 0 khi tất cả PASS — đây là cơ sở để cắm vào pipeline deploy.

Tóm tắt

Performance smoke test là tuyến phòng thủ đầu tiên, nhẹ và rẻ, chống lại các sự cố hiệu năng lọt qua test chức năng. Nó không thay thế load test, mà trả lời một câu hỏi hẹp nhưng sống còn: các endpoint quan trọng có phản hồi đủ nhanh sau mỗi deploy không?

Những điểm cần nhớ: dùng pm.response.responseTime làm nguyên liệu; đặt ngưỡng theo tầng riêng cho từng endpoint dựa trên baseline thật; lấy nhiều mẫu và xét p95 thay vì một lần đo; xử lý cold start bằng warm-up request; và biến collection thành cổng deploy thật sự qua exit code của Newman trong pipeline. Ba tình huống — regression bị chặn ở staging, cold start gây báo động giả, và ngưỡng chung gây alert fatigue — cho thấy giá trị lớn nhất của smoke gate nằm ở việc đặt ngưỡng đúng và tin cậy, để cả team thật sự hành động khi nó cảnh báo. Đó là điều biến một script đo thời gian đơn giản thành một công cụ bảo vệ chất lượng thực thụ.

Học xong bài này rồi? Tạo tài khoản miễn phí để lưu lại — lần sau vào là biết ngay đang dở ở đâu, và học hết khóa thì có chứng chỉ. Lưu tiến độ của tôi