Skrip Idempoten dan Logika Percobaan Ulang dengan Backoff
Rancang operasi yang aman dijalankan ulang dan tambahkan backoff eksponensial untuk panggilan eksternal yang tidak stabil.
Skrip Idempoten dan Logika Percobaan Ulang dengan Backoff adalah pelajaran Linux Command Line & Bash Scripting Mastery gratis di CoddyKit. Ini adalah pelajaran 4 dari 4. Kamu bisa membaca pelajaran lengkapnya di bawah secara gratis — lalu praktikkan langsung di browser dengan editor kode bawaan dan tutor AI 24/7. Ini adalah bagian dari jalur belajar Linux Command Line & Bash Scripting Mastery, dan progresmu tersinkronisasi di web dan aplikasi CoddyKit. Kursus Linux Command Line & Bash Scripting Mastery mencakup 4 pelajaran total.
Apa Itu Idempotensi dan Mengapa Penting
Idempotensi berarti menjalankan operasi yang sama beberapa kali menghasilkan hasil yang sama seperti menjalankannya sekali. Dalam pembuatan skrip Bash, hal ini sangat penting karena skrip dapat mengalami kerusakan, jaringan dapat terputus, dan manusia dapat menjalankan ulang sesuatu secara tidak sengaja.
- Skrip yang tidak idempoten dan membuat pengguna dua kali mungkin gagal atau menggandakan data.
- Skrip idempoten memeriksa terlebih dahulu: apakah ini sudah ada?
- Skrip idempoten aman digunakan dalam pekerjaan Cron, alur CI, dan loop percobaan ulang.
Aturan utamanya: periksa sebelum bertindak. Setiap operasi yang menghapus atau membuat sesuatu harus dilindungi oleh pengujian prasyarat.
Melindungi Pembuatan Berkas dan Direktori
Pola idempotensi yang paling umum adalah memeriksa apakah suatu sumber daya sudah ada sebelum membuatnya. Bash menyediakan perintah satu baris yang ringkas untuk hal ini.
[ -d dir ]—benar jika direktori ada[ -f file ]—benar jika berkas biasa adamkdir -p—membuat direktori hanya jika belum ada (idempotensi bawaan)
Utamakan opsi bawaan seperti -p dan --no-clobber daripada pemeriksaan manual jika tersedia—opsi tersebut bersifat atomik dan aman dari kondisi perlombaan.
#!/usr/bin/env bash
set -euo pipefail
CONFIG_DIR="$HOME/.myapp"
CONFIG_FILE="$CONFIG_DIR/config.ini"
# Idempotent: mkdir -p never fails if dir already exists
mkdir -p "$CONFIG_DIR"
# Idempotent: only write config if it doesn't exist yet
if [ ! -f "$CONFIG_FILE" ]; then
echo '[defaults]' > "$CONFIG_FILE"
echo 'timeout=30' >> "$CONFIG_FILE"
echo "Created $CONFIG_FILE"
else
echo "Config already exists, skipping."
fiPengelolaan Pengguna dan Grup secara Idempoten
Tugas administrasi sistem seperti menambahkan pengguna atau grup harus bersifat idempoten—menjalankan ulang skrip pada mesin yang sama tidak boleh menghasilkan kesalahan atau membuat duplikat.
id usernamemengembalikan 0 jika pengguna adagetent group groupnamememeriksa keberadaan grup- Bungkus setiap operasi dengan penjaga agar skrip aman untuk dijalankan ulang
Pola ini menjadi dasar alat pengelolaan konfigurasi seperti Ansible—setiap tugas merupakan operasi idempoten yang dilindungi penjaga.
#!/usr/bin/env bash
set -euo pipefail
APP_USER="apprunner"
APP_GROUP="appgroup"
# Idempotent group creation
if ! getent group "$APP_GROUP" &>/dev/null; then
groupadd "$APP_GROUP"
echo "Group '$APP_GROUP' created."
else
echo "Group '$APP_GROUP' already exists."
fi
# Idempotent user creation
if ! id "$APP_USER" &>/dev/null; then
useradd -m -g "$APP_GROUP" -s /bin/bash "$APP_USER"
echo "User '$APP_USER' created."
else
echo "User '$APP_USER' already exists."
fiMenggunakan Berkas Kunci untuk Mencegah Eksekusi Bersamaan
Skrip idempoten pun dapat menimbulkan masalah jika dua instans berjalan secara bersamaan. Berkas kunci memastikan hanya satu instans yang berjalan pada satu waktu.
- Buat berkas kunci saat memulai; hapus saat keluar.
- Gunakan
trapuntuk membersihkan kunci meskipun skrip dihentikan. mkdirpada satu jalur bersifat atomik di sebagian besar sistem berkas Linux—lebih aman daripadatouchuntuk penguncian.
Tanpa kunci, pekerjaan Cron yang lambat dan eksekusi ulang secara manual dapat bertabrakan serta merusak keadaan bersama.
#!/usr/bin/env bash
set -euo pipefail
LOCKFILE="/tmp/myapp_deploy.lock"
# Atomic lock acquisition using mkdir
if ! mkdir "$LOCKFILE" 2>/dev/null; then
echo "ERROR: Another instance is running (lock: $LOCKFILE). Exiting." >&2
exit 1
fi
# Guarantee lock removal on any exit
trap 'rmdir "$LOCKFILE"; echo "Lock released."' EXIT
echo "Lock acquired. Running deployment..."
sleep 2 # simulate work
echo "Deployment complete."Melacak Langkah yang Selesai dengan Berkas Keadaan
Untuk skrip dengan banyak langkah (migrasi, instalasi, penyediaan), Anda dapat melacak langkah yang sudah selesai menggunakan berkas keadaan. Setiap langkah memeriksa berkas keadaan sebelum dijalankan dan menulis ke berkas tersebut setelah selesai.
- Murah dan portabel—tidak memerlukan basis data.
- Memungkinkan skrip yang gagal melanjutkan dari bagian terakhir.
- Simpan keadaan di lokasi yang dapat diprediksi seperti
/var/lib/myapp/atau~/.myapp/state/.
Pola ini digunakan oleh alat utama seperti apt, cloud-init, dan kerangka kerja migrasi basis data.
#!/usr/bin/env bash
set -euo pipefail
STATE_DIR="/tmp/myapp_state"
mkdir -p "$STATE_DIR"
run_step() {
local step_name="$1"
local step_cmd="$2"
local marker="$STATE_DIR/${step_name}.done"
if [ -f "$marker" ]; then
echo "[SKIP] $step_name already completed."
return 0
fi
echo "[RUN] $step_name ..."
eval "$step_cmd"
touch "$marker"
echo "[DONE] $step_name"
}
run_step "install_deps" "echo 'Installing dependencies...'"
run_step "configure_db" "echo 'Configuring database...'"
run_step "start_service" "echo 'Starting service...'"Pengantar Logika Percobaan Ulang
Panggilan eksternal—permintaan HTTP, pencarian DNS, dan panggilan API cloud—pada dasarnya tidak dapat diandalkan. Satu kegagalan tidak seharusnya menghentikan seluruh skrip Anda. Logika percobaan ulang secara otomatis mencoba kembali perintah yang gagal.
- Percobaan ulang naif: lakukan loop sebanyak N kali hingga perintah berhasil.
- Selalu tetapkan jumlah maksimum percobaan ulang untuk menghindari loop tanpa akhir.
- Catat setiap percobaan agar kegagalan dapat didiagnosis.
Fungsi percobaan ulang paling sederhana membungkus perintah apa pun dan mencobanya kembali hingga jumlah tertentu dengan jeda tetap. Ini cukup baik untuk banyak kasus penggunaan, tetapi memiliki kelemahan penting saat beban tinggi—yang akan dibahas berikutnya.
#!/usr/bin/env bash
set -euo pipefail
# Simple fixed-delay retry (3 attempts, 2s apart)
retry() {
local max_attempts=3
local delay=2
local attempt=1
until "$@"; do
if (( attempt >= max_attempts )); then
echo "ERROR: Command failed after $max_attempts attempts: $*" >&2
return 1
fi
echo "Attempt $attempt failed. Retrying in ${delay}s..." >&2
sleep "$delay"
(( attempt++ ))
done
}
# Example: retry a curl call
retry curl --silent --fail --max-time 5 https://httpbin.org/get -o /dev/null
echo "Request succeeded."Masalah Kawanan yang Mengamuk
Ketika banyak klien mencoba kembali secara bersamaan setelah kegagalan, mereka membentuk kawanan yang mengamuk—semuanya mencoba kembali pada interval tetap yang sama, membebani server tepat pada saat yang sama dan membuat pemulihan menjadi mustahil.
- 100 skrip mencoba kembali setiap 5 detik → 100 permintaan bersamaan setiap 5 detik.
- Server sudah mengalami kesulitan; beban yang tersinkronisasi membuatnya semakin buruk.
- Solusinya adalah penundaan eksponensial: gandakan waktu tunggu setelah setiap kegagalan.
- Tambahkan jitter (gangguan acak) untuk menghilangkan sinkronisasi percobaan ulang di antara klien.
Penundaan eksponensial dengan jitter merupakan standar industri yang digunakan oleh SDK AWS, klien Google Cloud, dan semua sistem terdistribusi utama.
Menerapkan Penundaan Eksponensial
Penundaan eksponensial meningkatkan waktu tunggu secara eksponensial setelah setiap kegagalan: 1 dtk, 2 dtk, 4 dtk, 8 dtk, 16 dtk... Dengan demikian, sistem jarak jauh memiliki waktu untuk pulih sekaligus mengurangi beban total.
Rumusnya: delay = base * (2 ^ attempt)
- Tetapkan batas (penundaan maksimum) agar waktu tunggu tidak bertambah tanpa batas.
- Parameter yang perlu disesuaikan:
base_delay,max_delay,max_attempts.
Fungsi ini dapat digunakan kembali — berikan perintah apa pun sebagai argumen.
#!/usr/bin/env bash
set -euo pipefail
retry_with_backoff() {
local max_attempts="${RETRY_MAX_ATTEMPTS:-5}"
local base_delay="${RETRY_BASE_DELAY:-1}"
local max_delay="${RETRY_MAX_DELAY:-30}"
local attempt=0
local delay="$base_delay"
until "$@"; do
(( attempt++ ))
if (( attempt >= max_attempts )); then
echo "ERROR: '$*' failed after $max_attempts attempts." >&2
return 1
fi
echo "Attempt $attempt failed. Backing off for ${delay}s..." >&2
sleep "$delay"
# Double the delay, but cap it
delay=$(( delay * 2 ))
(( delay > max_delay )) && delay=$max_delay
done
echo "Command succeeded on attempt $(( attempt + 1 ))."
}
retry_with_backoff echo "Simulated success"Menambahkan Keacakan pada Penundaan
Keacakan menambahkan unsur acak pada penundaan sebelum percobaan ulang. Meskipun menggunakan penundaan eksponensial, jika semua klien mulai pada waktu yang sama, mereka tetap akan mencoba ulang secara bersamaan. Keacakan memutus sinkronisasi ini.
Dua strategi keacakan yang umum:
- Keacakan penuh:
sleep random(0, cap)— penyebaran maksimum dan beban puncak terendah. - Keacakan setara:
sleep cap/2 + random(0, cap/2)— menjamin waktu tunggu minimum dan mencegah permintaan berulang secara langsung.
Dalam Bash, gunakan $RANDOM (0–32767) untuk menghasilkan angka acak. Sesuaikan skalanya dengan rentang penundaan menggunakan operasi modulo.
#!/usr/bin/env bash
set -euo pipefail
retry_with_jitter() {
local max_attempts="${1:-5}"; shift
local base_delay=1
local max_delay=32
local attempt=0
local cap=$base_delay
until "$@"; do
(( attempt++ ))
if (( attempt >= max_attempts )); then
echo "ERROR: Giving up after $max_attempts attempts." >&2
return 1
fi
# Full jitter: random value in [0, cap]
local jitter=$(( RANDOM % (cap + 1) ))
echo "Attempt $attempt failed. Sleeping ${jitter}s (cap=${cap}s)..." >&2
sleep "$jitter"
# Grow cap exponentially, bounded by max_delay
cap=$(( cap * 2 ))
(( cap > max_delay )) && cap=$max_delay
done
}
retry_with_jitter 4 curl --silent --fail --max-time 3 https://httpbin.org/get -o /dev/null
echo "Done."Menggabungkan Idempotensi dan Percobaan Ulang dalam Alur Kerja Nyata
Dalam skrip produksi, logika idempotensi dan percobaan ulang bekerja bersama. Alur kerja penerapan yang umum dapat:
- Memperoleh kunci (mencegah proses berjalan secara bersamaan)
- Memeriksa berkas status (melewati langkah yang sudah selesai)
- Menggunakan percobaan ulang dengan penundaan untuk panggilan eksternal (pengunduhan, API, DNS)
- Menandai langkah sebagai selesai hanya setelah keberhasilan dikonfirmasi
- Melepaskan kunci melalui
trap
Kombinasi ini membuat skrip aman dijalankan kembali kapan saja — setelah kerusakan, waktu habis, atau pembatalan manual — tanpa membuat sistem berada dalam keadaan rusak.
#!/usr/bin/env bash
set -euo pipefail
STATE_DIR="/tmp/deploy_state" && mkdir -p "$STATE_DIR"
LOCK="/tmp/deploy.lock"
mkdir "$LOCK" 2>/dev/null || { echo "Already running."; exit 1; }
trap 'rmdir "$LOCK"' EXIT
step_done() { [ -f "$STATE_DIR/$1.done" ]; }
mark_done() { touch "$STATE_DIR/$1.done"; }
retry_backoff() {
local attempt=0 delay=1
until "${@:2}"; do
(( ++attempt >= $1 )) && { echo "Failed after $1 attempts."; return 1; }
echo "Retry $attempt in ${delay}s..."; sleep $delay; delay=$(( delay * 2 ))
done
}
if ! step_done "download_artifact"; then
retry_backoff 4 curl -fsSL https://httpbin.org/get -o /tmp/artifact.json
mark_done "download_artifact"
echo "[DONE] download_artifact"
else
echo "[SKIP] download_artifact"
fi
echo "Deployment finished successfully."Menangani Kesalahan yang Tidak Dapat Dicoba Ulang
Tidak semua kesalahan sebaiknya dicoba ulang. Mencoba ulang 404 Not Found atau 403 Forbidden hanya membuang waktu — permintaan tersebut tidak akan pernah berhasil tanpa campur tangan manusia. Logika percobaan ulang Anda harus membedakan:
- Kesalahan sementara — waktu habis jaringan, 503 Service Unavailable, kegagalan DNS → coba ulang
- Kesalahan permanen — 401 Unauthorized, 404 Not Found, masukan tidak valid → gagal seketika
Dengan curl, periksa kode status HTTP dan lewati percobaan ulang untuk respons 4xx. Gunakan --write-out '%{http_code}' untuk menangkap status secara terpisah dari isi respons.
#!/usr/bin/env bash
set -euo pipefail
fetch_with_retry() {
local url="$1"
local max_attempts=4
local delay=1
local attempt=0
local http_code
until http_code=$(curl -s -o /dev/null -w '%{http_code}' --max-time 5 "$url"); do
: # curl itself failed (network error)
(( ++attempt >= max_attempts )) && { echo "Network error, giving up."; return 1; }
sleep $delay; delay=$(( delay * 2 ))
done
# Permanent client errors — do not retry
if [[ "$http_code" =~ ^4 ]]; then
echo "ERROR: HTTP $http_code for $url — not retrying." >&2
return 1
fi
# Transient server errors — retry
if [[ "$http_code" =~ ^5 ]]; then
(( ++attempt >= max_attempts )) && { echo "Server error, giving up."; return 1; }
echo "HTTP $http_code — backing off ${delay}s..."
sleep $delay; delay=$(( delay * 2 ))
fetch_with_retry "$url"
return
fi
echo "HTTP $http_code — success."
}
fetch_with_retry "https://httpbin.org/status/200"Uji Pengetahuan: Memilih Strategi Penundaan
Sebuah skrip penerapan mengunduh artefak rilis dari bucket S3. Dalam insiden baru-baru ini, 80 instans alur pemrosesan gagal secara bersamaan karena gangguan S3 singkat. Ketika S3 pulih setelah 10 detik, semua 80 instans mencoba ulang pada saat yang sama, sehingga menyebabkan kelebihan beban lain dan memperpanjang gangguan selama 3 menit.
Strategi percobaan ulang mana yang paling efektif untuk mencegah rangkaian efek kawanan yang menyerbu ini dalam insiden mendatang?
Ringkasan: Idempotensi dan Percobaan Ulang dengan Penundaan
Dalam pelajaran ini, Anda mempelajari cara merancang skrip Bash yang aman dijalankan kembali dan tahan terhadap kegagalan sementara.
Pola idempotensi:
- Lindungi setiap operasi dengan pemeriksaan keberadaan (
[ -f ],[ -d ],id,getent). - Gunakan
mkdir -pdan opsi bawaan idempoten lainnya jika tersedia. - Gunakan berkas kunci (
mkdiratomik) untuk mencegah proses berjalan secara bersamaan. - Gunakan berkas status (berkas penanda untuk setiap langkah) agar proses dapat dilanjutkan setelah kegagalan.
Pola percobaan ulang dengan penundaan:
- Selalu tetapkan jumlah percobaan maksimum — jangan pernah mencoba ulang selamanya.
- Gunakan penundaan eksponensial: gandakan penundaan setelah setiap kegagalan.
- Tambahkan keacakan untuk mencegah sinkronisasi kawanan yang menyerbu.
- Bedakan kesalahan sementara (coba ulang) dari kesalahan permanen (gagal seketika).
Menggabungkan pola-pola ini menghasilkan skrip berkelas produksi: aman, dapat diamati, dan mampu memulihkan diri.
Pertanyaan yang Sering Diajukan
Apakah pelajaran “Skrip Idempoten dan Logika Percobaan Ulang dengan Backoff” gratis?
Ya — teks lengkap “Skrip Idempoten dan Logika Percobaan Ulang dengan Backoff” gratis dibaca di sini di web. Untuk praktiknya secara interaktif (editor kode bawaan dan tutor AI 24/7) dan buka sisa kursus Linux Command Line & Bash Scripting Mastery, upgrade ke CoddyKit PRO. Kursus Linux Command Line & Bash Scripting Mastery mencakup 4 pelajaran total.
Apa yang akan aku pelajari di “Skrip Idempoten dan Logika Percobaan Ulang dengan Backoff”?
Rancang operasi yang aman dijalankan ulang dan tambahkan backoff eksponensial untuk panggilan eksternal yang tidak stabil. Kamu berlatih Linux Command Line & Bash Scripting Mastery dengan kode praktik yang langsung kamu jalankan di browser, dan tutor AI 24/7 menjawab pertanyaanmu saat kamu mengerjakan pelajaran ini.
Apakah aku perlu pengalaman untuk memulai Linux Command Line & Bash Scripting Mastery?
Tidak diperlukan pengalaman sebelumnya. Linux Command Line & Bash Scripting Mastery di CoddyKit dirancang untuk pemula hingga pelajar tingkat lanjut, jadi kamu bisa memulai di sini atau dari awal dan belajar sesuai kecepatan kamu sendiri. Ini adalah pelajaran 4 dari 4.
Berapa lama pelajaran “Skrip Idempoten dan Logika Percobaan Ulang dengan Backoff” memakan waktu?
Sebagian besar pelajaran CoddyKit memakan waktu sekitar 5–10 menit. Setiap pelajaran ringkas dan interaktif, jadi kamu membuat kemajuan stabil dan melanjutkan dari tempat kamu tinggalkan di web dan aplikasi.
Bisakah aku menulis dan menjalankan kode dalam pelajaran Linux Command Line & Bash Scripting Mastery ini?
Ya. Setiap pelajaran Linux Command Line & Bash Scripting Mastery menyertakan editor kode bawaan, jadi kamu menulis dan menjalankan kode nyata langsung di browser dan mendapatkan umpan balik AI instan — tidak diperlukan penyiapan lokal.
Semua pelajaran dalam kursus ini
- Mode Ketat dengan set -euo pipefail
- Penangan Trap untuk Pembersihan dan Sinyal
- File Sementara yang Aman dan Direktori Kunci
- Skrip Idempoten dan Logika Percobaan Ulang dengan Backoff