These are chat archives for heureka/php-rq

31st
Jan 2016
Jakub Kulhan
@jakubkulhan
Jan 31 2016 15:03
@grongor Ahoj, jsem tu pro detaily :) PhpRQ vypadá zajímavě, ale opravdu mi to přijde jako zneužívání Redisu na něco, pro co moc není stavěný.
Jakub Chábek
@grongor
Jan 31 2016 15:09
Cuzz super :-) Jasné, chápu, možná je - třeba přijdeš na lepší alternativu. Unikátní frontu používáme například pro Google Shopping API (Google PLA). Sypeme tam všechny produkty z Heureky (a to je velká spousta). Google vyžaduje, aby byly informace v PLA vždy stejné jako na weby. Tyto informace se ale u nás mění velice často (změní se TOP nabídka, odpadne e-shop, změní se cena, změní se dostupnost) a každou tu informaci musíme propagovat skrze celou Heureku až do PLA. Stává se, že než se tedy tahle změna vypropaguje až nakonec, tak se mezi tím pro stejný produkt pošlou dalších 5 požadavků na změnu. Windowing zde nepomůže - potřebuji to nad celou frontou.
Jakub Kulhan
@jakubkulhan
Jan 31 2016 15:12
Pokud ale tu message z unikátní fronty odbavím, ACKnu a další vteřinu přijde nová zpráva, tak se stejně pošle dotaz na Google dvakrát v krátkém časovém rozmezí, ne?
Jakub Chábek
@grongor
Jan 31 2016 15:14
Pool je pak fakt specialitka, pro kterou se těžko hledá use-case. My to aktuálně používáme na přepočet ranků a dalších informací o produktech. Problém je, že obrovská část codebasu Heureky je fakt legacy kód a když se začali řešit tyhle fronty atp, tak byl velký problém najít všechna místa, kde se informaci měnili atp. Zrovna ty ranky atp nebylo ale nutné mít up-to-date každou minutu, právě naopak - když to bylo den staré, tak se teoreticky nic neděle ... Proto se vymymslel tenhle pool, kde se všechny produkty pořád dokola točí a přepočítávají. Když ale na nějakém místě víme, že došlo ke změně, tak to do poolu přidáme, čímž se efektivně dostane na začátek poolu. Takže změny se provádí co nejdříve, ale v nejhorším případě (když se někde info o změně nepropaguje) se provede třeba do jednoho dne (záleží na nastavení). Tohle je fakt special use-case, ale funguje to pro nás no :)
Jo, jasně, pokud se fronta stíhá odbavovat, tak není problém a samozřejmě je windowing a deduplikace v consumerovi jasné řešení. Problém je ale právě v momentě, kdy se fronta odbavovat nestíhá a paralelizace není řešení. Zde nás totiž brzdí Google API - je příliš pomalé. Paralelizovat to navíc nelze kvůli omezenému počtu spojení atp. Navíc by to pak znamenalo mnohem složitější kód, než máme teď (a i teď je opravdu komplexní). Holt ty PLA nebyly myšleny pro marketplaces jako jsme my, ale pro obchody - ty to mají samozřejmě mnohem snažší :-)
Většinu času se ta fronta zpracovává, vše je ok. Čas od času je tam ale peak a trvá to třeba 30 minut, hodinu než se to zpracuje. V ten moment je právě ten mechanismus unikátnosti k nezaplacení - nepřeplňuje se to zbytečnostmi ale jen relevantními zprávami.
Jakub Kulhan
@jakubkulhan
Jan 31 2016 15:26
This message was deleted
Jakub Kulhan
@jakubkulhan
Jan 31 2016 15:38

Ve Skrzu se řešily úplně stejné problémy - synchronizace mezi primární databází (MySQL), ostatními databázemi a externími systémy (Facebook Ads apod.) - a opět, že nelze dost dobře podchytit, který legacy systém kdy/co změnil. Udělal jsem BunnyBundle https://github.com/skrz/bunny-bundle, který má takovou šikovnou věc - tickMethod - metodu, která se zavolá periodicky mezi zprávami, např.:

/**
  * @Consumer(tickSeconds=1, meta="...")
  */
class ElasticsearchUpdateConsumer {

    /**
      * @var \Bunny\Channel
      *
      * @Autowired
      */
    public $channel;

