BullMQ si ratelimiter dupa groupKey

Salut,
Problema: Am o coada in care se scriu evenimente care ar trebui sa fie grupate pe vendorId pentru ca se face request la un api care are rate limiter.
Research: BullMQ ofera posibilitatea de a avea rate limiter dupa groupKey dar zic ei foarte clar ca din cauza unor probleme de performanta au scos incepand cu versiunea 3. Tot ei ofera pentru versiunea pro un rate limiter pe grop care nu mai are probleme de performanta :slight_smile: . Inteles si apreciez munca lor dar din pacate e mai mult un PoC si nu as vrea sa platesc versiunea Pro acum.
In versiunea standard de bullMq spun ei ceva de manual dar nu prea exista functiile alea in ultima versiunea.
Alternativa 1: O alta solutie car fi sa fac cozi de forma nume-coada_{vendorID} dar asta inseamna ca trebuie sa declar dinamic cozile in functie de vendori si daca mai apare un vendor nou ar trebui sa il adaug si pe asta pe toti workerii si tot asa, lucru care mi se pare ca aduce un overhead groaznic. **Alternativa 2:** Sa imi creez niste chei in redis de forma app:nume_coada:{vendorID} si sa pun jobul pe care il proceseaza si practic sa tin in redis un fel de tabel taskurile la care lucreaza workerii. Eventual un TTL egal cu timpul configurat dupa care un job este pus inapoi in waiting daca nu este terminat. Operatiile pe redis in care tin in picioare lista ori o fac pe baza de liseneri pe cozi (Dar daca am 10 workeri pe instante diferite la fiecare schimbare de job ar trebui sa vad daca cheia exista si sa o sterg. Operatie care o sa fie facuta de un random queueEvents lisener si resul doar intreaba redisul daca exista cheia si daca nu exista o sterge.) sau pe baza de cozi (am o coada pentru management si fiecare instanta de node are un worker pe coada asta si se duce la primul worker disponibil).

Inclin foarte tare pe Alternativa 2 , varianta cu evenimente in coada ca mi se pare ca aduce mai putin stres pe redis.

Vedeti vreo problema de performanta in abordarea mea? (Cei de la BullMq spuneau ca au scos pentru ca performata slaba)

Aveti o solutie mai desteapta?

Multumesc in avans.

Ca fapt divers, exista conceptul de exponential backoff, care face automat retry in: 1, 2, 4, 8, 16 secunde si tot asa din ce in ce mai incet. Bine, poti configura tu strategia de retry, asta e doar un exemplu standard.


Dar analizand varianta propusa de tine, nu prea am inteles mare lucru. Pare o varianta complicata, sau, cel putin, asa ai prezentat-o tu. Si zic asta pentru ca problema, in sine, nu e una atat de complexa.

Eu as simplica solutia in felul urmator: iti trebuie sa salvezi intr-un DB (fie el si Redis) numarul de request-uri available (pana la rate limit). De fiecare data cand faci un request catre acel API, updatezi valoarea (adica n = n - 1). Atata timp cat acest numar e mai mare de 0, atunci iei mesaje din coada si le procesezi.

Mi se pare ca esti pe calea cea buna cu solutia. Cateva aspecte:

  • groupKey vine cu avantajul ca nu mai trebuie sa setezi queue-uri noi cu listeneri noi
  • queue-urile separate sunt mai usor de gestionat, dar mai greu de setat multe queue-uri noi
  • ratelimitul dupa cum te-ai gandit e o combinatie intre listeneri (cu sistem de cache) si mecanismul de queue
  • Poti sa stai linistit si sa folosesti Redis. Tine mai mult decat ai crede
1 Like

Mulțumesc pentru raspunsuri. Tot pe zona de shopify ma invart. Am “trecut” pe graphql. Nu sriu de ce am fugit ca tehnologia o cunoșteam de mult.
Shopify, ca orice firma care se respecta, are rate limiter. Aplicatia e multivendor. Folosind o coadă pentru toti, există cazul in care un shop pune de exemplu 6000 de taskuri in coadă. Pot sa execut din calculele mele cam 8-10 pe secunda (mă referă strict din perspectiva costului si fara sa iau in calcul network lag). Asta inseamna ca un shop poate sa tina lejer ocupata o blucla si 10 minute.
Partea de grupuri nu merge gratuit, asa ca o sa îmi implementeze eu versiunea mea care o sa faca asa:
Fiecare shop o sa aiba o cheie de forma app:queue:shopDomain care o sa aiba in json de forma workers (cine lucreaza la taskuri) si raspunsul loc cu costul ca sa sriu cat de multe puncte mai sunt disponibile.
Le fiecare job primit de worker se uita daca mai sunt locuri disponibile pt workeri sau ami sunt puncte. In caz de nu ori pun jobul ca delayed ori aruncu o eroare de bullmq care imi pune jobul iar in pending.
Ce mai vreau sa fac este sa pun logurile pe job in redis.
Am lucrat cu bullmq si redis pe alt proiect si desi redisul se comporta impecabil noi puneam foarte multe date pe joburi si datorită limitărilor de cati workeri sa fie pe cozi si nr de taskuri ajungeam ca redisul sa creasca ușor si sigur ca erau mai multe joburi puse decât consumam. Si ajungea redisul la 40gb si crapa pt ca asa era configurat :slight_smile:
Abordarea noastra era cu o coada per vendor daca venea cu dificultăți la partea de management ca a atunci cand im db apare un client nou toate instantele trebuie sa afle ca sa ii faca coada si workeri .
E super interesanta abordarea cu cozi, dar uneori trebuie o reglare fina.
Sunt curios varianta mea finala cum o sa aiba.
Nu ma pot decide :
-daca ar fi mai bine in redis sa am cheie in care am un mic json sau sa am cate o cheie pt forcare element din json.

  • sa tin logurile pe taskul de redis
  • sa dumpez taskurile de redis intr-un sqlite ?