- Published on
AWS Lambda Cold Start - dlaczego twoja funkcja śpi i jak ją obudzić
- Authors

- Name
- Piotr Kołodziejczyk

Twoja Lambda działa świetnie - dopóki nie dostanie nowego ruchu po dłuższej przerwie. Nagle pierwsze requesty trwają sekundę, dwie, albo pięć. To cold start i każdy backend developer pracujący z serverless prędzej czy później na niego trafi.
Co to jest cold start?
Gdy AWS Lambda nie ma uruchomionej instancji dla Twojej funkcji, musi ją uruchomić od zera. Ten proces składa się z kilku kroków:
- Alokacja kontenera (~50–100ms) - AWS wybiera i startuje izolowane środowisko
- Ładowanie runtime (~100–200ms) - inicjalizacja Node.js, Python, JVM itp.
- Pobranie i rozpakowanie kodu (~50–300ms zależnie od rozmiaru paczki)
- Inicjalizacja modułów - wykonanie kodu poza handlerem (importy, połączenia DB)
Dopiero po tych krokach handler dostaje event i zaczyna właściwą pracę.
Kluczowy punkt: każda równoległa instancja to osobny cold start. Jeśli w tej samej sekundzie przychodzą 50 requestów, a Lambda ma tylko 5 ciepłych instancji - pozostałe 45 przejdą cold start. Każda z nich, niezależnie.
Dlaczego to problem?
Typowe czasy cold startu w zależności od runtime:
| Runtime | Cold start | Hot start |
|---|---|---|
| Node.js | 300–500ms | 1–10ms |
| Python | 400–600ms | 1–10ms |
| Go | 100–200ms | 1–5ms |
| Java (JVM) | 5–10s | 1–20ms |
| C# (.NET) | 1–4s | 1–10ms |
Dla użytkownika czekającego na odpowiedź API, dodatkowe 500ms to odczuwalna różnica. Przy SLA na poziomie 200ms - masz problem. Timeouty API Gateway domyślnie ustawione są na 29 sekund (dla Regional i Private REST API można ten limit zwiększyć), ale integrations downstream mogą być znacznie bardziej restrykcyjne.
Scenariusz spike'owy (tzw. "Black Friday problem"): ruch wzrasta 10x w ciągu kilku sekund. Lambda skaluje się automatycznie - ale każda nowa instancja przechodzi cold start. Przy 200 równoległych wywołaniach i Javie jako runtime, możesz mieć masowe timeouty właśnie wtedy, kiedy ruch jest największy.
Problem z importami - miejsce, gdzie tracisz najwięcej
Kod poza handlerem wykonuje się raz podczas cold startu i jest cache'owany między wywołaniami tej samej instancji. To dobra wiadomość dla połączeń z bazą danych. Ale to pułapka, jeśli importujesz wszystko na górze pliku.
Źle - ładujesz cały SDK przy każdym cold starcie:
import { S3Client } from '@aws-sdk/client-s3'
import { DynamoDBClient } from '@aws-sdk/client-dynamodb'
import { SESClient } from '@aws-sdk/client-ses'
import { SNSClient } from '@aws-sdk/client-sns'
import { LambdaClient } from '@aws-sdk/client-lambda'
// Ta funkcja używa tylko S3, reszta ładuje się bez sensu
export const handler = async (event) => {
const s3 = new S3Client({})
// ...
}
Każdy z tych clientów ma własne zależności, które muszą zostać załadowane i zinicializowane. Dodajesz 100–300ms do cold startu funkcji, która potrzebuje tylko S3.
Dobrze - importuj tylko to, czego faktycznie potrzebujesz:
// Klient tworzony raz, poza handlerem - reużywany między wywołaniami
let s3Client
export const handler = async (event) => {
if (!s3Client) {
const { S3Client } = await import('@aws-sdk/client-s3')
s3Client = new S3Client({})
}
// ...
}
Albo jeszcze prościej - jeśli używasz bundlera (esbuild, webpack), skonfiguruj tree-shaking i importuj tylko konkretne komendy:
import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3'
// zamiast: import AWS from 'aws-sdk';
Stary aws-sdk v2 (jeden ogromny pakiet) vs nowy @aws-sdk v3 (modularny) - to może być różnica 1–2MB w rozmiarze paczki, co bezpośrednio przekłada się na czas cold startu.
Rozwiązania
Provisioned Concurrency
AWS utrzymuje N instancji zawsze ciepłych - bez cold startu, ale z kosztem stałym. Idealne dla krytycznych endpointów.
resource "aws_lambda_provisioned_concurrency_config" "api" {
function_name = aws_lambda_function.api.function_name
qualifier = aws_lambda_alias.api.name
provisioned_concurrent_executions = 5
}
Koszt zależy od rozmiaru pamięci funkcji - sprawdź AWS Lambda Pricing, ale dla typowych funkcji to ułamek centa za godzinę per instancja.
Keep-Warm Pattern (EventBridge ping)
Tańsza alternatywa: EventBridge wywołuje Lambda co 5 minut, żeby utrzymać instancję ciepłą. Działa dla pojedynczych instancji, nie dla skalowania.
export const handler = async (event) => {
// Odpowiadaj na pingi schedulera - nie rób nic kosztownego
if (event.source === 'aws.events' && event['detail-type'] === 'Scheduled Event') {
return { statusCode: 200, body: 'warm' }
}
// Właściwa logika funkcji
return processEvent(event)
}
EventBridge rule w Terraform:
resource "aws_cloudwatch_event_rule" "keep_warm" {
name = "lambda-keep-warm"
schedule_expression = "rate(5 minutes)"
}
resource "aws_cloudwatch_event_target" "keep_warm" {
rule = aws_cloudwatch_event_rule.keep_warm.name
target_id = "KeepWarmLambda"
arn = aws_lambda_function.api.arn
}
Kiedy nie przejmować się cold startami?
Cold start nie ma znaczenia gdy:
- funkcja przetwarza eventy asynchronicznie (SQS, SNS, S3 events) - użytkownik nie czeka
- robisz batch processing, cron jobs, nightly exports
- czas wykonania funkcji mierzysz w sekundach, nie milisekundach (cold start ginie w szumie)
- masz Lambda@Edge - cold starty są tam podobne do standardowej Lambdy, ale optymalizacja skupia się na innych aspektach (rozmiar funkcji, lokalizacja edge)
Inwestuj w optymalizację cold startu tylko tam, gdzie naprawdę boli: synchroniczne API, user-facing endpointy, SLA poniżej 1s.