Waktu kompilasi Rust pada runner ephemeral GitHub Actions sering menjadi bottleneck utama dalam deployment pipeline. Proyek backend berbasis Actix Web mengeksekusi ratusan crate dependensi, parsing proc-macro yang berat (seperti actix-web-codegen dan serde_derive), serta linking biner dalam jumlah besar. Tanpa strategi caching terdistribusi, runner harus membangun seluruh pohon dependensi dari nol di setiap commit.
Akar Masalah: Kompilasi Dependensi dan Proc-Macro Actix Web
Pohon dependensi Actix Web mencakup ekosistem asynchronous Tokio, parser HTTP, layer kriptografi, dan serangkaian crate proc-macro. Proc-macro harus dikompilasi terlebih dahulu untuk mesin host (runner architecture) sebelum rustc dapat memproses kode aplikasi Anda.
Karakteristik runner GitHub Actions yang bersifat ephemeral (dihancurkan setelah selesai berjalan) memicu dua kendala teknis:
- Kompilasi Ulang Utuh: Direktori
target/dan~/.cargohilang di setiap run jika tidak disimpan secara eksplisit. - Overhead I/O Incremental Compilation: Fitur incremental compilation Rust dirancang untuk local disk dengan latensi rendah, bukan environment CI.
Optimasi Flag Cargo: Nonaktifkan CARGO_INCREMENTAL
Kesalahan umum dalam pipeline CI Rust adalah membiarkan incremental compilation aktif. Di lingkungan lokal, flag ini mempercepat rebuild dengan menyimpan cache AST dan graf dependency ke disk. Namun, di CI, flag ini menghasilkan ribuan file kecil yang memperlambat proses kompresi/dekompresi cache dan jarang dapat digunakan kembali antar-runner.
Tetapkan environment variable berikut secara global di workflow:
env:
CARGO_INCREMENTAL: 0
RUST_BACKTRACE: 1Menonaktifkan incremental compilation (CARGO_INCREMENTAL: 0) memaksa compiler menghasilkan artefak monolitik yang lebih bersih, mengurangi ukuran cache archive hingga 60%, dan meningkatkan kompilasi bersih (clean build) secara konsisten.
Arsitektur Layering: Swatinem/rust-cache vs sccache
Strategi caching yang efektif menggunakan dua layer terpisah:
- Target Cache (Swatinem/rust-cache): Menyimpan direktori
~/.cargo/registry,~/.cargo/git, dantarget/berdasarkan hash dariCargo.lock. Sangat cepat saat cache hit penuh (warm build), namun rapuh terhadap perubahan file lock sekecil apa pun. - Object Cache (sccache): Berfungsi sebagai
RUSTC_WRAPPER. Caching dilakukan pada level pemanggilan compiler individual untuk setiap crate berdasarkan hash kode sumber, flag compiler, dan versi rustc. JikaCargo.lockberubah dantarget/terhapus,sccachemencegah kompilasi ulang dependensi pihak ketiga yang tidak berubah.
Menggabungkan keduanya memberikan mitigasi: rust-cache menangani warm builds instan, sementara sccache mengamankan cold builds parsial via GitHub Actions Cache API.
Implementasi Workflow GitHub Actions
Berikut adalah konfigurasi lengkap dan siap pakai pada .github/workflows/ci.yml menggunakan mozilla/sccache-action yang memanfaatkan GitHub Actions cache service secara native:
name: CI Pipeline
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
env:
CARGO_TERM_COLOR: always
CARGO_INCREMENTAL: 0
RUSTC_WRAPPER: sccache
SCCACHE_GHA_ENABLED: "true"
jobs:
build-and-test:
name: Build & Test Actix Web
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install stable Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Setup sccache-cache
uses: mozilla/[email protected]
- name: Setup Cargo Cache
uses: Swatinem/rust-cache@v2
with:
# Cegah invalidasi cache akibat perubahan file non-Rust
cache-all-crates: "true"
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Check code formatting
run: cargo fmt --check
- name: Run Clippy
run: cargo clippy --all-targets --all-features -- -D warnings
- name: Run Tests
run: cargo test --workspace --all-features
- name: View sccache statistics
run: sccache --show-statsMitigasi Cache Invalidation akibat Cargo.lock
Ketika dependensi yang tidak relevan (seperti crate linter dev-only) diperbarui di Cargo.lock, hash kunci cache pada rust-cache berubah total. Runner terpaksa mengabaikan direktori target/ sebelumnya.
Dengan menyertakan sccache yang didukung GitHub Cache API (SCCACHE_GHA_ENABLED: "true"):
- Meski direktori
target/kosong, rustc memanggil wrappersccache. sccachemenghitung hash dari Actix Web, Serde, dan Tokio. Karena dependensi inti ini tidak berubah,sccachemengunduh binary object langsung dari cache.- Fase linking tetap berjalan, tetapi waktu kompilasi crate dependensi dipangkas dari hitungan menit menjadi hitungan detik.
Batasi penyimpanan cache branch hanya pada branch utama (misal: refs/heads/main) menggunakan opsi save-if pada Swatinem/rust-cache. Hal ini mencegah pull request dengan eksperimen dependensi mencemari kuota 10 GB GitHub Actions cache pada repository Anda.
Observabilitas dan Evaluasi Performa
Metrik performa CI bervariasi bergantung pada jumlah dependensi, namun rasio umum yang terobservasi pada runner Linux 2-core:
- Cold Build (Tanpa Cache): 8 – 12 menit untuk instalasi toolchain, dependensi Actix Web, dan kompilasi biner aplikasi.
- Partial Invalidation (Cargo.lock berubah, sccache hit): 1.5 – 3 menit. Crate dependensi diunduh dari object cache, hanya crate aplikasi lokal dan final link step yang dieksekusi ulang.
- Warm Build (Cache hit penuh rust-cache): 30 – 60 detik. Runner hanya memeriksa timestamp dan langsung mengeksekusi test suite.
Periksa step sccache --show-stats di akhir job log untuk memastikan efektivitas caching. Pastikan baris "Cache hits" meningkat seiring berjalannya pipeline berikutnya:
Compile requests 412
Compile requests executed 412
Cache hits 389
Cache misses 23
Cache timeouts 0
Cache errors 0
Non-cacheable compilations 0Jika "Compile requests executed" bernilai tinggi namun "Cache hits" nol secara konsisten, pastikan permission runner memiliki hak akses actions: write untuk mengizinkan interaksi dengan backend GitHub Cache API.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!