Diary of a Perpetual Student

Perpetual Student: A person who remains at university far beyond the normal period

パイオニアチャレンジ

という遊びをたまにやっている。MtG は全く関係なく、自分が Twitter に投稿しようと思ったネタツイを検索して、世界で初めてそのネタを思いついた人間かどうかを確かめるというものだ。

今日は「i~cloud~~」と歌う華原朋美がふと脳内再生されたので早速検索してみた。

こんなレベルではお話にならない。パイオニアチャレンジ失敗である。

自分の Twitter の履歴を検索しても、失敗した例はあれど成功した例は一つもない。凡才なので仕方ないね。

FY2023上半期を振り返る・広く浅く

こんな形で今後も public な振り返りを定期的に書いていきたいですね。

blog.arthur1.dev

ということで、2022年8月〜2023年1月の振り返りを書いていきます。

幅広い技術とコンポーネントに触れた

Mackerel は多くのコンポーネントから構成されています。Web のフロントエンド・バックエンドはもちろんのこと、裏には内製の時系列データベースがいたり外形監視やクラウドインテグレーションを実現するためのクローラーがいたりします。OSS として公開しているアプリケーションもたくさんあります。

サブコンポーネントに詳しいアプリケーションエンジニアや SREs と一緒のチームで働く利点を活かして、これら多くのソフトウェア・インフラに幅広く触れることができました。

今では自分がチームの中で相対的に、より細かい挙動・仕様を知っているコンポーネントや機能もあります。また、可観測性やコスト面を改善するアーキテクチャの提案もできました。最近の話だと、mackerel-agent の Windows での挙動について調査したり改修したりしています。問題を見つけ、取り組める形に分解するところまでは比較的できているので、今後は手早く安全に問題を解決し、価値を届ける力を身につけたいです。

技術面で言うと、Go 言語や Terraform が全く触れない状態から、ある程度のものを作ったりエコシステムを理解できている状態にすることができました。これは普段のチームの仕事だけでなく、開発合宿や個人開発でも積極的に触れることで身につけることができました。

コードレビューできるようになってきた

この半期はコードレビューの量・質を高めることを目指し、エンジニアメンターと目標設定をして取り組みました。上記で示したように幅広い範囲をキャッチアップできたこともあり、目指した通りにレビュー力を上げることができました。

これは index が張られたようなイメージで、「このレビューをするために見なければならない情報はここにある」という知識が脳の引き出しに入っていて、それを取り出してレビューするのにかかる時間を短くすることができました。

「良い設計」に関してはテックリードの考える所にはまだまだ遠く辿り着けていないなあと思っています。「安定したコードが不安定なコードに依存してはならない」と言うのがシンプルに本質を表す金言だなと感じていて、この言葉を噛み締めて開発に取り組んでいきます。

「監視」が分かってきた

入社してすぐの、グラフ定義で選べる単位を追加する*1というタスクに取り組んでいた頃は、監視というドメインへの理解がかなり浅かったなと思います。Mackerel の中の一機能への理解と、それを実現する技術のキャッチアップで精一杯でした。

日々の仕事の中で、自分が開発者として何をどう監視したいかと言うイメージが湧いてくるようになりました。こういったものを可視化したい、監視したい!と思い、メトリック化したりダッシュボードを整備したり、と言う動きもできました。

そうやって Mackerel を利用する主体になって初めて、Mackerel に対する夢・アイデアがどんどん膨らんできました。ユーザー目線に立って、PBI (Product Backlog Item) が生み出す価値について言語化したり、価値を理解した上で実現方法を取捨選択することもできました。

社内/外のアウトプット

社内向けのアウトプットにしては、自分が選んだ選択や仕事で行った創意工夫のベースにある思考を言語化して共有する機会がありました。また、ほたてに小ネタを持ち込んだり、Slack で技術に関する議論を巻き起こしたりなど、アウトプット・情報交換の場を盛り上げることも併せてできたかなと思います。

社外に関しては、Mackerel Advent Calendar 2022 の運営と、参加者としてエントリを投稿したのが一番大きかったです。まとめエントリも書きましたので、Mackerel ユーザの方はぜひお読みください。

blog.arthur1.dev

また、先日開催された Hatena Engineer Seminar に登壇しました。先ほど Mackerel に対する夢について書きましたが、Mackerel チームの夢の膨らませ方とその実現について話したので、よろしければご覧ください。

www.youtube.com

まとめ

総括すると、広く浅くさまざまなことに取り組んだ半期だったなと思います。来期どういう風に生きたいかはまだあんまり考えていないのですが、とにかく価値提供したい、という気持ちが今は強いです。

そしてありがたいことに、半期末の納会で新人賞を頂きました。来期も頑張ります。

*1:cf.) 時間やネットワークトラフィックの単位が追加された Mackerel でネットのスピード監視 - Diary of a Perpetual Student https://blog.arthur1.dev/entry/2022/12/01/000100

パフォーマンスだけを考えると WMI を使わないほうが良いのかもしれない

