De list: wat je gratis krijgt
Je applicatie stuurt een welkomstmail direct bij registratie, de gebruiker wacht drie seconden op een leeg scherm, en jij noemt dat een probleem van de mailserver.
Redis als wachtrij is de standaardoplossing voor dat probleem. Niet omdat het de beste is, maar omdat je het al hebt draaien. Bijna iedereen die Redis voor caching gebruikt, kan er ook een queue in kwijt. Dat is precies de verleiding én de valkuil.
De vraag is niet of Redis een wachtrij kan zijn. De vraag is welke wachtrij je nodig hebt, en of Redis daar de juiste tool voor is.
De list: wat je gratis krijgt
De simpelste wachtrij in Redis is een list. Met `LPUSH` zet je iets aan de linkerkant, met `RPOP` haal je het aan de rechterkant eruit. FIFO, klaar.```bash redis-cli LPUSH emails:queue '{"to":"user@example.com","subject":"Welkom"}' redis-cli RPOP emails:queue ```
Dat is alles. Geen broker om te installeren, geen cluster om te beheren, geen schema om te onderhouden. Voor een paar honderd taken per minuut is dit genoeg, en je hebt het waarschijnlijk al draaien.
De prijs: `RPOP` blokkeert niet. Is de lijst leeg, dan krijg je direct `nil` terug. Je worker zit dus in een loop te pollen, en elke ronde kost CPU die niets oplevert. Voor één worker is dat verwaarloosbaar. Voor twintig workers die allemaal om de seconde kijken of er werk is, is dat verspilling.
Daar is `BRPOP` voor. Dat blokkeert de consumer tot er iets binnenkomt.
```bash redis-cli BRPOP emails:queue 30 ```
De `30` is de timeout in seconden. Na dertig seconden geeft het commando `nil` terug en kun je beslissen of je blijft wachten. Zet die timeout nooit op `0` tenzij je zeker weet dat je verbinding nooit wegvalt. Een worker die oneindig blokkeert op een dode verbinding is een worker die je pas opmerkt als de wachtrij vol loopt.
Tot zover de list. Voor veel teams is dit het einde van het verhaal, en dat is prima. De kosten zijn laag, de complexiteit is laag, en de grenzen zijn duidelijk.
Waar de list je in de steek laat
Een list heeft geen geheugen. Zodra een worker een item met `BRPOP` eruit haalt, is het weg uit Redis. Als die worker crasht voordat hij klaar is, is de taak verdwenen. Geen foutmelding, geen herkansing, niets.Dat is voor een nieuwsbrief niet erg. Voor een betaling wel.
Je kunt dit oplossen met een tweede lijst: `BRPOPLPUSH` schuift het item atomair naar een "in behandeling"-lijst, en pas als de worker klaar is verwijder je het daaruit. Dat werkt, maar je bouwt dan zelf het bevestigingsmechanisme dat andere systemen ingebouwd hebben. Elke regel die je zelf schrijft over crash-herstel is een regel die iemand anders al een keer fout heeft gedaan.
De list kent ook geen consumer groups. Twee workers op dezelfde lijst werkt wel — Redis is atomair per commando, dus twee `BRPOP`-aanroepen kunnen nooit hetzelfde item pakken — maar je kunt niet zeggen "deze drie workers horen bij elkaar en delen de last". Elke worker is een losse consument van dezelfde stapel.
Voor één queue is dat geen probleem. Voor tien queues met verschillende prioriteiten wordt het handwerk.
Streams: meer dan je vroeg
Sinds Redis 5.0 heb je Streams. Dat is geen lijst met extra's, dat is een ander datatype met een eigen model.```bash redis-cli XADD emails:stream '*' to user@example.com subject Welkom redis-cli XREADGROUP GROUP workers alice COUNT 1 BLOCK 5000 STREAMS emails:stream '>' ```
De `*` laat Redis de ID genereren. Die ID is tijdgebaseerd, dus je houdt volgorde én je kunt terugkijken. Met `XREADGROUP` lees je als lid van een consumer group, en pas als je `XACK` stuurt, weet Redis dat het bericht verwerkt is.
Wat je hiermee krijgt: berichten blijven staan tot iemand ze bevestigt. Een worker die crasht laat het bericht in de pending-lijst achter, en je kunt het opnieuw toewijzen. Meerdere consumers delen het werk zonder dat je dat zelf hoeft te coördineren.
Wat het je kost: meer concepten. Je moet nadenken over consumer groups, over pending entries, over wat er gebeurt als een bericht tien keer mislukt. De list had één commando nodig. De stream heeft er vier, en een idee over hoe je met vastgelopen werk omgaat.
Voor wie van list naar stream stapt omdat "het moderner is": dat is geen reden. Stap over als je bevestigingen nodig hebt, of als je meerdere consumers wilt groeperen, of als je wilt kunnen terugkijken wat er ook alweer verwerkt is. Anders is de stream een oplossing voor een probleem dat je niet hebt.
Wat je niet in Redis moet stoppen
Redis is single-threaded voor commando's. Dat is geen tekortkoming, dat is het ontwerp dat atomiciteit gratis maakt. Maar het betekent wel dat één langzaam commando alles ophoudt. Een `KEYS *` op een grote database legt je hele queue stil. Een worker die per ongeluk een `SORT` op een miljoen items doet, doet dat ook.Voor hoge doorvoer — denk tienduizenden berichten per seconde — schaal je met Redis Cluster, dat keys over meerdere nodes verdeelt. Dat werkt, maar het is geen gratis schaalvergroting. Je moet nadenken over welke keys bij elkaar horen, want commando's die meerdere keys raken werken niet zomaar over nodes heen.
En dan het punt dat veel teams te laat ontdekken: Redis is in-memory. Standaard ben je bij een herstart alles kwijt. Voor een queue betekent dat: taken die nog niet verwerkt zijn, zijn weg.
Dat los je op met persistence. RDB maakt periodieke snapshots — snel, maar je verliest alles sinds de laatste snapshot. AOF logt elk schrijfcommando en is veiliger, tegen de prijs van meer I/O.
```bash redis-cli CONFIG SET appendonly yes redis-cli CONFIG SET appendfsync everysec ```
`everysec` is de gebruikelijke middenweg: maximaal één seconde dataverlies, en de schijf wordt niet bij elk commando gesynchroniseerd. Wil je dat laatste wel, dan zet je `always`. Dat is trager, en voor een wachtrij met wegwerpwerk meestal niet nodig.
Voor een echte broker met garanties over levering, complexe routing of opslag van maanden aan berichten, is Redis niet de juiste tool. RabbitMQ en Kafka doen dat beter, en het heeft geen zin om dat te ontkennen. Maar die dingen kosten een apart systeem om te draaien, te upgraden en te monitoren. Voor een queue die uren aan werk vasthoudt, niet maanden, is dat een prijs die je niet hoeft te betalen.
De laag erboven
Je kunt Redis direct aanspreken, maar de meeste teams gebruiken een framework dat de queue voor je beheert. Celery voor Python, Sidekiq voor Ruby, Bull voor Node.js. Die doen hetzelfde onder de motorkap — Redis als broker — maar voegen herkansingen, planning en monitoring toe.Dat is de echte afweging. Zelf `LPUSH` en `BRPOP` schrijven is tien regels code en volledige controle. Celery erbij halen is een dependency, een configuratiebestand en een leercurve, maar je krijgt exponentiële backoff en een dashboard waar je ziet welke taken mislukken.
Kijk naar je team, niet naar je use case. Als je de enige bent die 's nachts wakker wordt als de queue vastloopt, is het framework zijn gewicht waard. Als je precies weet wat je doet en je wilt geen laag die je niet begrijpt, schrijf het zelf.
Eén ding om te weten voordat je kiest: een queue zonder monitoring is een queue waarvan je pas ontdekt dat hij vastloopt als gebruikers klagen. De lengte van je wachtrij is een getal dat je wilt zien. Een grafiek die omhoog schiet betekent een trage of dode worker, en dat wil je weten voordat de wachtrij je schijf vult.
Waar het van afhangt
Redis als wachtrij werkt, en voor de meeste toepassingen werkt het goed genoeg. De vraag is niet of je het kunt gebruiken, maar of je de grenzen ervan accepteert.Heb je bevestigingen nodig, dan is het list of stream. Heb je meerdere consumers die samenwerken, dan is het stream. Blijft je werk uren staan en niet maanden, dan is Redis prima. Moet een taak gegarandeerd precies één keer verwerkt worden, ook als je datacenter omvalt, dan wil je iets anders.
Begin bij de list. Stap over naar streams als je tegen de grenzen aanloopt. En kijk of je een framework wilt dat het beheer overneemt, of dat je het liever zelf in de hand houdt.
Wat je ook kiest: schrijf op wat er gebeurt als een worker midden in een taak sterft. Dat antwoord bepaalt meer dan welke datastructuur je ook pakt.