| @雑談

今津

2023 年の出来事

1 月

インターネットについて書いた記事がバズった。

以前から漠然と思っていたこと(ショッピングプラットフォームを使わず、独自のカートシステムで作られているショップのサイトに Google 経由でたどり着けない)を書いたら同意してくれる人がたくさんいた。

自分としては Google だけが問題と言いたかったわけではないが、あとから読み返してみると全部 Google に問題があるように読める。

この記事をきっかけにして Safari のデフォルト検索エンジンを Google から DuckDuckGo に変えた( iPhone 、 Mac とも)。お店の名前で検索したときに検索結果に Google マップが出てこないのが少し不便だが、 Google マップの情報が必要なら Google マップで検索すれば良いだけなのでまぁ慣れてしまった。

ちなみに車で CarPlay 越しに使うカーナビも Google マップから Apple マップに乗り換えた。 CarPlay の便利さについては 4 月に記事を書いている。

2 月

阿蘇草原マラニックに参加した。

中学生の頃に陸上をしていたとき以来のレースっぽいやつ(厳密にはレースではなく、順位を競わないマラニックだった)。これでトレランの面白さに目覚めた。それまでも家の近所を走ったり、たまに山を走ったりしていたが、あくまで登山がメインでランニングは体力向上を目的としていた。阿蘇草原マラニックに参加してから主客が逆転し、ランニングがメインで登山はサブになった。

Running - Saturday, February 4, 2023.jpg

2 月には iPad Air も買っていた。

ハチャメチャ使いにくいと書いているが、ちょっとしたメモ書きには便利。ノートパソコンの代替として使おうとするとやっぱり無理があって、ちゃんとしたフルピッチのキーボードにはかなわない。それよりも図を描いたりしつつ考えをまとめるメモ帳として使うと、紙のメモでは不可能な、一回書いた文字や図をぐるっと囲んで別の位置に移して整理したりといった使い方が可能。これによって新たなアイディアが浮かんでくることもある。 iPad Air 、高かったが買って良かった。ペイディ払いさまさま。

3 月

3 月は五ケ山・脊振クロストレイルというトレランのレースに出場した。マラニックではないちゃんとしたレース。 2 月に一回試走していたが試走でヒザがオワコンになり、ちゃんとゴールできるかとても心配だったがなんとかゴールした。

Running - Sunday, March 12, 2023.jpg

3 月はブログの検索機能を改造して、ショートカットキーから検索窓を開けるようにして、矢印キーで検索結果を選択できるようにした。これがめっちゃ便利。自分のブログで Mac の人気ランチャー Alfred の検索機能を使えるような感じ。

4 月

CarPlay について記事を書いた。

Apple CarPlay は本当によくできている。車の給油ランプが付いたときに「ガソリンスタンドを探しますか?」というプッシュ通知が効いたときには本当に驚いた。とてもよくできている。

4 月にもトレランのレースに出ている。平尾台トレイルランニング。石川弘樹さんと記念撮影させてもらった。

Running - Sunday, April 16, 2023.jpg

5 月

仕事で下関に行ったので朝から下関の街をランニングした。知らない街に行ってランニングすることの面白さを知った。歩きだと時間がかかるし、自転車だと早すぎて通り過ぎてしまうが、ランニングならパッパッと移動できつつ、気になった場所は止まって写真を撮ったりできる。

Running - Monday, May 29, 2023.jpg

ブログでは Apple Watch をトレランに使うための記事を書いた。

純正のワークアウトアプリではトレランに使うのは難しい(ルート案内がない)が、サードパーティーのアプリを使えば Garmin などの専用ウォッチと同等の機能を提供してもらえる。おまけに日常では Apple Pay が使えたり音楽の操作ができたりプッシュ通知を受け取ったりできて専用ウォッチよりも便利な機能も享受できる。 100 マイルレースとなるとさすがにバッテリー切れしてしまいそうだが、そんなに長い距離を走らないのであれば Apple Watch Ultra でトレランできると思う。

6 月

ブログ書いてない。写真を見返してもあまりパッとしない。仕事では「おつかれ山」の刷新に関わっていた。

Running - Sunday, June 11, 2023.jpg

7 月

二つトレランのレースに出た。 7/2 に南阿蘇カルデラトレイル、 7/24 に霧島・えびの高原エクストリームトレイルに参加した。