    private $ids = [];
    private $msgs = [];

    public function handleMessage(ChangeVO $vo, \Bunny\Message $msg) {
        $this->ids[$vo->getId()] = true;
        $this->msgs[] = $msg;
    }

    public function tick() {
        // ... upsert $ids into Elasticsearch
        $this->ids = [];
        foreach ($this->msgs as $msg) { $this->channel->ack($msg); }
        $this->msgs = [];
    }
}

Díky tomu se jednoduše řeší windowing na zprávy. Tu unikátnost podle mě ani moc nechceš řešit na úrovni messaging brokeru, ale musí to vyřešit každý klient (consumer) sám, protože každý definuje duplicitnost jinak. Např. zpráva je duplicitní, pokud proběhl poslední import do ES/Google/... po datumu změny. Řešit problém toho, že consumeři nestíhají odbavovat message tímhle způsobem je skrývání problému.

V tom poolu se točí jednotlivé produkty a pro každý se přepočítává rank jednotlivě? To musí být strašně neefektivní, ne? :) Vždycky když se přepočítával rank na Skrzu, bylo nejrychlejší vzít bulk dat pro všechny produkty a spočíst to naráz.

Jakub Chábek
@grongor
Jan 31 2016 15:59

S definicí duplicitnosti máš pravdu - jakmile posíláš něco složitějšího než třeba ID, tak se už pomalu ty plusy vytrácí a musíš to řešit na straně consumera. 90% našich front ale tvoří simple zprávy obsahující jen ID, takže to opravdu využijeme (a řešení na straně brokeru je tak jednodušší a v těchto use-casech i efektivnější). Ad skrývání problému - ne tak docela. Tím jsme jen docílili toho, že problém není - zmenšili jsme počet zpráv natolik, že není třeba řešit cokoliv jiného. Rozhodně to není všespásné a aplikovatelné na každý use-case - to rozhodně ne. Zrovna u těch PLA jsme ale bohužel jinou možnost neměli (Google se s námi o navýšení limitů odmítá bavit).

Obecně nevidím důvod, proč neumožnit unikátní frontu. Když víš, že nemá žádný reálný smysl posílat zprávy do fronty vícekrát, tak proč to dělat, když nemusíš? Tím jsme na mnoha místech snížili počet zpráv a tím v důsledku i počet čtení/zápisů do databáze či jiných služeb. Je to podle mě obecně nice-to-have feature.

Ad Pool - ne, jasně že ne, tam se to bere po chuncích (můžeš zvolit, kolik jich chceš). Funguje to prostě tak, že se vybere třeba 500 zpráv, které mají timestamp nižší než je aktuální. Acknowledge vlastně znamená jen úpravu toho timestampu na např {aktuální timestamp} - 12 hodin. Takže ikdyž bude v poolu spousta zpráv, tak se ti při zavolání getItems() nemusí nic vrátit, když je vše označeno ke zpracování až za xx hodin.

Jo a ad tickMethod - docela cool koncept :-) Na většinu aplikací asi dostačující. Problém windowingu na straně consumera ale také vidím třeba v tom, že v případě více consumerů (paralelní zpracování fronty) bude reálně docházet ke zpracování stejných zpráv v jednom okně (protože si to prostě řešení každý consumer jenom v rámci toho svého chunku). Samozřejmě je to už extrém a pro většinu aplikací je to stejně jedno, ale opět v tomhle případě vyhrává ta unikátnost na straně brokeru (dokud to teda jsi schopný reálně řešit - zprávy jsou primitivní).
Jakub Chábek
@grongor
Jan 31 2016 16:07

Btw celkově mě trochu zarazilo, že jsi napsal, že psát frontovací systém nad Redisem je abuse technologie. Já to vidím jinak - Redis je prostě key-value storage se spoustou možností. Já jsem prostě za využití jeho možností naimplementoval frontu. Přišlo mi, že jeho vlastnosti a možnosti jsou ideální pro stavbu nečeho takového. Co je na tom podle tebe špatného?

Chápal bych, kdyby jsi se divil, proč něco takového vzniklo, když je tu spousta jiných technologií na fronty - to je podle mě validní dotaz a je opravdu možné, že prostě jen neznám technologii, který by mi umožnila dělat to, co jsem potřeboval. Zatím jsem na takovou ale nenarazil - musel bych je různě složitě ohýbat atp. Proto mi ve výsledku nepřišlo jako špatný nápad napsat si něco vlastního, co bude plnit požadavky, které jsem na to měl :-)

