テレビのアニメはどこから来る? / 第48話

第48話:イトは、browser cacheを全部消さなかった|古いCSSはどこから来た?

ピコルート第48話。new HTMLにold CSSが混ざる一台を、イトがresource単位で観測し、versioned URIとvalidationで直します。

browser private cachedocumentとsubresourceETagと304 validationversioned asset URI10分更新

第47話で、イトはshared cacheのold 404だけをinvalidateした。

viewerはcurrent 301を受け取り、new finale pageへ進んだ。

poster、CSS、imageのunrelated shared-cache hitsは残してある。

それなのに、一台のlaptopだけ、finale本文は新しく、背景はold blue curtainのままだった。

もう一台では、同じ本文の背後にnew gold curtainが見える。

「このbrowserの保存を、全部消せばそろう」

イトはclear browsing dataの大leverを開いた。

fixtureのleverには、cache、Cookie、historyの三箱が一緒につながっている。

中継operatorとしてlogin中のlaptopを、あと六十秒で投影しなければならない。

new pageでも、材料は一つずつ別request

ピコは二台のlaptopではなく、一台のresource waterfallを照らした。

イト

本文がnewなら、page全部をnewで受け取ったんじゃないの?

ピコ

main documentがcurrentでも、そこから読むCSS、image、font、scriptは別々のrequestとresponseだよ

イト

一枚のpageに、networkの今とbrowser cacheの前の返事が混ざる?

ピコ

それぞれのstored responseが再利用できるなら混ざり得る。resourceごとに確かめよう

この話のbrowser、resource waterfall、clear-data lever、origin asset manifestは、外のInternetから切り離した学習fixtureだ。

browser製品ごとのmenuや内部memory / disk policyを再現しない。

diagnostic source labelとversioned asset操作も、このfixture専用だ。

browser private cacheも、HTTP responseを保存する

RFC 9111はprivate cacheを、一人のuserに専用のcacheとして整理し、user agentのcomponentとして配置されることが多いと説明する。

browser cacheは、画像だけのalbumではない。

cacheableなHTML、CSS、JavaScript、image、font等へのHTTP responseを、request keyとfreshness rulesに沿って保存 / 再利用し得る。

何をmemoryに置くか、diskへ置くか、いつevictするかはbrowser実装や環境で変わる。

だから「browser cacheなら必ずこのfolder」という説明はしない。

protocol上で見るのは、どのrequestに、どのstored responseを再利用できるかだ。

documentとCSSを、同じ「page」にまとめない

イトはfixture waterfallを開いた。

document: /program/finale-night
response: 200 current
source: network

stylesheet: /assets/finale.css
response: stored 200 old
Cache-Control: max-age=600
current age in fixture: 310
source: browser private cache

font: /assets/festival.woff2
response: stored 200 current
source: browser private cache

main documentはcurrentだ。

しかし、documentが参照した/assets/finale.cssには、browserが以前保存したresponseがある。

fixture clockではage 310秒、freshness lifetime 600秒だから、まだfreshだ。

fresh responseは、通常、origin validationなしで再利用できる。

old CSSにはblue curtain imageへの参照が残っていた。

だからdocument本文だけnew、styleとbackgroundだけoldになった。

「HTMLがnewだからbrowser cacheは無関係」とは言えない。

source labelは、fixtureの補助証拠

このfixtureはnetworkとbrowser private cacheというsource labelを表示する。

実browserのdeveloper toolsにも、似た転送元表示がある場合はある。

でも、名称、表示条件、reload時のbehaviorは製品やversionで異なる。

source labelだけで結論を出さない。

exact URI、method、status、Cache-Control、Age相当の観測、ETag等validator、request timingを一緒に見る。

shared cacheのAge fieldがprivate cache responseで必ず見えるとも限らない。

「別browserならnewだった」という比較も着眼点にはなる。

ただし、extension、profile、login state、service worker等の差もあり得るため、それだけでbrowser cacheへ断定しない。

staleになったら、捨てるだけではない

stored responseがfreshでなくなっても、必ずbodyを全部取り直すとは限らない。

RFC 9111のvalidationでは、cacheはconditional requestを使い、stored responseがcurrent representationとしてまだvalidかを確認できる。

代表的なvalidatorがETagだ。

GET requestへstored responseのentity tagをIf-None-Matchで付ける。

RFC 9110のIf-None-Matchでは、selected current representationのtagが一致すれば、GET / HEADに304 Not Modifiedを返せる。

304は「新しいbodyを返した」という意味ではない。

clientがすでに持つrepresentationを使えるため、contentを再転送する必要がないというresponseだ。

tagが一致しなければ、条件は満たされ、通常のGET処理からfull responseが返り得る。

cacheはそのfull responseを使い、条件を満たせば保存できる。

同じURIの中身を変えたときの圧力

origin asset shelfには、new gold CSSが置かれている。

URI: /assets/finale.css
current ETag: "gold-v2"

browserに残るold CSS responseは、同じURIを持つ。

stored ETag: "blue-v1"

deploymentでは、内容を変えたのに長いfreshnessを持つ同じasset URIを再利用していた。

HTTP cacheは、freshなstored responseを再利用することで期待どおりに動いている。

「browserが勝手に壊れた」とは言えない。

site側のasset namingとcache policyが、change frequencyに合っていたかも見る。

contentが変わるたび別のversioned URIへする方法なら、new documentはbrowserがまだ保存していないtargetをrequestできる。