阿蘇草原マラニックに参加して、普通では立ち入れない牧野を走ることの楽しさを味わったので、南阿蘇カルデラトレイルにも参戦してこのときしか走れない景色を楽しみたいと思った。結構頑張ったつもりだったがタイムは 6 時間超で五ケ山・脊振クロストレイルのときと大差なかった。結構走ってたつもりなのに残念。

Running - Sunday, July 2, 2023.jpg

7/24 に走った霧えびでは獲得標高が少なかったこともあったのか、 6 時間以内でゴールできていた。しかしこのレースは景色が良いところが少なく、ほとんど林道できつかった。それでも一緒に参加した人たちと前日入りで霧島まで移動してピコラナイえびのに泊まったのは修学旅行みたいで楽しかった。えびの高原の雰囲気も阿蘇や久住に似ていて良かった。いつか韓国岳に登りに来たい。

Running - Sunday, July 23, 2023.jpg

7 月はレースのほかに長崎の七高山めぐりトレランもやった。 4 時間くらいで終わるつもりが 5 時間以上かかった。真夏の日中低山トレランはやばかった。七高山めぐりは規定の 1/15 までの間にチャレンジしてみたい。

Running - Sunday, July 16, 2023.jpg

ブログ関係ではサイトがめちゃくちゃ重いことがあるので原因を調べていたら、他サイトの OGP を読み込むための仕組みがスパマーによって悪用されていて、すさまじい数の HTTP リクエストが来ていることが発覚した。クエリパラメーターに URL を渡せば OGP を読み込みに行く機能だったが、 HTML 片を返してその中にオリジナルサイトへのリンクがあるので、足跡を残さずに別サイトへリンクするときの踏み台として乱用されていた。

もしこのブログを AWS などの従量課金系のサーバーで公開していたら多分とんでもない金額を請求されてクラウド破産していた。

8 月

ランニング中に立ち寄った自販機で買ったスコールパイン味がめっちゃうまくてランニングする度に買って飲んでいた。自販機で買うと値段が高いのでスーパーで探すものの、スコールパイン味はケース売りしていないようだった。なのでカルピスのパイン味を買って家で作った炭酸水で割って飲んでいた。パインと乳酸飲料の相性抜群なので良かったらお試しください。

スコールパイン味

下旬に一人で金山に行って野営した。防水の靴を夏に履くと自分の汗がなかなか抜けず逆に足がめっちゃ蒸れることを学んだ。夏は通気性の良い靴の方がよい。渡渉で足が濡れてもすぐ乾くし。

国民宿舎金山

このときの野営では地べたに座ったところ、アリにかまれてめっちゃつらかった。また地面に置いた荷物にも大量にアリが侵入してきて困った。このときの経験からハンモック泊するときはリッジラインを張って荷物は極力吊るすようにした。

9 月

ブログ記事なし。良く走ってはいたようだ。

トレランの用のシューズメーカーの Altra の靴が値上がりしすぎてつらいので 9 月に NIKE のセールでカイガー 9 というトレランシューズを買った。 9500 円くらいだった。 Altra のシューズは安くても 20000 円くらいするのでめっちゃお得。 NIKE のシューズは幅が狭いので足に合わないのだが、カイガーは Altra ほどではないにせよそこそこ幅が広くて 10km 程度のトレランでは問題なかった。

9 月は初めてあたたかい季節に九州脊梁に行った。脊梁はそれまで冬枯れの時期にしか訪れたことがなく、晩夏の脊梁は緑いっぱいで新鮮だった。

Hiking - Saturday, September 9, 2023.jpg

10 月

課金術についての記事を書いて少しバズった。

プログラミングなどの情報に比べて、この手の情報はインターネット上に非常に少ない。やっぱりみんなこの手の情報を必要としていたのだろう。

ちなみに課金手法は正解はない。業種によって様々だ。しかし確実に言えることは、自分がどんな価値を顧客に対して提供して、どういう課金手法だったらお金を払ってもらえるかを突き詰めて考えることだと思う。

10 月は何のレースにも出ていないが、翌月の福岡マラソンに備えてよく走っていた。レースなしで 238km 走っていた。 30km 走を後半に 2 回行っている。キロ 5 分台で 20km 近く走ったのはこれが初めてだったが、これで走りの次元が変わったように思う。キロ 5 分台への恐怖がなくなった。

Running - Monday, October 30, 2023.jpg

11 月

11/12 に福岡マラソンに出場した。初マラソン。もしかしたらサブフォーいけるのではと思っていたが、 6 分オーバーしてしまった。トレランのレースは本番に備えるといっても近所の山を走る位だったが、マラソンは入念に準備をした。インターバル走したり、ロング走したり。準備をして本番に臨む感じは初めての経験で新鮮だった。トレランよりもハイペースで走り続けるのでゴール後の疲労感も異なる。声がかすれるくらいに一生懸命に走ったのは初めてだった。

