AWS Lambda Deep Dive — DevOps

Serverless dengan AWS Lambda AWS Lambda menjalankan kode tanpa mengelola server. Bayar hanya saat fungsi berjalan — tidak ada idle cost. Anatomy of a Lambda…

AWS Lambda adalah layanan FaaS milik Amazon: kamu menyerahkan satu function, dan AWS yang menyalakan server, menjalankan function itu saat ada event, lalu menambah salinannya saat ramai. Kamu bayar per request dan per milidetik selama function jalan, jadi jam sepi gak ada biaya idle. Ingat juru masak panggilan dari pelajaran Cloud Computing Basics. Saat pertama datang, dia harus menata meja, mengasah pisau, dan menyalakan kompor dulu (cold start). Setelah itu tiap pesanan langsung dimasak (handler). Kalau dia masih di dapur saat pesanan berikutnya datang, persiapannya gak diulang (warm start). Pesanan datang bersamaan? Agensi mengirim juru masak tambahan, tapi cuma sampai batas kontrak (concurrency). Tiap juru masak memegang stopwatch (timeout), dan meja yang lebih besar lebih mahal per menit tapi masaknya lebih cepat (memory).

🧪 Tebakan cepat: batas konkurensi catatan-ekspor diset 2. Lima request ekspor PDF datang di detik yang sama. Berapa yang dikerjakan, dan apa yang terjadi pada sisanya? Tebak dulu, jawabannya ada di bagian "Konkurensi dan throttle".

Handler: event masuk, response keluar

Pelajaran Serverless Functions sudah memperkenalkan bentuknya. Sekarang kita pakai sungguhan: fitur ekspor PDF catatan-api makan banyak CPU dan ramainya cuma di jam kerja, jadi route POST /catatan/{id}/ekspor dipindah dari web-1 ke Lambda bernama catatan-ekspor (Node.js 22, region ap-southeast-3 Jakarta, sama dengan bucket catatan-lampiran dari pelajaran Object Storage).

// index.mjs (handler: index.handler)
import pg from "pg";
import { buatPdf } from "./pdf.mjs";

// INIT: jalan sekali per execution environment
const pool = new pg.Pool({ connectionString: process.env.DATABASE_URL, max: 1 });

export const handler = async (event, context) => {
  const id = Number(event.pathParameters?.id);
  if (!Number.isInteger(id)) {
    return { statusCode: 400, body: JSON.stringify({ error: "id harus angka" }) };
  }
  const { rows } = await pool.query("SELECT judul, isi FROM catatan WHERE id = $1", [id]);
  if (rows.length === 0) {
    return { statusCode: 404, body: JSON.stringify({ error: "catatan tidak ditemukan" }) };
  }
  console.log(`ekspor ${id}, sisa waktu ${context.getRemainingTimeInMillis()} ms`);
  const pdf = await buatPdf(rows[0]); // kerja CPU paling berat
  return {
    statusCode: 200,
    headers: { "Content-Type": "application/pdf" },
    isBase64Encoded: true,
    body: pdf.toString("base64"),
  };
};

Event dari API Gateway

API Gateway adalah layanan AWS yang menerima request HTTP dari internet lalu meneruskannya ke Lambda sebagai event JSON. Untuk HTTP API (format payload 2.0), event POST /catatan/42/ekspor kira-kira begini (dipangkas):

{
  "version": "2.0",
  "routeKey": "POST /catatan/{id}/ekspor",
  "rawPath": "/catatan/42/ekspor",
  "headers": { "content-type": "application/json", "authorization": "Bearer eyJhbGciOi..." },
  "pathParameters": { "id": "42" },
  "requestContext": {
    "http": { "method": "POST", "path": "/catatan/42/ekspor", "sourceIp": "198.51.100.7" },
    "requestId": "Ab3xQjYzSQ0EJXw="
  },
  "body": "{\"ukuran\":\"A4\"}",
  "isBase64Encoded": false
}

Method ada di event.requestContext.http.method, dan {id} dari route masuk ke event.pathParameters.id sebagai string. body juga string, jadi perlu JSON.parse(event.body). REST API yang lebih tua memakai format 1.0, dengan event.httpMethod dan event.path. Cek dulu format mana yang dipakai sebelum membaca field-nya.

Init sekali, handler berkali-kali

Lambda menjalankan function-mu di dalam execution environment (yang di pelajaran Serverless Functions disebut instance): sebuah sandbox kecil dengan runtime Node.js, kode-mu, dan memory-nya sendiri. Satu environment mengerjakan satu request pada satu waktu.

Kejadian Yang jalan Contoh di Lab (512 MB)
Cold start: belum ada environment yang nganggur init (start runtime, import, new pg.Pool) lalu handler Init Duration 412,08 ms, lalu Duration 926,36 ms
Warm start: ada environment yang nganggur handler saja Duration 862,71 ms

Request pertama di environment baru juga membuka koneksi ke Postgres (64 ms). Karena pool dibuat di luar handler, request berikutnya di environment yang sama memakai koneksi itu lagi. Kalau new pg.Pool() dipindah ke dalam handler, tiap request membuka koneksi baru, dan koneksi lama tetap menggantung selama environment-nya hidup. Environment yang lama nganggur dibuang AWS (berapa lama, AWS gak janji; di Lab, 15 menit). Mengubah kode atau konfigurasi seperti memory juga membuat environment baru, jadi request sesudahnya cold start.

Untuk endpoint yang gak boleh kena cold start, Provisioned Concurrency menyiapkan sejumlah environment yang sudah di-init sebelum request datang. Biayanya tambahan, dibayar per jam walau gak ada request. Penyebab cold start dan cara menekannya dibahas di Serverless Patterns & Cold Start.

