#
814
どうも、こんばんは。zenncastのお時間です。パーソナリティのマイクです。
今日は二千二十六年八月十三日、木曜日の朝七時台、お届けしていきます。
この番組では、技術系アウトプットプラットフォーム「Zenn」から、今日のトレンド記事をピックアップして、ゆるっと、でも中身はしっかりめにご紹介していきます。

今日はお便りはお休みなので、そのぶん記事をじっくりめにいきましょうか。
きょう紹介する記事は、ぜんぶで五本です。メモリリークっぽいmacOSの話、Nプラスいち問題の再入門、Webサービスの「撤退戦」、JavaScriptエンジンVエイトの最適化、そしてWSLツーの入門Tipsまで、幅広くいきますよ。

それでは、さっそく一つ目から紹介していきます。

まず一つ目。
macOSを使っていて、「CursorUIViewService」という謎のプロセスが、じわじわとメモリを食い続ける、というちょっと怖い現象を扱った記事です。
このプロセス、場合によってはメモリ使用量が十五ギガバイトくらいまで膨らんでしまうこともあって、実質、メモリリークみたいな挙動をすることが報告されています。

名前から、コードエディタの「Cursor」と関係あるのかな、って思いがちなんですが、まったくの別物で、Apple純正のサービスです。
何をしているかというと、テキストカーソルの近くに、Caps Lockのオンオフとか、入力ソースの状態をポップアップで出してくれる「新しいテキストカーソル機能」を提供しているサービスなんですね。
キーボードの上の方に「Caps Lockオン」って出たり、かな入力と英数入力の切り替えを、カーソルのあたりにちょこっと出してくれる、あれの裏側を担当しているのが、このCursorUIViewServiceです。

で、この記事のポイントは、この問題への対処法。
launchdの設定をごりごりいじるような、ちょっと危ない手ではなくて、UIKit側の「Feature Flag」の設定ファイルを変更することで、安全めに無効化してしまう、というアプローチを紹介しています。
具体的には、`redesigned_text_cursor`っていうフラグの「Enabled」をフォルスにして書き込んで、それからMacを再起動すると、「新しいテキストカーソル機能」自体がオフになります。
この機能がオフになると、連動してCursorUIViewServiceプロセスも立ち上がらなくなるので、メモリを食い続ける問題そのものを避けられる、というわけです。

機能を切ってもどうなるかというと、カーソル付近に出ていたCaps Lockや入力ソースのインジケーター表示が消えるだけで、Caps Lockキーそのものや、通常の日本語入力・英語入力は、そのまま普通に使えるとのことです。
また、この設定はターミナルからコマンドで削除すれば元に戻せるので、「ちょっと様子を見たい」「やっぱり新しいカーソル表示が欲しい」という人も、試しやすい形になっています。
メモリがなんか最近やたら食われてるな、っていうmacOSユーザーの方は、一度アクティビティモニタで「CursorUIViewService」チェックしてみるといいかもしれません。

。。.。。.。。.

続いて二つ目。
こちらは一覧ページなんかで発生しがちな「Nプラスいち問題」を、実際に発行されるSQLの本数をちゃんと数えながら理解し直そう、という記事です。
使っているORMはSQLAlchemyなんですが、ORM全般に通じる話になっています。

Nプラスいち問題って、理屈ではだいたい知ってる人も多いと思うんですが、記事では、あえて「何本SQLが飛んでるのか」を実測することで、理解を深めています。
ORMは、関連データをその場で必要になったときに取りにいく「遅延ロード」、レイジーロードですね、これをするので、例えば「著者a.books」みたいな属性アクセスがループの中にあると、見えないところでクエリがバラバラに発行されていきます。
結果として、「一覧を一気に出してるはずなのに、実は一ページの表示で、親オブジェクトを取るクエリが一本、そのあとに子を一件ずつ取りに行くクエリがN本、合計で一本プラスN本のSQLが走っている」という状態になっていることを、ちゃんと数値で示してくれているんですね。
これによって、DBとの往復回数はレコード数に比例して増えてしまう、というのが体感を伴って理解できます。