11 月はトレランのレースにも出た。八幡山岳会カントリーレース。マラソンで完全燃焼したので楽しむつもりで気楽に走ろうと思って出たが、なかなかハードなコースでかなりきつかった。朝、寒かったので厚着してスタートしたところ、昼間は結構気温が上がって厚手の長袖シャツを着ていたため体温調節に苦しんだ。

Running - Sunday, November 26, 2023.jpg

マラソン、トレランレースとイベントが多かったが、子どもから風邪をうつされて 11 月下旬から全然走れなくなってしまった。この体調不良は翌月まで尾を引いた。

12 月

12 月は仕事でグループ位置共有という機能をリリースしたのでいろいろ忙しかった。この機能は残念ながら山をなめるなおじさんクラスターから叩かれて炎上してしまった。「集団行動が前提の登山で離れても良いなどと言うとは何事か、けしからん!」的な反応だった。

Hiking - Friday, December 1, 2023.jpg

仕事は忙しかったが、ハッピーハイカーズの法華院ギャザリングというイベントに参加したり、南阿蘇カルデラトレイルの冬大会にも参加した。

法華院ギャザリングのイベント自体は自分は人見知りなこともあって全くダメだった。坊ガツルでの 12 月の野営はとても寒く、外に置いていたエバニューのウォーターキャリーの中の水が凍っていた。

Hiking - Sunday, December 3, 2023.jpg

南阿蘇カルデラトレイルはこれまでで最長となる 50km のコースにエントリーした。 6:00 の暗い時間にスタートし、 16:00 までにゴールしなければならない。これまで自分は持久力があると思っていたが、 30km を過ぎたあたりでシャリバテしてしまい、 30km ~ 45km くらいはずっと歩いていた。このままでは DNF になると焦りながらなんとか各エイドの関門時刻をギリギリ通過していき、最後にちょっと元気が出てきてなんとか制限時間内にゴールできた。本当にきついレースだった。

きついレースではあったが、この日は寒波が到来していて南阿蘇の標高 1000m ちょいの外輪山は雪が積もり、しかも気温が低いため雪がさらさらのままで、非常に幻想的な景色の中を走ることができた。こういうスノーランも楽しい。

Running - Sunday, December 17, 2023.jpg

2024 年の抱負

2023 年はもう 42 歳にもなるのに何者にもなれない人生だった。親孝行もできていない。何のために生きているのかと自問自答する日々だった。もうこの歳になって人生一発逆転できることはないのはわかってはいるが、 2024 年は両親や故郷に対して恩返しとなる何かをやっていきたい。

具体的にはアプリを作りたいと思っている。アプリ開発の技術的なノウハウは皆無だが、どうやったら人々が欲しがるアプリを作れるかはほんのちょびっとだけその辺の人よりは詳しくなった自負がある。 2024 年中にリリースすることを目標にコツコツ勉強していきたい。

| @WWW

以前、以下の記事でこのウェブサイトへのアクセス元 User Agent について書いた。

そのとき Hatena::Russia::Crawler というのが謎だということを書いた。最近のアクセスログを見ても相変わらずこの User Agent からのアクセスが多い。またアクセス頻度も高く、同一の URL に対して何度もアクセスしている。

これはやはりはてなの名を騙った怪しいクローラーなのではないかと思い調べてみた。

まず Hatena::Russia::Crawler という User Agent からのアクセスの IP アドレスを調べてみたところ以下だった。

cat log/access.log | grep 'useragent:Hatena::Russia::Crawler/0.01' | cut -f2 | sort | uniq -c | sort -nr
    434 remote_addr:52.68.0.227
    419 remote_addr:54.249.85.140
    417 remote_addr:54.92.97.59
    379 remote_addr:54.250.227.185

whois してみると AWS で運用されているものであることがわかるが、はてなのものかは断定できない。

もしこの IP からはてなブックマークやはてなアンテナなどの User Agent でのアクセスもあれば Hatena::Russia::Crawler ははてなのクローラーであると断定できるだろう。ということで調べてみたところこんな感じだった。

