Parallelization และ Sharding
จบบทนี้แล้วคุณจะ...
- ตั้งค่า workers และ fullyParallel
- ใช้ sharding เพื่อกระจาย tests ข้าม CI jobs
- ออกแบบ matrix strategy สำหรับ CI
- คำนวณ optimal shard count จาก test suite size
1 · Parallelization — รัน Tests พร้อมกัน
Workers
// playwright.config.ts
import { defineConfig } from '@playwright/test';
export default defineConfig({
workers: 4, // ใช้ 4 processes พร้อมกัน
fullyParallel: true, // ทุก test รัน parallel (ไม่ใช่แค่ file-level)
});
workers คือจำนวน process ที่รัน test พร้อมกัน:
- ค่า default: 50% ของ CPU cores
workers: 1= sequential (ช้าแต่ปลอดภัย)workers: '100%'= ใช้ทุก CPU core
fullyParallel
fullyParallel |
พฤติกรรม |
|---|---|
false (default) |
tests ใน file เดียวกัน รันตามลำดับ, ต่าง file รัน parallel |
true |
ทุก test รัน parallel ไม่ว่าจะอยู่ file เดียวกันหรือไม่ |
ข้อควรระวัง: fullyParallel: true ต้องแน่ใจว่าทุก test independent — ไม่แชร์ state กัน
2 · Test Ordering Considerations
// tests ที่ต้องรันตามลำดับ → ใช้ serial
test.describe.serial('Order lifecycle', () => {
test('create order', async ({ page }) => { /* ... */ });
test('update order', async ({ page }) => { /* ... */ });
test('cancel order', async ({ page }) => { /* ... */ });
});
// tests ที่ independent → fullyParallel ได้เลย
test.describe('Product search', () => {
test('search by name', async ({ page }) => { /* ... */ });
test('search by category', async ({ page }) => { /* ... */ });
test('search with filters', async ({ page }) => { /* ... */ });
});
หลักการ: ทำให้ tests independent ให้มากที่สุด → parallel ได้ → เร็วขึ้น
3 · Sharding — กระจาย Tests ข้าม CI Machines
Sharding แบ่ง test suite ออกเป็นหลายส่วน แล้วรันบน CI jobs ที่ต่างกัน:
# Machine 1: รัน shard 1 จาก 3
npx playwright test --shard=1/3
# Machine 2: รัน shard 2 จาก 3
npx playwright test --shard=2/3
# Machine 3: รัน shard 3 จาก 3
npx playwright test --shard=3/3
Playwright จะแบ่ง tests ให้เท่าๆ กันอัตโนมัติ
4 · Sharding ใน GitHub Actions
# .github/workflows/playwright.yml
name: Playwright Tests
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
shard: [1/4, 2/4, 3/4, 4/4]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ci
- name: Install Playwright Browsers
run: npx playwright install --with-deps chromium
- name: Run Playwright tests
run: npx playwright test --shard=${{ matrix.shard }}
- uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report-${{ strategy.job-index }}
path: playwright-report/
retention-days: 14
- uses: actions/upload-artifact@v4
if: failure()
with:
name: test-results-${{ strategy.job-index }}
path: test-results/
retention-days: 7
# รวม reports จากทุก shard
merge-reports:
needs: test
if: always()
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- name: Download all reports
uses: actions/download-artifact@v4
with:
pattern: playwright-report-*
path: all-reports/
- name: Merge reports
run: npx playwright merge-reports --reporter html all-reports/
- uses: actions/upload-artifact@v4
with:
name: merged-playwright-report
path: playwright-report/
retention-days: 14
สิ่งสำคัญ:
fail-fast: false— ไม่หยุด shard อื่นเมื่อ shard หนึ่ง fail- artifact name ต้างกัน (
${{ strategy.job-index }}) — ไม่ overwrite กัน merge-reportsjob รวม reports จากทุก shard เป็นฉบับเดียว
5 · Config สำหรับ CI Performance
// playwright.config.ts
import { defineConfig } from '@playwright/test';
export default defineConfig({
// CI settings
workers: process.env.CI ? 2 : undefined,
fullyParallel: true,
retries: process.env.CI ? 1 : 0,
// ลด overhead
use: {
screenshot: 'only-on-failure',
video: 'off', // video ใช้ resource เยอะใน CI
trace: 'on-first-retry',
},
// รันเฉพาะ Chromium ใน CI (เร็วขึ้น 3x)
projects: process.env.CI
? [{ name: 'chromium', use: { browserName: 'chromium' } }]
: [
{ name: 'chromium', use: { browserName: 'chromium' } },
{ name: 'firefox', use: { browserName: 'firefox' } },
{ name: 'webkit', use: { browserName: 'webkit' } },
],
});
6 · คำนวณ Optimal Shard Count
สูตร:
optimal_shards = ceil(total_test_time / target_time_per_job)
ข้อจำกัด:
- CI machine มี overhead ~2 นาที (checkout, install, browser install)
- จำนวน shard มากเกินไป → overhead รวมสูงกว่าเวลาที่ประหยัดได้
- GitHub Actions free tier จำกัด concurrent jobs
แบบฝึกหัด: คำนวณ Optimal Shard Count
สถานการณ์:
- Test suite มี 120 tests
- แต่ละ test ใช้เวลาเฉลี่ย 8 วินาที
- Total test time (sequential) = 120 x 8 = 960 วินาที (16 นาที)
- CI overhead per job = 2 นาที (checkout + install + browser)
- เป้าหมาย: CI ทั้งหมดไม่เกิน 8 นาที
- workers per shard = 2
คำถาม:
- ถ้าใช้ 2 shards เวลารวมเท่าไหร่?
- ถ้าใช้ 4 shards เวลารวมเท่าไหร่?
- ถ้าใช้ 8 shards เวลารวมเท่าไหร่?
- จำนวน shard ที่ optimal คือเท่าไหร่?
ดูคำตอบแนะนำ
ข้อมูลตั้งต้น
- Total test time = 960 วินาที
- workers per shard = 2
- CI overhead per job = 2 นาที
สรุปการคำนวณ
| Shards | Tests ต่อ shard | เวลา test ต่อ shard | รวมกับ overhead | สรุป |
|---|---|---|---|---|
| 2 | 60 tests | (60 x 8) / 2 = 240 วินาที = 4 นาที | 4 + 2 = 6 นาที | ผ่านเป้า 8 นาที |
| 4 | 30 tests | (30 x 8) / 2 = 120 วินาที = 2 นาที | 2 + 2 = 4 นาที | ผ่านเป้า 8 นาที |
| 8 | 15 tests | (15 x 8) / 2 = 60 วินาที = 1 นาที | 1 + 2 = 3 นาที | ผ่านเป้า แต่ overhead สูงเกินไป |
ตอบคำถาม
- ถ้าใช้ 2 shards เวลารวมคือ 6 นาที
- ถ้าใช้ 4 shards เวลารวมคือ 4 นาที
- ถ้าใช้ 8 shards เวลารวมคือ 3 นาที
- จำนวน shard ที่ optimal คือ 2 shards
เหตุผลที่เลือก 2 shards
- ผ่านเป้า 8 นาทีสบาย
- overhead ต่ำกว่าทางเลือกที่ shard เยอะกว่า
- setup และการ merge reports ยังไม่ซับซ้อนเกินจำเป็น
- 4 shards ก็ใช้ได้ ถ้าทีมต้องการ speed มากขึ้นจริง แต่จุดคุ้มที่สุดยังเป็น 2 shards
สิ่งที่ต้องจำ
fullyParallel: true+ workers = parallel ภายใน machine เดียว- Sharding = parallel ข้าม machines — เร็วขึ้นแบบ linear
fail-fast: falseสำคัญ — ไม่งั้น shard หนึ่ง fail จะหยุด shard อื่นด้วย- Merge reports หลัง sharding เพื่อรวมผลเป็นฉบับเดียว
- Optimal shards = จุดที่ test time / overhead สมดุล — มากเกินไปไม่คุ้ม
- ทำให้ tests independent เสมอ — เป็นพื้นฐานของ parallelization