Redis Lua-scripts voeren meerdere Redis-commando’s, inclusief voorwaarden, geïsoleerd op de server uit. Zo ontstaat er tussen het lezen, controleren en schrijven geen tegenstrijdige tussenstand door andere clients. 'Atomaar' betekent in dit geval niet dat er automatisch een rollback plaatsvindt: Invoer en foutpaden moeten vooral vóór schrijfbewerkingen zorgvuldig worden ontworpen. Cruciaal zijn duidelijk gedefinieerde sleutels, stabiele retourwaarden, korte uitvoeringstijden en een geschikt model – van de native opdracht tot de Redis-functie.
Atomaire Redis Lua-scripts ordenen
Redis Lua-scripts voeren de specifieke datalogica rechtstreeks op de Redis-server uit. Terwijl een script wordt uitgevoerd, verwerkt Redis geen andere serveractiviteiten; de opdrachten in het script zijn daarom geïsoleerd ten opzichte van andere clients. Hierdoor kunnen meerdere eenvoudige opdrachten worden samengevoegd tot één atoomoperatie koppelen, bijvoorbeeld een limietcontrole gevolgd door een update van de teller of een afschrijving die alleen plaatsvindt als er voldoende saldo is.
Zonder script kan een client eerst met GET een teller uitlezen, de limiet in de applicatiecode controleren en vervolgens INCR verzenden. Tussen deze stappen kan echter een andere client dezelfde teller wijzigen. Een script leest, controleert en verhoogt daarentegen zonder deze waarneembare tussenliggende toestand. Dit lost de race condition van de samengestelde regel op, maar biedt niet automatisch een oplossing voor kwesties als geschikte grenswaarden, verwerkingstijden of retourformaten.
Minimalistisch en geïsoleerd betekent niet dat Redis Lua-scripts Databasetransacties met automatische rollback. Als er na een reeds voltooide schrijfbewerking een runtime-fout optreedt, worden eerdere wijzigingen niet zonder meer ongedaan gemaakt. Daarom moeten scripts de invoer, gegevenstypen en zakelijke voorwaarden controleren voordat ze voor het eerst schrijven; foutpaden na wijzigingen vereisen een bewust ontworpen afhandeling.
Typische regels zijn: toegang alleen toestaan binnen een bepaalde limiet, of een voorraad alleen verminderen als er voldoende voorraad is. Controleer eerst of een bestaande afzonderlijke Redis-opdracht de regel al volledig weergeeft. Een script is zinvol wanneer meerdere Redis-bewerkingen, inclusief hun voorwaarden, atomair moeten samenwerken.
Een Lua-patroon voor ‘vergelijken en verwijderen’ vergelijkt de opgeslagen waarde met een doorgegeven eigendomstoken en verwijdert de sleutel alleen als deze overeenkomt. Hierdoor kan een proces dat te laat start, een sleutel die inmiddels opnieuw is toegewezen niet verwijderen louter op basis van zijn oude token.
Dit vergelijkingsmodel beschrijft uitsluitend de veilige volgorde voor een enkele Redis-key. Het biedt geen antwoord op de bredere vraagstukken rond gedistribueerde vergrendelingen, zoals geschikte lease-duur, procespauzes, storingen of de coördinatie van meerdere Redis-instanties. De atomiciteit van een commando of script heeft bovendien alleen betrekking op de betrokken Redis-gegevens, en niet op betalingen, de database, e-mail of externe API's.
Lua-sandbox en duidelijke grenzen
Redis Open Source maakt gebruik van Lua 5.1 voor scripts. Deze runtime is niet te vergelijken met een lokaal geïnstalleerde of actuele hoofdversie van Lua: de beschikbare taalfuncties en beveiligingsregels worden bepaald door Redis. Wie Redis Lua-scripts ontwikkelt, moet deze daarom toetsen aan de daadwerkelijk gebruikte Redis-versie en mag niet uitgaan van de eigenschappen van een willekeurige externe Lua-omgeving.
De uitvoering vindt plaats in een Sandbox met opzettelijk strakke beperkingen. Een script moet Redis-gegevens en doorgegeven argumenten verwerken, maar mag geen gebruik maken van het bestandssysteem, het netwerk of besturingssysteemdiensten. Externe HTTP-aanroepen, het verzenden van berichten of toegang tot lokale bestanden horen daarom thuis in de applicatiecode of in een daarvoor bestemde dienst, niet in cache scripting.
Redis biedt KEYS en ARGV als globale variabelen. Voor je eigen tussenwaarden en hulpfuncties gebruik je daarentegen lokale variabelen met local. Zo blijft duidelijk welke waarden alleen voor deze aanroep gelden, en creëert de scriptlogica geen vermijdbare afhankelijkheden. Redis-commando’s roep je gericht aan via redis.call of redis.pcall op.
De sandbox is geen vervanging voor capaciteitsplanning. Tijdens de reguliere uitvoering blokkeert een script andere clients op de server; daarom zijn lange lussen, onbeperkte hoeveelheden gegevens en rekenintensieve analyses ongeschikt. Beperk het werk tot een klein aantal, vooraf bekende sleutels en kleine berekeningen. Uitgebreide analyses, op SCAN gebaseerde totale voorraden of communicatie met externe systemen zouden de operationele risico's vergroten, zonder de atomiciteit op zinvolle wijze uit te breiden.
EVAL, KEYS en ARGV begrijpen
Om een script direct uit te voeren, gebruik je de volgende vorm EVAL script numkeys [key …] [arg …]. Volgens de broncode bepaalt `numkeys` hoeveel van de volgende parameters sleutels zijn. Het script bereikt deze via KEYS met indexering vanaf 1; alle overige waarden staan in ARGV. Dit onderscheid is essentieel: sleutels beschrijven de Redis-gegevens, argumenten de inhoudelijke invoer zoals grenswaarde, bedrag of verwachte token.
Een limietscript ontvangt bijvoorbeeld de teller als KEYS[1] en de maximumwaarde als ARGV[1]. Het leest de huidige stand, converteert de grenswaarde met tonumber(ARGV[1]) zet deze om in een getal en vergelijkt beide waarden voordat het wordt verhoogd. Door deze omzetting wordt de beoogde numerieke regel expliciet gemaakt, in plaats van te vertrouwen op een impliciete verwerking van argumentwaarden. Als er een teller ontbreekt, kan het script de gelezen waarde doelbewust als nul behandelen.
Elke sleutel die het script leest of schrijft, moet vooraf als sleutelargument worden opgegeven. Het samenstellen van sleutelnamen in het script uit voorvoegsels of het afleiden ervan uit opgeslagen gegevens is geen betrouwbaar patroon. Met name bij Redis Open Source met een geactiveerde cluster kan Redis dan vóór de uitvoering niet achterhalen welke gegevens het script nodig heeft. Geef daarom bekende sleutels volledig door via KEYS en variabele waarden uitsluitend via ARGV.
Bij Redis Open Source met een geactiveerde cluster moeten de sleutels die aan een script worden doorgegeven bovendien in dezelfde hash-slot liggen. De eerdere declaratie maakt deze controle mogelijk, maar vervangt deze niet. Voor bij elkaar horende gegevens kan een bewust gekozen hash-tag helpen, bijvoorbeeld account:{4711}:balance en account:{4711}:reservations. Het gedeelte tussen accolades bepaalt hier de toewijzing van de slots; dynamisch vastgestelde sleutels zouden deze planning ondermijnen.
Fixed-Window-tellers atomair bijwerken
Het volgende voorbeeld is een atomaire Teller met vast venster voor een lokale testomgeving. Het controleert de tellerstand en de limiet in één serverrun en stelt de aflooptijd pas in bij de eerste succesvolle toegang binnen het tijdsvenster. Hierdoor valt het tijdsvenster weg tussen een GET in de applicatiecode en een latere INCR, waarin een andere client de teller zou kunnen wijzigen.
De aanroep geeft de teller-key, de limiet en de vensterduur in seconden door. Status 1 betekent toegestaan, status 0 betekent dat de limiet is bereikt. Status 2 meldt een ongeldige invoer die bij de voorcontroles is gedetecteerd, een daar afgewezen string-tellerwaarde of een bestaande string-teller zonder TTL. Als de sleutel een ander Redis-gegevenstype bevat, mislukt GET al met een technische typefout; het script levert dan geen status 2. Ook andere Redis-runtimefouten moeten worden onderscheiden van de functionele retourstatus. Het voorbeeld is geen sjabloon voor toegangsgegevens, productieve limieten of belastingstests.
Vóór elke schrijfbewerking controleert het script of alle getallen eindige positieve gehele getallen zijn binnen een bewust laag maximum. Dit is meer dan alleen een controle met tonumber: Waarden zoals 1.5 of 1e3 worden afgewezen. De grens van een miljoen voorkomt bovendien dat Lua-getalprecisie of die van INCR verwachte integer-string buiten het voorbeeldbereik relevant wordt. De maximale vensterduur van 86.400 seconden beperkt ook de EXPIRE het aantal seconden dat is doorgegeven.
De reguliere expressie accepteert alleen decimale cijfers; vervolgens controleert de hulpfunctie de numerieke waarde, of het een geheel getal is en de bovengrens. Een reeds bestaande teller mag alleen een niet-negatieve gehele waarde binnen hetzelfde beperkte bereik zijn. Hierdoor kan een negatieve, gebroken of te grote waarde de limietsemantiek niet onopgemerkt wijzigen. Pas na deze controles volgt INCR.
Als de sleutel niet aanwezig is, begint het script bij 0. Als er al een geldige string-teller zonder vervaltijd bestaat, geeft het status 2 weer en schrijft het niets. Na de eerste INCR zet EXPIRE de TTL die eerder volledig is gecontroleerd. Bij latere treffers blijft deze ongewijzigd, zodat het venster niet voortdurend wordt verlengd.
De Retourovereenkomst maakt deel uit van de interface: het eerste element van de array beschrijft de status, het tweede levert, afhankelijk van de status, de tellerstand of een foutcode. De aanroepende code moet een inhoudelijke afwijzing met status 0 anders behandelen dan status 2, die wijst op een geschonden voorwaarde. Voor meer informatie over het kiezen en bewaken van looptijden, zie het artikel De vervaldatum van Redis-sleutels analyseren en optimaliseren een aanvullende basis.
De TTL wordt hier bewust alleen bij de eerste hit ingesteld. Een patroon dat bij elke toegang wordt vernieuwd, zou een andere tijdsemantiek hebben en zou geen ‘fixed window’ meer zijn. Lua-atomiciteit Dit lost alleen de race condition op. Of een ‘fixed window’, ‘sliding window’ of ‘token bucket’ de gewenste eerlijkheid en lastverdeling oplevert, wordt bepaald door het gekozen algoritme, niet door de scripttaal.
Het juiste verstuivingsmodel kiezen
Niet elke samengestelde vereiste vereist een script. Als er één enkele Redis-opdracht bestaat die de bedrijfsregel al volledig weergeeft, is deze meestal eenvoudiger uit te voeren en te controleren. Bij regels met meerdere stappen moeten daarentegen voorwaarden, gegevenstypen en het retourtype gezamenlijk in overweging worden genomen.
Vanaf Redis Open Source 8.4 zijn er native Compare-and-Set- en Compare-and-Delete-bewerkingen voor afzonderlijke string-sleutels: SET ondersteunt de vergelijkingsopties IFEQ/IFNE/IFDEQ/IFDNE; DELEX zorgt voor het voorwaardelijk verwijderen. Voor geschikte gevallen met één sleutel is dus geen apart vergelijkingsscript nodig. In Redis 8.2, 8.0 en 7.x zijn deze nieuwe SET-opties en DELEX niet beschikbaar; daar blijven geschikte WATCH- of Lua-patronen relevant.
Voor optimistische ‘Compare-and-Set’ kan WATCH vóór MULTI en EXEC geschikt zijn: als een geobserveerde sleutel vóór EXEC verandert, wordt de transactie afgebroken en beslist de client of er een nieuwe poging wordt ondernomen. Ook transacties bieden bij fouten tijdens EXEC geen algemene rollback. WATCH blijft daarom een optie wanneer de vereiste voorwaarde niet door één enkele native opdracht kan worden weergegeven.
| Model | Geschikte toepassing | Code en aanroep | Na een herstart of failover | Gedrag van de client en grens |
|---|---|---|---|---|
| Inbouwcommando | Een bestaande afzonderlijke bewerking geeft de regel weer | Geen programmacode; direct commando | Geen scriptcache betrokken | Geen herladen van scripts; beperkt tot bestaande semantiek |
| Native CAS/CAD vanaf Redis Open Source 8.4 | Een afzonderlijke string-sleutel instellen of verwijderen op basis van de waarde | SET met IFEQ/IFNE/IFDEQ/IFDNE; DELEX met vergelijkingsvoorwaarde | Geen scriptcache betrokken | Controleer de versielimiet en de vergelijkingsvoorwaarde; geen samengestelde regel met meerdere sleutels |
| MULTI/EXEC met WATCH | Optimistisch lezen, toetsen en schrijven | WATCH, MULTI, EXEC | Geen programmageheugen | Bij wijzigingen vóór EXEC opnieuw lezen en beslissen; geen rollback bij EXEC-fouten |
| EVAL | Klein script dat direct wordt uitgevoerd | Broncode bij elke EVAL | De scriptcache is niet permanent | Geen herlaadbeurt van de digest; de brontekst wordt opnieuw verzonden |
| SCRIPT LOAD plus EVALSHA | Hergebruikt script met bekende digest | Laden, daarna oproepen via SHA1-digest | Cache kan ontbreken | NOSCRIPT verwerken en opnieuw laden; de fallback voor de pipeline zorgvuldig plannen |
| Redis-functies vanaf versie 7.0 | Genoemde, herbruikbare datalogica | FUNCTION LOAD, daarna FCALL | Bibliotheken worden gerepliceerd en opgeslagen | Er is een versie- en implementatieproces nodig; dit mag niet worden verward met EVAL |
EVAL-scripts zijn gekoppeld aan de scriptcache en ontvangen hun invoer via KEYS en ARGV. Redis-functies zijn vanaf Redis 7.0 beschikbaar als benoemde bibliotheken: ze worden geregistreerd met FUNCTION LOAD, aangeroepen met FCALL en samen met de database opgeslagen en gerepliceerd. Hun sleutels en argumenten worden als parameters aan de functie doorgegeven; hieruit volgt een ander leverings- en aanroepmodel dan bij EVAL.
Voor kleine, toepassingsgerichte logica is EVAL daarom een directe instap. Meerdere clients en langdurig onderhouden gegevenslogica pleiten vaak voor Functions, mits de gebruikte open-sourceversie van Redis deze ondersteunt. Bij de beslissing moet bovendien rekening worden gehouden met implementatie, machtigingen, foutafhandeling en een duidelijk gedocumenteerde retourwaarde, en niet alleen met het aantal Redis-commando's.
Clusters, fouten en retourcontracten
Bij Redis Open Source met een geactiveerde cluster moeten de doorgegeven sleutels van een script met meerdere sleutels in dezelfde hash-slot liggen. Met hash-tags kun je dit regelen: Bij account:{4711}:balance en account:{4711}:reservations De inhoud tussen accolades bepaalt de slot. Beide sleutels kunnen daarom gezamenlijk worden aangesproken. De ‘same-slot’-voorwaarde geldt daar ook voor de hier besproken operaties met meerdere sleutels en MULTI/EXEC-transacties. Andere product- en clusterconfiguraties kunnen bij afzonderlijke commando’s afwijken. Hieruit volgt geen algemene cross-slot-toestemming voor Lua: de documentatie over operaties met meerdere sleutels classificeert EVAL/EVALSHA ook bij Redis Software met een geactiveerde cluster en met of zonder OSS Cluster API als een single-slot-operatie.
Alle gebruikte sleutels moeten vóór de aanroep als sleutelargumenten worden gedeclareerd. Een script mag sleutelnamen niet afleiden uit opgeslagen waarden of dynamisch samenstellen. Deze regel stelt Redis in staat om vóór de uitvoering de juiste slotcontrole uit te voeren en voorkomt verborgen afhankelijkheden die in een standalone-instantie onopgemerkt blijven, maar in Redis Open Source met geactiveerde clusterfunctie tot fouten leiden.
Met redis.call() wordt een fout in het aangeroepen Redis-commando als scriptfout doorgegeven aan de client. redis.pcall() geeft deze daarentegen terug aan Lua, zodat het script er gericht mee kan omgaan. pcall heeft alleen zin als er een specifieke reactie is gedefinieerd, bijvoorbeeld een duidelijk gestructureerd foutbericht of een alternatief toegestaan verloop. Fouten stilletjes negeren verhult problemen met gegevens en integriteit.
A Onjuiste overeenkomst maakt onderscheid tussen technische fouten en inhoudelijke resultaten. WRONGTYPE betekent bijvoorbeeld dat het opgeslagen Redis-gegevenstype niet overeenkomt met de verwachte opdracht en moet worden onderzocht. Een afgewezen reservering vanwege een ontbrekende voorraad is daarentegen een verwacht resultaat en kan bijvoorbeeld de status en de resterende voorraad weergeven. Toepassingen mogen deze categorieën niet op dezelfde manier behandelen of beide zonder onderscheid herhalen.
Scriptlevering op een robuuste manier uitvoeren
EVAL is geschikt voor directe aanroepen: de client verzendt de volledige Lua-broncode samen met sleutel- en argumentwaarden. Voor een veelgebruikt, ongewijzigd script kan de toepassing het in plaats daarvan met SCRIPT LOAD in de scriptcache laden. Redis retourneert hiervoor een SHA1-digest; EVALSHA voert vervolgens precies de bijbehorende broncode uit. Dit bespaart herhaalde overdracht, maar verandert noch de atomiciteit, noch de functionele verantwoordelijkheid van het script.
De Scriptcache is niet permanent. Na een herstart, failover of SCRIPT FLUSH kan een oproep via een digest worden gedaan met NOSCRIPT mislukken. De applicatie zou dit geval op de gebruikelijke manier moeten afhandelen: het script opnieuw laden en de technisch correcte aanroep herhalen, voor zover de eigen retry-logica dit toestaat. Een digest mag daarom niet worden opgevat als een garantie dat het script al op elke doelserver aanwezig is.
Bij pipelines is deze fallback beperkt. Als er al meerdere opdrachten gezamenlijk zijn verzonden, kan de toepassing een daarin voorkomende NOSCRIPT-Fouten niet met terugwerkende kracht vervangen door het bestand te laden en het op dezelfde plaats opnieuw uit te voeren. Redis raadt in dergelijke gevallen het gebruik van geparametriseerde EVAL als alternatieve strategie. Wie replicatie en failover plant, moet bovendien begrijpen welke rol de replicatiebuffer speelt bij het opnieuw verbinden van een replica: Inzicht krijgen in de replicatie-achterstand van Redis.
Variabele waarden horen niet thuis in de Lua-broncode, maar in ARGV. Anders genereert elke drempelwaarde een ander script en wordt de cache onnodig vergroot. Sinds Redis 7.4 kan via EVAL of EVAL_RO geladen scripts bij het bereiken van de cachelimiet volgens het LRU-principe worden verwijderd; dit vervangt noch de parametrisering, noch de verwerking van NOSCRIPT.
Lange scripts en spelfouten onder de knie krijgen
Een Lua-script blokkeert tijdens de normale uitvoering andere serveractiviteiten. Dit zorgt voor isolatie, maar bij lange looptijden leidt dit tot Bedrijfsrisico. Als een script de geconfigureerde busy-reply-threshold, reageert Redis op normale commando’s met BUSY; het script wordt hierdoor niet automatisch beëindigd. Beperk scripts daarom tot een klein aantal bekende toetsen en kleine, beperkte berekeningen.
Schrijfbewerkingen die aan een fout of een eindeloze lus voorafgaan, zijn bijzonder kritiek. Als een script al gegevens heeft gewijzigd, kan SCRIPT KILL het niet veilig afsluiten. Controleer daarom de invoer voordat je de eerste keer schrijft en vermijd onbeperkte lussen, evenals SCAN over de totale voorraden. Tests moeten het datavolume en de foutpaden van de geplande inzet weergeven.
| Zaak | Duidelijk antwoord | Typische oorzaak | Vastberadenheid |
|---|---|---|---|
| NOSCRIPT | Foutmelding NOSCRIPT | Digest ontbreekt in de tijdelijke scriptcache | Script laden of geparametriseerd EVAL gebruiken; alleen herhalen volgens de eigen herhalingsregel. |
| CROSSSLOT | CROSSSLOT bij Redis Open Source met ingeschakelde clustering | De doorgegeven sleutels van het script bevinden zich in verschillende hash-slots | Wijzig het sleutelontwerp en declareer alle benodigde sleutels. |
| WRONGTYPE | Redis-fout WRONGTYPE | Key heeft een onverwacht gegevenstype | Corrigeer het gegevensmodel of de scriptvoorwaarde; beschouw dit niet als een inhoudelijke afwijzing. |
| Opslagdruk via maxmemory | Een schrijfbewerking kan het script afbreken | Redis overschrijdt bij het opstarten al de opslaglimiet | Niet blindelings herhalen; zorg bij `redis.pcall` voor een veilige, gedocumenteerde foutafhandeling. |
| BEZIG | Foutmelding BUSY bij andere commando’s | Het script overschrijdt de ‘busy-reply-threshold’ | De belasting verminderen en het script verkleinen; na schrijfbewerkingen niet vertrouwen op het afsluiten. |
| Vaktechnische afwijzing | Gedocumenteerde statuswaarde | Bijvoorbeeld: limiet bereikt of saldo te laag | De status beoordelen en de transactie op de juiste wijze afwijzen. |
Op maxmemory hangt het verloop af van de eerste schrijfbewerking. Als Redis de limiet al heeft overschreden, kan een commando dat veel geheugen in beslag neemt bij redis.call het script afbreken; redis.pcall geeft de fout terug aan Lua en vereist een bewust ontworpen foutafhandeling. Reeds doorgevoerde wijzigingen worden hierdoor niet ongedaan gemaakt.
Een eerste bewerking zonder extra geheugenruimte, bijvoorbeeld DEL of LREM, kun je het script gewoon laten draaien; latere schrijfbewerkingen kunnen het verbruik via maxmemory verhogen. Technische fouten zoals WRONGTYPE of CROSSSLOT Bij Redis Open Source met een geactiveerde cluster zijn aanpassingen aan het gegevensmodel of het sleutelontwerp vereist, terwijl alleen het script zelf een functionele afwijzing als stabiele status kan definiëren.
Bewust kiezen voor geschikte toepassingen
Bij een voorlopige reservering kan een script de voorraad controleren, een te lage waarde afwijzen en bij succes de resterende voorraad weergeven. De atomair reservering omvat echter alleen Redis. Betalingen, relationele databases, e-mail en externe API’s vereisen een eigen afstemming en eventueel compensatielogica.
De keuze hangt af van de Redis-versie en het gegevensmodel. Vanaf Redis Open Source 8.4 kunnen de vergelijkingsopties van SET een voorwaardelijke instelling en DELEX het vergelijken en verwijderen van een enkele string-key uitvoeren. Vóór Redis 8.4 of bij een complexere voorwaarde is WATCH met MULTI/EXEC Een alternatief: als een geobserveerde sleutel vóór EXEC verandert, wordt de transactie afgebroken en beslist de client of hij de gegevens opnieuw moet inlezen en de transactie moet herhalen. Een kort Lua-script is geschikt wanneer meerdere commando’s of gegevensstructuren, inclusief hun bedrijfsregels, aan de serverzijde moeten samenwerken.
Voor gedistribueerde vergrendelingen volstaat noch de afzonderlijke opdracht, noch het Lua-patroon als totaalconcept. Leaseduur, procespauzes, storingen, herhalingen, failover en scenario's met meerdere instanties moeten afzonderlijk worden beoordeeld. Geef de voorkeur aan een native commando als de gebruikte versie en de semantiek ervan de gehele regel dekken. Anders zijn WATCH en een kort script af te wegen, afhankelijk van de foutafhandeling en de locatie van de domeinlogica. Voor herbruikbare server-side logica kan een Redis-functie geschikt zijn. Scripts die alleen-lezen zijn mogen vanaf Redis 7.0 via EVAL_RO of EVALSHA_RO werken, maar alleen bij een gegarandeerd schrijfvrije logica.
Bronnen en stand van zaken op vakgebied
Stand van het onderzoek:
Onderzoeks- en versiestatus: 23 september 2026. Dit artikel gaat over Redis Open Source en maakt een onderscheid tussen EVAL-scripts en Redis Functions vanaf Redis 7.0. Controleer de versiegrenzen en beschikbare commando's voordat je ze gebruikt, om er zeker van te zijn dat ze compatibel zijn met de daadwerkelijk gebruikte Redis-versie.
https://redis.io/docs/latest/develop/programmability/eval-intro/
https://redis.io/docs/latest/develop/programmability/
https://redis.io/docs/latest/commands/eval/
https://redis.io/docs/latest/develop/using-commands/multi-key-operations/
https://redis.io/docs/latest/develop/using-commands/transactions/
https://redis.io/docs/latest/develop/programmability/functions-intro/
https://redis.io/docs/latest/commands/evalsha_ro/




