Fatura, etiket veya makbuz servisinizi Cloudflare Workers’a taşıdınız, çünkü stack’in geri kalanı zaten orada çalışıyor. Gecikme hesabı harika görünüyordu: en yakın colo’ya 5 ms, 1 ms CPU, request tamam.
Sonra PDF generation geldi. p99 800 ms oldu, worker bundle 50 MB uyarıları verdi ve yanlış runtime seçilmiş gibi hissettirdi. Çoğu zaman sorun Workers değil; long-lived server için tasarlanmış PDF stack’inin isolate runtime’a taşınması.
Workers Lambda değildir
Cloudflare Workers serverless container değildir. V8 isolates üzerinde çalışır ve sınırları farklıdır:
- CPU time limit: Free plan’da request başına 50 ms, Workers Paid Bundled’da 30 seconds, Unbound’da 5 minutes. Wall time daha uzun olabilir ama billable.
- Memory cap: isolate başına 128 MB.
- Bundle size: Free plan 1 MB, paid 10 MB.
- Filesystem yok.
fs.readFileSyncyok; her şey memory veya fetch. - Native binaries yok. Sadece pure JavaScript / WebAssembly;
node-canvas, native zlib veya Ghostscript shell-out yok. - Cold start yaklaşık 5 ms. Hızlı, çünkü başlatılacak büyük bir şey yok.
“Workers’ta PDF yavaş” şikayetlerinin çoğu bu sınırların birini, genellikle CPU veya bundle size’ı, ihlal eder.
Gerçekten yavaş olan beş şey
1. Chromium renderer’ı Workers’a sokmak
Bu çalışmaz. Puppeteer yaklaşık 250 MB Chromium ve gerçek OS ister. Cloudflare Browser Rendering API veya Browserless kullanılabilir, ama Workers değildir; Worker’ın çağırdığı ayrı bir servistir ve yaklaşık 500 ms round-trip + render time ekler.
“Worker tabanlı PDF” aslında “remote browser çağıran Worker” ise latency floor yaklaşık 500 ms’dir. Bu Worker sorunu değil, browser tax’tir.
Diagnosis: fetch("https://browser-rendering.cloudflare.com/...") veya benzeri call var mı bakın. Varsa ölçtüğünüz upstream latency.
2. Layout’u JavaScript’te yapmak
Box position ve text wrapping hesaplayan kendi JS layout engine’inizi yazdıysanız CPU limitine çarparsınız. JS hızlıdır, ama 30+ element ve wrapping içeren layout Workers Free’de kolayca 50 ms’yi aşar, Bundled’da 100-300 ms olabilir.
Bu pipeline pahalıdır:
JSON → JS layout pass → SVG generation → SVG-to-PDF library → emit
Aynı data üzerinde dört CPU-bound pass; hepsi JS, hepsi GC pressure ve intermediate tree allocation yaratır.
Diagnosis: Bir render için wrangler tail log’una bakın. I/O’dan önce 50 ms üstü CPU görüyorsanız bottleneck compute’tur.
3. Fontları her request’te yüklemek
Fontlar 50-250 KB’dir. Renderer her render’da KV / R2’den font okuyorsa, font başına request başına network round-trip ekler. Beş font, render başlamadan 50-150 ms demektir.
Diagnosis: Font-loading code’a timing ekleyin. p50’yi o yutuyorsa sorun budur.
Fix: Fontları request handler içinde değil, Worker dosyasının top level’ında module init sırasında bir kez yükleyin. Isolate ömrü boyunca bytes cache’te kalır.
// Module-init: runs ONCE per isolate
import notoSans from "./fonts/noto-sans.woff2";
const FONT_BYTES = new Uint8Array(notoSans);
export default {
async fetch(request) {
// Per-request: zero font I/O
return renderPdf({ fontBytes: FONT_BYTES, ...data });
}
};
Bundler fontu bytes olarak inline ediyorsa daha iyi: hiç I/O yok.
4. Workers için tasarlanmamış JS PDF library kullanmak
pdfkit, pdf-lib, jsPDF Workers’ta çalışır, ama bedeli vardır:
pdfkitNodeBuffershims ister. Çalışır, ama yaklaşık 500 KB ekler ve compute’u yaklaşık 30% yavaşlatabilir.pdf-libmevcut PDFs düzenlemek için iyidir; sıfırdan emit ederken abstraction her page’e yaklaşık 10 ms ekleyebilir.jsPDFbrowser-first’tür; aynı Buffer problemi ve tree-shake edilmesi zor geniş API surface taşır.
“JSON oku, PDF bytes yaz” pipeline’ında PDF’i doğrudan emit eden özel bir engine genelde 5-20 kat hızlıdır. Rust veya C++’tan WebAssembly’ye derlenen tight loop’lar bu işe uygundur.
5. Bundle gizlice 4 MB oldu
Workers Free bundle limitini 1 MB, Workers Bundled 10 MB olarak koyar. Birçok ekip bunu wrangler deploy “Script exceeds size limit” dediğinde öğrenir. Bazıları daha önce yavaş cold start olarak görür, çünkü V8 fazla code compile eder.
wrangler deploy output’ta bundle size gösterir. 500 KB üstü her şey incelenmeli:
- Bundled fonts. Workers Assets’e taşıyın ve module init’te bir kez fetch edin.
node:shim layer. Source map’te__cf_KVveyapolyfills:görünüyorsa bundler gereksiz Node APIs shimliyor olabilir.- Unused dependencies. Wrangler 4+ ile
npm run build -- --analyzetreemap verir.
Workers’ta hızlı PDF nasıl görünür
Structured documents için edge-native bir renderer, örneğin gPdf, tipik olarak şu aralıktadır:
| Metric | Typical | Why |
|---|---|---|
| Cold-start | 5-20 ms | V8 isolate boot + WASM module first-load |
| Per-render CPU | 1-4 ms | WASM tight loop, no GC pressure |
| Per-render wall | 3-8 ms | CPU + a few microseconds of crypto for PDF object IDs |
| Bundle size | 4-6 MB | Renderer + bundled fonts (Latin + CJK NotoSans) |
| Memory peak | 8-20 MB | Document tree + emitted PDF buffer |
Tipik “Worker remote Puppeteer/browser rendering çağırıyor” yoluyla karşılaştırın: p50 500-1000 ms, başka serviste 1-2 GB browser memory ve yaklaşık $0.001/render upstream cost.
Hızlı triage
Workers’ta PDF şu an yavaşsa önce şu listeyi geçin:
- Zaman nereye gidiyor?
wrangler tailiçine timestamps koyun. CPU, upstream fetch ve cold start’ı ayırın. - JS layout var mı? Varsa CPU’nun çoğu budur. Same-isolate renderer kullanın.
- Fontlar request başına mı yükleniyor? Module init’e taşıyın.
- External browser mı çağırıyorsunuz? Latency floor o servistir. Same-isolate rendering’e geçin.
- Bundle 1 MB üstü mü? Cold start bundle size ile büyür. Unused deps’i kesin.
Workers’taki en hızlı PDF, document data’nın tek bir fetch handler call içinde PDF bytes’a dönüştüğü, internal fetch() olmayan ve ağır JS layout taşımayan şekildir.
Kısa versiyon
Cloudflare Workers single-digit milliseconds içinde PDF render edebilir, ama renderer isolate-shaped runtime için tasarlanmışsa. Node için yazılmış JS PDF libs, remote browser services, request başına font loading ve JavaScript layout p50’yi origin’den bile yavaş yapabilir.
Bunu kendiniz yapmak istemiyorsanız gPdf Playground deneyin. Aynı edge runtime üzerinde çalışır. Render PDF düğmesine basın, Network tab’a bakın; pipeline runtime ile kavga etmediğinde Workers’ın hızı budur.