tutorial

  1. Deadline dan Timeout pada gRPC

66. Deadline dan Timeout pada gRPC: Panduan Engineer Profesional

Saat kita membangun sistem terdistribusi atau microservices, salah satu aspek kritis yang sering luput dari perhatian adalah bagaimana layanan kita menangani proses permintaan yang lambat atau tak berujung (hanging). Dalam konteks gRPC, dua konsep penting yang perlu kita pahami dan gunakan secara tepat adalah Deadline dan Timeout. Keduanya berperan fundamental dalam menjaga robustness layanan dan pengalaman pengguna yang baik.

Pada artikel kali ini, saya akan membahas secara mendalam perbedaan keduanya, kapan harus menggunakannya, dampaknya pada aplikasi, serta contoh implementasinya pada gRPC menggunakan Go beserta simulasi dan diagram alur untuk memperjelas mekanismenya.


Apa itu Deadline dan Timeout di gRPC?

Sebelum membuat kode, kita perlu memahami konsep ini secara abstrak:

  • Timeout adalah batas waktu maksimal untuk menjalankan sebuah operasi sebelum dianggap gagal. Timeout biasanya ditentukan oleh client saat memanggil server.
  • Deadline adalah titik waktu absolut (timestamp) kapan permintaan harus selesai. Kadang istilah Deadline dan Timeout digunakan secara bergantian, namun Deadline biasanya lebih “eksplisit”: ini adalah waktu kapan permintaan akan di-cancel.

Di gRPC, timeout akan otomatis diubah menjadi deadline di belakang layar, dan diteruskan ke setiap request hingga ke microservice paling ujung.

Tabel: Perbedaan Deadline dan Timeout

AspekTimeoutDeadline
TipeDurasi relative (3s)Waktu absolut (unix timestamp)
Siapa yang setClientClient
PropagasiPropagasi ke downstreamPropagasi ke downstream
Cara set di GoContext dengan TimeoutContext dengan Deadline
Di header gRPCTerkirim sebagai deadlineTerkirim sebagai deadline

Kenapa Deadline dan Timeout Penting?

1. Hindari Resources Hanging

Tanpa batas waktu, layanan kita bisa saja menggantung menangani permintaan yang tak pernah selesai. Akibatnya, resource boros dan experience memburuk.

2. Propagasi End-to-end

Sering kali, 1 permintaan dari client bisa men-trigger layanan A memanggil B, lalu B memanggil C, dst. Tanpa propagasi deadline, layanan di hilir tidak tahu kapan boleh berhenti bekerja jika upstream sudah kehabisan waktu.

3. Fail Fast

Membuat sistem yang fail fast membantu deteksi, pelaporan error, dan handling fallback lebih baik dalam aplikasi real world.


Implementasi Deadline dan Timeout di gRPC (Golang)

Mari kita simulasikan sebuah sistem sederhana:

  • gRPC Client: Memanggil method “/Ping” ke server.
  • gRPC Server: Menjawab Ping, namun bisa disimulasikan delay.
  • Client menentukan deadline dengan context.

1. Contoh Server

// server.go
package main

import (
    "context"
    "fmt"
    "log"
    "net"
    "time"

    pb "your/proto/path"
    "google.golang.org/grpc"
)

type server struct{
    pb.UnimplementedPingerServer
}

func (s *server) Ping(ctx context.Context, req *pb.PingRequest) (*pb.PingResponse, error) {
    // Simulasi proses lambat
    select {
    case <-time.After(2 * time.Second): // Delay 2 detik
        return &pb.PingResponse{Message: "Pong"}, nil
    case <-ctx.Done():
        // Request dibatalkan karena deadline atau client cancel
        return nil, ctx.Err()
    }
}

func main() {
    lis, _ := net.Listen("tcp", ":50051")
    grpcServer := grpc.NewServer()
    pb.RegisterPingerServer(grpcServer, &server{})
    if err := grpcServer.Serve(lis); err != nil {
        log.Fatalf("failed to serve: %v", err)
    }
}

