15 Mar 2025·6 menit membaca

Timeout context di Go untuk API: dari HTTP handler hingga SQL

Timeout context Go untuk API membantu meneruskan deadline dari handler HTTP ke panggilan SQL, mencegah permintaan macet, dan menjaga layanan tetap stabil di bawah beban.

Timeout context di Go untuk API: dari HTTP handler hingga SQL

Mengapa permintaan bisa macet (dan mengapa itu berbahaya di bawah beban)

Permintaan disebut "macet" ketika menunggu sesuatu yang tidak kembali: kueri database yang lambat, koneksi pool yang terblokir, gangguan DNS, atau layanan upstream yang menerima panggilan tetapi tidak menjawab.

Gejalanya terlihat sederhana: beberapa permintaan memakan waktu selamanya, lalu semakin banyak yang menumpuk di belakangnya. Anda sering melihat memori naik, jumlah goroutine bertambah, dan antrean koneksi terbuka yang tampaknya tak pernah surut.

Di bawah beban, permintaan yang macet merugikan dua kali lipat. Mereka membuat worker sibuk, dan mereka menahan sumber daya langka seperti koneksi database dan lock. Itu membuat permintaan yang biasanya cepat menjadi lambat, yang menciptakan overlap lebih banyak, yang pada gilirannya menciptakan lebih banyak penantian.

Retry dan lonjakan lalu lintas memperburuk spiral ini. Klien time out dan mengulang sementara permintaan asli masih berjalan, jadi sekarang Anda membayar dua permintaan. Kalikan itu dengan banyak klien selama perlambatan singkat, dan Anda bisa membebani database atau mencapai batas koneksi meskipun trafik rata-rata baik-baik saja.

Timeout secara sederhana adalah janji: "kita tidak akan menunggu lebih lama dari X." Itu membantu Anda gagal cepat dan melepas sumber daya, tetapi tidak membuat pekerjaan selesai lebih cepat.

Ia juga tidak menjamin pekerjaan berhenti seketika. Misalnya, database mungkin tetap mengeksekusi, layanan upstream mungkin mengabaikan pembatalan Anda, atau kode Anda sendiri mungkin tidak aman ketika pembatalan terjadi.

Yang dijamin oleh timeout adalah handler Anda bisa berhenti menunggu, mengembalikan error yang jelas, dan melepaskan apa yang dipegangnya. Batas penantian itulah yang mencegah beberapa panggilan lambat berubah menjadi outage penuh.

Tujuan menggunakan timeout context di Go adalah satu deadline bersama dari tepi hingga panggilan terdalam. Tetapkan sekali di boundary HTTP, teruskan ctx yang sama melalui kode servis Anda, dan gunakan pada panggilan database/sql sehingga database juga diberi tahu kapan harus berhenti menunggu.

Context di Go dengan istilah sederhana

context.Context adalah objek kecil yang Anda teruskan melalui kode untuk menggambarkan apa yang sedang terjadi sekarang. Ia menjawab pertanyaan seperti: "Apakah permintaan ini masih valid?", "Kapan kita harus menyerah?", dan "Nilai scope-permintaan apa yang harus ikut bersama kerja ini?"

Keuntungannya adalah satu keputusan di tepi sistem Anda (HTTP handler) bisa melindungi setiap langkah downstream, selama Anda terus meneruskan context yang sama.

Apa yang dibawa context

Context bukan tempat untuk data bisnis. Ia untuk sinyal kontrol dan sedikit metadata scope-permintaan: pembatalan, deadline/timeout, dan metadata kecil seperti request ID untuk log.

Timeout vs pembatalan sederhana: timeout adalah salah satu alasan pembatalan. Jika Anda menetapkan timeout 2 detik, context akan dibatalkan saat 2 detik berlalu. Tapi context juga bisa dibatalkan lebih awal jika pengguna menutup tab, load balancer memutus koneksi, atau kode Anda memutuskan permintaan harus dihentikan.

