Menu
ESC

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

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

Đang tải...

Performance regression tracking — trend over time

Performance Testing with JMeter and k6 Bài 49/60

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

Hãy tưởng tượng bạn vừa chạy một bài load test rất công phu tối thứ Sáu. Kết quả đẹp: p95 latency 320ms, throughput 1.200 req/s, error rate 0%. Bạn tự tin ghi vào báo cáo "hệ thống chịu tải tốt", rồi tắt máy đi nghỉ cuối tuần. Ba tuần sau, khách hàng phàn nàn trang chậm. Bạn chạy lại đúng bài test đó: p95 giờ là 780ms. Câu hỏi ám ảnh nhất lúc này không phải "hệ thống chậm bao nhiêu" mà là "nó chậm đi từ lúc nào, và vì commit nào?". Nếu bạn không có dữ liệu lịch sử, bạn sẽ phải mò kim đáy bể qua hàng trăm commit.

Đây chính là lý do bài này tồn tại. Một bài performance test đơn lẻ chỉ cho bạn một snapshot — một tấm ảnh chụp hiệu năng tại một thời điểm. Nhưng hiệu năng của phần mềm không đứng yên: mỗi lần merge code, mỗi lần thêm index thiếu, mỗi lần nâng version thư viện, hiệu năng có thể xấu đi một chút. Từng chút một, âm thầm, cho đến ngày nó vượt ngưỡng chịu đựng của người dùng. Hiện tượng này gọi là performance regression — sự thoái hóa hiệu năng theo thời gian.

Trong bài này, chúng ta tập trung vào một chủ đề rất cụ thể và thường bị bỏ quên trong ngành QA Việt Nam: cách theo dõi xu hướng (trend) hiệu năng qua thời gian để phát hiện regression sớm, trước khi người dùng phát hiện giúp bạn. Đây là bước chuyển từ "kiểm thử hiệu năng như một sự kiện" sang "giám sát hiệu năng như một quá trình".

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

Snapshot vs Trend — hai tư duy khác nhau

Một snapshot trả lời câu hỏi: "Hệ thống có đạt SLA ngay bây giờ không?". Nó nhị phân — pass hoặc fail. Còn trend trả lời câu hỏi sâu hơn: "Hệ thống đang tốt lên hay xấu đi, và với tốc độ nào?". Trend cho bạn khả năng dự báo. Nếu p95 tăng đều 5ms mỗi tuần, bạn biết trong 10 tuần nữa nó sẽ vượt ngưỡng — và bạn có thời gian hành động thay vì bị động chữa cháy.

Sự khác biệt cốt lõi nằm ở chiều thời gian. Snapshot là một điểm dữ liệu. Trend là một chuỗi điểm dữ liệu được ghi lại có hệ thống, gắn với metadata cho phép truy vết: commit hash, ngày giờ, môi trường, phiên bản.

Performance regression là gì và tại sao nó lén lút

Regression hiệu năng khác hoàn toàn với bug chức năng. Bug chức năng thường rõ ràng: nút không bấm được, tính năng sai kết quả — test tự động bắt được ngay. Regression hiệu năng thì âm thầm và tích lũy. Một query N+1 mới thêm vào chỉ làm chậm thêm 40ms — không ai để ý trong môi trường dev với vài bản ghi. Nhưng trên production với 2 triệu bản ghi, 40ms đó thành 4 giây.

Các nguồn regression phổ biến bạn cần nhận diện:

  • Code change: thêm vòng lặp gọi API, query không index, serialization nặng hơn.
  • Dependency upgrade: nâng version framework, thư viện ORM đổi cách sinh SQL.
  • Data growth: bảng phình to làm query chậm dần — regression này không đến từ code mà từ dữ liệu.
  • Config drift: connection pool bị giảm, cache TTL thay đổi, JVM heap khác đi giữa các môi trường.

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

