Commit c336508
authored
fix(producer): treat 4xx from Google Fonts as deterministic "not served", not as failClosed trigger (#957)
After c8e8fdc added a Google Fonts supplement-fetch to the Path 1
(bundled-font) branch, every `plan()` call against a composition whose
CSS named a non-Google family that Google Fonts 400s on (e.g.
`"Segoe UI"`, `"Arial"`, `"Futura"`) started failing on distributed
renders with `FONT_FETCH_FAILED`. Distributed renders default to
`failClosedFontFetch: true`, and the existing code treated *all* non-2xx
responses uniformly: throw if failClosed, swallow otherwise.
That conflates two very different failure modes:
- **4xx** is a *deterministic* answer — Google Fonts does not serve
this family, and won't serve it on retry either. The byte-identical-
retry contract distributed renders rely on is unaffected; the render
falls back to embedded faces / the composition's font-family chain
(which is what it would have done pre-c8e8fdcf anyway). No reason
to fail-close here.
- **5xx** (and network / DNS / fetch exceptions) is *non-deterministic*
infrastructure failure. A retry might succeed and produce different
pixel output than the first attempt — exactly what
`failClosedFontFetch` is meant to protect against. Keep failing
closed in this mode.
Fix: split the !res.ok branch in both the CSS fetch and the woff2 fetch
inside `fetchGoogleFont` — only `>= 500` paired with failClosed throws;
4xx returns `[]` in both modes. Network/DNS exceptions in the catch
block are unchanged (still failClosed-gated).
This:
- Unblocks distributed renders for compositions that name any
cross-alias system font Google doesn't serve (Segoe UI, Arial, etc.).
- **Preserves the current regression baseline** — Google Fonts
actually *does* serve some non-canonical names (e.g. "Helvetica" and
"Helvetica Neue" both return HTTP 200 with real @font-face rules,
confirmed via curl), so the supplement-fetch still runs and binds
those real faces to the composition's CSS family names exactly as
today. style-7-prod (which uses `"Helvetica Neue", Helvetica, Arial,
sans-serif`) continues to render against real Helvetica glyphs.
- Leaves the FONT_ALIASES table and call-site untouched. The fix is
in the right place — the error semantics inside fetchGoogleFont —
not in any composition-aware logic upstream.
Tests:
- 2 new positive cases on `failClosedFontFetch: true`:
400 and 404 responses no longer throw, render falls back cleanly.
- 2 new negative cases on `failClosedFontFetch: true`:
503 throws `FONT_FETCH_FAILED`, error includes URL + family.
- 1 new case on `failClosedFontFetch: false`: 5xx swallowed as before.
- The pre-existing "does NOT throw when the HTML uses a pre-bundled
font" test was broken by c8e8fdc (the supplement-fetch always fires
for self-aliased bundled fonts now). Updated it to use a successful
empty CSS response, which is the actual invariant we want.
12/12 tests pass.
Followup discussion: should `FONT_ALIASES` exist at all in a
deterministic cloud renderer? Today `font-family: "Helvetica"` in CSS
silently produces real Helvetica glyphs (via Google Fonts' undocumented
alias serving) and `font-family: "Segoe UI"` silently produces embedded
Roboto, with no warning to the author. That's a WYSIWYG violation worth
its own proposal — but not in scope for this fix.1 parent 21f5066 commit c336508
2 files changed
Lines changed: 79 additions & 16 deletions
Lines changed: 63 additions & 12 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
7 | | - | |
8 | | - | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
9 | 13 | | |
10 | 14 | | |
11 | 15 | | |
| |||
38 | 42 | | |
39 | 43 | | |
40 | 44 | | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
41 | 58 | | |
42 | 59 | | |
43 | 60 | | |
| |||
57 | 74 | | |
58 | 75 | | |
59 | 76 | | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
60 | 85 | | |
61 | 86 | | |
62 | 87 | | |
| |||
84 | 109 | | |
85 | 110 | | |
86 | 111 | | |
87 | | - | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
88 | 135 | | |
89 | 136 | | |
90 | 137 | | |
91 | 138 | | |
92 | | - | |
| 139 | + | |
93 | 140 | | |
94 | 141 | | |
95 | 142 | | |
96 | 143 | | |
97 | 144 | | |
98 | 145 | | |
99 | | - | |
| 146 | + | |
100 | 147 | | |
101 | 148 | | |
102 | 149 | | |
103 | | - | |
| 150 | + | |
104 | 151 | | |
105 | 152 | | |
106 | 153 | | |
107 | 154 | | |
108 | | - | |
| 155 | + | |
109 | 156 | | |
110 | 157 | | |
111 | 158 | | |
| |||
114 | 161 | | |
115 | 162 | | |
116 | 163 | | |
117 | | - | |
118 | | - | |
119 | | - | |
120 | | - | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
121 | 170 | | |
122 | 171 | | |
| 172 | + | |
| 173 | + | |
123 | 174 | | |
124 | 175 | | |
125 | | - | |
| 176 | + | |
126 | 177 | | |
127 | 178 | | |
128 | 179 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
430 | 430 | | |
431 | 431 | | |
432 | 432 | | |
433 | | - | |
| 433 | + | |
| 434 | + | |
| 435 | + | |
| 436 | + | |
| 437 | + | |
| 438 | + | |
| 439 | + | |
| 440 | + | |
| 441 | + | |
434 | 442 | | |
435 | 443 | | |
436 | 444 | | |
437 | 445 | | |
438 | 446 | | |
439 | 447 | | |
440 | | - | |
441 | | - | |
| 448 | + | |
| 449 | + | |
| 450 | + | |
442 | 451 | | |
443 | 452 | | |
444 | 453 | | |
| |||
467 | 476 | | |
468 | 477 | | |
469 | 478 | | |
470 | | - | |
| 479 | + | |
| 480 | + | |
| 481 | + | |
| 482 | + | |
471 | 483 | | |
472 | 484 | | |
473 | 485 | | |
| |||
0 commit comments