Context mengalir melalui panggilan fungsi dengan menjadi parameter eksplisit, biasanya yang pertama: func DoThing(ctx context.Context, ...). Itu tujuannya. Sulit untuk "lupa" ketika muncul di setiap titik pemanggilan.

Saat deadline berakhir, apa pun yang mengamati context tersebut harus berhenti dengan cepat. Misalnya, kueri database yang menggunakan QueryContext seharusnya kembali lebih awal dengan error seperti context deadline exceeded, dan handler Anda bisa merespons dengan timeout daripada menggantung sampai server kehabisan worker.

Model mental yang baik: satu permintaan, satu context, teruskan ke mana-mana. Jika permintaan mati, pekerjaan juga harus mati.

Menetapkan deadline yang jelas di boundary HTTP

Jika Anda ingin timeout end-to-end bekerja, putuskan di mana jam dimulai. Tempat paling aman adalah tepat di tepi HTTP, sehingga setiap panggilan downstream (business logic, SQL, layanan lain) mewarisi deadline yang sama.

Anda bisa menetapkan deadline di beberapa tempat. Timeout tingkat server adalah baseline yang baik dan melindungi dari klien yang lambat. Middleware bagus untuk konsistensi di grup route. Menetapkannya di dalam handler juga sah ketika Anda menginginkan sesuatu yang eksplisit dan lokal.

Untuk kebanyakan API, timeout per-permintaan di middleware atau handler paling mudah untuk dipahami. Jaga agar realistis: pengguna lebih suka kegagalan cepat dan jelas daripada permintaan yang menggantung. Banyak tim menggunakan anggaran lebih pendek untuk operasi baca (mis. 1-2s) dan sedikit lebih panjang untuk tulis (mis. 3-10s), tergantung apa yang dilakukan endpoint.

Berikut pola handler sederhana:

func (s *Server) getReport(w http.ResponseWriter, r *http.Request) {
    ctx, cancel := context.WithTimeout(r.Context(), 2*time.Second)
    defer cancel()

    report, err := s.reports.Generate(ctx, r.URL.Query().Get("id"))
    if err != nil {
        http.Error(w, err.Error(), http.StatusGatewayTimeout)
        return
    }

    json.NewEncoder(w).Encode(report)
}

Dua aturan menjaga ini tetap efektif:

  • Selalu panggil cancel() sehingga timer dan sumber daya dilepas dengan cepat.
  • Jangan pernah menggantikan request context dengan context.Background() atau context.TODO() di dalam handler. Itu memutus rantai, dan panggilan database dan permintaan keluar Anda dapat berjalan selamanya meskipun klien telah pergi.

Meneruskan context lewat basis kode Anda

Setelah Anda menetapkan deadline di boundary HTTP, pekerjaan sebenarnya adalah memastikan deadline yang sama mencapai setiap lapisan yang bisa memblokir. Idenya adalah satu jam yang dibagi oleh handler, kode servis, dan apa pun yang menyentuh jaringan atau disk.

Aturan sederhana menjaga semuanya konsisten: setiap fungsi yang mungkin menunggu harus menerima context.Context, dan itu harus menjadi parameter pertama. Itu membuatnya jelas di titik panggilan, dan menjadi kebiasaan.

Pola signature praktis

Utamakan signature seperti DoThing(ctx context.Context, ...) untuk service dan repository. Hindari menyembunyikan context di dalam struct atau membuatnya kembali dengan context.Background() di lapisan bawah, karena itu diam-diam menjatuhkan deadline pemanggil.

func (h *Handler) CreateOrder(w http.ResponseWriter, r *http.Request) {
    ctx := r.Context()

    if err := h.svc.CreateOrder(ctx, r.Body); err != nil {
        // map context errors to a clear client response elsewhere
        http.Error(w, err.Error(), http.StatusRequestTimeout)
        return
    }
}

func (s *Service) CreateOrder(ctx context.Context, body io.Reader) error {
    // parsing or validation can still respect cancellation
    select {
    case <-ctx.Done():
        return ctx.Err()
    default:
    }

    return s.repo.InsertOrder(ctx, /* data */)
}

Menangani exit dini dengan bersih

