Späť na blog

Redis AOF fsync latency špičky: keď sa durabilita stane tvojím p99

Tento incident vyzerá ako “Redis je občas pomalý”, ale je bolestivo opakovateľný, keď vieš čo hľadať:

  • p99 latencia špičkuje v burstoch (často každú sekundu alebo okolo persistence aktivity)
  • CPU nie je pinned
  • sieť je OK
  • retry to maskuje, kým nespadne tail SLO

Ak máš Redis s AOF, existujú dvaja klasickí zabijaci latencie:

  1. fsync a disk contention (durabilita súperí so serving requestov)
  2. AOF rewrite fork CoW špičky (pamäťový tlak a paging počas BGREWRITEAOF)

Testované na: Redis 7.2–7.4, Linux 6.1–6.6, Kubernetes s PV storage (rýchle SSD aj pomalšie network disky).

Incident (anonymizovaný)

Zapli sme AOF na Redis inštancii, aby sme znížili data loss pri výpadku nodu. Okamžite:

  • p99 išlo z pár ms na 50–300ms špičky
  • počas rewrite okien vyskočilo RSS a pod sa blížil k OOM
  • aplikácia začala retryovať a amplifikovať traffic

Constraint: AOF sme nemohli vypnúť. Bol to produktový requirement. Potrebovali sme persistenciu spraviť predvídateľnú a zabrániť tomu, aby rewrite spravil memory cliff.

Timeline

  • T-0: p99 špičky sa objavia krátko po zapnutí AOF.
  • T+10m: INFO persistence ukáže rast aof_delayed_fsync počas špičiek.
  • T+20m: disk latencia koreluje so špičkami.
  • T+30m: BGREWRITEAOF koreluje s RSS rastom (CoW správanie).
  • T+45m: mitigácia: znížiť fsync contention počas rewrite a pridať memory headroom.
  • T+2h: p99 sa stabilizuje; rewrite už nehrozí OOM.
  • T+1d: guardrails: alerty na delayed fsync, fork time a rewrite frekvenciu.

Mechanizmus: odkiaľ prichádza latencia

AOF spraví z durability reálnu IO prácu

Pri AOF Redis appenduje write príkazy do AOF súboru a fsyncuje podľa politiky (appendfsync). Aj keď je fsync často asynchrónny, disk contention stále rozhoduje:

  • filesystem a disk sú shared resource
  • background write a fsync flush sa bijú s read/write requestami
  • na pomalom disku dostaneš p99 špičky

Praktický signál je aof_delayed_fsync: “chceli sme fsync, ale nestihli sme”.

BGREWRITEAOF = fork + CoW

Rewrite vytvorí kompaktný AOF. Redis forkne:

  • child zapisuje nový AOF
  • parent ďalej obsluhuje traffic
  • copy-on-write spraví pamäťové špičky pri modifikáciách stránok

Ak je memory headroom tesný, rewrite ťa vie zatlačiť do:

  • reclaim stallov
  • major faultov
  • alebo OOMKilled

Runbook: dokáž, že príčina je AOF

1) Stav persistencie a delayed fsync

redis-cli INFO persistence | rg -n "aof_enabled|appendfsync|aof_delayed_fsync|aof_rewrite_in_progress|aof_current_size|aof_base_size|latest_fork_usec"

Sledujem:

  • aof_enabled:1
  • appendfsync politika
  • aof_delayed_fsync rast pri špičkách
  • aof_rewrite_in_progress:1 pri memory špičkách
  • latest_fork_usec (fork budget)

2) Disk latencia

Na node je iostat najrýchlejšia pravda:

iostat -x 1 10

Ak await skáče pri špičkách, platíš storage latenciou v p99.

3) Memory headroom a OOM risk

V Kubernetes:

kubectl -n <ns> top pod <redis-pod>
kubectl -n <ns> describe pod <redis-pod> | rg -n "Limits|Requests|OOMKilled|Restart" -n

Ak BGREWRITEAOF tlačí RSS k limitu, potrebuješ headroom alebo inú persistence stratégiu.

Bezpečné mitigácie (praktické poradie)

1) Sane fsync politika

V praxi často appendfsync everysec (trade-off: približne do 1 sekundy data loss pri crash).

2) Znížiť fsync contention počas rewrite

Veľmi praktický knob:

no-appendfsync-on-rewrite yes

Zníži fsync tlak počas BGREWRITEAOF okien.

3) Rewrite thresholdy ako budget

auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 512mb

Konkrétne čísla závisia od workloadu. Kontrakt je: rewrite frekvencia je bounded a vysvetliteľná.

4) Pamäťový headroom pre fork CoW

Ak nemáš CoW headroom, nemáš headroom na rewrite. V Kubernetes to často znamená:

  • vyšší memory limit
  • Guaranteed QoS (requests == limits) pre kritický Redis
  • dedikovaný node pool

5) Predvídateľný disk

AOF na pomalom disku je p99 daň každý deň. Ak chceš durabilitu, zaplať za stabilnú latenciu.

Rizikové mitigácie

  • appendfsync no: mení durabilitu a data loss môže byť veľký.
  • opakovane zabíjať BGREWRITEAOF: vieš si vyrobiť rewrite backlog a ešte väčší rewrite neskôr.
  • točiť knobs bez merania aof_delayed_fsync a fork time: lietaš naslepo.

Čo sme zmenili (konkrétne)

1) Predvídateľná persistencia (config)

Reprezentatívny redis.conf:

appendonly yes
appendfsync everysec
no-appendfsync-on-rewrite yes
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 512mb

2) Explicitný memory headroom budget

Kritickú inštanciu sme spravili Guaranteed:

resources:
  requests:
    memory: "4Gi"
  limits:
    memory: "4Gi"

3) Storage so stabilnou latenciou

PV sme presunuli na storage class so stabilnou latenciou (alebo lokálne SSD, kde to dávalo zmysel).

4) Alerty, ktoré to chytia skoro

Alertujeme:

  • aof_delayed_fsync nad baseline
  • fork time (latest_fork_usec) nad budget
  • rewrite príliš často alebo príliš dlho

Ako verifikovať

  • p99 je stabilnejšie (špičky sú zriedkavejšie a menšie).
  • aof_delayed_fsync ostáva okolo nuly.
redis-cli INFO persistence | rg -n "aof_delayed_fsync"
  • BGREWRITEAOF už netlačí RSS k limitu.

Súvisiace čítanie

Súvisiace články

Citujte tento článok

Ak na článok odkazujete, pridajte pôvodnú URL a uveďte autora.

Michal Drozd. "Redis AOF fsync latency špičky: keď sa durabilita stane tvojím p99". https://www.michal-drozd.com/sk/blog/redis-aof-fsync-latency-spicky/ (Publikované 9. januára 2026).