これはdeployment patternの一つであり、HTTPが必ずversion名を付けろと命じているわけではない。

イトの前に、三つの選択肢が開く

projector接続まで、あと三十五秒。

clear-all leverの先には、このfixtureでlogin stateを持つCookie boxと、history ribbonもつながっている。

A cache、Cookie、historyをまとめて消す

この一台のold CSSは見えなくなるかもしれない。

しかし、原因resourceとpolicyの問題を隠したまま、fixture login stateとhistoryまで失う。

別の利用者のbrowserに残る同じentryは直せない。

B reloadを連打する

reload操作がどのrequest directiveやcache behaviorになるかは、browser / UI / contextで異なり得る。

何がnetworkへ出たかを記録せず回数だけ増やしても、CSS、image、fontのどれが変わったか残らない。

C resource responseを分け、changed assetへnew URIを渡す

main document、CSS、image、fontを別slotへ置く。

exact URI、source、freshness、ETagを記録する。

old / current validatorをconditional GETで比較する。

changed CSSはnew versioned URIへ割り当て、documentのasset manifestを更新する。

unchanged font、Cookie、history、unrelated cache entriesは消さない。

ピコはclear-all leverへ触れなかった。

「一台を空にする前に、次の一台も正しくnew assetを選べるrouteにしよう」

イトはCを選んだ。

clear-all coverを閉じ、cache / Cookie / historyをつなぐbarを外した。

current document、old CSS、old curtain image、unchanged fontを、四つのtransparent resource slotsへ自分の手で分けた。

validatorは、変わったものと同じものを分けた

イトはfixture validation laneで、CSSとfontへconditional GETを送った。

old CSSのIf-None-Matchは"blue-v1"。

origin current CSSは"gold-v2"で、一致しない。

fixture originは304ではなく、new CSS bodyを持つ200 responseを返した。

fontのstored ETagは、origin current fontと一致した。

font requestには304 Not Modifiedが返り、browserはstored font bodyを再利用できた。

changed CSSにはnew full response。

unchanged fontにはsmall 304 responseとstored body。

二つを「cacheから出た」で同じにしない。

new URIを、changed CSSにだけ渡す

validationでoriginのcurrent representationを確認したあと、イトはdeployment manifestを開いた。

changed CSSへ、fixture用のnew URIを割り当てる。

old: /assets/finale.css
new: /assets/finale.gold-v2.css

new CSSは、new gold curtain imageのversioned URIを参照する。

イトはcurrent HTML manifestもnew CSS URIへ更新した。

old CSS entryをbrowserの棚から破り捨てない。

current documentが参照しないold keyとして、transparent old slotへ残す。

イトがcurrent document、old blue CSS、old curtain image、unchanged fontを四つのbrowser resource slotへ分け、old CSSは透明なunused slotへ残したまま、goldのnew CSS cardへ一本だけnew violet URI keyを渡し、Cookie boxとhistory ribbonはclosed coverの向こうに保持している
browser dataを全部消さず、resourceごとのresponseを分け、内容が変わったCSSだけをnew URIでrequestできるようにする。

new requestは、old keyを選ばない

同じlaptopでcurrent documentをrequestする。

documentはnew manifestを受け取った。

browserは/assets/finale.gold-v2.cssをrequestする。

private cacheに、そのexact new URIのstored responseはまだない。

originから200 new CSSを受け取り、そこからnew gold curtain imageも200で取得した。

finale本文、gold style、gold curtainが一つのpageへそろう。

unchanged fontはstored bodyのまま表示に使われた。

Cookie boxを開けなかったため、このfixtureのoperator login badgeも残っている。

history ribbonも消していない。

別のclean-profile browserでもnew documentを開き、同じnew CSS URI / gold curtainを取得した。

一台だけの応急処置ではなく、次のclientもcurrent assetを選べるrouteになった。

browser cacheを疑うときも、resourceを主語にする

projectorにgold finale pageが映った。

browser shelfには、四つの結果が残っている。

new URIで取得したgold CSSの200 card。

同じvalidatorで304を受け、再利用したfont body。

current documentから参照されなくなったold blue CSS card。

closed coverの向こうに、触れなかったCookie boxとhistory ribbon。

main pageがnewかoldかだけで決めず、documentとsubresourcesを分ける。exact URI、source、freshness、validatorを確認し、changed assetだけがnew responseを選べるrouteを作る。原因を見ないままbrowser dataを全部消さない。

「cacheを残すことと、old assetを使い続けることは同じじゃない」

イトはold CSS cardを捨てず、unused keyの透明slotへ置いた。

隣のCookie boxで、小さなlogin tokenが点滅した。

表示材料はそろったのに、そのtokenを別hostへ送ろうとするとgateが閉じる。

次回:Cookieは、どこへ送られる?

Cookie boxには、name / value以外にも小さなscope ringsが付いている。

domain、path、expiry、securityのgateだ。

「loginを覚える小箱なら、どのrequestにも付けていいの?」

イトはCookieをcache slotへ入れず、次の検証台へ運んだ。

次回、ピコルート第49話。

イトは、Cookieを全部のrequestへ付けなかった|小さな記録はどこへ送られる?

この物語を、解説で深める

第48話で生まれた疑問を、解説記事で少し深く見ていきます。物語で感じた不思議さを残したまま、言葉と仕組みを順番に整理しましょう。

ブラウザキャッシュとは|画像やCSSをブラウザに残す保存棚を読む