Anggap ctx.Done() sebagai jalur kontrol normal. Dua kebiasaan membantu:

  • Periksa ctx.Err() sebelum memulai pekerjaan mahal, dan setelah loop panjang.
  • Kembalikan ctx.Err() ke atas tanpa diubah, sehingga handler bisa merespons cepat dan berhenti membuang sumber daya.

Saat setiap lapisan meneruskan ctx yang sama, satu timeout bisa memutus parsing, business logic, dan penantian database sekaligus.

Menerapkan deadline ke kueri database/sql

Generate real Go source code
Prototipe tanpa kode, lalu ekspor layanan Go siap-produksi ketika Anda butuh kontrol penuh.
Bangun Backend

Setelah handler HTTP Anda memiliki deadline, pastikan kerja database Anda benar-benar mendengarkannya. Dengan database/sql, itu berarti menggunakan metode yang sadar context setiap saat. Jika Anda memanggil Query() atau Exec() tanpa context, API Anda bisa terus menunggu kueri yang lambat meski klien sudah menyerah.

Gunakan ini secara konsisten: db.QueryContext, db.QueryRowContext, db.ExecContext, dan db.PrepareContext (lalu QueryContext/ExecContext pada statement yang dikembalikan).

func (s *Store) GetUser(ctx context.Context, id int64) (*User, error) {
	row := s.db.QueryRowContext(ctx,
		`SELECT id, email FROM users WHERE id = $1`, id,
	)
	var u User
	if err := row.Scan(&u.ID, &u.Email); err != nil {
		return nil, err
	}
	return &u, nil
}

func (s *Store) UpdateEmail(ctx context.Context, id int64, email string) error {
	_, err := s.db.ExecContext(ctx,
		`UPDATE users SET email = $1 WHERE id = $2`, email, id,
	)
	return err
}

Dua hal mudah terlewat.

Pertama, driver SQL Anda harus menghormati pembatalan context. Banyak yang melakukannya, tetapi pastikan dengan menguji kueri yang sengaja lambat dan memeriksa bahwa itu batal cepat ketika deadline tercapai.

Kedua, pertimbangkan timeout di sisi database sebagai backstop. Misalnya, Postgres dapat menegakkan batas per-statement (sering disebut statement timeout). Itu melindungi database bahkan jika ada bug aplikasi yang lupa meneruskan context.

Ketika operasi berhenti karena timeout, tangani itu berbeda dari error SQL normal. Periksa errors.Is(err, context.DeadlineExceeded) dan errors.Is(err, context.Canceled) dan kembalikan respons yang jelas (seperti 504) daripada memperlakukan itu sebagai "database rusak." Jika Anda menghasilkan backend Go (misalnya dengan AppMaster), menjaga jalur error ini terpisah juga memudahkan pembacaan log dan perilaku retry.

Panggilan downstream: HTTP client, cache, dan layanan lain

Bahkan jika handler dan kueri SQL Anda menghormati context, sebuah permintaan masih bisa menggantung jika panggilan downstream menunggu selamanya. Di bawah beban, beberapa goroutine yang macet bisa menumpuk, menghabiskan pool koneksi, dan mengubah perlambatan kecil menjadi outage penuh. Perbaikannya adalah propagasi yang konsisten ditambah backstop keras.

HTTP outbound

Saat memanggil API lain, bangun request dengan context yang sama sehingga deadline dan pembatalan mengalir otomatis.

req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil { /* handle */ }
resp, err := httpClient.Do(req)

Jangan hanya mengandalkan context. Konfigurasikan juga client HTTP dan transport agar Anda terlindungi jika kode tidak sengaja menggunakan background context, atau jika DNS/TLS/koneksi idle macet. Atur http.Client.Timeout sebagai batas atas untuk seluruh panggilan, atur timeout transport (dial, TLS handshake, response header), dan gunakan kembali satu client alih-alih membuat client baru per permintaan.

Cache dan antrean

Cache, message broker, dan RPC client sering memiliki titik tunggu sendiri: memperoleh koneksi, menunggu balasan, terblokir pada antrean penuh, atau menunggu lock. Pastikan operasi tersebut menerima ctx, dan juga gunakan timeout di level library bila tersedia.