OS 名は WMI から取得したほうが良いが……

前にこんなエントリを書いた。

blog.arthur1.dev

このエントリを要約すると、Windows の OS 名を取得するとき、レジストリにアクセスするのではなく WMI を使って取得しよう。なぜなら、Win 11 が Win 10 として認識されてしまうから」という内容である。

mackerelio/mackerel-agent には、WMI を利用せずに基盤の情報を取得している箇所がいくつかある。たとえば、レジストリの値を参照していたり、dll を読んで Windows API を呼び出したりしている。こういった場所も WMI に置き換えたほうが良いのだろうか。

それは、現状持ち合わせている情報では分からない、というのが誠実な答えになるだろう。OS 名に関してレジストリの値が信用ならないことは分かったが、それが一般に成り立つとは限らない。

Windows の細かい話は素人にはよく分からないので総合的な良し悪しはあまり語れないが、今回はわかりやすいパフォーマンス面での比較をしようと思う。

パフォーマンス比較実験

実験のために書いたプログラムは以下のリポジトリで公開している。

github.com

実験設定

比較対象は3つの手法である:

  1. dll を読み込み Windows API を呼び出す手法
  2. WMI に WQL クエリを投げる手法
  3. レジストリの値を読み出す手法

この3つの手法それぞれで同等な情報を得られるものを探した結果、プロセッサのアーキテクチャを取得させることにした。

各手法の実装は、mackerelio/mackerel-agent で利用している util パッケージを import する、もしくは同等のコードを書くことによって行った。

ベンチマークには Go 言語に標準で付随する testing パッケージの Benchmark を利用する。

比較対象は、自分所有の PC *1において以下のコマンドで benchmark を実行したときに表示される ns/op (関数1実行あたりにかかったナノ秒)である。

go test .\windows\ -bench .

実験結果

上記コマンドを実行すると、以下のような出力が得られた。

> go test .\windows\ -bench .
goos: windows
goarch: amd64
pkg: github.com/Arthur1/windows-api-benchmark/windows
cpu: 12th Gen Intel(R) Core(TM) i7-12700F
BenchmarkGetProcessorArchitectureFromApi-20     1000000000
--- BENCH: BenchmarkGetProcessorArchitectureFromApi-20
    windows_test.go:9: 9
    windows_test.go:9: 9
    windows_test.go:9: 9
    windows_test.go:9: 9
    windows_test.go:9: 9
    windows_test.go:9: 9
BenchmarkGetProcessorArchitectureFromWmi-20     1000000000               0.01329 ns/op
--- BENCH: BenchmarkGetProcessorArchitectureFromWmi-20
    windows_test.go:14: 9
    windows_test.go:14: 9
    windows_test.go:14: 9
    windows_test.go:14: 9
    windows_test.go:14: 9
    windows_test.go:14: 9
    windows_test.go:14: 9
BenchmarkGetProcessorArchitectureFromReg-20     1000000000
--- BENCH: BenchmarkGetProcessorArchitectureFromReg-20
    windows_test.go:19: AMD64
    windows_test.go:19: AMD64
    windows_test.go:19: AMD64
    windows_test.go:19: AMD64
    windows_test.go:19: AMD64
    windows_test.go:19: AMD64
PASS
ok      github.com/Arthur1/windows-api-benchmark/windows        0.782s

この結果を見ると、Windows APIレジストリを用いる手法では ns/op が表示されないほど短い時間で実行できているが、WMI を用いる手法だと1実行あたり 0.013 ns ほど掛かっているということが分かる。

どこで詰まっているか(詰まっているというほどではないと思うが)気になったので、pprof を使って詳しい様子を観察してみた。概ね、WMI にクエリを投げるのに相当するシステムコールに時間が掛かっている、といったところだろうか。

まとめ

今の mackerel-agent の実装をすべて WMI を使う形に置き換えればいいじゃんと思っていたのだが、パフォーマンスを比較すると WMI が劣っているということを知った。劣っているといっても 0.1 ns にも満たない程度なので、(クエリにもよるだろうが)通常の利用の範囲では問題ないように感じる。

つまり、安定して情報が取得できるか、得た情報が正しいかどうか、などの他の軸に重きをおいて手法を評価したほうが良いということになる。冒頭に紹介したエントリでは得た情報の正しさを鑑みて WMI を選択したが、それに関しては良い選択だったのではないかと思う。

Windows に詳しい人はぜひいろいろ教えてほしい。

*1:Z690 / Core i7-12700F / RTX3080 / DDR5-4800 32GB の自作 PC。OS は Windows 11 Pro

GitHub Actions の ::set-output 脱出 上級編 便利ツールでカバーできないケース

::set-output 脱出 進んでますか?

github.blog

エンジニアで GitHub Actions を利用している人はだいたい知っているであろう、 ::set-output::save-state の非推奨化問題。このコマンドを利用していると2023年5月末にはエラーになってしまう予定となっています。

面倒がっているエンジニアのために、巷には便利なツールがあります。

