evijit HF Staff Claude Opus 4.7 (1M context) commited on
Commit
40339dc
·
1 Parent(s): bc08b3b

Disk-cache snapshot sidecars to skip cold-start re-downloads

Browse files

Next.js' built-in fetch cache rejects items over 2 MB so the 47 MB
comparison-index, 6 MB peer-ranks, and 2.5 MB hierarchy were re-fetched
from HuggingFace on every cold start. Cache them to a tmpdir
(`SIDECAR_CACHE_DIR`, default `/tmp/eval-card-sidecars`) with a
configurable TTL (`SIDECAR_CACHE_TTL_SECONDS`, default 1 h). Warm
containers now read from disk in sub-second time instead of
re-downloading the snapshot sidecars.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Files changed (1) hide show
  1. lib/sidecars.ts +64 -3
lib/sidecars.ts CHANGED
@@ -1,5 +1,10 @@
1
  import "server-only"
2
 
 
 
 
 
 
3
  import type {
4
  BackendManifest,
5
  ComparisonIndex,
@@ -30,21 +35,77 @@ function sidecarUrl(name: string) {
30
  return `${getSnapshotUrl()}/${name}`
31
  }
32
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  async function fetchJson<T>(name: string): Promise<T> {
34
  const url = sidecarUrl(name)
35
 
36
  if (url.startsWith("file://")) {
37
- const fs = await import("fs/promises")
38
- const text = await fs.readFile(new URL(url), "utf8")
39
  return JSON.parse(text) as T
40
  }
41
 
 
 
 
 
 
 
42
  const response = await fetch(url, { next: { revalidate: 3600 } })
43
  if (!response.ok) {
44
  throw new Error(`Snapshot sidecar fetch failed: ${response.status} ${response.statusText} for ${url}`)
45
  }
46
 
47
- return (await response.json()) as T
 
 
 
 
 
48
  }
49
 
50
  export function fetchManifest(): Promise<BackendManifest> {
 
1
  import "server-only"
2
 
3
+ import { createHash } from "node:crypto"
4
+ import { mkdir, readFile, stat, writeFile } from "node:fs/promises"
5
+ import { tmpdir } from "node:os"
6
+ import { join } from "node:path"
7
+
8
  import type {
9
  BackendManifest,
10
  ComparisonIndex,
 
35
  return `${getSnapshotUrl()}/${name}`
36
  }
37
 
38
+ // Disk cache directory + TTL for the multi-MB sidecar payloads. Next.js'
39
+ // built-in fetch cache rejects items over 2 MB so the 47 MB
40
+ // comparison-index / 6 MB peer-ranks / 2.5 MB hierarchy were re-fetched
41
+ // from HuggingFace on every cold start. With the disk cache, a warm
42
+ // container reads from `/tmp/eval-card-sidecars/*` (sub-second) instead
43
+ // of re-downloading. Override the directory via `SIDECAR_CACHE_DIR` and
44
+ // the TTL via `SIDECAR_CACHE_TTL_SECONDS`.
45
+ const DISK_CACHE_DIR =
46
+ process.env.SIDECAR_CACHE_DIR?.trim() || join(tmpdir(), "eval-card-sidecars")
47
+ const DISK_CACHE_TTL_MS =
48
+ Number.parseInt(process.env.SIDECAR_CACHE_TTL_SECONDS ?? "3600", 10) * 1000
49
+
50
+ function diskCachePath(url: string): string {
51
+ // The path encodes the URL hash so swapping SNAPSHOT_URL doesn't
52
+ // collide with the previous snapshot's cached payloads.
53
+ const hash = createHash("sha1").update(url).digest("hex").slice(0, 16)
54
+ const safeName = url.split("/").slice(-1)[0]?.replace(/[^a-zA-Z0-9._-]/g, "_") ?? "sidecar"
55
+ return join(DISK_CACHE_DIR, `${hash}-${safeName}`)
56
+ }
57
+
58
+ async function readFromDisk(path: string): Promise<string | null> {
59
+ try {
60
+ const info = await stat(path)
61
+ if (Date.now() - info.mtimeMs > DISK_CACHE_TTL_MS) return null
62
+ return await readFile(path, "utf8")
63
+ } catch {
64
+ return null
65
+ }
66
+ }
67
+
68
+ async function writeToDisk(path: string, payload: string): Promise<void> {
69
+ try {
70
+ await mkdir(DISK_CACHE_DIR, { recursive: true })
71
+ // Atomic-ish write: stage to a temp file then rename so concurrent
72
+ // readers never observe a partial payload.
73
+ const tmpPath = `${path}.${process.pid}.${Date.now()}.tmp`
74
+ await writeFile(tmpPath, payload, "utf8")
75
+ const fs = await import("node:fs/promises")
76
+ await fs.rename(tmpPath, path)
77
+ } catch (err) {
78
+ // Cache writes are best-effort — log and move on so a read-only FS
79
+ // doesn't break the request.
80
+ console.warn(`[sidecars] failed to write disk cache ${path}: ${err instanceof Error ? err.message : String(err)}`)
81
+ }
82
+ }
83
+
84
  async function fetchJson<T>(name: string): Promise<T> {
85
  const url = sidecarUrl(name)
86
 
87
  if (url.startsWith("file://")) {
88
+ const text = await readFile(new URL(url), "utf8")
 
89
  return JSON.parse(text) as T
90
  }
91
 
92
+ const cachePath = diskCachePath(url)
93
+ const cached = await readFromDisk(cachePath)
94
+ if (cached !== null) {
95
+ return JSON.parse(cached) as T
96
+ }
97
+
98
  const response = await fetch(url, { next: { revalidate: 3600 } })
99
  if (!response.ok) {
100
  throw new Error(`Snapshot sidecar fetch failed: ${response.status} ${response.statusText} for ${url}`)
101
  }
102
 
103
+ const text = await response.text()
104
+ // Fire-and-forget: we don't want disk I/O on the hot path for the
105
+ // first request, but we do want subsequent requests in the same
106
+ // container to read from disk.
107
+ void writeToDisk(cachePath, text)
108
+ return JSON.parse(text) as T
109
  }
110
 
111
  export function fetchManifest(): Promise<BackendManifest> {