詩と創作・思索のひろば

ドキドキギュンギュンダイアリーです!!!

Fork me on GitHub

ISUCON12予選敗戦記(からあげネイティブ、3899点)

今回のISUCON12は惨敗だったのであまり記憶も残ってない……。

songmutoricls、motemen のメンバーでの挑戦も3度目となり、いよいよ優勝したい、そのために当然予選突破はしたい、という年だった。

今回も自分が構築、他の2人が作戦と実装、という役割分担。これはまあ、一年に一度の頻度でしかやってないなりに経験値がたまってきた。予選前にはきちんと予習をした年でもあって、そのおかげで初期構築はかなりスムーズにいったはず。1時間もせずにデプロイと計測ができる状態になったのは進歩だった。あとここ2回まともにやってなかった pprof に出番があったのもよかった。

今回の鬼門はやはり SQLite で、これをどうするか……というところで方針を強く決められなかったな~。

慣れてるし取り扱いやすい MySQL に移行するか、SQLite のまま進めてみるか、というところで複線で進めてしまって、MySQL 移行にチームの全力を注ぐことができなかったのがまずかった。たぶんどちらの方法でも予選突破は可能なのだと思うけど、SQLite のデータがかなりでかい、となったときに MySQL 運用の知識でサポートするみたいな動きが能力的にも思考的にも取れなかったのが根本的には敗因。

あとプロファイルを取るのは当然として、データの性質をちゃんと読み取ることができていないと打つ施策に意味がなくなってしまう。これ毎回思うんだけど、なかなか決まった時間のなかでやりきることができない。SQLite を MySQL 化するときに flock を愚直に消してしまって整合性が取れなくなり、そりゃそうかとなる、みたいな経験がしばしばある。

あとはやったことを羅列しておくぜ!

  • alp, pt-query-digest, pprof を順に取って、取ってる間にソースを見てうーんこれ SQLite どうしようかってなったところからスタート。
  • docker-compose は剥がさなかった。
  • テナントの頭文字でアプリケーションサーバ分けたりするかな~と思ってnginxの設定書いてみたがとくに使わず。
  • visit_history を Redis 化。
  • ランキングの N+1 除去。

手元構築可能になったら復習して、来年も優勝狙うぞ~。一年間この不完全燃焼で暮らすの嫌なので、社内 ISUCON や野良 ISUCON があったら招待してください。

GitHub - motemen/isucon12-qualifier

Chrome 拡張のポップアップが表示されるタイミングは onload っぽい

Chrome 拡張のポップアップがどのタイミングで表示されるのか、ちょっとググった限りでは分からなかったので調べた。 掲題の通りで、結論としてはまあそりゃそうだ、とはなるが、ポップアップ内で img や iframe により外部リソースを読み込んでるようなときは、すべてがロードし終わるまでユーザへの反応が何もなくなるので注意が必要そう。

ポイントになりそうなのは ExtensionPopup::DocumentOnLoadCompletedInPrimaryMainFrame で、これはコールバックとして呼ばれるっぽい。

// This method is invoked once the onload handler of the primary main frame's current document (i.e., |render_frame_host|) has completed. This happens when the primary main document has finished running onload events after loading all content (images, scripts, etc).

結果はシンプルだけど、ここに至るまでにいくらか読んで回った。Chromium のソースコードを読んだのは初めてだったけど、Chromium Code Search がよくできてて助かった。さすがにこれだけコードベースがでかいと、こういうツールも普通に必要になるわけだ。


検証用のリポジトリも一応作った。https://httpbin.org/delay/{delay} という遅れてレスポンスを返す便利エンドポイントがあるので、これを iframe で埋め込んでみている。await chrome.tabs.query や setTimeout を挟むと

GitHub - motemen/experiment-webext-popup-timing

20分でつくるChrome拡張 に刺激されて Chrome 拡張を爆速で作る方法を求めている。今回は Chrome だけじゃなくて webextension として対応したいなーと思って @samrum/vite-plugin-web-extension を使ってみたけど、Manifest Version 3 をターゲットにしたら結局 Firefox では見られなかった。Chrome による MV3 移行圧力とその他のブラウザのサポート具合がぜんぜん違って戸惑うな~。

ページ内のテキストを読めなくするChrome拡張を作った

世はまさにハイパーメディア時代。何をするにも視覚的なキャッチがないとやっていけない時代です。ブラウザのスクリーンショットを撮ることも多いでしょう。しかしプライベートな内容もそこに映り込んでしまうこともありがち。かといって画像をいちいち加工するのも面倒……というわけで、DOM操作によってテキストを隠す拡張を作りました。

GitHub - motemen/webextension-obfuscate-texts

2022-07-20 追記: Chrome Web Store に出ました

Obfuscate texts - Chrome ウェブストア

Manifest V3 で作ったせいでほかのブラウザは未対応。

スクリーンキャスト

Chrome extension: Obfuscate texts - YouTube

ページ内の要素を選択し、「Obfuscate」することで▗​▝​▌​▏​▇​▁みたいな文字列に置き換えます。スタイルシートがそのまま適用されるのでページに馴染みますね。

ほかにもいくつか様子を載せておきます。

以下は開発中に気づいたことや困ったことをランダムに。

文字幅を計算してる

やってることは見てのとおり、文書中のテキストをランダムなユニコードのブロック文字で置き換えているだけだけど、細かくいろいろやっている。ひと文字をひとつのブロック文字で置き換えると、元がアルファベットのときに横幅が広くなって元々の見た目と乖離するので、Canvas を使って文字の幅を計算してだいたい合うようにしている。

ゼロ幅スペース(U+200B)を挟む

同様にスタイルを崩さないために、文字にゼロ幅スペースを挟んでいる。これをしないと、うまいこと折り返しがおこなわれずに横長の要素が発生してしまう。

フレーム内のテキストは隠せない

Chrome 拡張の制限。ページ内のテキストの置き換えを行うにはページにアクセスする権限が必要だが、そのために最初からあらゆる URL(*://*/*)の権限を要求するのはあまりに行儀が悪い。activeTab なら、ユーザがアクションを起こしたタブのページに対してのみ権限をもらえて、これが適切。しかしこの場合フレーム内にはアクセスできないらしい。ページ内のテキストをすべて不可読にしようとしても、広告やソーシャルボタンだけ生き残っていて人類滅亡後の世界っぽくなる。

452944 - Add activeTab.allFrames permission - chromium

Shadow DOM 内のテキストも隠せない

これは Selection の都合らしい。最近シャドードムが増えてきたのでちょっと困る。

もともと3年前のアイデアだった

https://motemen.hatenablog.com/entry/2019/06/gas-taskcal のスクリーンショットを撮るのに同じようなことをしていたけど、このときは手作業でやってた。

はてなで一緒に働きませんか?