Konkurensi dan throttle

Concurrency adalah jumlah environment yang sedang mengerjakan request di saat yang sama. Hitungan kasarnya: request per detik × durasi rata-rata dalam detik. 20 request per detik × 0,5 detik = sekitar 10 environment bersamaan.

Kuota bawaan konkurensi biasanya 1.000 per region untuk semua function di akunmu (akun baru bisa lebih kecil). Reserved concurrency memberi satu function batas sendiri. Di atas batas itu Lambda menolak request dengan error TooManyRequestsException (status 429, Rate Exceeded.). Itulah throttle. Jadi dengan batas 2 dan lima request bersamaan, dua dikerjakan (dua cold start) dan tiga ditolak 429. Itu jawaban tebakannya. Request yang ditolak gak pernah menjalankan kode-mu, jadi gak ada log-nya. Pemanggil sinkron seperti API Gateway menerima error, sedangkan event async (S3, EventBridge) disimpan dan dicoba lagi oleh Lambda sampai 6 jam.

Kenapa justru sengaja dibatasi? Tiap environment memegang koneksi Postgres sendiri. 1.000 environment berarti 1.000 koneksi, padahal max_connections bawaan Postgres cuma 100. Batas konkurensi plus max: 1 di pool menjaga database tetap hidup. Dasarnya ada di pelajaran Connection Pooling, dan untuk trafik besar AWS punya RDS Proxy yang menjadi pool di antara Lambda dan database.

Memory, CPU, dan timeout

Memory bisa diset dari 128 MB sampai 10.240 MB, dan CPU ikut naik sebanding dengan memory (sekitar 1.769 MB = 1 vCPU penuh). Timeout bawaannya 3 detik, paling lama 900 detik (15 menit). Lewat dari itu, request dihentikan dengan pesan Task timed out after 3.00 seconds. Angka contoh dari Lab untuk warm start catatan-ekspor:

Memory Waktu yang dibutuhkan Hasil dengan timeout 3 detik Biaya per 1 juta request
128 MB 3.412,71 ms dihentikan di 3.000 ms (gagal)
512 MB 862,71 ms 200 $7,39
1.024 MB 437,71 ms 200 $7,50

Menaikkan memory dari 512 MB ke 1.024 MB membuat PDF dua kali lebih cepat, tapi biayanya hampir sama, karena harga dihitung dari GB × detik: memory dua kali, durasi setengahnya. Di 128 MB, menaikkan timeout ke 10 detik memang menghilangkan error, tapi user menunggu 3,4 detik. Yang benar di sini menambah memory.

Membaca baris REPORT di CloudWatch

Semua console.log dan log dari Lambda masuk ke CloudWatch Logs, di log group /aws/lambda/catatan-ekspor. Tiap request diapit tiga baris dari Lambda sendiri. Ini cold start di 512 MB (angka contoh):

INIT_START Runtime Version: nodejs:22.v51	Runtime Version ARN: arn:aws:lambda:ap-southeast-3::runtime:7c1e4b0d9a3f52e8c6b1d0a9f8e7d6c5b4a39281706f5e4d3c2b1a0f9e8d7c6b
START RequestId: 8f5a1c2e-4b7d-4e19-9c3a-000000000001 Version: $LATEST
2026-10-09T02:00:00.488Z	8f5a1c2e-4b7d-4e19-9c3a-000000000001	INFO	ekspor 42, sisa waktu 2924 ms
END RequestId: 8f5a1c2e-4b7d-4e19-9c3a-000000000001
REPORT RequestId: 8f5a1c2e-4b7d-4e19-9c3a-000000000001	Duration: 926.36 ms	Billed Duration: 1339 ms	Memory Size: 512 MB	Max Memory Used: 97 MB	Init Duration: 412.08 ms

Biaya = jumlah request × $0,20 per juta + GB × detik × $0,0000166667 (harga contoh x86, cek halaman harga region-mu). Warm start 512 MB: 0,5 GB × 0,863 detik × $0,0000166667 ≈ $0,0000072 per request, plus $0,0000002. Sejuta request jadi sekitar $7,39.

Trigger selain HTTP

API Gateway hanya satu dari banyak trigger, yaitu sumber event yang memanggil Lambda:

Ukuran kode juga dibatasi: 50 MB untuk zip yang di-upload langsung, 250 MB setelah diekstrak (termasuk layer), atau sampai 10 GB kalau dikemas sebagai container image.

⚠️ Jebakan yang sering ketemu

TL;DR: Lambda memanggil handler(event, context) sekali per request dan menagih per request plus GB × detik, tanpa biaya idle. Kode di luar handler (init) jalan sekali per execution environment saat cold start, lalu dipakai ulang di warm start, jadi buat koneksi database di sana dengan pool kecil. Satu environment = satu request pada satu waktu, concurrency ≈ request per detik × durasi, kuota bawaannya biasanya 1.000 per region, dan di atas batas Lambda menolak dengan 429 TooManyRequestsException (throttle). Memory 128 MB sampai 10.240 MB menentukan CPU, timeout bawaan 3 detik dan paling lama 15 menit. Baris REPORT di CloudWatch berisi Duration, Billed Duration, Memory Size, Max Memory Used, dan Init Duration yang cuma muncul di cold start. Trigger selain API Gateway: S3 Event, SQS, EventBridge, DynamoDB Streams. Provisioned Concurrency menjaga environment tetap warm dengan biaya tambahan.

Yang akan kamu pelajari