L
Cloud Architecture & Serverless 🚨 Post-Mortem & Incident Runbook â€ĸ Literally Web ID

Serverless WebSockets: Mengoptimalkan AWS API Gateway dan Lambda untuk Biaya Minimal

âąī¸ 15 menit baca â€ĸ 📅 21 Oct 2026 â€ĸ đŸ‘ī¸ 1 views
Serverless WebSockets: Mengoptimalkan AWS API Gateway dan Lambda untuk Biaya Minimal
🔊 Dengarkan Artikel Ini

Post-Mortem & Incident Runbook: Serverless WebSockets di AWS

1. Alarm Tengah Malam: Saat Layanan Produksi Mulai Tumbang

Pukul 02:14 WIB, pager di meja samping tempat tidurku menjerit melengking, membangunkan tidur nyenyak. Layar ponsel menampilkan notifikasi merah dari PagerDuty: metrik latensi WebSocket melonjak drastis dari rata-rata 45 milidetik menjadi lebih dari 8.000 milidetik, dibarengi dengan lonjakan error 504 Gateway Timeout di dashboard CloudWatch. Sebagai Senior Staff Engineer yang bertanggung jawab atas infrastruktur real-time, malam ini berubah menjadi investigasi forensik kilat. Pengguna kami, yang mayoritas adalah trader finansial dan pengguna aplikasi kolaborasi real-time, mulai mengeluh bahwa pesan chat dan pembaruan grafik harga terputus total. Koneksi klien yang biasanya stabil tiba-tiba berguguran seperti daun di musim gugur, dan tagihan AWS billing harian melonjak tajam tanpa kendali yang jelas.

Sambil membuka laptop di dalam kamar yang remang-remang, aku langsung masuk ke terminal dan menyalakan VPN korporat. Ada dua hal yang langsung terpikir di kepala: apakah lambatnya layanan ini disebabkan oleh lonjakan trafik organik yang tak terduga, atau ada kebocoran sumber daya di lapisan backend yang membuat AWS API Gateway WebSocket kewalahan? Untuk menjawabnya, kita tidak bisa menebak-nebak; kita harus turun langsung ke dalam mesin, menelusuri log, dan membedah metrik satu per satu.

2. Jejak Digital: Menelusuri Anomali Log & Lonjakan Metrik

Tanpa membuang waktu, aku langsung melompat ke AWS CloudWatch Insights untuk menarik log dari AWS API Gateway dan AWS Lambda yang bertindak sebagai otak di balik komunikasi WebSocket ini. Kueri log awal langsung memperlihatkan pola yang sangat aneh namun familier bagi arsitek serverless yang kurang hati-hati:

fields @timestamp, @message
| filter @message like /Task timed out/
| stats count(*) as timeout_count by @logStream
| sort timeout_count desc
| limit 20

Hasilnya mengerikan. Ribuan eksekusi AWS Lambda mengalami timeout setelah berjalan selama 30 detik penuh. Aku beralih ke konsol DynamoDB tempat kami menyimpan pemetaan connectionId klien. Grafiknya menunjukkan kapasitas Read/Write Capacity Units (RCU/WCU) tercekik habis karena lonjakan throttling.

Ternyata, setiap kali ada pesan masuk yang harus disiarkan (broadcast) ke ribuan klien aktif, fungsi Lambda kami melakukan pemindaian tabel (Table Scan) secara brutal ke DynamoDB untuk mencari siapa saja yang sedang online, alih-alih melakukan BatchGet atau query yang terindeks rapi. Lebih parah lagi, arsitektur yang kami bangun gagal mengantisipasi masalah Cold Start pada fungsi Lambda berukuran besar, ditambah tidak adanya penanganan ketika klien memutus koneksi secara paksa, meninggalkan tumpukan stale connections di dalam basis data.

3. Membongkar Biang Kerok: Akar Masalah yang Sebenarnya

Setelah menggali lebih dalam, akar masalah dari insiden dini hari ini mengerucut pada tiga dosa besar arsitektur serverless WebSocket:

  1. Desain Rute API Gateway yang Naif: Kami mengandalkan $default route tanpa memetakan aksi spesifik secara efisien, memaksa Lambda memproses logika routing yang berat di dalam kode.
  2. Manajemen DynamoDB yang Buruk: Menyimpan connectionId tanpa strategi partisi yang tepat dan melakukan operasi Scan alih-alih Query berbasis indeks sekunder global (GSI).
  3. Abaikan Cold Start & Keep-Alive: Fungsi Lambda ditulis dalam Node.js dengan ukuran deployment monolitik yang membengkak hingga 80MB karena modul pihak ketiga yang tidak perlu, memicu Cold Start hingga 3,2 detik setiap kali terjadi lonjakan trafik mendadak.

