Serverless

Lambda hidegindítás 2025-ben: mennyit számít még a cold start?

A hidegindítás évekig a serverless legfőbb ellenérve volt. Provisioned concurrency, SnapStart és karcsúbb futtatókörnyezetek után viszont érdemes újra feltenni a kérdést: kit és mikor zavar egyáltalán?

Röviden: A hidegindítás évekig a serverless legfőbb ellenérve volt. Provisioned concurrency, SnapStart és karcsúbb futtatókörnyezetek után viszont érdemes újra feltenni a kérdést: kit és mikor zavar egyáltalán?

A hidegindítás sokáig a serverless architektúrák első számú mumusa volt: minden bemutatón előkerült, minden döntési megbeszélésen elhangzott, és nem ritkán ezen bukott el egy-egy Lambda-alapú javaslat. 2025-re a kép jóval árnyaltabb. A provisioned concurrency, a SnapStart, a könnyebb futtatókörnyezetek és a jobb tooling együtt oda vezettek, hogy a cold start ma inkább egy megoldható mérnöki részlet, mint sorsdöntő korlát.

Ez nem azt jelenti, hogy a hidegindítás megszűnt. Azt jelenti, hogy a kérdést pontosabban kell feltenni: nem azt, hogy "van-e cold start", hanem azt, hogy "ezt a konkrét forgalmi mintát és ezt a konkrét felhasználói elvárást zavarja-e, és ha igen, mennyibe kerül elkerülni".

Mi történik valójában hidegindításkor?

Amikor egy Lambda-hívás olyan futtatókörnyezetet igényel, ami épp nem áll készen, az AWS-nek elő kell készítenie egy új környezetet: le kell húzni a kódot vagy a konténerképet, el kell indítani a runtime-ot, majd le kell futtatni az inicializáló szakaszt. Ez utóbbi a te kódod: a handleren kívüli rész, ahol SDK-klienseket építesz, kapcsolatokat nyitsz, konfigurációt töltesz.

A tapasztalat az, hogy a hidegindítás érzékelt költségének nagy részét nem a platform adja, hanem a saját init-fázis. Egy nehéz dependency-fa, egy lusta helyett mohó módon betöltött SDK, egy szinkron hálózati hívás az inicializálásban — ezek jellemzően többet nyomnak a latba, mint maga a környezet-előkészítés.

Hol fáj, és hol nem

Egy aszinkron feldolgozó, ami SQS-ből olvas, jellemzően nem érzi meg: pár száz milliszekundum plusz egy üzenetnél nem számít. Egy éjszakai batch, egy webhook-fogadó, egy belső admin API ugyanígy tolerálja. Ahol viszont fáj: felhasználó által közvetlenül várt szinkron kérés, különösen ha a hívási lánc több Lambdán fut végig, és a hidegindítások összeadódnak.

A három fő eszköz és az áruk

A platform ma három érdemi mechanizmust kínál, és mindegyiknek más a jellege és a költségprofilja.

EszközMit old megÁra jellege
Provisioned concurrencyElőre melegen tartott környezetekFolyamatos, akkor is fizetsz, ha nincs forgalom
SnapStartInicializált állapot pillanatképéből indítElsősorban bizonyos runtime-oknál, kevés extra díj
Kód- és csomagoptimalizálásRövidebb init-fázisEgyszeri mérnöki munka

A provisioned concurrency a legkézenfekvőbb, de egyben a legdrágább is: lényegében megveszed a "mindig kész" állapotot, és a nem használt kapacitásért is fizetsz. Kiszámítható, csúcsos forgalomnál (például minden munkanap reggel 9-kor beeső terhelés) megéri, autoscalinggel a napszakhoz igazítva.

A SnapStart más filozófia: az inicializált környezetről készít pillanatképet, és abból éleszt új példányokat. Ahol elérhető és a kód pillanatkép-barát, ott a hidegindítás jelentős része eltűnik anélkül, hogy folyamatosan kapacitást vennél. Cserébe figyelni kell arra, hogy a snapshotba be ne fagyjon olyasmi, aminek példányonként egyedinek kell lennie — például egy előre legenerált véletlenszám-mag vagy egy adott időpontra sült érték.

Először a saját kódnál kezdd