2. Contoh Client dengan Deadline

// client.go
package main

import (
    "context"
    "log"
    "time"

    pb "your/proto/path"
    "google.golang.org/grpc"
)

func main() {
    conn, _ := grpc.Dial("localhost:50051", grpc.WithInsecure())
    defer conn.Close()
    client := pb.NewPingerClient(conn)

    // Set deadline 1 detik dari sekarang
    ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
    defer cancel()

    _, err := client.Ping(ctx, &pb.PingRequest{})
    if err != nil {
        log.Printf("Ping failed: %v", err)
    }
}

Pada contoh di atas, server butuh waktu 2 detik untuk membalas, tapi client mengatur deadline 1 detik. Hasilnya, server akan menerima cancel signal dari context dan permintaan di-cancel otomatis. Ini bisa dihandle di server maupun client.


Diagram Alur Deadline Propagation

Mari kita gambarkan alurnya menggunakan diagram mermaid:

sequenceDiagram
    participant Client
    participant ServiceA
    participant ServiceB

    Client->>ServiceA: gRPC request (deadline=5s)
    ServiceA->>ServiceB: gRPC request (deadline=4s)
    Note right of ServiceA: Kurangi overhead processing
    ServiceB-->>ServiceA: Response / Error
    ServiceA-->>Client: Response / Error

Diagram di atas mengilustrasikan:

  • Client kirim request dengan deadline 5 detik ke ServiceA.
  • ServiceA perlu waktu untuk proses internal.
  • Jika ServiceA memanggil ServiceB, ia harus menurunkan deadline, mengurangi waktu proses yang telah dipakai.
  • Hal ini memastikan tidak ada request gantung di ServiceB jika waktu di ServiceA sudah habis.

Simulasi Output

Mari kita lihat output dari client jika mengatur deadline < waktu proses server:

2024/06/01 22:00:00 Ping failed: rpc error: code = DeadlineExceeded desc = context deadline exceeded

Jika deadline lebih besar dari proses server (misal, deadline 3s), respons akan berhasil. Ubah di kode client dan lihat efeknya.


Kapan Memakai Deadline dan Timeout?

  • Selalu atur deadline dari sisi client. Jangan biarkan permintaan berjalan selamanya.
  • Untuk internal service chaining, pastikan propagasi deadline ke downstream.
  • Sesuaikan deadline sesuai SLA/millisec pendefinisian bisnis.
  • Pada context Go, gunakan WithTimeout untuk mudahnya.

Dampak Deadline pada Fallback dan Retry

Deadline juga fundamental untuk membangun retry logic yang baik. Jika request gagal karena timeout/deadline, aplikasi bisa mengambil langkah fallback, mencoba ke service lain, atau mengembalikan error message user-friendly ke pengguna.


Kesalahan Umum yang Mesti Dihindari

  1. Tidak meneruskan context ke downstream
    Propagasi context sangat krusial agar deadline tetap dihormati oleh seluruh stack layanan.

  2. Mengatur deadline terlalu sempit
    Berpotensi memotong proses yang sebenarnya valid

  3. Tidak handle ctx.Err() di server
    Pastikan ada handling untuk mengecek apakah context sudah done sebelum memproses request berat.


Best Practice

  • Propagasi konteks context.TODO() → context.Background() → context.WithTimeout/WithDeadline()
  • Cek ctx.Err() periodik di long-running worker/server.
  • Logging & metric error DeadlineExceeded untuk pemantauan.

Kesimpulan

Menggunakan deadline dan timeout dengan benar adalah kunci membuat layanan gRPC yang resilient, robust, dan fail fast. Otomasi ini bukan hanya membantu resource management, tapi juga membantu user experience dan scalability aplikasi Anda.

Jadi di setiap project gRPC berikutnya, pastikan setiap call memakai deadline/timeout yang proporsional — dan selalu propagasikan context dengan benar ke downstream.


Salam,
Software Engineer Professional

comments powered by Disqus