[!WARNING] Menggunakan operasi Scan pada DynamoDB untuk melacak koneksi WebSocket aktif adalah tiket express menuju tagihan AWS bengkak dan pelambatan sistem secara total.

Untuk memperbaiki ini secara permanen, kita harus merombak fondasi kode dan infrastrukturnya. Mari kita lihat bagaimana cara membangun rute, penyimpanan, dan fungsi Lambda yang kebal terhadap lonjakan.

4. Tindakan Darurat: Menghidupkan Kembali Layanan dalam Hitungan Menit

Sambil merencanakan perbaikan jangka panjang, sistem produksi harus diselamatkan sekarang juga. Berikut adalah emergency runbook one-liner berbasis AWS CLI yang aku eksekusi di terminal untuk memangkas concurrency Lambda yang macet dan membersihkan koneksi mati secara paksa:

# Perintah darurat untuk membersihkan koneksi mati dan mereset throttle API Gateway
aws apigatewayv2 get-connections --api-id ${API_ID} --query "items[?connectedAt < `$(date -d '2 hours ago' -u +%Y-%m-%dT%H:%M:%SZ)`].connectionId" --output text | xargs -I {} aws apigatewayv2 delete-connection --api-id ${API_ID} --connection-id {}

Perintah tersebut langsung menyapu bersih ribuan koneksi zombie yang menggantung dan membebani memori Lambda. Sembari trafik mulai berangsur normal, mari kita tulis ulang arsitekturnya agar kejadian ini tidak terulang kembali.

Pertama, kita definisikan rute AWS API Gateway WebSocket yang bersih menggunakan Terraform:

resource "aws_apigatewayv2_api" "websocket_api" {
  name                       = "production-websocket"
  protocol_type              = "WEBSOCKET"
  route_selection_expression = "$request.body.action"
}

resource "aws_apigatewayv2_route" "connect_route" {
  api_id    = aws_apigatewayv2_api.websocket_api.id
  route_key = "$connect"
  target    = "integrations/${aws_apigatewayv2_integration.lambda_connect.id}"
}

resource "aws_apigatewayv2_route" "disconnect_route" {
  api_id    = aws_apigatewayv2_api.websocket_api.id
  route_key = "$disconnect"
  target    = "integrations/${aws_apigatewayv2_integration.lambda_disconnect.id}"
}

resource "aws_apigatewayv2_route" "sendmessage_route" {
  api_id    = aws_apigatewayv2_api.websocket_api.id
  route_key = "sendMessage"
  target    = "integrations/${aws_apigatewayv2_integration.lambda_sendmessage.id}"
}

Selanjutnya, untuk menangani penyimpanan connectionId di DynamoDB tanpa membuat tagihan meledak, kita gunakan skema tabel yang dioptimalkan dengan TTL (Time-To-Live) agar koneksi yang terbengkalai otomatis terhapus:

// Handler untuk $connect - Menyimpan Connection ID ke DynamoDB
const { DynamoDBClient } = require("@aws-sdk/client-dynamodb");
const { DynamoDBDocumentClient, PutCommand } = require("@aws-sdk/lib-dynamodb");

const client = new DynamoDBClient({});
const docClient = DynamoDBDocumentClient.from(client);

exports.handler = async (event) => {
    const connectionId = event.requestContext.connectionId;
    const ttl = Math.floor(Date.now() / 1000) + 7200; // Expired dalam 2 jam jika tidak ada aktivitas

    const command = new PutCommand({
        TableName: process.env.CONNECTIONS_TABLE,
        Item: {
            connectionId: connectionId,
            ttl: ttl,
            connectedAt: new Date().toISOString()
        },
    });

    try {
        await docClient.send(command);
        return { statusCode: 200, body: 'Connected.' };
    } catch (err) {
        console.error("Gagal menyimpan koneksi:", err);
        return { statusCode: 500, body: 'Failed to connect: ' + JSON.stringify(err) };
    }
};

Terakhir, untuk mengatasi Cold Start yang mematikan, kita optimalkan fungsi Lambda dengan menggunakan AWS Lambda Power Tuning, memperkecil bundle size menggunakan esbuild, dan menerapkan Provisioned Concurrency hanya pada jam-jam puncak trafik.