Không có baseline thì không có khái niệm regression. Baseline là kết quả tham chiếu mà bạn coi là "chuẩn tốt" để so mọi lần chạy sau. Baseline có thể là:

  • Kết quả của lần release ổn định gần nhất (ví dụ p95 của bản v2.3.0 đang chạy production mượt mà).
  • Trung bình trượt (rolling average) của 7 hoặc 14 lần chạy gần nhất — giúp lọc nhiễu.
Điểm quan trọng: baseline phải được cập nhật có kiểm soát. Nếu bạn cứ để baseline là "lần chạy hôm qua", thì một regression tăng chậm 2% mỗi ngày sẽ không bao giờ bị phát hiện, vì mỗi ngày chỉ lệch 2% so với hôm trước — hiện tượng gọi là boiling frog (con ếch bị luộc từ từ). Vì thế baseline nên neo vào một mốc cố định (release đã duyệt) và chỉ được "promote" lên baseline mới khi có người xác nhận đó là mức chấp nhận được.

Ngưỡng regression — tuyệt đối, tương đối, và thống kê

Khi nào thì gọi là regression? Bạn cần định nghĩa ngưỡng, và có ba cách:

  • Ngưỡng tuyệt đối: "p95 không được vượt 500ms". Đơn giản, dễ hiểu, nhưng cứng nhắc.
  • Ngưỡng tương đối (phần trăm): "p95 không được tăng quá 10% so với baseline". Đây là cách phổ biến nhất cho regression tracking vì nó tự thích nghi theo hệ thống.
  • Ngưỡng thống kê: dùng độ lệch chuẩn — báo động khi kết quả lệch quá 3 sigma so với trung bình lịch sử. Tinh vi nhất, lọc nhiễu tốt, nhưng cần đủ dữ liệu.
Trong thực tế, bạn thường kết hợp: dùng ngưỡng tương đối để bắt xu hướng, cộng thêm một biên nhiễu (noise margin) để tránh báo động giả khi hệ thống dao động tự nhiên vài phần trăm.

Nhiễu (noise) — kẻ thù của trend tracking

Không có hai lần chạy performance test nào cho ra con số y hệt nhau. Máy chủ có garbage collection ngẫu nhiên, mạng có độ trễ dao động, môi trường CI dùng chung tài nguyên. Sự dao động tự nhiên này gọi là noise. Nếu bạn báo động mỗi khi lệch 3%, bạn sẽ bị "alert fatigue" — quá nhiều báo động giả đến mức không ai còn tin. Chìa khóa của một hệ thống trend tracking tốt là phân biệt được tín hiệu (signal — regression thật) với nhiễu (noise — dao động tự nhiên). Cách làm: chạy đủ nhiều lần, dùng trung vị thay vì trung bình, và đặt ngưỡng cao hơn biên độ nhiễu đã đo được.

Tình huống thực tế

Tình huống 1 — Sàn TMĐT Việt Nam và regression "ẩn mình" trước mùa sale

Một sàn thương mại điện tử tại TP.HCM (gọi là ShopViet) có đội QA chạy load test thủ công mỗi khi sắp release lớn. Trước đợt sale 6.6, họ chạy test và thấy checkout API có p95 = 620ms — hơi cao nhưng "vẫn ổn". Không ai biết rằng ba tháng trước đó, con số này chỉ là 310ms. Vì họ chỉ chụp snapshot mỗi khi release, không có đường trend, nên họ mất hoàn toàn cảm nhận về việc hiệu năng đã xấu đi gấp đôi một cách âm thầm.

Khi sale 6.6 diễn ra, tải thật gấp 4 lần test, và checkout sập vì mỗi request ăn quá nhiều thời gian giữ connection. Điều tra sau đó phát hiện: một PR cách đó 11 tuần đã thêm một lệnh gọi service tính điểm loyalty ngay trong luồng checkout — mỗi lần thêm ~150ms. Sau đó một PR khác thêm 160ms nữa từ query lịch sử đơn hàng không index.