Mielőtt bármelyik fizetős kapcsolót bekapcsolnád, érdemes a legolcsóbb helyen keresni: a saját inicializálásban. A gyakorlati sorrend jellemzően ez:

  1. Mérj: nézd meg az Init Duration értéket a logokban, ne tippelj.
  2. Csökkentsd a csomagméretet: csak azt húzd be, amit tényleg használsz.
  3. Töltsd lustán: amit nem minden híváshoz kell, azt ne az init-ben építsd.
  4. Kerüld a szinkron hálózatot az init-fázisban, ahol lehet.
  5. Csak ezután dönts provisioned concurrency vagy SnapStart mellett.

Egy tipikus init-blokk, ahol a kliens a handleren kívül, egyszer épül fel, de a nehéz munka a hívásig halasztódik:

kód
const client = buildLightClient();
let heavy;

exports.handler = async (event) => {
  heavy = heavy ?? (await buildHeavyThing());
  return process(client, heavy, event);
};

Mikor ne foglalkozz vele egyáltalán?

Van egy csapda: a hidegindítás optimalizálása könnyen öncélúvá válik. Ha a szolgáltatásod P99 válaszideje amúgy is bőven a felhasználói tűréshatár alatt van, akkor a cold start elleni küzdelem tiszta ráfordítás megtérülés nélkül. Az uralkodó forgalmi mintát nézd: ha a függvény gyakorlatilag folyamatosan meleg marad, mert van elég állandó forgalom, a hidegindítás statisztikai zaj lesz csupán.

Fordítva is igaz: egy ritkán, de kritikus pillanatban hívott függvénynél (például egy fizetési visszahívás) a ritkaság maga a probléma, mert szinte mindig hidegen fut. Itt a provisioned concurrency vagy a rendszeres "melegen tartó" hívás közötti választás nem teljesítmény-, hanem üzleti kockázati döntés.

A memória mint rejtett gyorsítógomb

Kevesen tudják, hogy a Lambda-nál a memória beállítása nem csak a memóriát szabályozza, hanem a hozzárendelt CPU-t is. Több memória arányosan több számítási kapacitást jelent, ami az inicializálást és a feldolgozást is gyorsítja. Így paradox módon egy nagyobb memóriájú, drágábbnak tűnő függvény összességében olcsóbb lehet, mert hamarabb végez, és rövidebb ideig fut.

Érdemes ezt méréssel belőni: futtasd a függvényt több memóriabeállítással, és nézd a futási időt és a teljes költséget együtt. Gyakran van egy édespont, ahol a magasabb memória rövidebb futásidőt és alacsonyabb végösszeget ad. A puszta „állítsuk a lehető legkevesebbre a spórolásért” reflex itt félrevezet.

A VPC-be helyezett függvény esete

Ha a függvényed egy VPC-ben fut (mert privát adatbázist vagy belső erőforrást ér el), az régen komoly hidegindítási büntetést jelentett a hálózati interfész felállítása miatt. Ez az idők során sokat javult, de a hálózati konfiguráció így is befolyásolja az indulást. Ha VPC-ben futtatsz, ellenőrizd, hogy a beállítás indokolt-e, és hogy a downstream elérés nem ad-e felesleges latenciát minden híváshoz.

Provisioned concurrency: mikor tényleg

A provisioned concurrency akkor a helyes eszköz, amikor a hidegindítási csúcs előre jelezhető: napi kampányindítás, ismert forgalmi minta, tervezett terhelés. Ilyenkor a melegen tartott kapacitás garantálja a kiszámítható latenciát. De ha a forgalmad kiszámíthatatlanul lökésszerű, a provisioned concurrency vagy alultervezett lesz (nem elég a csúcsra), vagy pazarló (feleslegesen fizeted a tétlen készenlétet). Az automatikus, ütemezett skálázása segíthet, ha a mintázat ismétlődő.

Mit jelent ez neked?

A hidegindítás 2025-ben ritkán ok arra, hogy elvesd a serverlesst — sokkal inkább egy méréssel eldönthető részletkérdés. Kezdd a saját init-fázis megmérésénél és karcsúsításánál, mert az a legolcsóbb nyereség, és csak utána nyúlj a provisioned concurrency vagy a SnapStart fizetős, illetve korlátozott megoldásaihoz. A lényeg, hogy a konkrét forgalmi minta és a felhasználói elvárás döntsön, ne a régi reflex.