そこで登場するのが、`joinedload`と`selectinload`という二つのロード戦略です。
`joinedload`は、JOINを使って関連データを一気に取る方法、`selectinload`は、まず親を取って、そのあとでIN句を使って子をまとめて取る方法です。
記事では、とくに一対多とか多対多の関係だと、JOINすると行数が「親の件数×子の件数」みたいな掛け算で増えてしまうので、レスポンスとして返ってくる行がすごく膨らみがちだ、という欠点を指摘しています。
それに対して`selectinload`のようなやり方だと、「DBとの往復回数は少なくしつつ、返ってくる行数は実際のデータ量に近いままに保てる」ので有利だ、という説明がなされています。

さらに、「そもそも本当に全件必要なのか」という視点も大事だとしています。
一覧表示でも、場合によっては集計クエリだけで済ませて、詳細は別途取りにいく、みたいな設計にした方が、Nプラスいちを根本から避けられるケースもあるよ、という話ですね。

怖いのは、Nプラスいちが、アプリケーションのけっこう見えにくいところに潜んでいること。
シリアライザの裏側とか、テンプレートエンジンの中、あるいは「便利プロパティ」として追加したアクセサの中などに隠れていて、普段のページング付きの画面では気付かない。
ところが、ページングを外した「全件CSV出力」とか、バッチ処理、あるいは一度に大量のデータを扱う管理画面なんかで、急に表面化して、時間が爆発的にかかることがあります。
また、相手がデータベースではなく外部APIだと、これがレート制限の上限に引っかかったり、クラウドのLambdaみたいな環境だと無駄な課金やタイムアウトにつながる、という具体例も挙げられていました。

最後に記事では、Nプラスいち対策として「SQLを数える」という姿勢を強調しています。
ログやフックを使って、どのエンドポイントで何本SQLが流れているかを可視化し、その上でテストの中に「このAPIはSQL何本以内」という制約を埋め込む。
そうすることで、クエリの本数、つまりDBや外部サービスとの往復回数を、データ件数に比例するNではなく、定数の範囲に抑えることができる、というまとめになっています。
ORMに任せきりにせず、「ちゃんと数えてみる」がキーワードの、良い再入門記事でした。

。。.。。.。。.

三つ目。
こちらはちょっと切ないテーマ、「Webサービスを終わらせるときの撤退戦」についての実例と反省を語った記事です。
キラキラした新機能追加ではなく、「サービスが持っていた責任をどう順番に片付けるか」という視点で書かれています。

サービスを作るフェーズって、SaaSや外部サービスをどんどん足していくのがわりと気軽なんですよね。
認証はこのサービス、決済はこのプロバイダ、メール配信はこのSaaS、ログはあっち、みたいに組み合わせていける。
ところが、いざ終わらせるとなると、やることが山ほどある、という話が展開されていきます。

具体的には、まず新規登録を止めて、課金を止めて、書き込みを止める。
それから読み取り専用モードにして、最終停止に向けた準備をしていく。
最後にインフラやデータを削除して、本当にサービスの責任を終わらせる——という流れです。
でも現実には、「UIだけ」でボタンを消して済ませてしまいがちで、実は裏側のAPIや認証、Authまわりが生きていて、「URLを直叩きしたらまだ動いてしまう」といった問題が起きたりします。

記事では、cronジョブやバッチ処理をちゃんと止めたつもりが、デプロイ成功を過信して、実はまだ動いていた、とか、思った通りに止まっていなかった、といった「コケた」実例も紹介されています。
決済まわりでは、Webhookをちゃんと止めていなかったり、APIキーを無効化し忘れると、サービスを閉じたはずなのに責任がずっと残り続けてしまう、という怖さがありますよね。
ユーザーのお金や、メール送信、個人情報を扱っている場合は、なおさら慎重さが求められます。