Aturan praktis: jika permintaan pengguna tersisa 800ms, jangan mulai panggilan downstream yang mungkin memakan 2 detik. Lewati, degradasi, atau kembalikan respons parsial.

Tentukan sebelumnya apa arti timeout untuk API Anda. Kadang jawaban yang tepat adalah error cepat. Kadang data parsial untuk field opsional. Kadang data dari cache yang mungkin ketinggalan, diberi tanda jelas.

Jika Anda membangun backend Go (termasuk yang dihasilkan, seperti di AppMaster), ini bedanya antara "timeout ada" dan "timeout secara konsisten melindungi sistem" saat trafik melonjak.

Langkah demi langkah: refactor API untuk menggunakan timeout end-to-end

Deploy where your stack runs
Luncurkan aplikasi ke AppMaster Cloud atau AWS, Azure, atau Google Cloud Anda sendiri.
Deploy App

Refactor untuk timeout bermuara pada satu kebiasaan: teruskan context.Context yang sama dari tepi HTTP sampai ke setiap panggilan yang bisa memblokir.

Cara praktis melakukan ini adalah bekerja top-down:

  • Ubah handler dan metode inti service Anda untuk menerima ctx context.Context.
  • Perbarui setiap panggilan DB untuk menggunakan QueryContext atau ExecContext.
  • Lakukan hal yang sama untuk panggilan eksternal (HTTP client, cache, antrean). Jika sebuah library tidak menerima ctx, bungkus atau ganti dengan library lain.
  • Putuskan siapa yang mengelola timeout. Aturan umum: handler menetapkan deadline keseluruhan; lapisan lebih rendah hanya menetapkan deadline lebih pendek untuk operasi spesifik bila perlu.
  • Buat error dapat diprediksi di tepi: peta context.DeadlineExceeded dan context.Canceled ke respons HTTP yang jelas.

Berikut bentuk yang Anda inginkan antar lapisan:

func (h *Handler) GetOrder(w http.ResponseWriter, r *http.Request) {
    ctx, cancel := context.WithTimeout(r.Context(), 2*time.Second)
    defer cancel()

    order, err := h.svc.GetOrder(ctx, r.PathValue("id"))
    if errors.Is(err, context.DeadlineExceeded) {
        http.Error(w, "request timed out", http.StatusGatewayTimeout)
        return
    }
    if err != nil {
        http.Error(w, "internal error", http.StatusInternalServerError)
        return
    }
    _ = json.NewEncoder(w).Encode(order)
}

func (r *Repo) GetOrder(ctx context.Context, id string) (Order, error) {
    row := r.db.QueryRowContext(ctx, `SELECT id,total FROM orders WHERE id=$1`, id)
    // scan...
}

Nilai timeout harus membosankan dan konsisten. Jika handler punya total 2 detik, jaga kueri DB di bawah 1 detik untuk menyisakan ruang bagi encoding JSON dan pekerjaan lain.

Untuk membuktikan ini bekerja, tambahkan tes yang memaksa timeout. Satu pendekatan sederhana adalah metode repository tiruan yang memblok hingga ctx.Done() lalu mengembalikan ctx.Err(). Tes Anda harus memastikan handler mengembalikan 504 dengan cepat, bukan setelah delay tiruan.

Jika Anda membangun backend Go dengan generator (mis. AppMaster menghasilkan layanan Go), aturannya sama: satu request context, ditenun ke seluruh tempat, dengan kepemilikan deadline yang jelas.

Observability: membuktikan timeout bekerja

Connect common services quickly
Integrasikan modul auth, pembayaran, dan messaging tanpa menulis ulang plumbing yang sama.
Jelajahi Modul

Timeout hanya membantu jika Anda bisa melihatnya terjadi. Tujuannya sederhana: setiap permintaan punya deadline, dan ketika gagal Anda bisa tahu di mana waktu dihabiskan.

