jskelet 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +127 -0
- package/CHANGELOG.md +40 -0
- package/LICENSE +21 -0
- package/README.md +342 -0
- package/bin/jskelet.mjs +104 -0
- package/docs/01-baslangic.md +285 -0
- package/docs/02-mimari.md +287 -0
- package/docs/03-routing.md +437 -0
- package/docs/04-render-ve-sablonlar.md +490 -0
- package/docs/05-islands.md +429 -0
- package/docs/06-cache.md +409 -0
- package/docs/07-yapilandirma.md +673 -0
- package/docs/08-build.md +366 -0
- package/docs/09-dev-araclari.md +302 -0
- package/docs/10-dagitim.md +329 -0
- package/docs/11-tasima.md +352 -0
- package/docs/README.md +82 -0
- package/package.json +97 -0
- package/src/build/build.mjs +138 -0
- package/src/build/ensure-build.mjs +15 -0
- package/src/build/paths.mjs +118 -0
- package/src/build/resolve-peer.mjs +36 -0
- package/src/build/tasks/client.mjs +268 -0
- package/src/build/tasks/css.mjs +124 -0
- package/src/build/tasks/fonts.mjs +146 -0
- package/src/build/tasks/icons.mjs +224 -0
- package/src/build/tasks/images.mjs +244 -0
- package/src/build/tasks/precompress.mjs +78 -0
- package/src/client/devtools/overlay.js +1763 -0
- package/src/client/devtools/report.html +185 -0
- package/src/client/devtools/report.js +712 -0
- package/src/client/dom.js +95 -0
- package/src/client/index.js +26 -0
- package/src/client/registry.js +223 -0
- package/src/client/safe-image.js +91 -0
- package/src/client/store.js +36 -0
- package/src/config/defaults.js +102 -0
- package/src/config/index.js +433 -0
- package/src/config/pattern.js +107 -0
- package/src/dev-server.mjs +383 -0
- package/src/http/control-flow.js +56 -0
- package/src/http/request-cache.js +46 -0
- package/src/index.js +35 -0
- package/src/init.mjs +220 -0
- package/src/log.mjs +332 -0
- package/src/logo.png +0 -0
- package/src/runtime/alias-hooks.mjs +119 -0
- package/src/runtime/register.mjs +4 -0
- package/src/server/assets.js +119 -0
- package/src/server/create-app.js +167 -0
- package/src/server/dev/devtools.js +383 -0
- package/src/server/dev/report.js +351 -0
- package/src/server/head-hints.js +132 -0
- package/src/server/html-cache.js +166 -0
- package/src/server/metadata.js +102 -0
- package/src/server/middleware/compression.js +205 -0
- package/src/server/middleware/dev-gate.js +62 -0
- package/src/server/middleware/headers.js +37 -0
- package/src/server/middleware/redirects.js +32 -0
- package/src/server/middleware/static-precompressed.js +100 -0
- package/src/server/middleware/upstream-proxy.js +141 -0
- package/src/server/prewarm.js +283 -0
- package/src/server/render.js +356 -0
- package/src/server/router.js +121 -0
- package/src/server/status-page.js +164 -0
- package/src/server/upstream-tracking.js +51 -0
- package/src/start.mjs +7 -0
- package/src/templates/layout.ejs +44 -0
- package/src/version.mjs +17 -0
- package/src/views/components/loader.js +85 -0
- package/src/views/helpers/html.js +102 -0
- package/src/views/helpers/tags.js +193 -0
package/docs/06-cache.md
ADDED
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
# 06 — Önbellek ve prewarm
|
|
2
|
+
|
|
3
|
+
Bu belge JSkelet'in ISR ikamesini bütün ayrıntılarıyla anlatır: HTML TTL
|
|
4
|
+
önbelleği ve stale-while-revalidate davranışı, `revalidate`'in nereden geldiği,
|
|
5
|
+
cache anahtarının nasıl kurulduğu, `X-JSkelet-Cache` başlığının değerleri,
|
|
6
|
+
sıkıştırılmış gövdenin neden önbellekte durduğu, istek içi memoizasyon
|
|
7
|
+
(`withRequestCache` / `cache()`), upstream hatalarının önbelleği nasıl
|
|
8
|
+
etkilediği (`reportUpstreamFailure`) ve sunucu açılışındaki ısıtma turu.
|
|
9
|
+
Kararların arkasındaki ölçüm gerekçeleri [02-mimari.md](./02-mimari.md)'de,
|
|
10
|
+
config alanlarının tam referansı [07-yapilandirma.md](./07-yapilandirma.md)'de.
|
|
11
|
+
|
|
12
|
+
## Genel resim
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
route(controller, { revalidate })
|
|
16
|
+
└─ withHtmlCache(key, ttl, producer) ← TTL + stale-while-revalidate
|
|
17
|
+
└─ withUpstreamTracking(...) ← eksik veri tespiti
|
|
18
|
+
└─ withRequestCache(...) ← istek içi memoizasyon
|
|
19
|
+
└─ produce() → controller + renderPage
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Sıra önemli: **istek içi cache en içte** olmalı ki aynı render'daki iki çağrı
|
|
23
|
+
tek upstream isteğine düşsün; **upstream takibi HTML cache'in içinde** olmalı ki
|
|
24
|
+
eksik veriyle üretilen çıktı önbelleğe yazılmasın.
|
|
25
|
+
|
|
26
|
+
## `revalidate` — TTL nereden gelir
|
|
27
|
+
|
|
28
|
+
Bir route'un TTL'i iki kaynaktan gelebilir ve **config kazanır**:
|
|
29
|
+
|
|
30
|
+
1. `route(controller, { revalidate: 60 })` — route'un kendi süresi.
|
|
31
|
+
2. `jskelet.config.mjs` → `cache().html` içindeki eşleşen desen. Varsa
|
|
32
|
+
route'unkini ezer.
|
|
33
|
+
|
|
34
|
+
```js
|
|
35
|
+
// jskelet.config.mjs
|
|
36
|
+
export default {
|
|
37
|
+
async cache() {
|
|
38
|
+
return {
|
|
39
|
+
html: {
|
|
40
|
+
"/": 60,
|
|
41
|
+
"/haber/:slug": 300,
|
|
42
|
+
"/etiket/:slug": 120,
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Config ile ezme, tek bir dosyadan tüm sitenin tazelik profilini ayarlamayı
|
|
50
|
+
mümkün kılar; route dosyalarını dolaşmak gerekmez.
|
|
51
|
+
|
|
52
|
+
Çözüm sonucu **yol başına hatırlanır**, böylece her istekte desen taraması
|
|
53
|
+
yapılmaz. Hiç `cache().html` kuralı yoksa doğrudan route'un değeri kullanılır.
|
|
54
|
+
|
|
55
|
+
`revalidate` verilmemişse ya da 0 ise sayfa **hiç önbelleklenmez**: her istek
|
|
56
|
+
render edilir ve yanıta `Cache-Control` yazılmaz (yalnızca
|
|
57
|
+
`X-JSkelet-Cache: MISS`).
|
|
58
|
+
|
|
59
|
+
Önbellek ayrıca yalnızca `GET` istekleri için devreye girer.
|
|
60
|
+
|
|
61
|
+
## Cache anahtarı
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
`${req.path}?${new URLSearchParams(query).toString()}`
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Yani yol **ve tüm query parametreleri** anahtarın parçasıdır. `/liste?sayfa=2`
|
|
68
|
+
ile `/liste?sayfa=3` ayrı girdilerdir.
|
|
69
|
+
|
|
70
|
+
Bunun pratik sonucu: query string'e bağlı olmayan bir sayfa, farklı kampanya
|
|
71
|
+
parametreleriyle (`?utm_source=…`) çağrıldığında her kombinasyon için ayrı bir
|
|
72
|
+
girdi üretir. Bu tür parametreleri ters proxy katmanında temizlemek ya da
|
|
73
|
+
önbelleği kapatmak (`revalidate` vermemek) makul bir önlemdir; store en fazla
|
|
74
|
+
500 girdi tutar ve LRU ile en eskiyi düşürür.
|
|
75
|
+
|
|
76
|
+
## Stale-while-revalidate
|
|
77
|
+
|
|
78
|
+
Girdi yapısı:
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
expiresAt = now + ttl
|
|
82
|
+
staleUntil = now + ttl * 2 (STALE_FACTOR = 1)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Okuma davranışı:
|
|
86
|
+
|
|
87
|
+
| Durum | Yanıt | Arka plan |
|
|
88
|
+
| --- | --- | --- |
|
|
89
|
+
| `now < expiresAt` | Önbellekteki HTML, `HIT` | — |
|
|
90
|
+
| `expiresAt ≤ now < staleUntil` | Önbellekteki HTML **anında**, `STALE` | Tazeleme başlatılır |
|
|
91
|
+
| `now ≥ staleUntil` | Girdi silinir, taze render, `MISS` | — |
|
|
92
|
+
|
|
93
|
+
Stale penceresinde tazelemenin hatası isteği etkilemez: eski HTML pencere
|
|
94
|
+
boyunca geçerli kalır ve hata yalnızca loglanır
|
|
95
|
+
(`[html-cache] arka plan tazelemesi başarısız: …`).
|
|
96
|
+
|
|
97
|
+
Aynı anahtar için eşzamanlı tazelemeler tek bir çalışmada birleştirilir
|
|
98
|
+
(`inflight` haritası): yüz eşzamanlı istek tek render'a düşer.
|
|
99
|
+
|
|
100
|
+
Kazanç: ilk ısıtmadan sonra hiçbir istek render'ı beklemez. Bedel: HTML'deki
|
|
101
|
+
veri en fazla `revalidate + bir tazeleme turu` kadar geride olabilir. Bu bedel
|
|
102
|
+
kabul edilebilir, çünkü fiyat gibi canlı alanlar istemcide WebSocket'ten
|
|
103
|
+
güncelleniyor.
|
|
104
|
+
|
|
105
|
+
Store LRU'dur: erişilen girdi sona taşınır, `MAX_ENTRIES = 500` aşılınca en
|
|
106
|
+
eski düşürülür.
|
|
107
|
+
|
|
108
|
+
## Ne önbelleğe yazılır
|
|
109
|
+
|
|
110
|
+
Yalnızca şu iki koşulun **ikisini** birlikte sağlayan çıktı saklanır:
|
|
111
|
+
|
|
112
|
+
1. `status === 200`
|
|
113
|
+
2. `degraded !== true` — render sırasında geçici bir upstream hatası
|
|
114
|
+
bildirilmemiş.
|
|
115
|
+
|
|
116
|
+
Yani 404 sayfaları, redirect'ler ve eksik veriyle üretilmiş HTML önbelleğe
|
|
117
|
+
girmez.
|
|
118
|
+
|
|
119
|
+
## Yanıt başlıkları
|
|
120
|
+
|
|
121
|
+
`route()` her yanıta `X-JSkelet-Cache` yazar (başlık adı `brand.cacheHeader`
|
|
122
|
+
ile değiştirilebilir):
|
|
123
|
+
|
|
124
|
+
| Değer | Anlamı |
|
|
125
|
+
| --- | --- |
|
|
126
|
+
| `HIT` | Önbellekten, taze |
|
|
127
|
+
| `STALE` | Önbellekten, süresi geçmiş; arkada tazeleniyor |
|
|
128
|
+
| `MISS` | Bu istekte render edildi (ya da önbellek kapalı) |
|
|
129
|
+
|
|
130
|
+
Önbelleklenebilir yanıtlarda ayrıca:
|
|
131
|
+
|
|
132
|
+
```
|
|
133
|
+
Cache-Control: public, max-age=0, s-maxage=<revalidate>, stale-while-revalidate=60
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
`max-age=0` tarayıcıda saklamayı kapatır, `s-maxage` ara katmanlara (CDN, ters
|
|
137
|
+
proxy) süreyi bildirir. Böylece CDN önünde durduğunda aynı tazelik modeli iki
|
|
138
|
+
katmanda birlikte çalışır.
|
|
139
|
+
|
|
140
|
+
## Sıkıştırılmış gövdenin saklanması
|
|
141
|
+
|
|
142
|
+
Önbelleğe alınan her girdi bir `encoded` haritası taşır ve HTML ile aynı ömrü
|
|
143
|
+
paylaşır. Bir sayfa ilk kez brotli ya da gzip ile istendiğinde çıktı hesaplanıp
|
|
144
|
+
haritaya konur; sonraki isteklerde aynı buffer gönderilir. Aynı sayfa her
|
|
145
|
+
istekte yeniden brotli'lenmez.
|
|
146
|
+
|
|
147
|
+
Bu yolda `Content-Encoding`, `Vary` ve `Content-Length` doğrudan `route()`
|
|
148
|
+
tarafından yazılır; sıkıştırma middleware'i `Content-Encoding` gördüğü için
|
|
149
|
+
devreye girmez.
|
|
150
|
+
|
|
151
|
+
`HEAD` istekleri sıkıştırılmaz (gövde yok). İstemci ne brotli ne gzip kabul
|
|
152
|
+
ediyorsa düz HTML gönderilir.
|
|
153
|
+
|
|
154
|
+
## İstek içi memoizasyon: `cache()`
|
|
155
|
+
|
|
156
|
+
React'in `cache()` fonksiyonunun karşılığı: aynı istek içinde aynı argümanlarla
|
|
157
|
+
yapılan çağrılar tek kez çalışır.
|
|
158
|
+
|
|
159
|
+
```js
|
|
160
|
+
// lib/api/articles.js
|
|
161
|
+
import { cache } from "jskelet";
|
|
162
|
+
|
|
163
|
+
export const getArticle = cache(async (slug) => {
|
|
164
|
+
const response = await fetch(`${process.env.API_ORIGIN}/articles/${slug}`);
|
|
165
|
+
return response.json();
|
|
166
|
+
});
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Artık aynı render'da hem controller hem `hooks.layoutContext()` aynı yazıyı
|
|
170
|
+
isterse tek upstream isteği yapılır.
|
|
171
|
+
|
|
172
|
+
Ayrıntılar:
|
|
173
|
+
|
|
174
|
+
- Bağlam `AsyncLocalStorage` ile taşınır ve `route()` içinde
|
|
175
|
+
`withRequestCache()` tarafından kurulur.
|
|
176
|
+
- **Bağlam yoksa memoizasyon devre dışı kalır** ve fonksiyon doğrudan çağrılır.
|
|
177
|
+
Bir script'ten ya da başka bir sürecin içinden çağırmak güvenlidir.
|
|
178
|
+
- Anahtar `JSON.stringify(args)`; argümansız çağrılar `""` anahtarını
|
|
179
|
+
paylaşır. Serileştirilemeyen argümanlar (fonksiyon, `Symbol`, döngüsel nesne)
|
|
180
|
+
ile kullanmayın.
|
|
181
|
+
- Saklanan şey fonksiyonun **dönüş değeridir**, yani `async` fonksiyonlarda
|
|
182
|
+
Promise'in kendisi. Aynı Promise paylaşıldığı için eşzamanlı çağrılar da
|
|
183
|
+
birleşir.
|
|
184
|
+
- `withRequestCache(run)` dışa açıktır; `route()` dışında (ör. kendi yazdığınız
|
|
185
|
+
bir Express handler'ında) aynı kapsamı kurmak için kullanılabilir.
|
|
186
|
+
|
|
187
|
+
## Degraded render: `reportUpstreamFailure`
|
|
188
|
+
|
|
189
|
+
Render sırasında upstream düştüyse çıktı eksik veri içeriyor demektir. Böyle bir
|
|
190
|
+
HTML'i tüm TTL boyunca servis etmek yerine önbelleğe **hiç yazmamak** doğru
|
|
191
|
+
davranış: sonraki istek yeniden dener.
|
|
192
|
+
|
|
193
|
+
Bağımlılık yönü bilinçli olarak tersine çevrilmiştir: framework veri katmanını
|
|
194
|
+
tanımaz, veri katmanı framework'e haber verir. Hiç çağıran olmazsa maliyet boş
|
|
195
|
+
bir dizidir.
|
|
196
|
+
|
|
197
|
+
```js
|
|
198
|
+
// lib/api/client.js
|
|
199
|
+
import { reportUpstreamFailure } from "jskelet";
|
|
200
|
+
|
|
201
|
+
export async function apiGet(path) {
|
|
202
|
+
try {
|
|
203
|
+
const response = await fetch(`${process.env.API_ORIGIN}${path}`);
|
|
204
|
+
|
|
205
|
+
if (!response.ok) {
|
|
206
|
+
reportUpstreamFailure({ status: response.status, path });
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return response.json();
|
|
211
|
+
} catch (error) {
|
|
212
|
+
// Yanıt hiç gelmedi: status 0 ağ hatası anlamına gelir.
|
|
213
|
+
reportUpstreamFailure({ status: 0, path });
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Geçici ve kalıcı hata ayrımı
|
|
220
|
+
|
|
221
|
+
| Durum | Sayılır | Sonuç |
|
|
222
|
+
| --- | --- | --- |
|
|
223
|
+
| `0` (ağ hatası), `408`, `425`, `429`, `>= 500` | **Geçici** | Sayfa önbelleğe yazılmaz, uyarı: `[render] <yol> eksik veriyle üretildi, önbelleğe alınmıyor (…)` |
|
|
224
|
+
| Diğerleri (`400`, `403`, `404`, …) | **Kalıcı** | Yalnızca uyarı: `[render] <yol> eksik veriyle üretildi, upstream kalıcı hata veriyor (…)`. Önbellek engellenmez. |
|
|
225
|
+
|
|
226
|
+
Kalıcı hataların önbelleği engellememesi bilinçli: deterministik cevaplar tekrar
|
|
227
|
+
denemekle düzelmez. Onlar yüzünden önbelleği kapatmak sayfayı her ziyarette
|
|
228
|
+
baştan render etmek olur — içerik yine aynı eksik hâliyle döner, ziyaretçi
|
|
229
|
+
sadece render süresini öder.
|
|
230
|
+
|
|
231
|
+
## Önbelleği yönetmek
|
|
232
|
+
|
|
233
|
+
`jskelet` şu fonksiyonları dışa açar:
|
|
234
|
+
|
|
235
|
+
| Fonksiyon | Ne yapar |
|
|
236
|
+
| --- | --- |
|
|
237
|
+
| `withHtmlCache(key, ttlSeconds, producer)` | Önbelleği doğrudan kullanmak için. `ttlSeconds` 0 ise producer her zaman çalışır. |
|
|
238
|
+
| `clearHtmlCache()` | Store'u tamamen boşaltır. |
|
|
239
|
+
| `getHtmlCacheSize()` | Girdi sayısı. |
|
|
240
|
+
| `getHtmlCacheEntries()` | Döküm: `{ key, bytes, status, stale, expiresIn, encodings }`. HTML gövdesi dönmez, yalnızca boyutu. |
|
|
241
|
+
|
|
242
|
+
Bir yönetim ucu yazmak için:
|
|
243
|
+
|
|
244
|
+
```js
|
|
245
|
+
import { clearHtmlCache, getHtmlCacheEntries } from "jskelet";
|
|
246
|
+
|
|
247
|
+
export default function register(app) {
|
|
248
|
+
app.post("/_admin/cache/temizle", (req, res) => {
|
|
249
|
+
if (req.headers["x-admin-token"] !== process.env.ADMIN_TOKEN) {
|
|
250
|
+
res.status(404).end();
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
clearHtmlCache();
|
|
254
|
+
res.json({ ok: true });
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
app.get("/_admin/cache", (req, res) => {
|
|
258
|
+
res.json(getHtmlCacheEntries());
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
Dev sunucusu ayrıca manifest her değiştiğinde önbelleği kendiliğinden temizler:
|
|
264
|
+
saklanan HTML eski hash'li varlık URL'lerini taşıyor olur ve temizlenmezse sayfa
|
|
265
|
+
silinmiş dosyayı istemeye devam eder ([09-dev-araclari.md](./09-dev-araclari.md)).
|
|
266
|
+
|
|
267
|
+
Önbellek süreç belleğinde yaşadığı için birden fazla süreç/kopya çalıştırıyorsanız
|
|
268
|
+
her birinin kendi önbelleği olur; `clearHtmlCache()` yalnızca çağrıldığı süreci
|
|
269
|
+
etkiler.
|
|
270
|
+
|
|
271
|
+
## Prewarm — açılışta ısıtma
|
|
272
|
+
|
|
273
|
+
Next'teki build-time prerender'ın karşılığı, ama çıktı diske yazılmaz: önbellek
|
|
274
|
+
süreç belleğinde yaşadığı için ısıtma da süreç ayağa kalkınca yapılır. Kazanç
|
|
275
|
+
aynı — ilk ziyaretçi soğuk render'ı beklemez — fakat veri dondurulmaz; her girdi
|
|
276
|
+
route'un `revalidate` süresiyle yaşlanır ve stale-while-revalidate ile arkada
|
|
277
|
+
tazelenir.
|
|
278
|
+
|
|
279
|
+
Isıtma **gerçek HTTP istekleriyle** yapılır (`http://127.0.0.1:<port>`), çünkü
|
|
280
|
+
cache anahtarı, sıkıştırma ve middleware zinciri normal trafikle bire bir aynı
|
|
281
|
+
olsun.
|
|
282
|
+
|
|
283
|
+
### `hooks.prewarmPaths()`
|
|
284
|
+
|
|
285
|
+
Hangi yolların ısıtılacağını uygulama bildirir; genelde sitemap üreten
|
|
286
|
+
fonksiyonun aynısıdır.
|
|
287
|
+
|
|
288
|
+
```js
|
|
289
|
+
// jskelet.config.mjs
|
|
290
|
+
export default {
|
|
291
|
+
hooks: {
|
|
292
|
+
async prewarmPaths() {
|
|
293
|
+
const slugs = await getAllArticleSlugs();
|
|
294
|
+
return ["/", "/piyasalar", ...slugs.map((slug) => `/haber/${slug}`)];
|
|
295
|
+
},
|
|
296
|
+
},
|
|
297
|
+
};
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
Kurallar:
|
|
301
|
+
|
|
302
|
+
- Dizi döndürmezse uyarı basılır ve ısıtma yapılmaz.
|
|
303
|
+
- Yalnızca `/` ile başlayan string'ler alınır.
|
|
304
|
+
- `prewarmSkip` öneklerinden biriyle başlayanlar atlanır. Varsayılan liste:
|
|
305
|
+
`/api/`, `/_fragment/`, `/__jskelet/`. Oturuma bağlı sayfalar ısıtılmamalı.
|
|
306
|
+
- Tekilleştirme **sırayı korur**: liste `PREWARM_MAX` ile budandığı için
|
|
307
|
+
uygulamanın verdiği öncelik sırası anlamlıdır — en önemli sayfaları başa
|
|
308
|
+
koyun.
|
|
309
|
+
- Bu hook tanımlı değilse ısıtma hiç kurulmaz; zamanlayıcı bile açılmaz.
|
|
310
|
+
|
|
311
|
+
### Tur mantığı
|
|
312
|
+
|
|
313
|
+
1. Liste toplanır, `PREWARM_MAX` (varsayılan 400) ile budanır.
|
|
314
|
+
2. `PREWARM_CONCURRENCY` işçi paralel olarak istek atar (prod'da 4, dev'de 2).
|
|
315
|
+
Dev'de daha az paralellik: tarama, o an tarayıcıda açtığın sayfanın
|
|
316
|
+
render'ıyla CPU için yarışmasın.
|
|
317
|
+
3. Başarısız yollar için **tek seri tekrar turu** yapılır (`concurrency: 1`).
|
|
318
|
+
Hatalar çoğunlukla upstream rate limit'i (429): ilk tur yüzlerce sayfayı aynı
|
|
319
|
+
anda çekerken API'yi zorluyor. Tekrar turu bu sayfaların önbelleğe girmesini
|
|
320
|
+
sağlıyor; aksi hâlde ziyaretçi soğuk render'ı öder.
|
|
321
|
+
4. Özet loglanır:
|
|
322
|
+
`[prewarm] 128/130 sayfa ısıtıldı, 2 hata, 5 sayfa tekrar turunda kurtarıldı (12.4s)`
|
|
323
|
+
|
|
324
|
+
İstekler `user-agent: jskelet-prewarm` (`brand.prewarmUserAgent`) ve
|
|
325
|
+
`accept-encoding: br, gzip` başlıklarıyla gider; ikincisi sıkıştırılmış gövdenin
|
|
326
|
+
de önbelleğe girmesi için.
|
|
327
|
+
|
|
328
|
+
`DEV_TOKEN` ayarlıysa ısıtma token'ı çerez olarak taşır; yoksa dev gate tüm
|
|
329
|
+
sayfalara 404 döner ve önbellek hiç dolmaz.
|
|
330
|
+
|
|
331
|
+
Dev panelindeki istek listesi ve terminal, `prewarmUserAgent` taşıyan istekleri
|
|
332
|
+
filtreler: yüzlerce ısıtma isteği görünümü doldurmasın. İlerleme baloncuğun
|
|
333
|
+
yanındaki rozette görünür.
|
|
334
|
+
|
|
335
|
+
### Zamanlama
|
|
336
|
+
|
|
337
|
+
- Isıtma açılışta **gecikmeyle** başlar: ilk gerçek isteklerle yarışmasın.
|
|
338
|
+
Varsayılan gecikme prod'da 500 ms, dev'de 3000 ms. Dev'de daha uzun, çünkü
|
|
339
|
+
dosya kaydı süreci yeniden başlattığı için zamanlayıcı da ölür; yalnızca
|
|
340
|
+
sunucu bir süre sakin kalınca ısınır.
|
|
341
|
+
- `PREWARM_INTERVAL_SECONDS` / `cache().prewarm.intervalSeconds` > 0 ise tur
|
|
342
|
+
periyodik tekrarlanır. Girdiler `revalidate` ile yaşlandığı ve
|
|
343
|
+
stale-while-revalidate sayesinde ziyaretçi beklemediği için bu **opsiyoneldir**;
|
|
344
|
+
hiç ziyaret edilmeyen sayfaları da sıcak tutmak isteyen kurulumlar için.
|
|
345
|
+
- Tüm zamanlayıcılar `unref()` edilmiştir: süreç kapanışını geciktirmezler.
|
|
346
|
+
- Hiçbir ısıtma hatası süreci düşürmez.
|
|
347
|
+
|
|
348
|
+
### Ayarlar
|
|
349
|
+
|
|
350
|
+
Öncelik sırası: **ortam değişkeni → config → kod varsayılanı.** Env önde, çünkü
|
|
351
|
+
tek seferlik deneyler config'i düzenlemeden yapılabilsin.
|
|
352
|
+
|
|
353
|
+
| Ayar | Env | `cache().prewarm` | Varsayılan |
|
|
354
|
+
| --- | --- | --- | --- |
|
|
355
|
+
| Açık/kapalı | `PREWARM=0` kapatır, `PREWARM=1` config'i ezip açar | `enabled` | `true` |
|
|
356
|
+
| En fazla yol | `PREWARM_MAX` | `max` | `400` |
|
|
357
|
+
| Paralellik | `PREWARM_CONCURRENCY` | `concurrency` | prod 4, dev 2 |
|
|
358
|
+
| Başlangıç gecikmesi (ms) | `PREWARM_DELAY_MS` | `delayMs` | prod 500, dev 3000 |
|
|
359
|
+
| Periyot (saniye) | `PREWARM_INTERVAL_SECONDS` | `intervalSeconds` | `0` (kapalı) |
|
|
360
|
+
|
|
361
|
+
Sayısal ayarlar yalnızca **pozitif ve sonlu** değer kabul eder; geçersiz bir
|
|
362
|
+
değer sessizce bir sonraki katmana düşer.
|
|
363
|
+
|
|
364
|
+
### Elle tetikleme
|
|
365
|
+
|
|
366
|
+
```js
|
|
367
|
+
import { prewarm, prewarmProgress } from "jskelet";
|
|
368
|
+
|
|
369
|
+
await prewarm({ origin: "http://127.0.0.1:3000" }); // hook'tan yollar
|
|
370
|
+
await prewarm({ origin, paths: ["/", "/piyasalar"] }); // yalnızca bu yollar
|
|
371
|
+
await prewarm({ origin, quiet: true }); // özet basmadan
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
`paths` verilirse hook hiç çağrılmaz. Dönüş değeri
|
|
375
|
+
`{ ok, failed, total, elapsed }`.
|
|
376
|
+
|
|
377
|
+
`prewarmProgress` canlı durumu tutar ve dev paneli bunu okur:
|
|
378
|
+
|
|
379
|
+
```js
|
|
380
|
+
{
|
|
381
|
+
active, done, total, ok, failed, startedAt, finishedAt,
|
|
382
|
+
entries: [{ path, status, ms, bytes, cache, error }],
|
|
383
|
+
}
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
`entries` içindeki `cache` alanı o yolun `X-JSkelet-Cache` yanıtıdır; ısıtma
|
|
387
|
+
turunun gerçekten `MISS` → önbellek doldurup doldurmadığını buradan görürsünüz.
|
|
388
|
+
|
|
389
|
+
## Teşhis: sık görülen durumlar
|
|
390
|
+
|
|
391
|
+
- **Her istek `MISS` dönüyor.** Route'a `revalidate` verilmemiş ya da
|
|
392
|
+
`cache().html` içindeki desen 0 saniye veriyor. Veya sayfa `status: 200`
|
|
393
|
+
dışında bir kod dönüyor.
|
|
394
|
+
- **Sayfa `MISS` dönüyor ama upstream sağlam.** Geçici bir upstream hatası
|
|
395
|
+
bildirilmiş olabilir; logda `eksik veriyle üretildi, önbelleğe alınmıyor`
|
|
396
|
+
satırını arayın.
|
|
397
|
+
- **Sürekli eski veri.** `revalidate` çok yüksek; unutmayın ki gerçek gecikme en
|
|
398
|
+
fazla `revalidate` + bir tazeleme turudur.
|
|
399
|
+
- **Önbellek şişiyor.** Query parametreleri anahtara girdiği için kampanya
|
|
400
|
+
parametreleri girdi çoğaltıyor olabilir.
|
|
401
|
+
- **Isıtma hiç çalışmıyor.** `hooks.prewarmPaths` tanımlı değil, `PREWARM=0`
|
|
402
|
+
ayarlı ya da `cache().prewarm.enabled === false`.
|
|
403
|
+
|
|
404
|
+
## Sırada ne var
|
|
405
|
+
|
|
406
|
+
- Config alanlarının tam referansı ve env tablosu:
|
|
407
|
+
[07-yapilandirma.md](./07-yapilandirma.md)
|
|
408
|
+
- Önbelleği dev panelinden izlemek: [09-dev-araclari.md](./09-dev-araclari.md)
|
|
409
|
+
- CDN/ters proxy ile birlikte kullanım: [10-dagitim.md](./10-dagitim.md)
|