zcat -f log/access.log* | grep -E 'remote_addr:(52\.68\.0\.227|54\.249\.85\.140|54\.92\.97\.59|54\.250\.227\.185)' | cut -f13,2 | sort | uniq -c | sort -nr
  16687 remote_addr:54.250.227.185      useragent:Hatena::Russia::Crawler/0.01
  16448 remote_addr:54.92.97.59 useragent:Hatena::Russia::Crawler/0.01
  16370 remote_addr:54.249.85.140       useragent:Hatena::Russia::Crawler/0.01
  16272 remote_addr:52.68.0.227 useragent:Hatena::Russia::Crawler/0.01
     73 remote_addr:54.249.85.140       useragent:HatenaBookmark/4.0 (Hatena::Bookmark; Scissors)
     60 remote_addr:54.250.227.185      useragent:HatenaBookmark/4.0 (Hatena::Bookmark; Scissors)
     56 remote_addr:52.68.0.227 useragent:HatenaBookmark/4.0 (Hatena::Bookmark; Scissors)
     50 remote_addr:54.92.97.59 useragent:HatenaBookmark/4.0 (Hatena::Bookmark; Scissors)
     31 remote_addr:54.92.97.59 useragent:Hatena::Fetcher/0.01 (master) Furl/3.13
     31 remote_addr:54.250.227.185      useragent:Hatena::Fetcher/0.01 (master) Furl/3.13
     31 remote_addr:54.249.85.140       useragent:Hatena::Fetcher/0.01 (master) Furl/3.13
     26 remote_addr:52.68.0.227 useragent:Hatena::Fetcher/0.01 (master) Furl/3.13
     19 remote_addr:54.92.97.59 useragent:Hatena::Scissors/0.01
     19 remote_addr:54.250.227.185      useragent:Hatena::Scissors/0.01
     16 remote_addr:52.68.0.227 useragent:Hatena::Scissors/0.01
      9 remote_addr:54.249.85.140       useragent:Hatena::Scissors/0.01

なんと、 IP アドレスで検索してはてなのその他のクローラーもヒットしてしまった。つまり Hatena::Russia::Crawler ははてなのクローラーということだ。

ただしググっても一切情報が出てこない。 Hatena::Russia::Crawler で検索してトップヒットするのは自分のブログだ。

改めて Hatena::Russia::Crawler による直近 30 日間のアクセス状況を調べてみるとこんな感じだ。

zcat -f log/access.log* | grep 'useragent:Hatena::Russia::Crawler/0.01' | cut -f5 | sort | uniq -c | sort -nr
  15697 request_uri:/index.atom
   9913 request_uri:/2022/04/20/integrate-charts-category-with-select-boxs
   9373 request_uri:/2022/05/04/reputation-and-interpretation
   8422 request_uri:/2022/05/11/fly-to-kamikochi-from-fukuoka
   7716 request_uri:/2022/05/16/using-tantivy-over-tantiny
   5906 request_uri:/2022/04/17/quit-using-hey
   5139 request_uri:/2021/12/29/thoughts-on-manga-subscription
   1308 request_uri:/2021/12/13/keep-a-stack-books-whether-reading-them-or-not
    787 request_uri:/2022/06/23/each-entry-title-should-be-marked-up-with-h1
    741 request_uri:/2021/12/13/keep-stack-books-whether-reading-them-or-not
    456 request_uri:/2022/06/24/if-you-feel-apple-musics-recommendation-is-awful
    100 request_uri:/2022/06/14/thoughts-on-hatena-bookmark
     46 request_uri:/2015/12/07/thoughts-on-rural-life
     46 request_uri:/2015/12/02/thoughts-on-t-on-t
     46 request_uri:/2015/12/02/thoughts-on-christmas-song
     45 request_uri:/2019/12/02/stop-drinking-outside-frequently
     16 request_uri:/2020/11/08/where-i-went-in-2019
     11 request_uri:/2015/12/05/omm-writer-music-is-nice-to-listen-to-while-writing
      5 request_uri:/2022/05/04/the-golden-maintenance-week
      2 request_uri:/2022/06/24/
      2 request_uri:/2022/06/24

index.atom はフィードの URL なので除外するとして、特定の記事に対して数千回もアクセスがある。 30 日間で 9000 回ということは一日あたり 300 回だ。 1 時間あたり 12.5 回である。何のためにこんなに高頻度でクローリングしているのだろうか。

とここまで調べたところほかの Bot 系アクセスはどうなのかと改めて User Agent 毎のアクセス数を調べてみたらこんな感じだった。