Mulailah dengan log yang aman dan berguna. Alih-alih menumpahkan body lengkap, log cukup untuk menghubungkan titik-titik dan menemukan jalur lambat: request ID (atau trace ID), apakah deadline disetel dan berapa waktu tersisa di titik-titik penting, nama operasi (handler, nama kueri SQL, nama panggilan outbound), dan kategori hasil (ok, timeout, canceled, error lain).

Tambahkan beberapa metrik fokus agar perilaku di bawah beban jelas:

  • Jumlah timeout per endpoint dan dependency
  • Latensi permintaan (p50/p95/p99)
  • Permintaan yang sedang berjalan
  • Latensi kueri database (p95/p99)
  • Tingkat error terpisah menurut jenis

Saat menanganai error, beri tag dengan benar. context.DeadlineExceeded biasanya berarti Anda mencapai anggaran. context.Canceled sering berarti klien pergi atau timeout upstream terjadi lebih dulu. Pisahkan keduanya karena perbaikannya berbeda.

Tracing: temukan tempat terbuangnya waktu

Span tracing harus mengikuti context yang sama dari HTTP handler ke panggilan database/sql seperti QueryContext. Contohnya, sebuah permintaan timeout pada 2 detik dan trace menunjukkan 1.8 detik dihabiskan menunggu koneksi DB. Itu mengarah ke ukuran pool atau transaksi yang lambat, bukan teks query.

Jika Anda membuat dashboard internal untuk ini (timeout per route, kueri lambat teratas), alat no-code seperti AppMaster dapat membantu mengirimkannya cepat tanpa menjadikan observability proyek engineering terpisah.

Kesalahan umum yang membuat timeout sia-sia

Sebagian besar bug "masih kadang menggantung" datang dari beberapa kesalahan kecil.

  • Mengatur ulang jam di tengah jalan. Handler menetapkan deadline 2s, tapi repository membuat context baru dengan timeout sendiri (atau tanpa timeout). Sekarang database bisa terus berjalan setelah klien pergi. Teruskan ctx masuk dan hanya perketatnya jika ada alasan jelas.
  • Memulai goroutine yang tak pernah berhenti. Menjalankan pekerjaan dengan context.Background() (atau menjatuhkan ctx sepenuhnya) berarti itu akan terus berjalan walau permintaan dibatalkan. Teruskan request ctx ke goroutine dan select pada ctx.Done().
  • Deadline terlalu pendek untuk trafik nyata. Timeout 50ms mungkin bekerja di laptop Anda dan gagal di produksi saat spike kecil, menyebabkan retry, beban lebih, dan mini-outage yang Anda sebabkan sendiri. Pilih timeout berdasarkan latensi normal plus ruang aman.
  • Menyembunyikan error sebenarnya. Menganggap context.DeadlineExceeded sebagai 500 umum membuat debugging dan perilaku klien lebih buruk. Peta itu ke respons timeout yang jelas dan log perbedaan antara "dibatalkan oleh klien" dan "kedaluwarsa."
  • Meninggalkan sumber daya terbuka saat exit dini. Jika Anda return lebih awal, pastikan tetap defer rows.Close() dan panggil fungsi cancel dari context.WithTimeout. Row leaked atau pekerjaan yang tersisa bisa menghabiskan koneksi saat beban tinggi.

Contoh cepat: sebuah endpoint memicu kueri report. Jika pengguna menutup tab, ctx handler dibatalkan. Jika panggilan SQL menggunakan background context baru, query tetap berjalan, mengikat koneksi dan memperlambat semua orang. Saat Anda meneruskan ctx yang sama ke QueryContext, panggilan database terinterupsi dan sistem pulih lebih cepat.

Daftar periksa cepat untuk perilaku timeout yang andal

Add business logic without glue code
Bangun alur permintaan dengan Business Process Editor dan teruskan context melalui integrasi.
Buat Logika

