Kun queue on täynnä virheitä, ensimmäinen reaktio on helposti katsoa koko järjestelmää kerralla.
Se ei yleensä auta.
Ota yksi epäonnistunut job ja selvitä mitä sille tapahtui. Mistä se lähetettiin? Mihin queueen se meni? Löysikö worker sen? Mikä virhe syntyi? Ehtikö job tehdä jotain ennen virhettä?
Tällä tavalla ongelma alkaa yleensä rajautua melko nopeasti.
Esimerkiksi failed_jobs-taulussa voi näkyä selvästi, että job kaatui tietokantavirheeseen. Silloin workerin uudelleenkäynnistäminen ei ratkaise varsinaista ongelmaa.
Toisessa tapauksessa job ei ole koskaan epäonnistunut. Se vain odottaa jonossa, koska worker ei käsittele sitä.
Nämä ovat kaksi aivan eri vikaa, vaikka käyttäjälle molemmat näkyvät samalla tavalla.
Laravelissa job voidaan ohjata tiettyyn queueen.
dispatch(new GenerateReport)->onQueue('reports');
Jos worker kuuntelee vain oletusqueuea, raportti ei tietenkään valmistu.
Tässä ei välttämättä ole mitään varsinaista virhettä. Job on jonossa ja worker on käynnissä. Ne vain eivät puhu samasta jonosta.
Tämä tulee vastaan erityisesti projekteissa, joissa queue-rakennetta on muutettu ajan mittaan. Vanha worker-asetus voi jäädä tuotantoon, vaikka sovelluksen koodi käyttää jo uusia queue-nimiä.
Siksi tarkistaisin aina molemmat puolet: mihin job lähetetään ja mitä worker oikeasti kuuntelee.
Entä jos worker löytää jobin mutta se kaatuu?
Silloin tutkiminen siirtyy itse jobiin.
Esimerkiksi tällainen työ voi näyttää yksinkertaiselta:
public function handle()
{
$invoice = Invoice::findOrFail($this->invoiceId);
$response = Http::post($this->endpoint, [
'invoice' => $invoice->toArray(),
]);
}
Tuotannossa siinä on jo useita mahdollisia epäonnistumiskohtia.
Laskua ei ehkä enää löydy. Tietokantayhteydessä voi olla ongelma. Ulkoinen palvelu voi olla alhaalla. HTTP-pyyntö voi aikakatkaista.
Queue ei tässä tapauksessa ole ongelman varsinainen aiheuttaja.
Se vain suoritti koodin, joka epäonnistui.
Tämä ero kannattaa tehdä mahdollisimman aikaisin. Muuten queuea aletaan korjata tilanteessa, jossa vika on esimerkiksi tietokannassa tai ulkoisessa rajapinnassa.
Jos job kestää pitkään, timeoutia on helppo kasvattaa.
Se voi olla tarpeen, mutta ensin kannattaa selvittää, miksi työ kestää niin kauan.
Raportin muodostaminen voi oikeasti vaatia aikaa. Toisaalta job voi tehdä tuhansia tietokantakyselyitä yhden ison kyselyn sijaan.
Sama pätee tiedostojen käsittelyyn.
Jos yhden jobin pitäisi käsitellä esimerkiksi kokonainen suuri aineisto, ongelmaa ei välttämättä ratkaista muuttamalla timeoutia viidestä minuutista kahteenkymmeneen.
Työ voidaan joutua pilkkomaan pienempiin osiin.
Esimerkiksi yksi job voi käsitellä vain tietyn määrän rivejä ja lähettää seuraavan työn jatkamaan siitä. Tällöin yhden työn epäonnistuminen ei pysäytä koko aineiston käsittelyä.
Jos queue kasvaa, luonnollinen ajatus on käynnistää lisää workereita.
Joskus se toimii.
Joskus se pahentaa tilannetta.
Jos kaikki jobit käyttävät samaa tietokantaa, suurempi worker-määrä tarkoittaa myös enemmän samanaikaisia kyselyitä. Tietokanta voi olla jo valmiiksi rajalla.
Sama ongelma tulee vastaan ulkoisissa API-kutsuissa. Palvelu voi sallia vain tietyn määrän pyyntöjä minuutissa.
Silloin kymmenen uuden workerin käynnistäminen ei tee järjestelmästä kymmenen kertaa nopeampaa.
Queue-kapasiteettia pitää katsoa yhdessä niiden resurssien kanssa, joita jobit käyttävät.
Laravelin queue-worker on pitkäikäinen prosessi.
Se tarkoittaa, että worker on saattanut käynnistyä ennen kuin uusi sovellusversio julkaistiin.
Web-pyyntö käyttää jo uutta koodia, mutta vanha worker voi edelleen käyttää muistissa olevaa vanhaa versiota.
Tämä voi aiheuttaa tilanteen, joka näyttää aluksi täysin käsittämättömältä.
Uusi koodi toimii paikallisesti.
Deploy näyttää onnistuneen.
Silti queue antaa virheitä, joita uuden version ei pitäisi enää tuottaa.
Siksi workerit pitää ottaa huomioon deploy-prosessissa. Laravel tarjoaa tähän queue:restart-komennon, jolla käynnissä oleville workereille voidaan ilmoittaa hallitusta uudelleenkäynnistyksestä.
Tämän jälkeen prosessienhallinnan pitää käynnistää workerit uudelleen.
Tämä on ehkä kiinnostavin kysymys koko aiheessa.
Kehitysympäristössä queue toimii.
Tuotannossa ei.
Syynä ei välttämättä ole ympäristön “huonompi” Laravel-asennus. Tuotannossa data on usein suurempaa ja työn suoritusmäärä aivan toinen.
Paikallisesti job käsittelee sata tietuetta.
Tuotannossa niitä on miljoona.
Paikallisesti API vastaa nopeasti.
Tuotannossa pyyntöjä tulee samaan aikaan paljon enemmän.
Paikallinen Redis on samassa koneessa. Tuotannossa yhteys kulkee verkon yli.
Siksi queue-jobin testaaminen vain yhdellä pienellä testiaineistolla kertoo melko vähän sen tuotantokäyttäytymisestä.
Jos tuotannossa ilmoitetaan, että Laravel Queue ei toimi, en lähtisi heti muuttamaan workerien määrää tai retry-asetuksia.
Aloittaisin yhdestä epäonnistuneesta jobista.
Ensimmäisenä tarkistaisin, onko job todella tullut jonoon.
Sen jälkeen katsoisin, missä queue:ssa se on.
Jos job odottaa, tarkistaisin workerin.
Jos worker on käsitellyt työn, katsoisin exceptionin.
Jos virhe liittyy HTTP-pyyntöön, katsoisin myös ulkoisen palvelun lokit ja vastausajat.
Jos kyse on tietokannasta, mittaisin kyselyn eikä vain jobin kokonaiskestoa.
Jos job taas toimii käsin mutta epäonnistuu normaalissa ajossa, silloin ympäristö, worker-asetukset tai ajoitus voivat olla olennaisia.
Tällä tavalla tutkiminen pysyy konkreettisena.
Laravel Queue -jobin epäonnistuminen ei vielä kerro, missä vika on.
Se kertoo vain lopputuloksen.
Tuotannossa tärkeämpää on saada selville, mitä jobille tapahtui sen elinkaaren aikana: syntyikö se oikein, päätyikö oikeaan queueen, löysikö worker sen ja missä kohdassa suoritus katkesi.
Kun nämä asiat ovat näkyvissä, ongelmaa ei tarvitse arvailla.
Ja jos sama job epäonnistuu jatkuvasti, kannattaa katsoa vielä yksi askel pidemmälle. Onko itse job järkevässä muodossa? Onko työ liian suuri yhdelle suoritukselle? Voiko sen tehdä uudelleen turvallisesti? Riippuuko se palvelusta, jonka vastausaikaa ei oikeasti hallita?
Hyvä queue-ratkaisu ei ole sellainen, jossa yksikään job ei koskaan epäonnistu.
Parempi tavoite on järjestelmä, jossa epäonnistuminen on näkyvä, rajattavissa ja turvallisesti käsiteltävissä.