最終的にこの記事では、終了日には「本当に死んでいるか」を外から確認しよう、という教訓が語られています。
つまり、サーバーサイドの設定を見て安心するだけじゃなくて、HTTPレスポンスを実際にcurlで叩いてチェックして、「外から見たときに、ちゃんと終了状態になっているか」を確認する、ということですね。
また、ユーザーデータ、決済履歴、ソースコードを、同じ方針で一気に片付けてしまうのではなく、それぞれに応じた扱い方を考えるべきだ、としています。
ユーザーデータは削除すべき一方で、決済履歴は法的な観点から一定期間の保管が必要だったり、ソースコードはアーカイブしておきたい、といった違いがありますからね。

そして、印象的なまとめが、「デプロイして終わり」ではなく「アーカイブするまでが個人開発」という一文です。
作って公開するところまでではなく、終わらせ方までちゃんと面倒を見ることが、プロダクトに対する責任だよ、というメッセージがこもった記事でした。

。。.。。.。。.

四つ目。
ここからはJavaScriptエンジンVエイトのパフォーマンス改善のお話です。
テーマは、普段あまり存在感のない配列メソッド`copyWithin`。
この`copyWithin`を、ChromiumのJavaScriptエンジンVエイトで、最大およそ四百五十倍速くするように最適化していった、その中身が紹介されています。

`copyWithin`は、同じ配列の中で要素の一部を別の位置にコピーするメソッドで、コピー元とコピー先が配列の中で重なるケースもあるし、「穴」、つまり存在しない要素、ホールの扱いもあるし、さらにはプロトタイプ継承の問題も絡みます。
素直に実装しようとすると、一要素ごとに「このインデックスにプロパティはあるか」と`HasProperty`を呼び、あれば`Get`して、`Set`して、場合によっては`Delete`して……という、非常に重たいループになってしまうんですね。

そこで著者は、配列が「Fast JSArray」、つまり連続したメモリ領域に要素が並んでいる、いわゆる普通の配列の場合に限って、もっと低レベルで効率的な処理をする「fast path」を追加しました。
具体的には、backing storeと呼ばれる実際のメモリ領域上の範囲を、`MoveElements`という仕組みを使って、一気に移動させるようにします。
この`MoveElements`の中では、C言語の`memmove`が使われていて、オーバーラップを含めて安全かつ高速にコピーを行ってくれるので、JavaScriptレベルで一要素ずつ処理するより、桁違いに速くなります。

もちろん、なんでもかんでもfast pathに乗せるわけにはいかないので、条件チェックが工夫されています。
まずPACKED配列、つまりホールのない連続した配列であれば、無条件でfast pathを使います。
HOLEY配列、ホールありの配列の場合は、`Array.prototype`が改変されていないこと、プロトタイプチェーン上にインデックス付きの要素が追加されていないことなどをチェックしたうえで、はじめてfast pathを有効にします。
こうすることで、プロトタイプ側に定義したゲッターやセッターが、意図せずスキップされてしまう、といった挙動の違いを避けているわけですね。

さらにややこしいのが、引数の評価中に`valueOf`経由で配列のlengthが書き換えられるような、トリッキーなケースです。
こういう場合でも安全を保つために、memmoveを呼ぶ直前に、インデックスやlengthをもう一度検証し直すようになっていて、「JavaScriptならではの動的さ」による罠を避けています。

その結果、百万円素の配列、つまり百ミリオンではなく一ミリオン、百万円素くらいの大きな配列を`copyWithin`した場合に、従来実装と比べて数百倍の高速化が得られた、というベンチマークが出ています。
ほかのJavaScriptエンジン、たとえばJavaScriptCoreと比べても、さらに速いケースが多いとのことでした。
標準ライブラリの、しかも影が薄めなメソッドでも、実装の工夫次第でここまで性能が上がる、という好例になっている記事です。

。。.。。.。。.

