CV laden
Zurück zu Business Cases

Schedule

Ein Scheduler, der denselben Job viermal startete

withoutOverlapping lag in Memcached. HTML-Eviction warf den Mutex. Vier Rebuilds schrieben einen Listen-Key.

Der schedule-Container führt jede Minute php artisan schedule:run aus. catalog:rebuild stand auf everyMinute() mit withoutOverlapping(). Der Job brauchte vier Minuten auf einem Dirty-Set mit ein paar tausend IDs. Vier Prozesse schrieben dieselben Memcached-Keys. Redis catalog:dirty wurde nie leer, weil jeder Lauf IDs addierte, die der vorherige noch nicht fertig hatte.

withoutOverlapping() legt den Lock in den Default-Cache. CACHE_STORE war Memcached. Listen-Slab und Mutex teilten eine Slab-Klasse. Wenn der Katalog den Cache füllte, verschwand der Lock-Key. Die nächste Minute sah keinen Mutex und startete erneut.

Das Problem war ein Mutex in dem Store, der HTML absichtlich wirft. Vier Rebuilds kämpften um einen Listen-Key. Das Dirty-Set leerte sich nie. Der Lock muss neben Horizon liegen, in Redis, mit einem TTL länger als der langsamste Rebuild.

Vier Starts, ein Listen-Key, ein Redis-Dirty-Set, das nie leert.
Vier Starts, ein Listen-Key, ein Redis-Dirty-Set, das nie leert.

Der Lock-Store

Der Scheduler-Lock liegt in Redis. SET schedule:rebuild NX EX 600. Horizon ist schon dort. HTML-Eviction in Memcached kann den Key nicht werfen. Der Job liest weiter schmutzige IDs aus einem Redis-Set, baut Slabs, schreibt Memcached. Es läuft ein Builder.

Ein Mutex neben HTML-Slabs ist keiner. Ein Mutex neben Horizon ist einer.
Ein Mutex neben HTML-Slabs ist keiner. Ein Mutex neben Horizon ist einer.
  • onOneServer() nutzt dasselbe Redis, zwei schedule-Replicas starten also nicht beide.
  • Das Lock-Timeout ist länger als der langsamste Rebuild, den wir gemessen haben.
  • Eine verpasste Minute ist in Ordnung. Ein doppelter Rebuild nicht.

Das Dirty-Set leert sich. Memcached sieht einen Schreiber. Die CPU des schedule-Containers ist zwischen Ticks wieder idle. Der Bug war nicht cron. Der Bug war ein Lock in dem Store, der Seiten absichtlich wegwirft.

Was ich daraus mitnehme

withoutOverlapping() ist nur so gut wie der Store hinter CACHE_STORE. Memcached ist kein Lock-Dienst.

Ein Mutex-TTL kürzer als der Job ist ein zweiter Start. EX 600 ist langweilig und richtig.

Ich verpasse lieber eine Minute, als zwei Rebuilds zu starten. Überlappung kostet mehr als Delay.

Zurück zu Business Cases