Jakub Kulhan
@jakubkulhan
Jan 31 2016 16:12

Obecně nevidím důvod, proč neumožnit unikátní frontu.

Protože to nenaškáluješ na víc než jednu instanci a/nebo to bude strašně neefektivní.

Btw celkově mě trochu zarazilo, že jsi napsal, že psát frontovací systém nad Redisem je abuse technologie. Já to vidím jinak - Redis je prostě key-value storage se spoustou možností. Já jsem prostě za využití jeho možností naimplementoval frontu. Přišlo mi, že jeho vlastnosti a možnosti jsou ideální pro stavbu nečeho takového. Co je na tom podle tebe špatného?

Protože sám autor Redisu napsal http://antirez.com/news/88 - Many developers use Redis as a message queue [...] Redis apparently is at the same time the best and the worst system to use like that. [...] Redis HA / Cluster tradeoffs are totally biased towards large mutable values. a začal stavět messaging server Disque - https://github.com/antirez/disque.

Další věc je, že musíš používat nějakou knihovnu. PhpRQ je language-specific, pokud si jednou řekneš, že consumer XYZ by bylo lepší udělat v něčem jiném než v PHP, budeš muset nejdřív implementovat stejnou knihovnu na nové platformě.

RabbitMQ je super v tom, že má client knihovnu pro jakýkoli myslitelný jazyk, a tak když se ve skrzu potřeboval jeden consumer udělat v Go, prostě se použila knihovna pro Go a bylo vystaráno, nijak se to nedotklo dosavadních aplikací.

Jakub Chábek
@grongor
Jan 31 2016 16:25

No netřeba víc než jedna instance. Většinu front máme společně na jedné instanci. Ty nejvytíženější pak mají každá svou instanci. Plus každý master má několik slavů. Konfiguračně to není problém - jednoduchá konfigurace v Puppetu + na straně PHP máme factory, která dle fronty bere odpovídající spojení do Redisu.

Jojo Disque znám - vzniklo chvíli po tom, co jsem dokončil PhpRQ :D Kdyby to existovalo dřív, možná bych si vystačil s tím a nevytvářel vlastní systém ... každopádně pořád je to beta :/ a co jsem četl (už je to nějaký pátek, možná se věci změnili), tak to stejně neumí všechno to, co jsme od front očekávali.

To, že je PhpRQ language-specific, je by-design. Ale jo, souhlasím, může to být problém. Výhoda je, že naprostě většina logiky je na straně Redisu a tedy v LUA skriptech, které jsou portovatelné napříč těmi implementacemi. Aktuálně (velice pomalu, protože není taková potřeba) vzniká py-rq (Python implementace).

Rabbit je prostě rozšířený - to je rozhodně jeho obrovská výhoda. Jak jsem na svojí přednášce na Posobtě říkal, tak jsme s Rabbitem měli problémy. Možná to bylo tím, že v té době nebyl tak mature nebo jsme ho holt používali špatně (nemyslím si), ale prostě to nedával a ztrácel zprávy + byl tam memory-leak (nebo špatný GC v Erlangu, už je to doba, nejsem si jist). Anyway Rabbit mimo tyto problémy co jsme s ním měli nenabízel to, co má navíc PhpRQ ... prostě to pro nás nebylo vhodné řešení.

Ale jo, dělat RPC nebo tak něco, tak použiju RabbitMQ nebo ZMQ - už jen z toho důvodu více klientů :)

Jakub Chábek
@grongor
Jan 31 2016 16:32
Btw používal jsi tu Disque? Názory/srovnání s třeba RabbitMQ? :-)
Jakub Kulhan
@jakubkulhan
Jan 31 2016 16:42
Disque jsem nikdy nepoužil. Když se člověk naučí používat Rabbit a pochopí důvody, proč AMQP model vypadá, jak vypadá, není důvod používat cokoli jiného :) Navíc Disque (a Beanstalkd, Gearman apod. systémy) umí jediný z use cases messagingu - work queues. Ty jsem potřeboval pro menší počet případů než pub-sub a RPC :)