そして最後、五つ目の記事。
こちらはTips系で、WindowsでWSLツーを使った開発環境を、一から整えていく手順を、初心者でも迷わないレベルで丁寧にまとめた内容になっています。

まずはWSLツーのインストールからスタートします。
PowerShellでどのコマンドを打てばいいのか、どの画面で何をクリックすればいいのか、といったレベルで案内してくれていて、その流れの中で、Ubuntuの初期設定とパッケージの更新までを、一気に進めるような構成です。
「とりあえずUbuntu入れたけど、そのあと何をすればいいのか分からない」という、最初のつまずきをうまく潰してくれる記事ですね。

続いて、Ubuntu上でGitをインストールしていきます。
Git本体のインストール方法から、ユーザー名とメールアドレスを設定するコマンドまで説明されていて、そのあと、SSHキーを作ってGitHubに登録するところまでカバーされています。
公開鍵をどうやって取り出すのか、その文字列をGitHubのどの設定画面に貼り付ければいいのか、という細かいところまで書いてあるので、初めてSSHを触る人でも、「GitHubとSSHで安全に接続できる状態」までたどり着けるようになっています。

さらに、GitHub CLI、`gh`コマンドですね。
このリポジトリをどうやって登録してインストールするのか、そのあと`gh auth login`でGitHubアカウントにログインする手順まで紹介されているので、ターミナルだけでリポジトリの操作をする準備が整います。
ブラウザと行ったり来たりせず、コマンドライン中心でGitHubを使いたい人には、うれしいステップです。

おまけ的な内容も充実していて、たとえばGitコマンドのエイリアス設定。
`checkout`を短くしたり、`status`を一文字で呼べるようにしたり、といった、よく使う操作を短縮コマンドにして、作業効率を上げる方法が紹介されています。
また、Zedエディタの紹介もありまして、軽量で今っぽいエディタを試してみたい人への導入になっています。

そして、開発者的には地味に嬉しいのが、日本語入力まわりのTipsです。
Windowsの日本語入力を、「変換キーで日本語オン」「無変換キーで英語オン」という動きにする設定や、Google日本語入力と`keymap.txt`を使って、さらに細かいキー割り当てをカスタマイズする方法がまとまっています。
これによって、プログラミング中に、日本語と英語を切り替えるときのストレスをかなり減らせるようになっていて、「環境構築から入力の快適さまで一気に面倒見ます」という、至れり尽くせりな記事になっていました。

。。.。。.。。.

というわけで、きょうのzenncast、紹介した記事をざっとおさらいしていきます。
まず一つ目は、macOSでCursorUIViewServiceがメモリを食いつぶす問題と、その原因になっている「新しいテキストカーソル機能」をFeature Flagでオフにして回避する方法のお話。
二つ目は、SQLAlchemyを題材に、実際にSQLの本数を数えながらNプラスいち問題を捉え直して、`joinedload`と`selectinload`の使い分けや、「クエリ本数をテストで固定する」という実践的な対策まで解説した記事。
三つ目は、Webサービスを終わらせるときの撤退戦をテーマに、新規登録停止から最終停止、データ削除まで、責任をどう順番に片付けるか、「アーカイブするまでが個人開発」というメッセージが印象的な記事。
四つ目は、配列メソッド`copyWithin`をVエイトで最大約四百五十倍速くした、fast path実装の工夫と、安全性のための条件チェックの話。
そして五つ目は、WindowsでのWSLツー環境構築から、GitとGitHub、SSH設定、GitHub CLI、さらには日本語入力のカスタマイズまで、開発環境まわりを一気に整えるTips記事でした。

気になる記事があった方は、ぜひショーノートから元の記事もチェックしてみてください。
この番組への感想や、「こんなテーマも取り上げてほしい」といったリクエストも、どしどしお待ちしています。

それでは、そろそろお時間です。
zenncast、パーソナリティのマイクがお送りしました。
次回もまた、この時間にお会いしましょう。ありがとうございました。

Related episodes

内容の近いエピソードを推薦しています