CSS Grid

キャッシュとの闘い

キャッシュの何が問題なのか

「キャッシュ」といっても現金のことではなくて、ブラウザのキャッシュ機能のことです。

ブラウザのキャッシュ機能は以前閲覧したページのデータをバソコン内に保存しておき、次に同じページを開く時はその保存データを画面に表示します。その都度サーバーからデータを受け取る必要がないため、素早くページを表示することができます。同じページかどうかはファィル名(URL)で判定します。このキャッシュ機能は Google Chrome において特に強力で、サクサク動作するため快適に違いないのですが、比較的更新頻度の高いページにおいて、時として厄介な問題を引き起こすことがあります。

閲覧者にとっては、表示される画面が最新である保証はありません。しかも、そのことが本人にはわかりにくいことが問題を複雑にします。

コンテンツの更新を依頼した側にとっては、依頼した仕事がいつまで経っても完了しないため作業担当者に問い合わせ・催促をすることになります。動機は一般の閲覧者と同じで、常に最新のページが表示されると信じ込んでいることにあります。

また、コンテンツ更新の作業依頼を受けた側にとっては、仕事が完了したにも関わらず依頼元から催促され、説明に追われることになります。キャッシュというものの存在を説明し、それを迂回して新しいページを読み込む操作を依頼するのですから、手間も時間もかかります。何よりもイラ立ちと不信感という逆風をベースに催促されるのですから、そこから説明に納得して自分で何らかの操作を行うまでに気持ちを切り替えてもらうための努力は簡単ではありません。時として気まずい雰囲気になり、営利活動においては受注者側としての立場を再認識することになります。非営利活動においても程度の差こそあれ、似たような状況になることがあります。

分かり易くするため少し極端なパターンですが会話形式で紹介しましょう。

・依頼主:おねがいした更新は完了しましたか?

・受注側:完了してます。

・依頼主:でも画面は元のままです。

・受注側:キャッシュのせいです。スーパーリロードするか、キャッシュをクリアしてください。

・依頼主:難しくて面倒だけど、それでもうまく表示されないよ...しかし、色々やっているうちに新しい画面になったよ。(操作に不慣れだったり勘違いもある)

・受注側:了解しました。これで完了ですね。

・依頼主:(実は更新を忘れていて、私の連絡でついさっき更新したんじやないの...)

・受注側:(...まあ、とりあえず最新画面を確認できたんだからそれで良しとしましょうよ)

括弧書きの会話は通常は無いと思いますが、相互の心の中に残る不信感は蓄積されていき、何らかのミス、例えば作業側のミスを契機にこの不信感が表に出てしまうことも考えられます。

そうならないためにも日頃からのコミュニケーションと信頼関係の醸成が必要なのですが、その甲乙間の関係にキャッシュ機能は常に負の方向に強力に作用しているわけです。

ここまで読んで、「キャッシュとの闘い」という言葉使いにピンときていただけたでしょうか。実際のところ、「おおげさに...」と思われる方も多いと思いますが、わかる方にはわかっていただけるかと思います。

ブラウザ側での対応

さて、このように人間関係に負の作用をもたらす可能性のあるキャッシュ機能ですが、これを何とか制御できないものかと模索してきた過程と成果を書き落としていきたいと思います。

最初に取り組んだのは、閲覧する際にF5キーでリロードしてもらうこと。しかし、リロードは単なる「再読み込み」であって、その「読み込み」の中にはキャッシュを利用することも含まれるわけで、これでは問題解決に何の意味もないことがすぐにわかりました。

そこで、スーパーリロードをやってもらうことをお勧めしたのですが、これはブラウザによって操作が異なるため、面倒な説明になってしまいました。その後、「Ctrl + Shift + R」がある程度共通に利用できる方法と知り、現在は画面が最新かどうか疑わしいときは「Ctrl + Shift + R」をやってもらうようにしています。 「+」の部分を説明するのには少し工夫が必要です。「Ctrl キーを押したまま、加えて Shift キーを押し、さらに加えて R キーを押す」といえばだいたいはわかってもらえます。

ただ、現実問題として、都度「Ctrl + Shift + R」の操作をすることは煩わしくて、閲覧の利便性を著しく損ねることになります。

URLパラメーター

ネット上を色々と探してみて、ヒントを得たのはクエリーパラメータ(クエリー文字列、 URL パラメータ、URL 文字列、URLのパラメータ)の利用でした。ブラウザがキャッシュを利用するか、サーバーからリロードするかの判定根拠はファィル名の比較ですが、そのファイル名の中にはクエリーパラメータも含まれるということを利用するものです。

しかし、よく考えてみると、ページ A からページ B に遷移するときに、ページ B のファイル名にクエリーパラメータ付加するのですが、その処理を行うのは当然ながらページ A です。つまりページ B を更新して、キャッシュを迂回したいときには合わせてページ A も更新しなければなりません。さらに、ページ B に遷移する遷移元が複数あったらどうでしょう。あちこちから飛んでくるページです。それら遷移元の全てを更新しなければクエリーパラメーターは変更できません。これでは作業量がどんどん増えてしまいます。

さらに、もっと決定的なのは、ブックマークです。ブックマークに登録されたパスで直接的に遷移してくるため、クエリーパラメーターを更新するタイミングがそもそもありません。これを例外として割り切る考え方もあるのでしょうが...

自己リダイレクト

結局、これもダメかと諦めかけた時にひらめいたのが「自己リダイレクト」なる発想でした。つまり自分専用の遷移先に一旦遷移して、そこで PHP や javascript によって時刻や乱数を使った毎回ユニークなクエリーパラメータを付加して元のところに戻ってくるという処理です。別の表現をすれば「行ってこい」という形になります。

この処理に際しては、リンクは 1 度だけにしてループを避けなければなりません。そのための手段としてセションストレージを使うことにしました。セションストレージは HTML5 から採用された比較的新しい機能ですが、今やほとんどのブラウザでサポート済で、現実的には問題なく使用できます。

なにしろループと隣あわせの動作なので、ローカルストレージ機能の有無、使用の可否、エラー処理などを含めて完璧なフェールセーフの構造にする必要があります。怪しいときは、さっさとクエリーパラメータの付加をあきらめるという具合に徹底しました。

普遍化・共通化

「自己リダイレクト」専用の javascript と HTML ページを個別に 1 対 1 で作成していたのでは、これまた大変なことになります。一度作成してしまえばメンテナンスは不要とはいっても数が膨大なものになってしまいます。このような処理は機能を普遍化し、処理を共通化して汎用ルーチン化するに限ります。

具体的には、この自己リダイレクトページ(refresh.html)に遷移するための処理(refresh.js)を javascript の外部ソースとして home ディレクトリに置き、それを利用する各ページは最初にその javascript ソースを取り込みます。ルートに置くことによって、どの階層からでも"/"でアクセスすることができます。

取り込んだ javacript は自分のページの URL をクエリーパラメータに組み込んで(refresh.html)に遷移します。(refresh.html)は(refresh.js)同様、 home ディレクトリに置くことによって、どの階層からでも "/" でアドレッシング可能となります。(refresh.html)は受け取ったクエリーパラメータを戻り先の URL として取り出します。

このような仕組みによって、汎用の(refresh.js)と汎用の(refresh.html)の 2 つの制御モジュールのみで、どのページからも「自己リダイレクト」を利用できるようになりました。

その後、「自己リダイレクト」とは無関係な元々のクエリーパラメータとハッシュをそのまま持ち回るようにし、異常処理の完成度を高めた汎用版(refresh_v3.js、refesh_v3.html)が完成し、順調に稼働しています。