fetch() di bun bisa bikin production mati perlahan
ENfetch() di bun bisa bikin production mati perlahan
ini cerita beberapa hari debugging fetch timeout di production. kalau kamu jalanin bun 1.3.x di docker dan ada outgoing HTTPS calls, semoga ini ngebantu
konteks awal
API server jalan di docker swarm, multiple replicas di belakang nginx. app-nya bikin outgoing HTTPS ke beberapa external services, ada yang di cloud run, push notification, SMS gateway
awalnya pakai bun 1.2.18 dengan compiled binary (bun build —compile). secara fungsional aman, fetch ke external services gak ada masalah
tapi bun 1.2.18 punya bug GC HeapHelper yang bikin tiap process makan CPU ~28-50% walaupun idle. kalau jalanin 5 replicas berarti 150-250% CPU cuma buat garbage collector doing nothing
akhirnya upgrade ke bun 1.3.13 buat fix GC CPU issue. CPU langsung turun
ini CPU dan memory waktu masih di bun 1.2.18

gejalanya
setelah upgrade ke bun 1.3.13, outgoing fetch ke external API mulai timeout di exactly 15 detik. gak selalu, intermittent. kadang 50% request timeout, kadang 0%
yang bikin susah, cuma kejadian under real traffic. semua isolated test aman
perjalanan debug
service pihak ketiga yang lambat
insting pertama, external API-nya yang lambat. tapi curl dari container yang sama? 65-200ms. selalu. jadi bukan service-nya
DNS-nya bermasalah
cari di bun issues, nemu oven-sh/bun#10731 soal c-ares DNS. udah coba:
- BUN_DNS_RESOLVER=getaddrinfo pas build
- setDefaultResultOrder(‘ipv4first’) di code
- disable IPv6 pakai sysctl
DNS dari container aman (1-20ms). gak ada yang fix
compiled binary-nya beda
kita pakai bun build —compile. install bun runtime di container yang sama dan test secara isolated, hasilnya aman. ganti ke runtime mode
tapi under real traffic tetep kejadian
connection pooling-nya bocor
nemu oven-sh/bun#9034 soal keep-alive reuse dead socket. tambahin keepalive: false di semua fetch
gak fix
IPv6-nya bermasalah
nemu cloud run target gak punya AAAA record. test curl IPv6 hasilnya HTTP 000, 1-6 detik. bun coba IPv6 duluan!
disable IPv6, tambahin ipv4first. timeout turun dari 50% ke ~5%. tapi masih ada
perlu HTTP/2
nemu oven-sh/bun#13586, maintainer bun confirm fetch pakai HTTP/1.1, beberapa server respond lebih cepat ke HTTP/2. coba {protocol: “http2”} (experimental di 1.3.14)
isolated test mantap. under load masih timeout. fitur experimental belum stabil
breakthrough
install bun runtime di container dan jalanin comparison test:
// bun fetch()
const r = await fetch(url, { headers })
// vs node:https
const r = await httpsRequest(url, { headers })
keduanya identik di isolated test. tapi under real traffic:
| client | isolated | under load |
|---|---|---|
| fetch() | 65ms | 15,000ms timeout |
| node:https | 55ms | 55ms |
| curl | 65ms | 65ms |
node:https dan curl gak terpengaruh concurrent load sama sekali. cuma bun fetch() yang degradasi
ini outgoing dan incoming latency waktu masalah terjadi:


yang menarik, outgoing mentok 15 detik tapi incoming P99 cuma keliatan ~3-4 detik. padahal request yang trigger outgoing fetch harusnya juga 15 detik. alasannya karena histogram bucket incoming (dari hono-prometheus) cuma sampai 10 detik, setelah itu langsung +Inf. histogram_quantile interpolate antara 10s dan infinity, hasilnya gak akurat. sementara outgoing histogram punya bucket sampai 15 detik (kita define sendiri), jadi bisa nunjukin angka sebenarnya
pattern-nya
degradasinya progresif, bukan tiba-tiba:
- 0-10 menit: ~100ms (normal)
- 10-20 menit: ~2-4s (mulai naik)
- 20-30 menit: ~8-15s (timeout)
dan kena semua external HTTPS yang pakai fetch(). yang gak kena cuma internal HTTP calls dan worker process yang sequential tanpa Bun.serve()
point terakhir itu kunci. bun version sama, fetch() sama, docker container sama, tapi worker (yang proses job sequential, tanpa Bun.serve()) gak pernah kena
root cause
bun 1.3.x punya regresi di implementasi native fetch() saat dipakai buat outgoing HTTPS under concurrent Bun.serve() incoming load. internal TLS connection pool degradasi seiring waktu
gak kejadian di:
- node:https (networking stack beda di bun)
- curl (completely separate)
- bun 1.2.18 (internals fetch beda)
- sequential processing (tanpa concurrent Bun.serve())
fix-nya
ganti fetch() ke node:https buat external HTTPS calls:
import https from 'node:https'
function httpsRequest(url: string, options: RequestOptions): Promise<HttpResponse> {
return new Promise((resolve, reject) => {
const u = new URL(url)
const req = https.request({
hostname: u.hostname,
path: u.pathname + u.search,
method: options.method || 'GET',
headers: options.headers || {},
family: 4,
}, (res) => {
let data = ''
res.on('data', (chunk) => data += chunk)
res.on('end', () => resolve({ status: res.statusCode || 0, data }))
})
req.on('error', reject)
req.setTimeout(options.timeout || 15000, () => {
req.destroy(new Error('Request timeout'))
})
if (options.body) req.write(options.body)
req.end()
})
}
setelah deploy, external API calls dari 15s timeout jadi konsisten 50-200ms. zero degradasi over time
after (pakai node:https):


yang aku pelajarin
-
isolated test bohong. masalahnya cuma muncul under real concurrent load. mau test bun script.js berapa kalipun gak bakal reproduce
-
curl dari container buktiin network aman. kalau curl jalan tapi runtime gak, masalahnya di runtime
-
bandingin worker vs API itu breakthrough. code sama, container sama, concurrency model beda, kalau satu jalan dan satu gak, itu concurrency bug
-
node:https itu escape hatch di bun. bun implement Node.js API pakai code path beda dari native fetch(). kalau satu rusak, yang lain bisa jalan
-
monitor outgoing latency terpisah. kita punya prometheus histogram buat outgoing calls. tanpa ini, gak bakal notice degradasi progresifnya
bun issues yang related:
- oven-sh/bun#9034 networking performance jelek dengan connection reuse
- oven-sh/bun#13586 fetch delay (HTTP/1.1 vs HTTP/2)
- oven-sh/bun#17525 fetch hang kalau bun.serve jalan
- oven-sh/bun#7260 fetch hang random di HTTPS
kalau kamu kena masalah serupa, coba node:https dulu sebelum nyalahin infra