Timeout hanya membantu jika konsisten. Satu panggilan yang terlewat bisa membuat goroutine sibuk, menahan koneksi DB, dan memperlambat permintaan berikutnya.

  • Tetapkan satu deadline jelas di tepi (biasanya handler HTTP). Semua di dalam permintaan harus mewarisinya.
  • Teruskan ctx yang sama melalui service dan repository. Hindari context.Background() di kode permintaan.
  • Gunakan metode DB yang sadar context di mana-mana: QueryContext, QueryRowContext, dan ExecContext.
  • Lampirkan ctx yang sama pada panggilan outbound (HTTP client, cache, antrean). Jika membuat child context, buat lebih pendek, bukan lebih panjang.
  • Tangani pembatalan dan timeout secara konsisten: kembalikan error bersih, hentikan pekerjaan, dan hindari loop retry di dalam permintaan yang sudah dibatalkan.

Setelah itu, verifikasi perilaku di bawah tekanan. Timeout yang memicu tapi tidak cukup cepat membebaskan sumber daya tetap merusak reliabilitas.

Dashboard harus membuat timeout jelas, bukan disembunyikan dalam rata-rata. Lacak beberapa sinyal yang menjawab "apakah deadline benar-benar ditegakkan?": timeout permintaan dan timeout DB (terpisah), persentil latensi (p95, p99), statistik pool DB (koneksi in-use, hitungan tunggu, durasi tunggu), dan pemecahan penyebab error (context deadline exceeded vs error lain).

Jika Anda membangun alat internal di platform seperti AppMaster, daftar periksa yang sama berlaku untuk layanan Go apa pun yang Anda hubungkan: definisikan deadline di boundary, teruskan, dan konfirmasi melalui metrik bahwa permintaan macet berubah menjadi kegagalan cepat alih-alih penumpukan lambat.

Skenario contoh dan langkah selanjutnya

Tempat umum di mana ini bermanfaat adalah endpoint pencarian. Bayangkan GET /search?q=printer melambat ketika database sibuk dengan kueri laporan besar. Tanpa deadline, setiap permintaan masuk bisa menunggu kueri SQL panjang. Di bawah beban, permintaan yang macet menumpuk, mengikat goroutine worker dan koneksi, dan seluruh API terasa beku.

Dengan deadline jelas di handler HTTP dan ctx yang sama diteruskan ke repository, sistem berhenti menunggu saat anggaran habis. Ketika deadline tercapai, driver database membatalkan query (jika didukung), handler mengembalikan, dan server bisa terus melayani permintaan baru alih-alih menunggu selamanya.

Perilaku yang terlihat pengguna lebih baik bahkan saat terjadi masalah. Alih-alih berputar selama 30–120 detik lalu gagal dengan cara berantakan, klien mendapatkan error cepat dan dapat diprediksi (sering 504 atau 503 dengan pesan singkat seperti "request timed out"). Lebih penting, sistem pulih cepat karena permintaan baru tidak diblokir di belakang yang lama.

Langkah berikut untuk membuat ini menjadi kebiasaan di seluruh endpoint dan tim:

  • Pilih timeout standar per jenis endpoint (search vs write vs export).
  • Wajibkan QueryContext dan ExecContext di code review.
  • Buat error timeout eksplisit di tepi (kode status jelas, pesan sederhana).
  • Tambahkan metrik untuk timeout dan pembatalan agar Anda melihat regresi lebih awal.
  • Tulis satu helper yang membungkus pembuatan context dan logging sehingga setiap handler berperilaku sama.

Jika Anda membangun layanan dan alat internal dengan AppMaster, Anda bisa menerapkan aturan timeout ini secara konsisten di layanan Go yang dihasilkan, integrasi API, dan dashboard di satu tempat. AppMaster tersedia di appmaster.io (no-code, dengan generasi source code Go nyata), sehingga dapat cocok praktis ketika Anda menginginkan penanganan permintaan dan observability yang konsisten tanpa membangun setiap alat admin dari awal.

FAQ

Apa arti permintaan “macet” di API Go?

Permintaan disebut “macet” ketika menunggu sesuatu yang tidak kembali, seperti kueri SQL yang lambat, koneksi pool yang terblokir, masalah DNS, atau layanan upstream yang tidak merespon. Di bawah beban, permintaan yang macet menumpuk, mengikat worker dan koneksi, dan bisa mengubah perlambatan kecil menjadi gangguan lebih luas.

