Skip to content
Santekno.com | Level Up Your Engineering Skills
ID
📖 0%
04 Jul 2025 · 6 mnt baca ·Artikel 26 / 110
Go

26 Menangani Error dan Context pada Streaming RPC

IH
Ihsan Arif
Penulis di Santekno · Backend Engineer

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:

JenisClient StreamServer StreamContoh
UnaryTidakTidakLogin(username, password)
Client-side StreamingYaTidakUpload log secara batch
Server-side StreamingTidakYaNotifikasi update status
Bidirectional StreamingYaYaChat, 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 CodePenjelasanSolusi Umum
OKTidak ada error (sukses)
CanceledClient membatalkan requestBersihkan resource
DeadlineExceededTimeout pada streamReview context/timeout
UnavailableServer tidak tersedia (down)Coba reconnect/backoff
InternalError internal di serverPeriksa log server
DataLossData stream corruptValidasi, 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:

go
 1stream, err := client.SendLogStream(ctx)
 2if err != nil {
 3    log.Fatalf("gagal membuka stream: %v", err)
 4}
 5
 6for _, entry := range logs {
 7    if err := stream.Send(entry); err != nil {
 8        log.Printf("gagal mengirim log: %v", err)
 9        break
10    }
11}
12
13resp, err := stream.CloseAndRecv()
14if err != nil {
15    log.Fatalf("gagal mendapatkan respons: %v", err)
16}
17log.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.

go
 1func (s *server) StreamNotifikasi(req *pb.Request, stream pb.Service_StreamNotifikasiServer) error {
 2    for notif := range notifikasiCh {
 3        if err := stream.Send(notif); err != nil {
 4            // Biasanya context canceled atau deadline exceeded
 5            log.Printf("gagal kirim notifikasi: %v", err)
 6            return err
 7        }
 8    }
 9    return nil
10}

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

go
1ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
2defer cancel()
3
4stream, err := client.SendLogStream(ctx)
5// ...
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

go
 1for {
 2    select {
 3    case <-stream.Context().Done():
 4        // Client disconnected atau cancel
 5        log.Println("stream dibatalkan oleh client")
 6        return stream.Context().Err()
 7    case notif := <-notifikasiCh:
 8        if err := stream.Send(notif); err != nil {
 9            // Gagal kirim (misal client disconnect)
10            return err
11        }
12    }
13}

Diagram Alur: Error dan Context pada Server Streaming

MERMAID
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

protobuf
1service LogUploader {
2    rpc UploadLogs(stream LogChunk) returns (UploadSummary);
3}
4message LogChunk {
5    bytes content = 1;
6}
7message UploadSummary {
8    int64 total = 1;
9}

2. Implementasi di Server: Error dan Context Handling

go
 1func (s *server) UploadLogs(stream pb.LogUploader_UploadLogsServer) error {
 2    total := int64(0)
 3    for {
 4        select {
 5        case <-stream.Context().Done():
 6            // Client disconnect/timeout/cancel
 7            log.Printf("stream dibatalkan oleh client: %v", stream.Context().Err())
 8            return status.Error(codes.Canceled, "client membatalkan upload")
 9        default:
10            chunk, err := stream.Recv()
11            if err == io.EOF {
12                return stream.SendAndClose(&pb.UploadSummary{Total: total})
13            }
14            if err != nil {
15                log.Printf("error read stream: %v", err)
16                return status.Error(codes.Internal, "gagal membaca data")
17            }
18            // Simulasi error: chunk terlalu besar
19            if len(chunk.Content) > MAX_CHUNK { 
20                return status.Error(codes.InvalidArgument, "chunk terlalu besar")
21            }
22            total += int64(len(chunk.Content))
23        }
24    }
25}

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.
StepClientServer
Mulai streamMembuka streamMenunggu/terima Recv
Upload sebagian chunkKirim chunkTerima, simpan, cek error
Cancel pada clientCancel contextStream Context Done triggered
Stream ditutupTerima error CanceledKeluar dari handler

Rekomendasi Praktis untuk Engineer

  1. Selalu check error pada setiap operasi stream — baik Send() maupun Recv().
  2. Observe context di server: Pantau channel <-stream.Context().Done() untuk cleanup cepat jika client disconnect atau timeout.
  3. Tangani status code dengan benar: Kembalikan error yang relevan (bukan hanya Internal). Gunakan status.Error di Go.
  4. Gunakan timeout sesuai kebutuhan: Jangan biarkan operasi streaming berjalan tanpa batas.
  5. 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! 🚀

Artikel Terkait

💬 Komentar