Bài học: Nếu ShopViet ghi lại kết quả mỗi đêm và vẽ trend, họ đã thấy hai "bậc thang" nhảy lên rõ rệt vào đúng hai ngày merge hai PR đó. Regression không phải là một cú sốc — nó là một chuỗi bước nhỏ mà chỉ có trend mới lộ ra. Sau sự cố, họ thiết lập nightly test và một dashboard trend đơn giản; lần regression tiếp theo bị bắt trong vòng 24 giờ.

Tình huống 2 — Fintech Singapore và ngưỡng tương đối cứu bàn thua

Một công ty fintech ở Singapore (gọi là PayNimbus) tích hợp k6 vào CI/CD. Ban đầu họ đặt ngưỡng tuyệt đối: http_req_duration p95 < 400ms. Test luôn pass vì API của họ nhanh, p95 quanh 180ms. Một hôm, một dev nâng version thư viện JSON serialization, khiến p95 nhảy từ 180ms lên 340ms — tăng gần 90%, một dấu hiệu regression cực kỳ nghiêm trọng. Nhưng vì 340ms vẫn dưới ngưỡng tuyệt đối 400ms, CI vẫn xanh, không ai được cảnh báo.

Ba tuần sau, dưới tải cao điểm, 340ms đó cộng dồn với các độ trễ khác đẩy hệ thống vượt SLA thật với khách hàng. Điều tra ngược mất hai ngày vì họ không lưu lịch sử theo commit.

Bài học: Ngưỡng tuyệt đối chỉ bảo vệ bạn khỏi "quá tệ", nhưng không phát hiện được "đang xấu đi nhanh". PayNimbus chuyển sang mô hình lưu baseline (p95 của lần release ổn định) và thêm một gate tương đối trong CI: fail nếu p95 tăng quá 15% so với baseline. Ngay tuần sau, một PR làm chậm 20% bị chặn ngay tại pull request, kèm comment tự động chỉ rõ commit và metric lệch. Regression được sửa trước khi vào main.

Tình huống 3 — SaaS logistics và cái bẫy baseline trôi

Một startup SaaS logistics ở Hà Nội (gọi là GiaoNhanh) đã có nightly test và lưu kết quả. Nhưng họ mắc lỗi tinh vi: baseline của họ luôn là kết quả đêm hôm trước. Trong hai tháng, p95 của API tra cứu vận đơn tăng đều đặn khoảng 1,5% mỗi đêm do bảng tracking phình to mà không có index phù hợp. Mỗi đêm so với đêm trước chỉ lệch 1,5% — dưới ngưỡng báo động 10% — nên không đêm nào báo động.

Nhưng sau 60 đêm, tích lũy lại p95 đã tăng gần 2,4 lần. Khi khách hàng lớn nhất phàn nàn, đội mới giật mình mở lại toàn bộ lịch sử và thấy đường trend đi lên đều như một con dốc.

Bài học: Đây là boiling frog kinh điển. Baseline phải neo vào một mốc cố định (ví dụ release ổn định gần nhất, hoặc trung bình 14 ngày trước), không được để nó "trôi" theo từng ngày. GiaoNhanh sửa lại: baseline = kết quả của bản release được QA duyệt, chỉ đổi khi có người chủ động promote. Đồng thời họ thêm một cảnh báo dài hạn: so kết quả hôm nay với mốc 30 ngày trước, giúp bắt cả những regression tăng chậm.

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

Dưới đây là quy trình dựng một hệ thống performance regression tracking từ con số không, áp dụng được cho cả JMeter lẫn k6.

Bước 1 — Cố định điều kiện test (test phải lặp lại được). Trend chỉ có ý nghĩa khi mỗi lần chạy giống nhau về: số user ảo, kịch bản, dữ liệu đầu vào, môi trường, và cấu hình. Nếu hôm nay chạy 100 VU, mai chạy 150 VU thì so sánh là vô nghĩa. Hãy dùng chung một test script cố định, chạy trên một môi trường ổn định (lý tưởng là môi trường riêng cho performance, không dùng chung với ai).