一つは rhysd/actionlint です。::set-output のような非推奨機能を使っていると actionlint の検査に引っかかります。こちらについては過去記事で紹介したのでご覧ください。

blog.arthur1.dev

もう一つは azu/set-env-to-github_env です。このコマンドをインストールして実行すると、workflow ファイルを新しい書き方に書き換えてくれます。便利。

今日は、残念ながらこれらのツールでは引っ掛からない例とその対応策をご紹介します。

スクリプトファイルを呼び出しているところに注意

mackerelio/mackerel-agentGitHub Actions の結果を見ていると、Annotation で warning が大量に表示されていることに気づきました。利用している action の更新をしたり、actionlint を実行して非推奨な記述を見つけたりして修正したのですが、まだ ::set-output 使っているよ、という warning が表示されていました。

actionlint に引っ掛からなかったのに出るのはおかしいな、と思って、::set-output で文字列検索を掛けたところ、こんなコードが引っ掛かりました

(async function () {
  const version = "1.2.3"; // ここは本当は parse してアレコレしている
  console.log("::set-output name=VERSION::" + version);
})();

そして、この Node.js のスクリプトを Workflow 中で呼び出していました。

- run: node _tools/parse_version.js

なるほど!workflow から呼び出した外部のスクリプトが標準出力に ::set-output と書き込んでいたのに actionlint だと気づけなかった、というわけです。これは先ほど紹介した set-env-to-github_env でも無理だと思います。

解決策: actions/github-script を咬ます

先程のスクリプトを直すのには工夫がいります。新しいやり方では標準出力は利用せず専用のファイルに書き込まなければならないので、 console.log() を用いることはできません。例えば、以下のような方法で新しい方法に対応することができます。

const fs = require('fs/promises')

(async function () {
  const version = "1.2.3";
  const file = process.env.GITHUB_OUTPUT;
  if (file === undefined) {
    process.exit(1);
  }
  await fs.appendFile(file, `VERSION=${version}`);
})();

これは美しくないし、今後さらに変更があったときに見つけづらくなるなあと悩んでいたところ、 id:stefafafan さんが @actions/coresetOutput() という関数があるよ、と教えてくれました。これなら、仮にさらに新たな仕組みになったとしてもライブラリの更新に追従すればよいので良さそうです。

しかし、この npm ライブラリを利用するためだけにちょっとした js ファイルを bundle して生成するのは大袈裟です。少し調べていたところ、actions/github-script という公式の action に行き着きました。この action を経由して Node.js のスクリプトを動かすと、GitHub API や Workflow context を利用するためのライブラリを利用することができるようになります。

Workflow の yml と呼び出しているスクリプトを以下のように書き換えました。

- use: actions/github-script@v6
  with:
    script: |
      const script = require('./_tools/parse_version.js')
      await script({ core })
module.exports = async ({ core }) => {
  const version = "1.2.3";
  core.setOutput("VERSION", version);
};

すると、無事 warning が消え、かつ workflow の output を設定することができました!

まとめ

GitHub Actions の ::set-output 脱出の際には、以下の事項に気をつけましょう。

  • Workflow 定義外のスクリプトファイルを呼び出して ::set-output と標準出力に書き出しているところに気をつける
  • それが Node.js のスクリプトの場合は、actions/github-script 経由で呼び出すようにすると、setOutput() 関数が用意されたライブラリを利用可能

聞き手と共有しているコンテキストを意識して伝え方を選ぶ

自分は以下の2点を理由に、外国語を直訳したような堅い言葉で話すことがある。

  • ボードゲームの翻訳をしている
  • 日常の思考を日本語ベースで行っていないことがある

英語を翻訳していると a, an と言う冠詞に何度も遭遇する。日常会話では何も気にせずに済むのだが、ボードゲームの翻訳となるとそうはいかない。日本語に翻訳した後に、複数なのか単数なのかと言う情報が欠落する可能性があるからだ。ボードゲームに限らず一般にゲームをプレイする上で、ルールの曖昧さ・解釈の不一致はプレイへの集中を妨げる大きな障害となる。

前提とするコンテキスト(文脈を超えて、場や間といった概念も含まれるだろう)の大きさをいかに小さくするか、それはある意味正義なのだが、ある意味では正義ではない。表現として限りなく正確だったとしても、受け手がどう解釈するかは結局受け手次第なのである。

自分はコンビニで煙草を買うとき、レジで「◯◯ひとつください」という風に、目的のモノと数量を明示してお願いする。しかし、結構な確率で「ふたつですか?」と聞き返されてしまう。自分の滑舌が著しく悪いわけでもないのに、だ。子音は同じものの母音は違うので、聞き間違えにくいように思う。

ここには、単数がデフォルトで、いちいち数量を言うやつは何個か欲しいのだろう、と言うバイアスがあるのだと思う。忙しい人が買い物に来るコンビニでは尚更のことである。

5W(When, Where, Who, What, Why)を意識して、適切な物の伝え方 (How) が選べるようになりたいと思う。そして、それはこのブログでも同様である。


余談: なぜこんなエントリを書いたのか