zcat -f log/access.log* | cut -f13 | sort | uniq -c | sort -nr | head -10
 124894 useragent:Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)
  65794 useragent:Hatena::Russia::Crawler/0.01
  58274 useragent:Ruby
  31493 useragent:Mozilla/5.0 (iPhone; CPU iPhone OS 15_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1
  29454 useragent:Mozilla/5.0 (compatible; AhrefsBot/7.0; +http://ahrefs.com/robot/)
  21492 useragent:Tiny Tiny RSS/21.11-7cfc30a (https://tt-rss.org/)
  20351 useragent:Slackbot 1.0 (+https://api.slack.com/robots)
  18765 useragent:Mozilla/5.0 (compatible; SemrushBot/7~bl; +http://www.semrush.com/bot.html)
  18395 useragent:Mozilla/5.0 (compatible; MJ12bot/v1.4.8; http://mj12bot.com/)
  17942 useragent:Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)

bingbot からのアクセスの方が Hatena::Russia::Crawler からのアクセスの 2 倍近くあった。ただし bingbot は検索エンジンのクローラーらしく、サイト全体をまんべんなくクローリングするような挙動で、特定の URL に一ヶ月間で数千回アクセスするような感じではない。

zcat -f log/access.log* | grep 'useragent:Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)' | cut -f5 | sort | uniq -c | sort -nr | head -25
    571 request_uri:/robots.txt
    352 request_uri:/
    205 request_uri:/category/misc
    160 request_uri:/2007/01/13/732
    156 request_uri:/2005/10/28/129
    150 request_uri:/2009/03/23/1010
    148 request_uri:/category/music
    146 request_uri:/archives
    144 request_uri:/category/www
    143 request_uri:/2016/07/
    143 request_uri:/2009/08/31/1074
    139 request_uri:/2010/07/05/1140
    138 request_uri:/category/photo
    138 request_uri:/2009/02/
    137 request_uri:/2006/09/09/658
    136 request_uri:/tags/netatmo
    136 request_uri:/2010/07/17/1145
    136 request_uri:/2006/07/23/611
    135 request_uri:/2014/03/
    134 request_uri:/?page=32
    134 request_uri:/2007/02/09/747
    133 request_uri:/category/shopping
    133 request_uri:/2021/07/26/how-to-get-to-kamikochi-from-fukuoka
    133 request_uri:/2011/11/03/finally-got-hhkpro2
    132 request_uri:/2006/01/

Hatena::Russia::Crawler は同一 URL に数千回もアクセスして何をしているのだろう? 謎は深まるばかりだ。

| @WWW

secondlife さんがはてなブログの記事などを引き払って新しいドメインで個人ブログを作ってた。

secondlife さんは今年の春まで世界一周旅行をしていて、ブログで旅行の様子を書いていたが、数ヶ月前に Google Photos が突如過去に Ticker API で取得した写真の URL を無効化して折角の旅行の写真が閲覧不能になるということが起こっていた。

同じ現象は cho45 さんもブログに書いている。

結局お二方とも Google Photos の利用をやめてしまったようだ。

ブログサービスやストレージサービスを使うと楽だが、サービス提供者の胸三寸で機能が利用できなくなってしまうことがある。

secondlife さんの冒頭の記事は「心のざわめき」についてがメインのテーマだ。記事を書く度にはてなブックマークでのリアクションを気にしてしまってよくないので、それらのリアクションから遠い場所に移転する、という趣旨だ。

プラットフォームが提供するサービスは便利だったりコンテンツを生産するモチベーションを与えてくれたりする反面、プラットフォームへの依存度が高まったり、ときにはプラットフォームがもたらすネットワーク効果が負の副作用をもたらしたりする。そういうのに疲れた人は自前でウェブサイトを運用するようになる。

ながしまきょうさんはもっと早くにプラットフォーム依存を脱却しようとしていて、一時は Twitter さえもやめて自サイトで Microblogging していた。

自分はプラットフォームに依存せずにこれまでブログをやってきた。一時期画像のアップロード先に Flickr や Google Photos を使うことを試したけど、最近の記事ではやめていて完全に自前だ。さくらインターネットや AWS といったインフラ部分では IaaS に依存しているが、オペレーションの部分は自分で行っている。ソフトウェアエンジニアとして腕を磨くのが目的だったけど、だんだんとプラットフォームの足かせから自由でありたいという理由が大きくなってきている。

プラットフォームの中でブログを書くと、自分の書いたものがプラットフォームの中の一コンテンツでしかなくなってしまう。自分のブログなのにどこかの知らない人が書いた記事が「関連記事」として表示されてしまう。自分はそういう場所には違和感がある。

インターネットの端っこにいる人たちから「ウェブ縄文時代」に退行していくのではないかと思う。

| @技術/プログラミング

外出自粛かつ自分自身にコロ助疑惑があったのでずっと部屋に閉じこもってサイトのデザインをいじってた。

2020-04-07 デザインアップデート.jpg

上がこれまでで、左下が今後、右下が画像がワイドな場合のバージョン。これまで横幅 1280px 想定にして画像も横幅 1280px のサイズで表示するようにしていた。

ただしこれだと写真は見やすくても文字が読みにくい。人間の目は 1280px 繰り返し左右に移動させるのには適していないようだった( N=1 )。

そういうわけでまたまた cho45 さんのブログのレイアウトをパクって、文章は横幅短めに、写真はでかいサイズで表示するようにした。

文章部分の幅を 800px にして、画像を読み込んだときに一定の条件にマッチしたら写真の横幅を 1280px で表示するようにした。 margin-left: -250px; してるのがミソ。横幅 800px だと大分文章は読みやすいし、でかい写真は大きく見えて便利。

@media screen and (min-width: 1422px) {
  #content #main article .body > p {
    img[class~="large"] {
      width: $content-max-width;
      max-width: $content-max-width;
      margin-left: -250px;
    }
  }
}

JS はこんな感じのコードを書いた。

const checkImageSize = (target) => {
  if (typeof target === 'undefined') {
    return;
  }
  const width = target.naturalWidth;
  const height = target.naturalHeight;
  const isPhoto = RegExp('(lh3\.googleusercontent\.com|\.jpe?g$)').test(target.src);
  if (width > 1279 && width > height && isPhoto) {
    target.classList.add('large');
  }
}

const lazyImageObserver = new IntersectionObserver((entries, observer) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const lazyImage = entry.target;
      lazyImage.src = lazyImage.dataset.src;
      lazyImage.addEventListener('load', (event) => checkImageSize(event.target));
      lazyImageObserver.unobserve(lazyImage);
    }
  });
});

