Repository navigation
Cache and Performance
SPACK 的性能设计主要依赖 prepared snapshot、request-path fast path、small-file memory cache、sendfile 和清晰的 cache policy。
SPACK performance is built around prepared snapshots, request-path fast paths, small-file memory cache, sendfile, and explicit cache policy.
flowchart LR
Request["request"] --> Clean["path fast path"]
Clean --> Prepared["prepared snapshot lookup"]
Prepared --> Headers["precomputed headers"]
Prepared --> Body{"body source"}
Body -->|small| Memory["prepared body / memory cache"]
Body -->|large| Sendfile["sendfile"]
Body -->|range| Range["sendfile range"]
Headers --> Response["response"]
Memory --> Response
Sendfile --> Response
Range --> Response
中文:核心思想是把尽可能多的计算移动到 catalog 更新阶段,而不是 HTTP 请求阶段。
English: The core idea is to move as much computation as possible to catalog update time instead of HTTP request time.
Prepared snapshot 保存:
- route-indexed response plans
- source and generated variant pointers
- selected static headers
- cache policy result
- immutable cache headers for fingerprinted assets
- resource hints for HTML
- small eligible in-memory bodies
The prepared snapshot stores:
- route-indexed response plans
- source and generated variant pointers
- selected static headers
- cache policy result
- immutable cache headers for fingerprinted assets
- resource hints for HTML
- small eligible in-memory bodies
优点:
- 请求路径更短。
- 减少重复 map/list 遍历和 header 拼接。
- catalog 更新时原子发布新 snapshot。
- 事件触发 rebuild,不阻塞普通请求。
Benefits:
- Shorter request path.
- Less repeated map/list traversal and header construction.
- New snapshots are published atomically after catalog updates.
- Event-triggered rebuilds do not block normal requests.
SPACK 输出:
ETagLast-ModifiedCache-ControlExpiresVary
策略:
- HTML 通常保持可重新验证,避免 SPA 发布后客户端长期使用旧入口。
- fingerprinted assets 可以使用 long-lived immutable cache。
- compression variants 必须带
Vary: Accept-Encoding。 - image format negotiation 应带对应
Vary信息。
Policy:
- HTML is usually revalidated so SPA deployments do not keep stale entries forever.
- Fingerprinted assets can use long-lived immutable cache.
- Compression variants must include
Vary: Accept-Encoding. - Image format negotiation should emit corresponding
Varyinformation.
中文:frontend.stale_asset_recovery.enable 默认开启。旧页面请求已经不存在的带指纹 .js 文件时,运行时会返回 200、application/javascript 和 Cache-Control: no-store 的刷新脚本,尝试重新加载页面。这与小文件内存缓存及带指纹文件的长期 immutable 缓存是不同的机制;CSS、图片和普通 JS 不会触发。浏览器、Service Worker 或 CDN 若自行命中旧资源,请求不会到达该兜底。详见配置参考。
English: frontend.stale_asset_recovery.enable is on by default. If an old page requests a fingerprinted .js file that the runtime cannot resolve, SPACK responds with a reload script using 200, application/javascript, and Cache-Control: no-store. This is separate from the small-file memory cache and long-lived immutable caching of fingerprinted assets. It does not cover CSS, images, or plain JavaScript, and cannot act when a browser, service worker, or CDN serves the request without contacting SPACK. See Configuration.
Memory cache 面向小型热文件:
- 默认开启。
- 默认启动 warmup。
- 只缓存小于
http.memory_cache.max_file_size的文件。 - 使用
http.memory_cache.max_entries和http.memory_cache.max_bytes控制容量。 - TTL 默认
5m。
The memory cache targets small hot files:
- Enabled by default.
- Startup warmup is enabled by default.
- Only caches files smaller than
http.memory_cache.max_file_size. - Capacity is controlled by
http.memory_cache.max_entriesandhttp.memory_cache.max_bytes. - Default TTL is
5m.
SPACK 提供 Docker 化 k6 压测,完整走 HTTP 协议,对比:
- SPACK non-prefork
- SPACK prefork
- Nginx
SPACK provides Docker-based k6 benchmarks over real HTTP comparing:
- SPACK non-prefork
- SPACK prefork
- Nginx
命令:
task perf:k6
task perf:k6:frontend
task perf:k6:split调参:
$env:K6_VUS = "128"
$env:K6_DURATION = "60s"
$env:ACCEPT_ENCODING = "identity"
task perf:k6:frontend结果文件:
tmp/k6/results/spack.json
tmp/k6/results/spack-prefork.json
tmp/k6/results/nginx.json
tmp/k6/results/spack-frontend.json
tmp/k6/results/spack-prefork-images.json
tmp/k6/results/nginx-js.json
环境:Docker 本地压测,64 VUs,30s,完整前端页面加载模型。
Environment: local Docker benchmark, 64 VUs, 30 seconds, full frontend page-load model.
| Target | Requests/s | Pages/s | Throughput | Avg latency | p95 | p99 |
|---|---|---|---|---|---|---|
| SPACK | 2764.96 | 307.21 | 338.82 MB/s | 60.91 ms | 203.15 ms | 230.09 ms |
| SPACK prefork | 2693.15 | 299.24 | 330.02 MB/s | 60.50 ms | 207.01 ms | 242.25 ms |
| Nginx | 1366.44 | 151.82 | 166.65 MB/s | 104.39 ms | 276.69 ms | 336.85 ms |
资源分类 8s 快速基准:
Split resource quick baseline, 8 seconds:
| Group | SPACK rps / p95 | SPACK prefork rps / p95 | Nginx rps / p95 |
|---|---|---|---|
| HTML | 52965.61 / 3.73 ms | 52080.37 / 3.57 ms | 2901.56 / 46.52 ms |
| JS | 1001.03 / 95.85 ms | 995.52 / 101.83 ms | 775.08 / 196.40 ms |
| CSS | 38786.68 / 4.76 ms | 31586.26 / 5.91 ms | 2077.16 / 73.47 ms |
| Images | 3300.74 / 61.56 ms | 3347.00 / 62.24 ms | 1648.71 / 69.53 ms |
| Other | 5309.34 / 26.20 ms | 5106.19 / 27.41 ms | 2348.95 / 46.93 ms |
注意:这些数字是本地 Docker 环境下的基准,不应该直接当作生产承诺。它们更适合用于回归检测和实现改动前后的对比。
Note: These numbers are local Docker baselines, not production guarantees. They are best used for regression detection and before/after comparisons.
task perf:profile:resolver
task perf:profile:cache
task perf:profile:pipeline
task perf:profile:http
go tool pprof .\tmp\perf\resolver.cpu.pprof覆盖的热点:
resolver.Resolveassetcache.GetOrLoadpipeline.Service.Enqueue- HTTP asset delivery through memory-cache-hit and sendfile paths
Covered hot paths:
resolver.Resolveassetcache.GetOrLoadpipeline.Service.Enqueue- HTTP asset delivery through memory-cache-hit and sendfile paths
当前架构适合继续优化:
- 减少 header write 分支。
- 降低 variant selection allocations。
- 增强 prepared snapshot coverage。
- 让 source rescan 更增量化。
- 对 HTML resource hints 做更强缓存。
- 按 media type 和 size 优化 pipeline eligibility。
Future optimization areas:
- Reduce header write branches.
- Lower variant selection allocations.
- Expand prepared snapshot coverage.
- Make source rescans more incremental.
- Cache HTML resource-hint extraction more aggressively.
- Optimize pipeline eligibility by media type and size.