Medusa Commerce Load Testing
Medusa Commerce Load Testing k6 Stress Spike Soak PostgreSQL Node.js API Performance Bottleneck Throughput Production
| Test Type | Load | Duration | Purpose |
|---|---|---|---|
| Load Test | Expected (100-500 VUs) | 10-30 min | ยืนยัน Performance ที่ Load ปกติ |
| Stress Test | 2-5x Expected | 10-20 min | หาจุด Breaking Point |
| Spike Test | 0 → Max → 0 ทันที | 5-10 min | จำลอง Flash Sale Traffic Spike |
| Soak Test | Expected Load ต่อเนื่อง | 2-8 hours | หา Memory Leak Connection Leak |
k6 Test Script
# === k6 Load Test for Medusa Commerce ===
# import http from 'k6/http';
# import { check, group, sleep } from 'k6';
# import { Rate, Trend } from 'k6/metrics';
#
# const BASE_URL = 'http://localhost:9000/store';
# const checkoutErrors = new Rate('checkout_errors');
# const cartLatency = new Trend('cart_add_latency');
#
# export const options = {
# stages: [
# { duration: '2m', target: 50 }, // ramp-up to 50 VUs
# { duration: '5m', target: 50 }, // hold 50 VUs
# { duration: '2m', target: 200 }, // ramp-up to 200 (stress)
# { duration: '5m', target: 200 }, // hold 200 VUs
# { duration: '2m', target: 0 }, // ramp-down
# ],
# thresholds: {
# http_req_duration: ['p(95)<500', 'p(99)<1000'],
# http_req_failed: ['rate<0.01'],
# checkout_errors: ['rate<0.05'],
# cart_add_latency: ['p(95)<300'],
# },
# };
#
# export default function () {
# group('Browse Products', () => {
# const res = http.get(`/products?limit=20`);
# check(res, { 'products 200': (r) => r.status === 200 });
# sleep(2);
# });
# group('View Product Detail', () => {
# const res = http.get(`/products/prod_01`);
# check(res, { 'detail 200': (r) => r.status === 200 });
# sleep(1);
# });
# group('Add to Cart', () => {
# const start = Date.now();
# const res = http.post(`/carts/cart_01/line-items`,
# JSON.stringify({ variant_id: 'var_01', quantity: 1 }),
# { headers: { 'Content-Type': 'application/json' } });
# cartLatency.add(Date.now() - start);
# check(res, { 'cart 200': (r) => r.status === 200 });
# sleep(1);
# });
# group('Checkout', () => {
# const res = http.post(`/carts/cart_01/complete`);
# const ok = check(res, { 'checkout 200': (r) => r.status === 200 });
# if (!ok) checkoutErrors.add(1);
# sleep(3);
# });
# }
from dataclasses import dataclass
@dataclass
class TestScenario:
scenario: str
endpoint: str
method: str
target_p95: str
weight: str
scenarios = [
TestScenario("Browse Products",
"/store/products?limit=20",
"GET",
"< 200ms",
"40% of requests"),
TestScenario("Product Detail",
"/store/products/{id}",
"GET",
"< 150ms",
"25% of requests"),
TestScenario("Add to Cart",
"/store/carts/{id}/line-items",
"POST",
"< 300ms",
"15% of requests"),
TestScenario("Checkout",
"/store/carts/{id}/complete",
"POST",
"< 500ms",
"5% of requests"),
TestScenario("Search Products",
"/store/products?q={query}",
"GET",
"< 300ms",
"10% of requests"),
TestScenario("Order History",
"/store/customers/me/orders",
"GET",
"< 200ms",
"5% of requests"),
]
print("=== Test Scenarios ===")
for s in scenarios:
print(f" [{s.scenario}] {s.method} {s.endpoint}")
print(f" Target P95: {s.target_p95} | Weight: {s.weight}")
Bottleneck Analysis
# === Bottleneck Analysis ===
# PostgreSQL Slow Query Analysis
# SELECT query, calls, mean_exec_time, total_exec_time
# FROM pg_stat_statements
# ORDER BY mean_exec_time DESC
# LIMIT 20;
#
# Check Missing Index
# SELECT schemaname, tablename, seq_scan, idx_scan
# FROM pg_stat_user_tables
# WHERE seq_scan > idx_scan
# ORDER BY seq_scan DESC;
#
# Check Connection Pool
# SELECT count(*), state FROM pg_stat_activity GROUP BY state;
#
# Node.js Event Loop Lag
# const { monitorEventLoopDelay } = require('perf_hooks');
# const h = monitorEventLoopDelay({ resolution: 20 });
# h.enable();
# setInterval(() => {
# console.log(`Event Loop P99: ms`);
# }, 5000);
@dataclass
class Bottleneck:
layer: str
symptom: str
diagnosis: str
fix: str
impact: str
bottlenecks = [
Bottleneck("Database: Slow Query",
"P95 Latency สูง ทุก API ที่ Query DB",
"pg_stat_statements: mean_exec_time > 100ms",
"สร้าง Index ตาม Query Pattern Optimize Query",
"ลด Latency 50-90% (เร็วสุด ง่ายสุด)"),
Bottleneck("Database: Connection Pool",
"Connection Timeout Errors ที่ High Load",
"pg_stat_activity: active > pool_size",
"เพิ่ม Pool Size ใช้ PgBouncer",
"ลด Error Rate จาก Connection Exhaustion"),
Bottleneck("App: Event Loop Blocking",
"Response Time เพิ่มทุก API พร้อมกัน",
"Event Loop Delay P99 > 100ms",
"Offload Heavy Work Worker Thread/Queue",
"ลด Latency สำหรับทุก Request"),
Bottleneck("App: Memory Leak",
"Memory เพิ่มขึ้นเรื่อยๆ (Soak Test)",
"Heap Usage เพิ่มไม่ลง GC ทำงานบ่อย",
"Profile ด้วย --inspect หา Leak Fix Code",
"ป้องกัน OOM Crash Production"),
Bottleneck("Cache: Low Hit Rate",
"DB Load สูงแม้ Traffic ไม่มาก",
"Redis Hit Rate < 50%",
"Cache Popular Queries Product List Detail",
"ลด DB Load 50-80%"),
Bottleneck("External: Payment Gateway",
"Checkout Latency สูงกว่า API อื่น",
"Payment API P95 > 2s",
"Async Payment Processing Queue-based",
"ลด Checkout Latency ป้องกัน Timeout"),
]
print("=== Bottleneck Analysis ===")
for b in bottlenecks:
print(f"\n [{b.layer}]")
print(f" Symptom: {b.symptom}")
print(f" Diagnosis: {b.diagnosis}")
print(f" Fix: {b.fix}")
print(f" Impact: {b.impact}")
Production Optimization
# === Performance Optimization Checklist ===
@dataclass
class OptItem:
category: str
action: str
expected_gain: str
effort: str
optimizations = [
OptItem("Database",
"สร้าง Index ตาม pg_stat_statements Top Queries",
"Latency ลด 50-90% สำหรับ Slow Queries",
"ต่ำ (1-2 ชม.)"),
OptItem("Cache",
"Redis Cache: Product List Detail Category",
"DB Load ลด 50-80% Latency ลด 70%",
"ปานกลาง (1-2 วัน)"),
OptItem("Connection Pool",
"PgBouncer + Optimize Pool Size",
"รองรับ Concurrent Users เพิ่ม 2-5x",
"ต่ำ (2-4 ชม.)"),
OptItem("CDN",
"CDN สำหรับ Product Images Static Assets",
"Image Load Time ลด 50-80%",
"ต่ำ (1-2 ชม.)"),
OptItem("Horizontal Scale",
"เพิ่ม Medusa Instance + Load Balancer",
"Throughput เพิ่ม 2-4x ต่อ Instance",
"ปานกลาง (1 วัน)"),
OptItem("Read Replica",
"PostgreSQL Read Replica สำหรับ Read API",
"DB Write Performance ไม่กระทบจาก Read",
"ปานกลาง (1 วัน)"),
OptItem("Queue",
"Queue สำหรับ Payment Email Notification",
"Checkout Response Time ลด 30-50%",
"ปานกลาง (2-3 วัน)"),
]
print("=== Optimization Checklist ===")
for o in optimizations:
print(f" [{o.category}] {o.action}")
print(f" Gain: {o.expected_gain} | Effort: {o.effort}")
เคล็ดลับ
- k6: ใช้ k6 สำหรับ Load Test เขียน JavaScript ง่าย เร็ว
- Index: เริ่มจาก Database Index เร็วสุด Impact สูงสุด
- Cache: Cache Product List Detail ลด DB Load 50-80%
- Spike Test: ทำ Spike Test ก่อน Flash Sale Campaign
- Soak Test: ทำ Soak Test 2-8 ชม. หา Memory Leak
Medusa Commerce คืออะไร
Open Source Headless Commerce Node.js TypeScript PostgreSQL REST API Product Cart Checkout Order Payment Shipping Plugin
Load Testing คืออะไร
ทดสอบ Performance Load Stress Spike Soak k6 Locust JMeter VUs RPS P95 P99 Error Rate Apdex Threshold Concurrent
k6 Script เขียนอย่างไร
JavaScript stages VUs thresholds group check sleep http.get http.post SharedArray Trend Rate Custom Metrics influxdb grafana
Bottleneck หาอย่างไร
pg_stat_statements Slow Query Connection Pool Event Loop Memory Leak Redis Hit Rate Payment Gateway Index PgBouncer Cache Queue
สรุป
Medusa Commerce Load Testing k6 Stress Spike Soak PostgreSQL Index Cache PgBouncer CDN Scale Replica Queue Bottleneck Production