const selectors = "#content article .body img, #content article .similar img";
const lazyImages = [].slice.call(document.querySelectorAll(selectors));

if ("IntersectionObserver" in window) {
  for (let image of lazyImages) {
    const src = image.src;
    image.dataset.src = src;
    if (lazyImages.indexOf(image) === 0) {
      const promise = new Promise(resolve => resolve(image));
      promise.then(checkImageSize).catch(setTimeout(checkImageSize, 100))
      continue;
    }
    image.src = "";
  };

  lazyImages.forEach(lazyImage => {
    lazyImageObserver.observe(lazyImage);
  });
}

画像の読み込みが起こったタイミングにフックして画像のサイズチェックを行い、条件にマッチしたら img タグに class を追加し、 CSS のメディアクエリと合わせ技で大きく表示するようにしている。以前やった画像の遅延読み込みのコードを改良した。

その他、 HTML 5 対応が中途半端だったのでマークアップを見直して、適度に <header><secition><article><footer> を使ってマークアップし直した。 このせいだと思うけど Google Adsense の自動広告が差し込まれる位置が変わった。これまでヘッダーの下に入り込んでいたやつが <header><article> の間に入るようになった。

今後も外出禁止が続いたらひたすら自分のブログをいじってしまう予感。こういうエネルギーを仕事とか個人サービス開発とかに当てられるとよいのだろうけどなかなかそういう方向には向かない。

| @ブログ

Rubbish

ブログで使ってる Amazon S3 のバケットの画像のほとんどをミスって空ファイルにしてしまった…。 S3 に上がっている画像、 Cache-Control ヘッダーが付与されていないのですべての画像ファイルに Cache-Control ヘッダーを付与しようとしての事故だった。ファイル一覧を取得して AWS SDK Ruby で Cache-Control だけ付与するつもりだったのにファイルそのものを空で上書きしてしまって無となった。

大した操作じゃないと思って事前にバックアップを取っていなかった& S3 に上げておけば安心だと思って日常のバックアップも行っていなかった。スーパーアホ。写真ならアップロードし直すことは可能だけど、キャプチャとか、アニメーション Gif とか、ブログの内容に合わせて OmniGraffle で描いた画像は基本的には元のファイルが残ってなくて元に戻すことができなかった…。

CDN に残っていたものとローカルでキャッシュされていたものを探したが、 500 ファイル以上が失われてしまった。つらい…。

