← retour aux snippets

retry exponentiel avec jitter

Relancer une commande qui echoue avec backoff exponentiel et jitter, cap de delai et code retour propre.


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 timeout pour borner chaque tentative; retry borne 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 base et le cap selon la criticite et les quotas des endpoints (ex: api.data.pm).