Bước 2 — Chạy test định kỳ tự động. Thiết lập một cron chạy nightly (ví dụ 2 giờ sáng khi hạ tầng rảnh). Với k6: k6 run --out json=result.json scenario.js. Với JMeter non-GUI: jmeter -n -t plan.jmx -l result.jtl. Điểm mấu chốt là tự động — regression tracking dựa vào con người chạy tay sẽ chết yểu.

Bước 3 — Trích xuất metrics chuẩn hóa từ mỗi lần chạy. Từ file kết quả, rút ra các con số cốt lõi: p50, p95, p99 latency, throughput, error rate. Chuẩn hóa thành một dòng dữ liệu gọn, ví dụ một dòng JSON hoặc CSV: {date, commit, p95, throughput, error_rate}. Đây là "nguyên liệu" của trend.

Bước 4 — Lưu kết quả kèm metadata truy vết. Đây là bước quyết định. Mỗi kết quả phải đi kèm: commit hash (để biết code nào), timestamp, môi trường, version. Không có commit hash thì bạn có trend nhưng không truy được nguyên nhân. Lưu vào đâu tùy quy mô: nhỏ thì một file CSV/SQLite trong Git; lớn hơn thì đẩy vào time-series database (InfluxDB) để vẽ dashboard theo thời gian.

Bước 5 — Định nghĩa baseline và ngưỡng regression. Chọn baseline là kết quả release ổn định gần nhất, hoặc rolling median 14 ngày. Đặt ngưỡng tương đối, ví dụ: cảnh báo nếu p95 tăng > 10%, fail build nếu tăng > 20%. Nhớ cộng biên nhiễu đã đo được.

Bước 6 — So sánh tự động và cảnh báo. Sau mỗi lần chạy, một script so kết quả mới với baseline. Nếu vượt ngưỡng, gửi cảnh báo (email, Slack, Telegram) kèm thông tin: metric nào lệch, lệch bao nhiêu %, commit nghi ngờ. Nếu chạy trong CI/CD, cho fail pull request để chặn regression ngay từ đầu.

Bước 7 — Trực quan hóa trend. Vẽ đồ thị p95/throughput/error rate theo thời gian, đánh dấu các mốc release. Một biểu đồ đường tốt cho phép cả team "thấy" hiệu năng đang đi lên hay xuống chỉ trong một cái liếc mắt. Đây là công cụ giao tiếp mạnh nhất với lãnh đạo và dev.

Bước 8 — Điều tra và promote baseline. Khi có cảnh báo, dùng commit hash để khoanh vùng nguyên nhân (git bisect trên hiệu năng). Khi một cải thiện hiệu năng được xác nhận là ổn định, chủ động promote nó thành baseline mới để lần sau so sánh với mốc tốt hơn.

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

Lỗi: So sánh giữa các môi trường khác nhau. Chạy baseline trên máy mạnh, rồi so với lần chạy trên CI runner yếu — trend sẽ đầy nhiễu giả. Luôn cố định một môi trường cho regression tracking.

Lỗi: Không lưu commit hash. Bạn thấy p95 tăng vọt ngày 15/6 nhưng không biết code nào gây ra. Metadata truy vết quan trọng ngang với chính con số metric.

Lỗi: Ngưỡng quá nhạy gây alert fatigue. Báo động mỗi khi lệch 3% khiến team tê liệt và bắt đầu bỏ qua mọi cảnh báo — kể cả cái thật. Hãy đo biên nhiễu trước, rồi đặt ngưỡng cao hơn nó rõ rệt.

Lỗi: Baseline trôi theo từng ngày. Như tình huống GiaoNhanh — regression tăng chậm sẽ lọt lưới. Neo baseline vào mốc cố định và thêm so sánh dài hạn (30 ngày).

Lỗi: Chỉ theo dõi latency, bỏ quên throughput và error rate. Một hệ thống có thể giữ p95 đẹp nhưng throughput tối đa tụt giảm — vẫn là regression. Theo dõi bộ metrics đủ, không chỉ một con số.