画像の吹っ飛び、過去にも何回か起こっている。レンタルサーバーの障害で消えたパターンもあったけど、自分のミスで消してしまうパターンが多い。自分が一番信用ならないので画像はプロに管理してもらうのが一番だなと身にしみて思った…。とりあえず S3 バケットのバージョンコントロールを有効にしたいと思います…

追記

AWS SDK Ruby での正しい Cache-Control ヘッダー付与の仕方は以下だった。

object.copy_from(object, cache_control: 'max-age=2592000,s-maxage=31536000', metadata_directive: "REPLACE")

#copy_from の引数に medata_directive: "REPLACE" を渡す必要がある。

🙅🏻‍♀️🙅🏻‍♀️🙅🏻‍♀️以下は NG なので注意されたし ☠️☠️☠️

object.put(cache_control: 'max-age=2592000,s-maxage=31536000')

これやるとファイルの body が空になります 🈳🈳🈳

| @技術/プログラミング

キャッシュ

CloudFront 転送量削減の試みで色々やっていて、そういやウェブサイトのキャッシュについての説明記事っぽいの読んだことないし、開発現場だと「じゃあキャッシュすればいいじゃん」みたいな発言が行き交うけど、ひとくちにキャッシュと言っても色々あって、ウェブ開発始めたばかりの人にはよく分からないんじゃないかと思ったので書いておきます。

キャッシュの目的

キャッシュの目的.png

キャッシュの目的は大きく二つあって、コンテンツ表示の高速化とコストの削減がある。コンテンツの表示高速化は、時間のかかる処理の結果を捨てずに再利用して二回目以降の表示を高速化すること、コストの削減は、処理結果の再利用によってコンピューターの利用時間を減らしたり、データの通信量を減らすことをそれぞれ目的としている。表示高速化とコスト削減はどちらか一方だけを達成するものではなく、多くの場合で両方が同時に実現される。

キャッシュに向いているコンテンツ

キャッシュには向いているコンテンツと向いていないコンテンツがある。

キャッシュに向いているコンテンツ.png

ウェブサイトで配信するものに関して何でもキャッシュできるわけではなく、多くの人が共通して閲覧するものか、更新頻度が低いものでないとキャッシュしてはいけない。更新頻度が低くて多くの人が共通して閲覧するもの(画像、 CSS 、 JavaScript など)はキャッシュしやすい。だがショッピングサイトの購入履歴などは人それぞれなので一律にキャッシュしてはいけない。

キャッシュの種類

キャッシュにはブラウザーキャッシュとサーバーサイドのキャッシュがある。

キャッシュの種類.png

ブラウザーキャッシュ

ブラウザーキャッシュは、同じ人が繰り返し訪問するサイトで繰り返し利用されるサイト内画像や JavaScript 、 CSS (静的コンテンツ)に対して提供するのに向いている。二回目以降のアクセス時に前回端末にダウンロードした内容を使い回し、転送量を削減してユーザーの閲覧体験を高速化する。

ブラウザーキャッシュ図解.png

同じ人が繰り返し訪問しないタイプのサイト( SNS でバズって多くの人が訪れるが、一ページ見ただけで離脱するようなサイト)ではブラウザーキャッシュをフルに効かせても転送量の削減効果はない。逆に一度訪れた人が何度も訪れるような再訪率の高いウェブサイトでは、静的コンテンツに対して Cache-Control ヘッダーを付与することでブラウザーキャッシュを効かせ、転送量の削減とコンテンツの表示速度を高速化させることができる。

ブラウザーキャッシュを聞かせるには、適切な HTTP ヘッダー( Cache-Control ヘッダーなど)を付けてレスポンスを返せばよい。

サーバーサイドキャッシュ

サーバーサイドでキャッシュできるのは多くの人が共通して閲覧する、更新頻度の低いデータだけということに注意が必要。ログインが必要なサイトで、アクセスする人によってコンテンツを出しわけないといけないようなシステムではサーバーサイドのキャッシュはあまり利用できない。

サーバーサイドキャッシュ図解.png

サーバーサイドのキャッシュには転送量の削減効果はない(少なくとも自サイト訪問者との通信では)が、ランキング集計処理や外部 API の呼び出しなど CPU への負荷や時間がかかる処理の結果をキャッシュし、コンテンツの表示を高速化したいときに有効。

例えばこのサイトでは Amazon のアフィリエイトを利用しているが、 Amazon の API にリクエスト回数制限やリクエスト間隔制限があるので適度にキャッシュを行い、リクエスト回数制限に引っかからないようにしている。

