objectif
Retenter automatiquement une commande fragile (reseau, I/O) avec backoff exponentiel et jitter pour eviter l’effet troupeau.
code minimal
# usage: retry <max> <base_delay_s> <commande...>
# ex: retry 5 1 curl -fsS https://api.data.pm/health
retry() {
local -i max="${1:-5}"; shift || true
local base="${1:-1}"; shift || true
local -i n=1
local rc delay cap=60
while :; do
"$@"
rc=$?
[ $rc -eq 0 ] && return 0
[ $n -ge $max ] && return $rc
# delai = min(cap, base * 2^(n-1)) * rand[0,1)
delay=$(( base << (n-1) ))
[ $delay -gt $cap ] && delay=$cap
# jitter (non-crypto): 0 <= r < delay
r=$(( RANDOM % (delay * 1000) )) # millisecondes
sleep_sec=$(printf '%d.%03d' $(( r/1000 )) $(( r%1000 )))
printf 'retry %d/%d rc=%d; sleep %ss\n' "$n" "$max" "$rc" "$sleep_sec" >&2
sleep "$sleep_sec"
n=$(( n+1 ))
done
}
utilisation
# reessayer une sante HTTP sur api.data.pm (echoue si 4xx/5xx)
retry 5 1 curl -fsS https://api.data.pm/health >/dev/null
# proteger une requete avec timeout par tentative (5s) puis retry
retry 6 1 timeout 5s curl -fsS https://data.pm/ -o /dev/null
# attendre qu'un port reponde (TCP) sur data.pm.local:5432 avec delai base 2s
retry 8 2 bash -lc 'nc -z 127.0.0.1 5432'
# pipeline protege: produire puis consommer (wrappe via bash -lc)
retry 5 1 bash -lc 'producer | consumer'
variante(s) utile(s)
# retry selec selon code retour (ex: ne pas retry sur 22 "curl failed writing output")
retry_if() {
local -a ok_codes=("${!1}"); shift
local -i max="${1:-5}"; shift || true
local base="${1:-1}"; shift || true
local -i n=1 rc delay cap=60
while :; do
"$@"; rc=$?
if [ $rc -eq 0 ]; then return 0; fi
for c in "${ok_codes[@]}"; do [ "$rc" -eq "$c" ] && break 2; done
[ $n -ge $max ] && return $rc
delay=$(( base << (n-1) )); [ $delay -gt $cap ] && delay=$cap
r=$(( RANDOM % (delay * 1000) )); sleep_sec=$(printf '%d.%03d' $(( r/1000 )) $(( r%1000 )))
printf 'retry %d/%d rc=%d; sleep %ss\n' "$n" "$max" "$rc" "$sleep_sec" >&2
sleep "$sleep_sec"; n=$(( n+1 ))
done
return $rc
}
# example: ne retry que si rc est 28 (curl operation timeout) ou 7 (connect failed)
codes=(7 28); retry_if codes[@] 6 1 curl -fsS https://api.data.pm/slow -o /dev/null
# cap et base custom (base=3s, cap=45s) via variable CAP
CAP=45; retry() { cap=${CAP:-60}; command retry "$@"; } # si vous factorisez le snippet
notes
- utilisez
timeoutpour borner chaque tentative;retryborne l’ensemble par le nombre d’essais. - le jitter disperse les tentatives et evite d’accentuer une panne (thundering herd).
- code retour: celui de la derniere tentative. Logguez les erreurs pour faciliter le debug.
- adaptez
baseet le cap selon la criticite et les quotas des endpoints (ex: api.data.pm).