Di mana sebaiknya saya menetapkan timeout: middleware, handler, atau lebih dalam kode?

Tetapkan deadline keseluruhan di batas HTTP dan teruskan ctx yang sama ke setiap lapisan yang bisa menunggu. Deadline bersama itulah yang mencegah beberapa operasi lambat menahan sumber daya cukup lama sehingga menyebabkan timeout di mana-mana.

Kenapa saya harus memanggil `cancel()` jika timeout akan terjadi juga?

Gunakan ctx, cancel := context.WithTimeout(r.Context(), d) dan selalu defer cancel() di handler (atau middleware). Pemanggilan cancel melepaskan timer dan membantu menghentikan penantian dengan cepat ketika permintaan selesai lebih awal.

Apa kesalahan terbesar yang membuat timeout tidak berguna?

Jangan menggantinya dengan context.Background() atau context.TODO() dalam kode permintaan, karena itu memutus pembatalan dan deadline. Jika Anda menjatuhkan context permintaan, pekerjaan downstream seperti SQL atau HTTP outbound dapat terus berjalan meskipun klien sudah pergi.

Bagaimana saya harus menangani `context deadline exceeded` vs `context canceled`?

Perlakukan context.DeadlineExceeded dan context.Canceled sebagai hasil kontrol normal dan teruskan mereka ke atas tanpa diubah. Di tepi sistem, peta mereka ke respons yang jelas (sering 504 untuk timeout) sehingga klien tidak melakukan retry secara membabi buta terhadap apa yang tampak seperti 500 acak.

Panggilan `database/sql` mana yang harus menggunakan context?

Gunakan metode yang sadar context di mana-mana: QueryContext, QueryRowContext, ExecContext, dan PrepareContext. Jika Anda memanggil Query() atau Exec() tanpa context, handler Anda bisa timeout tetapi panggilan database mungkin tetap memblokir goroutine dan menahan koneksi.

Apakah pembatalan context benar-benar menghentikan query PostgreSQL yang berjalan?

Banyak driver memang menghentikan query saat context dibatalkan, tetapi Anda harus memverifikasinya di stack Anda sendiri dengan menjalankan kueri lambat sengaja dan memastikan ia kembali cepat setelah deadline. Juga bijak untuk mengonfigurasi statement timeout di sisi database sebagai backstop jika suatu jalur kode lupa meneruskan ctx.

Bagaimana menerapkan deadline yang sama ke panggilan HTTP outbound?

Bangun request outbound dengan http.NewRequestWithContext(ctx, ...) sehingga deadline dan pembatalan yang sama mengalir otomatis. Selain itu, konfigurasikan client dan transport (timeout, dial, TLS handshake, response header) sebagai batas keras, karena context tidak melindungi jika seseorang tidak sengaja menggunakan background context atau terjadi stall di level lebih rendah.

Haruskah lapisan bawah (repo/service) membuat timeout sendiri?

Hindari membuat context baru yang memperpanjang anggaran waktu di lapisan bawah; child timeout sebaiknya lebih pendek, bukan lebih panjang. Jika permintaan punya sedikit waktu tersisa, lewati panggilan downstream yang opsional, kembalikan data parsial jika sesuai, atau gagal cepat dengan error yang jelas.

Apa yang harus saya pantau untuk membuktikan timeout end-to-end bekerja?

Pantau timeout dan pembatalan secara terpisah per endpoint dan dependency, bersama persentil latensi dan jumlah permintaan sedang berjalan. Di tracing, ikuti context yang sama dari handler ke panggilan outbound dan QueryContext sehingga Anda dapat melihat apakah waktu dihabiskan menunggu koneksi DB, menjalankan query, atau terblokir oleh layanan lain.

Mudah untuk memulai
Ciptakan sesuatu yang menakjubkan

Eksperimen dengan AppMaster dengan paket gratis.
Saat Anda siap, Anda dapat memilih langganan yang tepat.

Memulai