サーバーサイドのキャッシュのやり方は色々ある。アプリケーションにキャッシュするためのコードを書いてメモリ上に保持したり、テキストファイルに保存したり、データベースに保存したり、 Redis などを使ったり。ウェブサーバーのキャッシュを使う方法もある。このブログでもいくつかのキャッシュ機構を組み合わせている。

アプリケーションキャッシュ.png

表示に関わる部分の一部をキャッシュする場合や、キャッシュの無効化をアプリケーションでコントロールしたい場合はアプリケーションでキャッシュする必要がある。

ウェブサーバーキャッシュ

レスポンス全体をキャッシュしてよい場合にはウェブサーバーのキャッシュを使うとよい。その方がアプリケーション層までリクエストが届かず、コンピューターリソースを節約できる。

様々なキャッシュの組み合わせ

更新頻度が低く、多くの人が共通して閲覧するコンテンツはサーバーサイドでもキャッシュできるしブラウザーキャッシュを効かせることができる。画像、 CSS 、 JavaScript などがそれで、これらのファイルはよく CDN ( Content Delivery Network )から配信される。 AWS だと CloudFront というのがある。この手のサービスはサーバーサイドのキャッシュとブラウザーキャッシュを効かせることに特化したもので、コンテンツの転送量を抑えつつウェブサイトの表示速度を高速化してくれる。

CDN もそうだが、サーバーサイドキャッシュとブラウザーキャッシュを併用すると、コンテンツを更新したいときに問題が出てくる。画像ファイルを新しいものに差し替えたが CSS が更新されず古いキャッシュが参照され、画像が非表示になってしまったり、ということが発生する。そういうことが起こらないように、 HTTP にはいくつか仕組みがある。

CDN だと ETag も自動付与してくれて、コンテンツの衝突を抑えてくれる。 CDN はよくできているのでよく分からない人はまずは CloudFront を使って勉強してみるとよいだろう。

特に注意が必要なのがアプリケーションキャッシュとウェブサーバーキャッシュの組み合わせだ。異なるレイヤーでキャッシュを効かせてしまうと、キャッシュを無効化したいときに狙い通りに無効化されず、意図しない障害になってしまったりする。コンテンツの特性に応じて、キャッシュは一つのレイヤーで行うようにするとよいだろう。画像や CSS はウェブサーバーでキャッシュし、更新頻度が低いが動的に生成される JSON はアプリケーションレイヤーでキャッシュするなど。

まとめ

  • ウェブサイトのコンテンツは何でもキャッシュすれば良いわけでない
  • サーバーサイドのキャッシュとブラウザーキャッシュの違いを理解する
  • 多段キャッシュに注意(特にアプリケーションキャッシュとウェブサーバーキャッシュの併用)

| @技術/プログラミング

DSC_4022.jpeg

Amazon Product Advertising API ( PA API )が 5.0 になるらしい。 4.0 は 3 月で廃止になるそうだ(当初は 2 月 11 日と言われていたが、 3 月まで伸びたみたい)。

最近 4.0 に対応させたのにな、と思って調べてみたら何と 4.0 対応したのは 10 年以上前だった。

PA API 4.0 までは AWS アカウントで利用する感じ1だったが、 PA API 5.0 では AWS から独立して Product Advertising API 専用のアカウントを登録しなければならないようだ。

AWS は US Amazon でアカウントを作るのに PA API のレポート画面へのログインでは Amazon Japan のアカウントを使うのは変だなと思っていたけど、 PA API 専用アカウントを設けることでその辺のねじれも解消されるだろう。

クライアントライブラリにはこれまで ecs という gem を使ってきたが、 PA API 5.0 対応はされてなくて、別の人が作った vacuum という gem に乗り換えた。

ecs という gem の名前に違和感をもつ人がいるかもしれない。いまでは Amazon で ECS といえば AWS の ECS ( Elastic Container Service ) のことを指すが、昔は Amazon 自身が PA API という名前ではなく Amazon ECS という名前でアフィリエイト用のシステムを提供していた。 Amazon は命名が色々紛らわしい。

PA API 5.0 は RESTful API ではなく GraphQL のような感じで、欲しいフィールド名を指定して API リクエストする感じになっている。レスポンスのサイズが小さくなって便利になった。

ちなみに移行ガイドは以下にあります。

DSC_4119.jpeg


  1. 10 年前にペパボの面接を受けに行ったときに「 AWS 使えますか?」と聞かれて「はい、使えます。アフィリエイトで小銭を稼いでいます」と答えてしまった。