Di era arsitektur cloud native, Remote Procedure Call (RPC) telah berevolusi dari sekadar permintaan-response sederhana menjadi arsitektur komunikasi canggih, salah satunya streaming RPC. Namun, semakin kompleks arsitekturnya, semakin menuntut pengembang memahami bagaimana menangani error dan context dengan baik. Dalam artikel ini, saya akan mengupas teknik menangani error dan context pada streaming RPC, terutama menggunakan gRPC, dan membungkusnya dengan studi kasus, contoh kode, simulasi, serta diagram alur untuk memperjelas konsepnya.
Apa Itu Streaming RPC?
Pada dasarnya, RPC adalah teknik agar sebuah aplikasi dapat memanggil fungsi/prosedur yang dijalankan di layanan berbeda (biasanya melalui jaringan). Pada streaming RPC, baik client, server, atau keduanya dapat mengirimkan sejumlah data secara berurutan (stream), bukan hanya satu permintaan dan satu jawaban.
Ada empat jenis RPC pada gRPC:
Jenis | Client Stream | Server Stream | Contoh |
---|---|---|---|
Unary | Tidak | Tidak | Login(username, password) |
Client-side Streaming | Ya | Tidak | Upload log secara batch |
Server-side Streaming | Tidak | Ya | Notifikasi update status |
Bidirectional Streaming | Ya | Ya | Chat, game lobby, live data |
Kenapa Error dan Context Penting pada Streaming RPC?
Sederhananya, streaming RPC berjalan dalam waktu yang lama atau berkelanjutan — misalnya client mengirim ribuan log ke server secara bertahap. Dalam proses itu, banyak hal bisa terjadi:
- Client tiba-tiba disconnect.
- Server overload lalu crash.
- Timeout karena jaringan terputus.
- Client ingin membatalkan proses (cancel).
- Server menemukan error fatal dan harus memutuskan stream.
Mengabaikan error handling dan context management pada stream akan membuat sistem rentan, sulit di-maintain, atau bahkan terjadi memory leak.
Cara Menangani Error pada Streaming RPC
1. Mengenali Error gRPC
gRPC memiliki sistem status code yang serupa dengan HTTP (walau istilahnya berbeda). Ada beberapa kode umum yang sering ditemui:
Status Code | Penjelasan | Solusi Umum |
---|---|---|
OK | Tidak ada error (sukses) | — |
Canceled | Client membatalkan request | Bersihkan resource |
DeadlineExceeded | Timeout pada stream | Review context/timeout |
Unavailable | Server tidak tersedia (down) | Coba reconnect/backoff |
Internal | Error internal di server | Periksa log server |
DataLoss | Data stream corrupt | Validasi, retry |
2. Menghandle Error pada Client Streaming
Misal, client streaming memberikan data secara bertahap ke server. Di sisi client Go, biasanya kita akan melihat pola:
stream, err := client.SendLogStream(ctx)
if err != nil {
log.Fatalf("gagal membuka stream: %v", err)
}
for _, entry := range logs {
if err := stream.Send(entry); err != nil {
log.Printf("gagal mengirim log: %v", err)
break
}
}
resp, err := stream.CloseAndRecv()
if err != nil {
log.Fatalf("gagal mendapatkan respons: %v", err)
}
log.Printf("Sukses upload: %v", resp.Total)
Bagaimana Jika Server Mengembalikan Error Saat Proses?
Ketika stream.Send()
error, client wajib cek: apakah context sudah cancelled? Apakah error karena server sudah menutup koneksi lebih dulu?
3. Error Handling pada Server Streaming
Di sisi server, biasanya akan rutin mengirimkan data ke client. Suatu saat client bisa saja disconnect, menyebabkan Send()
di server return error.
func (s *server) StreamNotifikasi(req *pb.Request, stream pb.Service_StreamNotifikasiServer) error {
for notif := range notifikasiCh {
if err := stream.Send(notif); err != nil {
// Biasanya context canceled atau deadline exceeded
log.Printf("gagal kirim notifikasi: %v", err)
return err
}
}
return nil
}
Perhatikan: Server WAJIB menangani error send untuk menghindari goroutine leak atau resource leak.
Mengelola Context pada Streaming RPC
Apa Itu Context di gRPC?
context.Context
adalah objek penting di Go/gRPC untuk membawa timeout, cancellation, dan deadline di seluruh proses request (termasuk stream). Context ini menjadi pengontrol utama untuk membatalkan stream, menyetop resource, menghentikan proses stream.
Merangkai Context pada Streaming
Setiap stream di gRPC terikat dengan context yang sama dengan request utama.
- Di sisi client, context biasanya diberikan saat pembuatan stream.
- Di sisi server, gRPC otomatis menyediakan context per stream (bisa diakses via
stream.Context()
).
Contoh Penggunaan Context pada Client
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
stream, err := client.SendLogStream(ctx)
// ...
Jika stream proses melebihi 5 detik, context otomatis canceled. Semua operasi pada stream akan mengembalikan error bertipe “context canceled” atau “deadline exceeded”.
Menangkap Context pada Server
for {
select {
case <-stream.Context().Done():
// Client disconnected atau cancel
log.Println("stream dibatalkan oleh client")
return stream.Context().Err()
case notif := <-notifikasiCh:
if err := stream.Send(notif); err != nil {
// Gagal kirim (misal client disconnect)
return err
}
}
}
Diagram Alur: Error dan Context pada Server Streaming
flowchart TD A[Mulai Stream] --> B{Kirim Notifikasi} B -->|Notifikasi Baru| C["stream.Send()"] C --> D{Error saat Send?} D -->|Ya| E["Periksa stream.Context().Err()"] D -->|Tidak| B E -->|Err Canceled/Deadline| F[Selesaikan stream] E -->|Error lain| G[Log error & return]
Studi Kasus: Upload File dengan Client Streaming
Bayangkan, kita ingin implementasi fitur upload file besar (misal log file) dari client ke server, secara chunked (streaming). Masalah: jaringan tidak stabil, client kadang cancel di tengah jalan, server overload.
1. Definisi Protobuf
service LogUploader {
rpc UploadLogs(stream LogChunk) returns (UploadSummary);
}
message LogChunk {
bytes content = 1;
}
message UploadSummary {
int64 total = 1;
}
2. Implementasi di Server: Error dan Context Handling
func (s *server) UploadLogs(stream pb.LogUploader_UploadLogsServer) error {
total := int64(0)
for {
select {
case <-stream.Context().Done():
// Client disconnect/timeout/cancel
log.Printf("stream dibatalkan oleh client: %v", stream.Context().Err())
return status.Error(codes.Canceled, "client membatalkan upload")
default:
chunk, err := stream.Recv()
if err == io.EOF {
return stream.SendAndClose(&pb.UploadSummary{Total: total})
}
if err != nil {
log.Printf("error read stream: %v", err)
return status.Error(codes.Internal, "gagal membaca data")
}
// Simulasi error: chunk terlalu besar
if len(chunk.Content) > MAX_CHUNK {
return status.Error(codes.InvalidArgument, "chunk terlalu besar")
}
total += int64(len(chunk.Content))
}
}
}
3. Simulasi: Kasus – Client Cancel Upload
Jika client call cancel()
pada context:
- Operasi server pada
stream.Context().Done()
akan terpicu. - Server bisa segera membersihkan resource dan keluar.
- Client akan menerima error
Canceled
.
Step | Client | Server |
---|---|---|
Mulai stream | Membuka stream | Menunggu/terima Recv |
Upload sebagian chunk | Kirim chunk | Terima, simpan, cek error |
Cancel pada client | Cancel context | Stream Context Done triggered |
Stream ditutup | Terima error Canceled | Keluar dari handler |
Rekomendasi Praktis untuk Engineer
- Selalu check error pada setiap operasi stream — baik
Send()
maupunRecv()
. - Observe context di server: Pantau channel
<-stream.Context().Done()
untuk cleanup cepat jika client disconnect atau timeout. - Tangani status code dengan benar: Kembalikan error yang relevan (bukan hanya
Internal
). Gunakanstatus.Error
di Go. - Gunakan timeout sesuai kebutuhan: Jangan biarkan operasi streaming berjalan tanpa batas.
- Tes resilience: Simulasikan berbagai kondisi error saat pengembangan (client tiba-tiba disconnect, server overload, dsb).
Kesimpulan
Menangani error dan context secara benar pada streaming RPC tidak hanya membuat aplikasi Anda lebih tahan banting, tapi juga lebih efisien dan mudah di-maintain di production. Ingat, stream akan selalu menjadi rawan gagal karena berjalan “lama” dan melewati banyak edge case. Engineer yang baik tidak hanya membuat fitur berjalan, tapi mampu menangani segala kemungkinan error dengan elegan.
Selamat mencoba dan jangan lupa refactor! 🚀
4 Arsitektur GraphQL Server dalam Go
Artikel Terhangat
4 Arsitektur GraphQL Server dalam Go
07 Jul 2025
25 Cara Mengontrol Aliran Data dengan Stream
07 Jul 2025
2 Perbandingan Singkat: GraphQL vs REST API
07 Jul 2025
24 Implementasi Bidirectional Streaming RPC
07 Jul 2025
23 Implementasi Client-side Streaming RPC
07 Jul 2025

4 Arsitektur GraphQL Server dalam Go

25 Cara Mengontrol Aliran Data dengan Stream

2 Perbandingan Singkat: GraphQL vs REST API

24 Implementasi Bidirectional Streaming RPC