Mẹo: Dùng trung vị (median) thay vì trung bình (mean). Trung bình bị kéo lệch bởi vài request cá biệt chậm; trung vị ổn định hơn, phản ánh xu hướng thật tốt hơn.

Mẹo: Chạy đủ số lần để lọc nhiễu. Với hệ thống dao động mạnh, chạy 3 lần rồi lấy trung vị của mỗi metric sẽ cho đường trend mượt và đáng tin hơn nhiều so với một lần chạy đơn.

Mẹo: Đánh dấu mốc release trên đồ thị trend. Khi thấy "bậc thang" nhảy lên trùng đúng ngày release, bạn khoanh vùng nguyên nhân chỉ trong vài giây.

Mẹo: Bắt đầu nhỏ, đừng chờ hoàn hảo. Một file CSV commit vào Git cộng một biểu đồ vẽ tay đã tốt hơn vô hạn so với không có gì. Hạ tầng InfluxDB/Grafana có thể thêm sau.

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

  • Dựng chuỗi 7 ngày dữ liệu giả lập. Tạo một file CSV với 7 dòng, mỗi dòng là kết quả một đêm: date, commit, p95_ms, throughput, error_rate. Cố ý cài một "bậc thang" — 4 ngày đầu p95 quanh 300ms, 3 ngày sau nhảy lên quanh 450ms. Vẽ đồ thị đường và xác định chính xác ngày regression xảy ra cùng commit nghi ngờ.
  • Viết logic so sánh baseline. Chọn baseline là p95 trung bình của 4 ngày đầu. Viết một đoạn script (ngôn ngữ tùy bạn) nhận kết quả mới và in ra "PASS", "WARN" (tăng > 10%), hoặc "FAIL" (tăng > 20%). Chạy thử với con số ngày 5 để xem nó báo gì.
  • Thiết kế chiến lược chống boiling frog. Với chuỗi dữ liệu tăng đều 2% mỗi ngày trong 20 ngày, hãy chứng minh trên giấy rằng so sánh "hôm nay với hôm qua" không bao giờ báo động, trong khi so sánh "hôm nay với 14 ngày trước" thì có. Rút ra ngưỡng và khoảng so sánh phù hợp.
  • Phân biệt tín hiệu và nhiễu. Cho một chuỗi dao động ngẫu nhiên trong khoảng ±6% quanh 300ms, hãy quyết định ngưỡng cảnh báo nào (10%? 15%?) sẽ không tạo báo động giả nhưng vẫn bắt được một cú nhảy 25%. Giải thích lựa chọn.

Tóm tắt

Một bài performance test đơn lẻ chỉ là một snapshot — nó nói cho bạn biết hiện tại, nhưng mù tịt về xu hướng. Performance regression tracking biến kiểm thử hiệu năng từ một sự kiện rời rạc thành một quá trình giám sát liên tục, cho phép bạn bắt regression khi nó còn nhỏ, âm thầm và dễ sửa — thay vì khi nó đã thành sự cố production trước mắt khách hàng.

Ba trụ cột cần nhớ: (1) chạy test định kỳ, tự động, trong điều kiện cố định để dữ liệu có thể so sánh; (2) lưu mỗi kết quả kèm metadata truy vết — đặc biệt là commit hash — để trend không chỉ cho biết "chậm đi" mà còn "vì cái gì"; (3) so với một baseline neo cố định bằng ngưỡng tương đối hợp lý, đủ nhạy để bắt regression thật nhưng đủ cao để không chết chìm trong báo động giả. Cẩn thận với hai cái bẫy lớn: ngưỡng tuyệt đối bỏ lọt regression đang xấu đi nhanh (PayNimbus), và baseline trôi khiến con ếch bị luộc từ từ (GiaoNhanh).

Khi bạn nhìn được đường trend hiệu năng của hệ thống mình như nhìn một biểu đồ nhịp tim, bạn đã bước từ vai trò "người kiểm thử" sang vai trò "người canh gác hiệu năng" — và đó chính là khác biệt giữa một QA bình thường và một Performance Engineer thực thụ.