[!TIP] Gunakan esbuild untuk melakukan bundling kode Lambda Node.js Anda. Ukuran file yang lebih kecil di bawah 5MB terbukti memangkas waktu Cold Start hingga 70%.

5. Kebijakan Retrospektif: Menambal Celah agar Tak Pernah Terulang

Insiden pukul 2 pagi ini memberikan pelajaran mahal tentang bagaimana memperlakukan arsitektur serverless secara disiplin. Kami mengadakan sesi blameless post-mortem keesokan harinya dan menetapkan aturan baru yang wajib dipatuhi seluruh tim engineering:

  • Dilarang Keras Melakukan Scan pada Database Real-time: Semua operasi pencarian koneksi wajib menggunakan partisi kunci yang spesifik atau memanfaatkan Global Secondary Index (GSI) dengan kapasitas On-Demand atau Auto-Scaling yang terkonfigurasi ketat.
  • Penerapan TTL Wajib: Setiap entri koneksi di DynamoDB wajib memiliki atribut TTL untuk mencegah penumpukan data basi akibat klien yang terputus tanpa memicu event $disconnect secara sempurna.
  • Optimasi Ukuran Paket Lambda: Setiap fungsi Lambda tidak boleh melebihi ukuran 10MB setelah di-kompres, dan wajib melaluiuji beban (load testing) menggunakan k6 sebelum disetujui masuk ke lingkungan produksi.

6. Pasang Alarm: Otomasi Deteksi Dini untuk Tim On-Call

Agar kita bisa tidur dengan tenang tanpa takut dikejutkan oleh lonjakan latensi mendadak, kita pasang sistem peringatan dini menggunakan CloudWatch Alarms yang terhubung langsung ke webhook Slack dan PagerDuty tim on-call:

resource "aws_cloudwatch_metric_alarm" "websocket_latency_alarm" {
  alarm_name          = "prod-websocket-high-latency"
  comparison_operator = "GreaterThanThreshold"
  evaluation_periods  = 2
  metric_name         = "IntegrationLatency"
  namespace           = "AWS/ApiGateway"
  period              = 60
  statistic           = "Average"
  threshold           = 1000
  alarm_description   = "Alarm jika latensi integrasi WebSocket API Gateway melebihi 1 detik"
  alarm_actions       = [aws_sns_topic.pagerduty_alerts.arn]

  dimensions = {
    ApiId = aws_apigatewayv2_api.websocket_api.id
  }
}

resource "aws_cloudwatch_metric_alarm" "lambda_errors_alarm" {
  alarm_name          = "prod-websocket-lambda-errors"
  comparison_operator = "GreaterThanThreshold"
  evaluation_periods  = 1
  metric_name         = "Errors"
  namespace           = "AWS/Lambda"
  period              = 60
  statistic           = "Sum"
  threshold           = 5
  alarm_description   = "Alarm jika terjadi error pada fungsi Lambda WebSocket"
  alarm_actions       = [aws_sns_topic.pagerduty_alerts.arn]

  dimensions = {
    FunctionName = aws_lambda_function.websocket_handler.function_name
  }
}

Dengan fondasi arsitektur yang tahan banting, penanganan DynamoDB yang optimal, serta sistem alarm otomatis yang siaga 24/7, ekosistem Serverless WebSocket kami kini siap menangani jutaan koneksi bersamaan dengan biaya operasional yang sangat efisien dan senyum lebar di wajah tim keuangan.

đŸˇī¸ Tags: #Nginx #High Concurrency #Performance #Sysadmin #Web Server

Apakah panduan tutorial ini membantu Anda?

Masukan Anda sangat berharga untuk terus memperbarui dan menyempurnakan kualitas materi teknis kami.

🎉

Terima kasih banyak atas apresiasi Anda!

Tim kami terus menyempurnakan tutorial agar selalu akurat dengan versi teknologi terbaru.

Bantu Kami Menyempurnakan Materi Ini

đŸ—ēī¸ Jalur Belajar #Cloud Architecture & Serverless Rangkaian Panduan
⚡ Literally Tech Digest

Update Tutorial & Developer Tools Terbaru Langsung ke Email Anda

Dapatkan kurasi artikel arsitektur software, tips DevOps, panduan database, serta rilis fitur developer utility gratis setiap minggu. Bebas spam, bisa berhenti kapan saja.

âŗ Mencari artikel...
Tutorial & Artikel Terkait
Developer Tools
Tidak ditemukan tutorial atau tool dengan kata kunci "".
Ketik minimal 2 karakter untuk mencari artikel atau nama tools.
Tekan ESC untuk menutup Cari di Semua Artikel →