تیم میگوید: «تست را با ۱۰۰۰ کاربر همزمان اجرا کردیم و سیستم جواب داد.» اما این عدد بهتنهایی هیچ مدل باری نمیسازد. آیا کاربران هر ثانیه درخواست میفرستادند یا بیشتر زمان را Think میکردند؟ چند نفر پرداخت میکردند؟ Cache گرم بود؟ Load generator خودش اشباع نشده بود؟
برنامه تست عملکرد باید رفتار موردانتظار سیستم را به Workload قابلبازتولید، معیار پذیرش و داده تشخیصی تبدیل کند. این مقاله یک Test Plan پیشرفته برای فروشگاه ایرانی میسازد: از Arrival rate و Journey mix تا p95/p99، محیط، Observability، اجرای امن و گزارش Bottleneck.
اگر هنوز تفاوت Load، Stress، Spike، Soak و Scalability را میخواهید، ابتدا راهنمای پایه تست عملکرد را بخوانید. این صفحه عمداً انواع را تکرار نمیکند و روی طراحی و اجرای برنامه تمرکز دارد.
خلاصه تصمیم: پیش از انتخاب ابزار، سؤال کسبوکار، مرز سیستم، مدل Open/Closed، توزیع تراکنش، نرخ ورود، داده، SLO، Telemetry، محدودیت محیط و شرط توقف را بنویسید. نتیجه بدون این زمینه قابلتفسیر نیست.
Test Plan عملکرد باید به چه سؤالهایی پاسخ دهد؟
- کدام رویداد کسبوکار یا ریسک Release را بررسی میکنیم؟
- مرز سیستم و وابستگیهای واقعی/شبیهسازیشده چیست؟
- بار چگونه وارد میشود و کاربران چه رفتاری دارند؟
- کدام SLI و Threshold در چه Percentile و Window پذیرفتنی است؟
- محیط و داده چقدر نماینده Production هستند؟
- چگونه Client، Service، Queue، Database و Dependency را مشاهده میکنیم؟
- چه زمانی تست را متوقف میکنیم؟
- نتیجه قرار است کدام تصمیم ظرفیت، معماری یا Release را تغییر دهد؟
Performance test «ببینیم چه میشود» نیز میتواند Exploratory باشد، اما باید سؤال و Safety boundary داشته باشد. عبارت «سایت سریع باشد» را با الگوی الزام غیرکارکردی قابلاندازهگیری دقیق کنید.
گام ۱: هدف و مرز سیستم را تعریف کنید
هدف نمونه
در کمپین فروش، سرویس Checkout باید نرخ هدف ایجاد Order و شروع Payment را بدون Duplicate order، با Latency و Error budget مشخص تحمل کند؛ پس از Spike نیز Queue باید در زمان قابلقبول تخلیه شود.
داخل مرز
- API Gateway، Checkout، Inventory، Pricing و Order؛
- Database، Cache و Message broker؛
- callback handler پرداخت با Stub قراردادی؛
- Monitoring، Trace و Log لازم برای تشخیص.
خارج مرز یا محدودشده
- درگاه واقعی، SMS و سرویس بیرونی بدون مجوز Load؛
- مرورگر واقعی برای تمام بار—پروتکلسطحی بار اصلی را میسازد؛
- پرداخت یا پیام به کاربر واقعی؛
- ظرفیت Production اگر محیط کوچکتر است؛ فقط نسبت و محدودیت گزارش میشود.
وابستگی خارج مرز را کور نکنید. Contract/Latency/Error profile آن را با Stub مدل و چند تست مجاز جدا روی Sandbox اجرا کنید. روش طراحی مرز و Idempotency در راهنمای تست API آمده است.
گام ۲: مدل بار را از داده واقعی بسازید
Concurrency با Throughput یکی نیست
صد کاربر همزمان میتوانند در یک دقیقه صد یا دههزار درخواست بسازند. Think time، طول Session، تعداد Request هر Journey و Response time رابطه را تغییر میدهند.
- Arrival rate: تعداد Journey/Request تازه در واحد زمان؛
- Concurrency: کارهای همزمان داخل سیستم؛
- Throughput: کار تکمیلشده در واحد زمان؛
- Think/Pacing: فاصله رفتار کاربر یا تکرار Script؛
- Service time/Latency: زمان پردازش و انتظار.
در یک سیستم پایدار، قانون Little بهصورت تقریبی N = X × R ارتباط تعداد متوسط کارهای داخل سیستم، Throughput و زمان متوسط را نشان میدهد. اگر Queue بدون حد رشد کند یا سیستم پایدار نباشد، استفاده ساده از این رابطه گمراهکننده است.
Open و Closed workload model
Open model ورود کار را مستقل از سرعت پاسخ با نرخ مشخص مدل میکند؛ برای ترافیک عمومی یا callback مناسبتر است. Closed model تعداد کاربر مجازی ثابت دارد و کاربر بعد از پاسخ/Think، چرخه بعدی را شروع میکند؛ برای Pool محدود یا Session تعاملی مفید است.
در Closed model وقتی سیستم کند میشود، کاربر مجازی درخواست بعدی را دیرتر میفرستد و بار ورودی خودبهخود کم میشود؛ این رفتار میتواند Overload واقعی Open system را پنهان کند. ابزار و Executor باید مدل هدف را پشتیبانی کند.
منابع داده Workload
- Access log و API gateway؛
- Analytics و RUM با توجه به Consent/Privacy؛
- Metric صف و callback؛
- تقویم کمپین، حقوق، ثبتنام یا رویداد خبری؛
- پیشبینی کسبوکار با ضریب عدمقطعیت؛
- نسبت Cache hit، Device، Region و نوع کاربر؛
- Background job، Import، Report و Cron همزمان.
میانگین روزانه Peak پنجدقیقهای را پنهان میکند. Window مناسب و Seasonality را ثبت کنید.
نمونه Workload فروشگاه ایرانی
اعداد زیر آموزشیاند و Benchmark عمومی نیستند. تیم از Peak مشاهدهشده و پیشبینی کمپین این سناریو را میسازد:
| Journey | سهم ورود Journey | نرخ هدف | داده/نکته |
|---|---|---|---|
| Browse category | ۵۰٪ | ۲۰۰ Journey/min | Cache hit/miss و Pagination |
| Search | ۲۵٪ | ۱۰۰/min | فارسی، نیمفاصله، فیلتر و Sort |
| Cart update | ۱۵٪ | ۶۰/min | موجودی، تخفیف و چندکالایی |
| Create order | ۷٫۵٪ | ۳۰/min | داده یکتا و Idempotency key |
| Start payment | ۲٫۵٪ | ۱۰/min | Stub درگاه و مبلغ معتبر |
callback پرداخت جریان جداگانه Open دارد: Baseline پنج callback/min، Peak بیست/min و Burst کنترلشده شصت/min. آن را داخل Think time کاربر ادغام نمیکنیم، چون منبع و الگوی ورود مستقل است.
شکل بار نمونه
- Warm-up کنترلشده برای آمادهشدن Runtime و Cache؛
- Ramp تا Baseline بدون شوک ناخواسته؛
- Steady state بهاندازه کافی برای مشاهده روند؛
- Ramp تا Peak کمپین؛
- Burst callback؛
- Recovery و Drain صف؛
- Cool-down و جمعآوری Artifact.
مدت هر فاز را با رفتار سیستم و هدف انتخاب کنید. «۲۰ دقیقه» قانون جهانی نیست؛ Soak برای Memory/connection leak به پنجره طولانیتر نیاز دارد.
گام ۳: SLI و معیار پذیرش را طراحی کنید
Latency در Percentile
Average دم توزیع را پنهان میکند. p50 تجربه معمول، p95/p99 تجربه کندترها و Maximum اغلب Outlier یا Noise است. Threshold باید per-transaction باشد.
| تراکنش | شرط نمونه | Guardrail کسبوکار |
|---|---|---|
| Search | p95 < ۸۰۰ms، p99 < ۱۵۰۰ms | نتیجه صحیح و Pagination پایدار |
| Create order | p95 < ۱۰۰۰ms، p99 < ۲۰۰۰ms | Duplicate order = 0 |
| Start payment | p95 < ۱۲۰۰ms | مبلغ و Idempotency صحیح |
| Callback | p99 < ۱۵۰۰ms در Peak | Transition نامعتبر/اثر تکراری = ۰ |
این Thresholdها مثالاند. SLO باید از نیاز کاربر، قرارداد، Baseline و ظرفیت هزینه بیاید. واحد، نقطه اندازهگیری و Window را مشخص کنید: Client observed، Gateway یا Service time یکسان نیستند.
Error rate معنادار
HTTP ۵۰۰ تنها خطا نیست. Timeout، Connection error، پاسخ Schema-invalid، Business rejection غیرمنتظره، Duplicate و داده ناسازگار را جدا کنید. ۴۲۹ ممکن است Protection موردانتظار یا نشانه کمبود ظرفیت باشد؛ زمینه تعیین میکند.
Throughput و Goodput
Throughput پاسخدادهشده میتواند شامل Retry و Fail باشد. Goodput تراکنش موفق و معتبر در محدوده SLO است. برای Checkout، تعداد Order معتبر و یکتا مهمتر از Request count خام است.
Saturation و ظرفیت
CPU، Memory، GC، Connection pool، Thread/event loop، Queue depth، Disk/Network I/O و DB lock را ببینید. Threshold عمومی «CPU همیشه زیر ۷۵٪» وجود ندارد؛ نوع Runtime، Burst، Headroom و SLO اهمیت دارند.
فصل Monitoring در کتاب رسمی Google SRE چهار Signal مهم Latency، Traffic، Errors و Saturation را توضیح میدهد.
گام ۴: محیط و داده را نماینده و قابلتفسیر کنید
Production-like به چه معناست؟
کپی کامل Production اغلب ممکن نیست. ابعاد مهم را ثبت کنید:
- Topology و نسخه سرویسها؛
- نسبت CPU/Memory/Replica؛
- DB engine، Index، Partition و حجم/توزیع داده؛
- Cache، CDN، Queue و Network path؛
- Configuration، Feature flag و Rate limit؛
- Autoscaling policy و محدودیت Cloud/Container؛
- وابستگی Stub/Sandbox/Real.
اگر محیط نصف Replicaهای Production دارد، نتیجه را خودکار دو برابر نکنید. Bottleneck و Scaling curve ممکن است غیرخطی باشند. روش Readiness در راهنمای محیط تست قابل استفاده است.
داده Performance
- حجم و توزیع نماینده، نه فقط تعداد رکورد؛
- Hot/cold keys و Cache cardinality؛
- حساب و Order یکتا برای Parallelism؛
- داده معتبر و نامعتبر مطابق Mix؛
- Cleanup بدون Lock و اثر روی Run؛
- هیچ PII یا داده Production بدون مجوز و Masking معتبر.
جستوجوی فارسی را با «ی/ی»، «ک/ک»، نیمفاصله، ارقام و Sort/Filter واقعی مدل کنید؛ Queryهای تکراری یک Cache hit غیرواقعی میسازند.
گام ۵: Observability را قبل از بار آماده کنید
- Clock همه Generatorها و سرویسها همگام است؛
- Build، Config و Run ID در Metric/Log ثبت میشوند؛
- Correlation/Trace ID از Gateway تا DB/Queue حفظ میشود؛
- Dashboard سرویس، Dependency و Generator جداست؛
- Slow query، Connection pool، GC و Queue قابلمشاهدهاند؛
- Sampling Trace با حجم تست سازگار و هزینهاش شناخته شده است؛
- Alert تست از Alert واقعی عملیات تفکیک میشود؛
- PII و Secret در Log/Trace Mask هستند.
راهنمای رسمی OpenTelemetry تفاوت و ارتباط Trace، Metric و Log را توضیح میدهد. فعالکردن Telemetry خود میتواند Overhead داشته باشد؛ Baseline با تنظیم Production-like بگیرید.
گام ۶: Script را پیش از Load اعتبارسنجی کنید
Functional smoke
- یک Journey با یک کاربر و داده شناختهشده اجرا شود؛
- Correlation، Token و Dynamic ID درست استخراج شوند؛
- Assertion کسبوکار، نه فقط Status code، وجود داشته باشد؛
- Setup/Cleanup تکرارپذیر باشد؛
- Think time و branching مطابق مدل باشند.
Data isolation
اگر هزار Virtual user با یک Cart یا Idempotency key مشترک کار کنند، Lock و Error مصنوعی میسازند. Data pool باید کافی، Thread-safe و قابلردیابی باشد.
Generator capacity
Load generator نباید Bottleneck باشد. CPU، Memory، Network، Socket و Error خودش را پایش کنید. با افزایش Generatorها بررسی کنید نرخ تولید مطابق انتظار Scale میشود. Distributed load فقط وقتی مفید است که Clock، Config و جمعآوری Result هماهنگ باشند.
برای یک پیادهسازی اولیه HTTP و اجرای CLI، آموزش JMeter از صفر را ببینید.
گام ۷: اجرای امن و قابلتکرار
ترتیب Run
- ثبت Build/Config و Health؛
- Smoke کمبار؛
- Warm-up با ثبت جدا؛
- Steady baseline؛
- Peak/Spike/Soak طبق Plan؛
- Recovery و Drain؛
- Cool-down، Snapshot و Archive؛
- تکرار روی همان Build/Config برای سنجش واریانس.
Stop conditions
- اثر روی کاربر یا داده واقعی؛
- خطای بحرانی یا ناسازگاری مالی؛
- Saturation بدون Headroom و خطر خرابی محیط؛
- Generator ناتوان از حفظ مدل بار؛
- Telemetry قطع و نتیجه غیرقابلتفسیر؛
- وابستگی بیرونی خارج از سقف مجاز؛
- هزینه Cloud فراتر از Budget Run.
تست Stress یا Production فقط با مجوز کتبی، اعلان عملیات، Rate cap، Synthetic data، Kill switch و Rollback انجام شود. «هدف یافتن نقطه شکست» مجوز آسیبزدن نیست.
Warm-up و Cache
نتیجه Cold و Warm هر دو ممکن است مهم باشند، اما مخلوطکردن آنها Summary را خراب میکند. Run را Annotate کنید: Cache reset، JIT/GC، Autoscaling event، Deploy، Backup یا اختلال شبکه.
گام ۸: Bottleneck را با همبستگی شواهد پیدا کنید
نمودار Latency بهتنهایی علت را نمیگوید. Timeline مشترک بسازید:
- در چه Load و زمانی SLO شکست؟
- Throughput/Goodput و Queue چه تغییری کردند؟
- کدام Resource یا Dependency به Saturation نزدیک شد؟
- Trace نمونه کجا زمان صرف کرده است؟
- Log و Slow query چه فرضیهای را پشتیبانی میکنند؟
- با یک تغییر کنترلشده، فرضیه قابلآزمون است؟
الگوهای تشخیصی
| مشاهده | فرضیه ممکن | شاهد بعدی |
|---|---|---|
| Latency بالا، CPU پایین، Queue رشد | Dependency/Pool/Lock | Trace، pool wait، DB locks |
| CPU اشباع، Throughput تخت | Compute bottleneck | Profile، GC، hot path |
| Error با افزایش Arrival، Retry بیشتر | Retry storm/Rate limit | Retry count، ۴۲۹/timeout، backoff |
| p99 بد، p50 ثابت | Tail dependency یا contention | Span distribution و slow queries |
| Generator CPU/Network اشباع | بار تولید نشده، نه سیستم کند | generator telemetry و multi-node run |
همبستگی علت را اثبات نمیکند. یک تغییر مانند Index، Pool size یا Query را جدا اعمال و همان Run را با Build/Config ثبتشده تکرار کنید.
گام ۹: گزارش تصمیممحور بنویسید
ساختار گزارش
- سؤال و تصمیم: ظرفیت کمپین یا Regression عملکرد؟
- نسخه و محیط: Build، Topology، Data، Config و محدودیت؛
- Workload: مدل Open/Closed، Mix، نرخ، Ramp، Think و Duration؛
- SLO: Transaction، Percentile، Window و Error semantics؛
- نتیجه: p50/p95/p99، Goodput، Error و Saturation؛
- Timeline: Load، Deploy، Scale و Incident annotations؛
- یافته: شواهد، فرضیه، آزمایش و Confidence؛
- ریسک باقیمانده: Dependency، Region، Device یا Production gap؛
- Recommendation: Release، محدودیت، Tuning، ظرفیت یا Retest.
نمونه نتیجه کوتاه
در Run P-۲۰۲، Arrival هدف ۴۰۰ Journey/min برای ۳۰ دقیقه حفظ شد. Create-order p95 برابر ۸۷۰ms و p99 برابر ۱۷۹۰ms بود و Duplicate صفر ماند. در Burst callback شصت/min، Queue تا ۸۵۰ رشد و ظرف ۶ دقیقه تخلیه شد؛ p99 callback از Threshold عبور کرد. Traceها انتظار Connection pool در Order DB را نشان میدهند. پیشنهاد: افزایش کور Pool پذیرفته نشود؛ Query/Pool experiment اجرا و Burst با همان Build تکرار شود.
«سیستم خوب بود» یا Screenshot ابزار گزارش نیست. Raw result، Script، Config و Dashboard snapshot را Archive کنید.
Performance testing در CI/CD
| Lane | تناوب | هدف | بودجه زمانی |
|---|---|---|---|
| Micro/Component benchmark | PR یا Commit مرتبط | Regression سریع الگوریتم/Query | کوتاه و پایدار |
| API threshold smoke | روزانه/پس از Deploy | افت بزرگ و Contract | چند دقیقه |
| Representative load | شبانه/هفتگی | Trend و ظرفیت نسبی | متوسط |
| Full-system Peak/Soak | پیش Release/On demand | ریسک کمپین و پایداری | محیط اختصاصی |
هر Run بزرگ را روی Pull Request اجرا نکنید. Lane، هزینه و Signal-to-noise را با معماری تست مستمر هماهنگ کنید.
انتخاب ابزار بر اساس نیاز، نه شهرت
- پشتیبانی مدل Open/Closed و نرخ ورود؛
- Protocol و Correlation موردنیاز؛
- Script as code، Version control و CLI؛
- Distributed execution و Clock؛
- Threshold/Exit code برای CI؛
- خروجی خام و اتصال به Observability؛
- مصرف Generator و ظرفیت واقعی؛
- License، TCO، Export و دسترسی پایدار در ایران؛
- مهارت تیم و قابلیت Debug.
مرورگر واقعی برای Core Web Vitals و Client experience لازم است، اما تولید صدها Browser هزینه متفاوتی دارد. بار Protocol-level و Browser synthetic/RUM را برای سؤالهای جدا استفاده کنید.
ملاحظات ایران و سرویسهای بیرونی
- درگاه، پیامک، نقشه و سرویس هویت را بدون اجازه زیر بار نبرید؛
- Stub باید Latency/Error/Timeout قراردادی را مدل کند و Contract test جدا داشته باشد؛
- مسیر شبکه داخلی/بینالمللی و CDN ممکن است نتیجه را تغییر دهد؛ محل Generator را ثبت کنید؛
- دسترسی و License ابزار خارجی، روش پرداخت و Fallback را پیش از وابستگی بررسی کنید؛
- داده فارسی، تاریخ شمسی، ارقام و Search normalization روی Cache/DB اثر دارند؛
- هزینه Cloud و پهنایباند هر Run را Budget کنید؛
- شماره تلفن، سفارش و Token واقعی در Script/JTL/Log ذخیره نشوند.
اشتباههای رایج برنامه تست عملکرد
- شروع با عدد کاربر همزمان بدون Arrival/Think/Journey؛
- استفاده از Average و Maximum بدون Percentile/Distribution؛
- اندازهگیری HTTP success بدون Business correctness؛
- بار دادن به Dependency بیرونی بدون مجوز؛
- یک Data key برای همه Virtual userها؛
- Generator اشباع و متهمکردن سیستم؛
- مخلوط Cold/Warm و Buildهای مختلف؛
- نداشتن Telemetry و حدس Bottleneck از نمودار Client؛
- تعمیم خطی محیط کوچک به Production؛
- تغییر چند Config و ناتوانی در نسبتدادن بهبود؛
- اجرای یک Run و اعلام ظرفیت قطعی؛
- Threshold عمومی CPU یا Response برای همه تراکنشها.
بسیاری از این الگوها در اشتباههای رایج فرایند تست بهصورت سیستمی بررسی شدهاند.
قالب یکصفحهای Performance Test Plan
هدف و تصمیم:
Build / تاریخ / مالک:
سیستم و Dependencyهای داخل/خارج مرز:
محیط و فاصله با Production:
منبع داده Workload:
مدل Open/Closed:
Journey mix / Arrival / Concurrency / Think:
داده و Cleanup:
Warm-up / Ramp / Steady / Spike / Recovery:
SLI/SLO per transaction:
Telemetry و Dashboard:
Generator sizing:
مجوز و Stop conditions:
تعداد Run و روش مقایسه:
Artifactها:
ریسک باقیمانده:
چکلیست پیش از اجرا
- سؤال و تصمیم Test روشن است.
- Workload از داده/فرض مستند آمده است.
- Arrival، Concurrency، Think و Mix تعریف شدهاند.
- SLO در Percentile، Window و نقطه اندازهگیری مشخص است.
- Business assertion و Goodput داریم.
- محیط، Data، Cache و Dependencyها ثبت شدهاند.
- Script با یک کاربر و یک نرخ کوچک معتبر شده است.
- Generator و Telemetry Headroom دارند.
- Run ID و Clock مشترکاند.
- مجوز، اعلان، Stop و Kill switch آمادهاند.
- Raw data و Config نسخهبندی/Archive میشوند.
- روش Retest و مقایسه از پیش معلوم است.
پرسشهای متداول
برای تست بار، کاربر همزمان مهمتر است یا RPS؟
هیچکدام بهتنهایی کافی نیست. مدل باید Arrival/Journey rate، Concurrency، Think time، طول Session و Request mix را نشان دهد. انتخاب به معماری Open/Closed و رفتار واقعی وابسته است.
p95 و p99 چه تفاوتی دارند؟
p95 یعنی ۹۵٪ Observationها در آن Window برابر یا سریعترند؛ p99 دم کندتر را نشان میدهد. هر دو باید per-transaction، همراه حجم نمونه و Error semantics تفسیر شوند.
چند بار تست عملکرد را تکرار کنیم؟
عدد جهانی وجود ندارد. آنقدر روی Build/Config یکسان تکرار کنید که واریانس و Noise را بفهمید. Run بعد از Tuning باید همان Workload و شرایط قابلمقایسه را داشته باشد.
آیا محیط تست باید دقیقاً مثل Production باشد؟
کپی کامل اغلب ممکن نیست. ابعاد مؤثر مانند Topology، نسخه، Data distribution، Cache، Network و Resource ratio را نماینده و Gapها را ثبت کنید. نتیجه محیط کوچک را خطی تعمیم ندهید.
آیا تست عملکرد را میتوان در Production اجرا کرد؟
فقط با مجوز، هدف محدود، Synthetic data، Rate cap، Monitoring، Kill switch و هماهنگی عملیات. بسیاری از سؤالها باید ابتدا در محیط جدا پاسخ داده شوند؛ تست Production جایگزین برنامه ایمن نیست.
جمعبندی
برنامه تست عملکرد از ابزار شروع نمیشود؛ از مدل بار و تصمیم شروع میشود. Arrival و Concurrency را جدا کنید، Journey mix و داده را از واقعیت بسازید، p95/p99 و Goodput را کنار Saturation ببینید و Telemetry را پیش از Run آماده کنید. نتیجه معتبر باید قابلبازتولید، ایمن و محدودیتهایش شفاف باشد؛ فقط در این صورت میتوان از آن برای ظرفیت، Tuning یا Release استفاده کرد.

