CupCode.cc
Section 04 · Module 09 · Lesson 02 · ~15–20 นาที

Parallelization และ Sharding

เวลาเรียน: 15–20 นาที ต้องรู้มาก่อน: Lesson 01 (GitHub Actions) รูปแบบ: อ่าน + Code Lab + แบบฝึก

จบบทนี้แล้วคุณจะ...

  • ตั้งค่า 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-reports job รวม 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

คำถาม:

  1. ถ้าใช้ 2 shards เวลารวมเท่าไหร่?
  2. ถ้าใช้ 4 shards เวลารวมเท่าไหร่?
  3. ถ้าใช้ 8 shards เวลารวมเท่าไหร่?
  4. จำนวน 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 สูงเกินไป

ตอบคำถาม

  1. ถ้าใช้ 2 shards เวลารวมคือ 6 นาที
  2. ถ้าใช้ 4 shards เวลารวมคือ 4 นาที
  3. ถ้าใช้ 8 shards เวลารวมคือ 3 นาที
  4. จำนวน 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