2023年の思い出

しゃかいじんじゅうねんめ

去年は https://eagletmt.hateblo.jp/entry/2022/12/31/165730

仕事

今年は会社の状況が大きく変わった年だった。退職勧奨やレイオフ、その変化による自主的な退職でとにかく人が減った。それによって自分の仕事にも当然変化があり、今年の後半はイギリスにいるメンバーとコミュニケーションをとる機会が増えた。その関係で一週間ほどイギリスに滞在する機会もあった。海外に行くのは2019年のシアトル以来で、シアトルもなかなか遠かったがイギリスは更に遠い。イギリスは楽しめたが14時間のフライトは拷問以外の何物でもない。

イギリスにいるメンバーとコミュニケーションをとる機会が増えたということは英語でコミュニケーションをとる機会が増えたということで、これが自分にとっては大変だった。Slack や GitHub で英語話者とコミュニケーションをとる機会は今までもあったが、頻度が増えた上に英語の長文 issue やドキュメントを読むのがとにかく遅いのが困る。ソフトウェアエンジニアだと日本人でも AWS コンソールを英語で使ったりスマートフォンを英語で使ったりしている人は普通によく見るが、自分は公式の日本語版がある場合は基本的に日本語版を利用している*1。自分は長い文章については一度全体を流し読みしてから重要そうな箇所を精読し直すような読み方を日本語ではよくしていて、それを英語でできるだけの力がないので日本語と比較して読むのにすごく時間がかかってしまう。英語の力を高めたい気持ちが無いわけではないが、英語メインで仕事をしたいかというとそれは全く無いので、モチベーションが難しい。仕事で必要だから JavaPython に詳しくなりたいけど、別に積極的に JavaPython を使いたいわけでは全くないよな…… みたいなところに英語もある。いや JavaPython よりはもうちょっとモチベーションは高いか。

趣味

ブルーアーカイブは引き続き継続できている。今年の頭には最終編という大きなコンテンツがあって、世間的 (?) にもめちゃくちゃ盛り上がってたと思う。一方で盛り上がりすぎて、一部の変なファンが目立ったりリアルイベントに人が多すぎたりで、数年前のホロライブみたいに急速に自分の興味を失う不安もある。まぁゲーム本編がちゃんと面白いのでゲームをやらなくなる心配はしていない。

あとは崩壊スターレイル (スタレ)、ワールドダイスター夢のステラリウム (ユメステ) と日常的にやるソシャゲが増えた。どちらもリリース初日でこそないがリリースから近いうちから始めた。スタレは PC でプレイできて画面がとても綺麗なのと、原神よりも攻略がだるくないのが気に入っている。対人でランキングを競う要素が無いのも気楽でよい。ユメステはスマホリズムゲーに欲しいものが全部詰まっているのが良い。最高難易度帯になると突然3本以上の指を使う必要がでてくるがずっと2本指だけでやってきた人が3本以上に慣れるためのほどよい難易度の譜面が無い、というのがスマホリズムゲーあるあるだと思うが、ユメステは3本以上必要な譜面を OLIVIER という別の難易度に隔離して、その中で低難易度から高難易度までレベル分けしてあるのがえらい。ただ、この特徴もプロセカ3周年で APPEND という形で実装されてしまい、独自性が早くも失われてしまったので、ちょっと今後が心配ですが…… ユメステはライブイベントも今年豊洲 PIT で行っていて、自分も行ったけどカバー曲無しでオリジナル曲だけという内容で、ゲームリリースから半年経たずにキャストが全員いるわけでもないのにそれができるのは結構すごいなーと思った。

あとはアイカツ10周年の映画とミュージックフェスタ FINAL も今年だった。映画はスタッフトーク回にも行って監督、脚本、プロデューサーのサインの入ったポスターが当たったのが本当に嬉しかった。今も部屋の目立つ位置に飾っている。

FINAL 1日目の冒頭に音響トラブルでダイヤモンドハッピーが中断されておたく大合唱になったり長い MC になったりしたのも良い思い出です。つい先日にフレンズ5周年のイベントがあったり Resound Stars のリベンジ公演があったように、アイカツシリーズの新作が止まってしまっても周年イベントは今後もあってほしいなぁ。5年刻みでも無印 (2012)、スターズ (2016)、フレンズ (2018)、オンパレード (2019)、プラネット (2020) で綺麗に毎年できるね。

他に今年行ったライブは Nornis 1st、i☆Ris 8th、Mia REGINA HEARTBEAT AGAIN、HIMEHINA LIVE 2023 あたりか。後半になるにつれてマスク無しで普通に声を出せるようになってよかった。Nornis は会社のチームメイトも行ってて面白かった。自分は主に戌亥、彼は主に町田目当てだったので今も穏やかな関係が続いています。Mia REGINA はラストライブということで、アイカツ共々今年に閉じたんだなという印象になった。どちらの最終ライブもとても楽しかった。HIMEHINA は今回も新たに別の友人を連れて行って布教した。安定感があるし万人に勧められるというかんじなので次のライブが早く決まってほし~。

音ゲー関連は実力的にもあんまり変わらず。CHUNITHM は今も頻繁に遊んでるが、オンゲキのほうは最寄りのゲーセンから消えたこともあってちょっと足が遠のいてしまっている。今年は自転車をよく使うようになったのでオンゲキのあるゲーセンに行くことも頻繁にあるんだけど、今の時期のように寒いとあまり自転車にも乗りたくないし、それとオンゲキに使う時間がスタレやユメステに変わってしまった感が無くはない。

今年最もプレイしたゲームはティアキンだった。一番面白かったのもティアキンと言っていいだろう。あとは先月末に出たばかりのゲームだけど TEVI https://store.steampowered.com/app/2230650/TEVI/ がとても面白かった。いわゆるメトロイドヴァニアと呼ばれるような 2D 探索アクション RPG をベースに、弾幕ゲーっぽい要素 (敵の攻撃が基本的に弾で、主人公の当たり判定が小さく、ボムで相手の弾を消せる) や格ゲーっぽい要素 (コマンド入力で攻撃) を足した盛り盛りなゲームになっている。自分は格ゲーに馴染みが無いのでコマンド入力せず基本的な攻撃のみで進めたけど。Steam のインディーゲーとしてはちょっと高めの値段だけど、その分ゲームボリュームはあるしサウンドも良いし日本語の声優がやたら豪華だったりする。

趣味開発は…… 今年はこれといったものは無いかなぁ。ISUCON13 で移植のお手伝いをしたくらいか。今年は Ruby と Rust の2つで移植を担当して、Rust のほうは去年までの Actix ではなく Axum を採用するというトライをした。自分が仕事や趣味で使うのもほぼ Axum 一択になってきている。Ruby にも何かしらのトライを入れたかったけど、Trilogy を使ってみるには mysql2-cs-bind 的な gem が無いのが苦しそうだったのと、RBS 対応をやってみようとしたけど Sinatra だと苦しそうだったので、ほぼ例年通りというかんじになってしまった。

*1:ただし AWS のドキュメントは除く

2022年の思い出

しゃかいじんきゅうねんめ

去年は https://eagletmt.hateblo.jp/entry/2021/12/30/164456

仕事

2022年が始まったときは自分で手を動かして新しいことに取り組みたいなと意気込んでいたが、2月にデータ基盤チームが解散となり、DWH をはじめとするデータ基盤まわりの仕事を引き継ぐことになり一年を通してこれが自分の仕事の中心となった。解散前にちゃんと引き継ぎのフェーズがあった*1し、DWH 自体は元々興味のある領域だったので DWH の仕事は嫌では全くなかったが、とはいえコードの多くが初見であり社内の標準的な技術スタックから外れたシステムが多かったので、問い合わせ対応やトラブル対応に時間がかかる日々がしばらく続いた。主な仕事としてはログデータに紛れ込んでしまった個人情報をマスキングするしくみを作ったり、Prism が利用している PostgreSQL のデータを削減したりしていた。

これ以外の新しいこととして、就業型インターンシップで学生を多く受け入れた。最近知ったことだが、チーム単位で就業型インターンシップを受け入れた数を比べると自分のチームが特別に多かったようだ。自分のチームには去年から就業型インターンシップを継続してくれている方もいる中で新規に受け入れようと思ったのは、いわゆるリファラルだけでなく現時点で全く繋がりがない人にもきてほしいという個人的な思いがあった。ただ、インターンシップの期間が短すぎたなという反省点がある。自分のチームではたくさんの小さなシステムを開発・運用しているためオンボーディングのコストが高く、最初に目に見える成果を作れるまでの時間がどうしてもかかってしまう。一方で短い期間内に作れそうなものとなるとこぢんまりとしたタスクになってしまう。もし今年うちのチームに就業型インターンシップで来た人がこれを読んでいたら、この点は自分の見積もりが甘くてすまなかったと思っていることを伝えたい。来年も受け入れは続けるが、おそらく今よりも長期間を前提にした募集にすると思う。

趣味

今年はブルーアーカイブへの熱が高まった年だった。去年の8月か9月くらいに始めて、今も継続している。そもそもリズムゲー以外のソシャゲをほぼやったことないというのはあるけど、ここまでソシャゲが継続するのは人生初。当時からシナリオの評判が高かったけど個人的には最初はそこまでピンときてなくて、いま最も評価が高いだろうエデン条約編のシナリオが結構進んだあたりでようやく面白いなと思えた。全体的に昔のエロゲを思い出すようなノリで、そのへんが好きだった世代に刺さりやすいんだと思う。自分の好きなイラストレーターもだいたいブルアカの二次創作をしてるし。ソシャゲ関連だと他にヘブンバーンズレッド、INFINITY SOULS をやった。ヘブバンは麻枝准にはずっと曲とゲームシナリオを書いていてほしいという気持ちが高まった。麻枝准のシナリオが好きならヘブバンのメインストーリーを楽しめると思うけど、ブルアカ以上にメインシナリオを読むための戦闘がキツいのが微妙…… Steam 版を出したのはまじでえらい。リズムゲー以外のあらゆるソシャゲは Steam 版を出してほしい。INFINITY SOULS はたしか2017年のコミケでチラシを配っていたものがようやくリリースされて遊んでみたけど、正直に言ってゲームとしてはだいぶつまらなかった。自分は都築真紀のファンなのでシナリオやキャラクターには惹かれるところがあったけど、戦闘やゲーム性はキツい。開始3ヵ月でテコ入れのようになのはがメインストーリーに絡み始めたのは驚いたが、そこから2ヵ月でサービス終了となり、半年ももたずに消えていってしまった。

今年やってとくに印象に残っているゲームは星のカービィディスカバリー、十三機兵防衛圏、ENDER LILIES かなぁ。例年よりちょっと少ないかも。ポケモン LEGENDS アルセウスはそこまで好きにはなれなかった。ポケモン SV についてはストーリーはポケモンとは思えないくらいしっかりとしていたし戦闘面での調整も基本的に良いものだと思うけど、イベントやマップによっては処理落ちがひどかったり、必要なテラピースの数が多すぎたり、ボックスの表示が遅すぎたりと、なかなか微妙な点が多かった。ポケモン SM や USUM のときもそうだったけど、現代のコンソールゲームにおいて通常プレイで処理落ちがする品質のゲームってちょっとな…… カービィディスカバリーは 3D 初とは思えないくらい違和感ない操作性や視認性があってよかった。ちゃんとクリア率100%までやった。十三機兵防衛圏はこれもまた戦闘パートが蛇足に感じたけどストーリーやキャラクターが良い作品だった。ENDER LILIES はつい最近やったゲームだけど、いわゆるメトロイドヴァニアでインディーとは思えないクオリティで楽しかった。

音ゲー関連では CHUNITHM はレートが MAX 16.16 -> 16.37、バトルランクが S3 -> SS2 になった。レートに対してバトルランクは高いほうだと思う。そして XL TECHNO -More Dance Remix- で無事に S ランクをとり、今年追加されたレベル15でもすべて S ランクをとり銀ポゼになった。次の区切りとしては金ポゼは遠いのでレート 16.50 かなぁ。オンゲキのほうははちゃめちゃに高難易度が追加されていって、解禁がとにかくダルいのがつらかった。オンゲキって今まで楽曲の解禁にノルマ的なものがなくてすごく楽なのが良かったのに…… コンテンツの終了宣言的なものがされ、その具体的な意味が今も分からないままで来年どうなってるんですかね。

今年の Q3 (って表現であってるのか?) は久しぶりにテレビアニメを見た。これも都築真紀の新作である Extreme Hearts が主目的で、ついでにリコリス・リコイル等も見ていた。Extreme Hearts はたいへん面白かったです。

ヒメヒナの現地ライブ良かったですね。前回、前々回のオンラインライブの再演ではあるけど、現地には現地の良さがある。あとは声を出せればというかんじだけど、この点についても最大限気を遣ってハミングなら OK というルールが設定されていたのも良かった。Studio LaRa になったのもこのときだったね。次のライブが来年8月5日と発表されていて、まだ少し先だけど楽しみ。

アイカツがついに10周年となり、記念の映画が公開されたり4ヵ月連続ライブが始まったりした。じわじわと新曲も出てきて良い。その一方でコンテンツとしては止まってしまっているので、来月の映画公開と2月の 2 days のミュージックフェスタ FINAL で終了なのかなぁ。

最後に趣味開発について触れると、slack-thread-expander くらいかな。あとは json-to-parquet も書いたりしたけど、どちらも仕事で欲しくなって書いたのであんまり趣味感がない。slack-thread-expander は Slack のスレッドが大好きな人たちがついスレッドを使ってしまったときにスレッドを無意味にするために書いて、社の Slack で使えるようにしてどうしてもスレッドを使ってほしくない特定のチャンネルのみで有効化したんだけど、自分が知らないチャンネルにも勝手に導入されていて (誰でもどこでも使えるようにしたのでこれ自体は全く構わない)、そこでは普通にスレッドがバンバン使われて slack-thread-expander が日常的に投稿していて、自分の意図とは異なる目的で利用されているのを目撃したりした。そこでは何のために slack-thread-expander が導入されていたのかは僕は分かりません。

*1:明確な引き継ぎがあるのは自分の経験上かなり希である

MySQL でも楽に bulk insert したい

以前 PostgreSQL で unnest を使って楽に bulk insert する方法を紹介した https://eagletmt.hateblo.jp/entry/2021/09/01/030453 。 これの MySQL 版が欲しかったものの array 型が存在しない MySQL では無理かなと思っていたんだけど、MySQL 8.0 からサポートされた JSON 型なら array を表現できて json_table() という関数を使うと達成できそうなことにふと気付いた。

json_table() を使うと JSON の値からテーブル (行) に変換することができる。つまりこれを使えば unnest に近いことが可能になる。
https://dev.mysql.com/doc/refman/8.0/en/json-table-functions.html

MySQL [(none)]> select * from json_table('[{"x": 1, "y": 2}, {"x": 3, "y": 4}]', '$[*]' columns (x integer path '$.x', y integer path '$.y')) as t;
+------+------+
| x    | y    |
+------+------+
|    1 |    2 |
|    3 |    4 |
+------+------+
2 rows in set (0.001 sec)

文法がなかなか独特だけど1つの JSON の値 (配列) から行に変換できている。なのでプログラム側は JSONシリアライズさえできれば insert into select で bulk insert が可能になる。 PostgreSQL 版と同様に Rust の sqlx を使った例はこんなかんじになる。

#[derive(Debug, serde::Serialize)]
struct Record {
    x: i32,
    y: i32,
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let records: Vec<_> = (0..1000)
        .map(|i| Record {
            x: 2 * i,
            y: 2 * i + 1,
        })
        .collect();

    let pool = sqlx::MySqlPool::connect("mysql://...").await?;

    let query = "insert into bulk_inserts (x, y) select * from json_table(?, '$[*]' columns (x integer path '$.x', y integer path '$.y')) as t";
    sqlx::query(query)
        .bind(sqlx::types::Json(records))
        .execute(&pool)
        .await?;
    Ok(())
}

がんばってプレースホルダを組み立てる必要がなくなって便利。

sqlx crate でフィールド毎に独自のデコード処理を挟みたい

sqlx-with というものを作ってみたので、その経緯について書く。

github.com

sqlx crate でフィールド毎に独自のデコード処理を挟みたい

sqlx では query_as() 等の関数を使うことでデータベースから取り出した行を struct にマッピングすることができ、このマッピングsqlx::FromRow という derive macro を利用することで自動で実装できる。

#[derive(sqlx::FromRow)]
struct Row {
    x: i64,
}

このような単純なマッピングではなく、特定のカラムの値に独自の変換処理を入れてから struct に入れたいとする。とくに意味は無いが、たとえばカラム x の値に対して (x, x + 2) という値を struct のフィールド x に入れたいとする。serde であればたとえば split_x という関数を定義して #[serde(deserialize_with = "split_x")] と指定すれば実現できる。これと同じような機能が sqlx::FromRow にあればよさそうだが、現状は無い。なので serde の deserialize_with みたいなものをどうやったら実装できるか考えてみる。最終的には以下のように書けるとよさそうだ。

#[derive(sqlx::FromRow)]
struct Row {
    #[sqlx(decode = "split_x")]
    x: (i64, i64),
}

まず sqlx::FromRow が具体的にどのようなマクロ展開をしているのか実装を見てみると、Row、ColumnIndex、Type、Decode といった様々な trait を使っていることが分かる。
https://github.com/launchbadge/sqlx/blob/v0.6.1/sqlx-macros/src/derives/row.rs
最初の例は以下のような impl を生成している。

struct Row {
    x: i64,
}
impl<'r, R> sqlx::FromRow<'r, R> for Row
where
    R: sqlx::Row,
    &'r str: sqlx::ColumnIndex<R>,
    i64: sqlx::Type<R::Database> + sqlx::Decode<'r, R::Database>,
{
    fn from_row(row: &'r R) -> sqlx::Result<Self> {
        Ok(Self {
            x: row.try_get("x")?,
        })
    }
}

特定のデータベースに依存しないようになっているのが特徴で、struct のフィールド名とその型の情報から

  • &str で特定のカラムにアクセスできる
  • i64 に対応するデータベース側の型がありデコードができる

という制約をつけている。

ここに split_x という関数を挟んでみると以下のような実装になるだろう。

struct Row {
    x: (i64, i64),
}
impl<'r, R> sqlx::FromRow<'r, R> for Row
where
    R: sqlx::Row,
    &'r str: sqlx::ColumnIndex<R>,
    i64: sqlx::Type<R::Database> + sqlx::Decode<'r, R::Database>,
{
    fn from_row(row: &'r R) -> sqlx::Result<Self> {
        Ok(Self {
            x: split_x("x", row)?,
        })
    }
}

fn split_x<'r, R>(index: &'r str, row: &'r R) -> sqlx::Result<(i64, i64)>
where
    R: sqlx::Row,
    &'r str: sqlx::ColumnIndex<R>,
    i64: sqlx::Type<R::Database> + sqlx::Decode<'r, R::Database>,
{
    let n: i64 = row.try_get(index)?;
    Ok((n, n + 2))
}

あとはこのように展開されるようなマクロを作るだけ…… とはいかず、このままだと無理なことが分かる。Row の定義から impl の制約を生成しなければならないが、i64 が Type + Decode であるという条件を Row の定義から知ることができないからである。これを知るには split_x のシグネチャを知る必要があるが derive macro には不可能なはず。というわけで sqlx::FromRow ではこのような decode オプションを実装することができない。

sqlx-with

sqlx::FromRow の derive macro で decode オプションを実装できない原因は実装があまりに generic だからだと考えた。sqlx::FromRow を derive macro を使わずに手書きで実装する場合、普通は特定のデータベースを前提に実装する。実際に sqlx::FromRow のドキュメント https://docs.rs/sqlx/latest/sqlx/trait.FromRow.html#manual-implementation でもそのように案内している。 そこで具体的なデータベースを渡せるような derive macro に変えれば、decode オプションを実装できるだけの generic さを残しつつ sqlx::FromRow と同様の便利さを得られそうだ。実際にそのように sqlx_with::FromRow という derive macro を実装してみた。
https://github.com/eagletmt/sqlx-with

use sqlx::Connection as _;

#[derive(sqlx_with::FromRow)]
#[sqlx_with(db = "sqlx::Sqlite")]
struct Row {
    #[sqlx_with(decode = "split_x")]
    x: (i64, i64),
    y: String,
}

fn split_x(index: &str, row: &sqlx::sqlite::SqliteRow) -> sqlx::Result<(i64, i64)> {
    use sqlx::Row as _;
    let n: i64 = row.try_get(index)?;
    Ok((n, n + 2))
}

#[tokio::main]
async fn main() {
    let mut conn = sqlx::SqliteConnection::connect(":memory:").await.unwrap();
    let row: Row = sqlx::query_as("select 10 as x, 'hello' as y")
        .fetch_one(&mut conn)
        .await
        .unwrap();
    assert_eq!(row.x, (10, 12));
    assert_eq!(row.y, "hello");
}

db というオプションにデータベースを渡すことでそのデータベースを前提とした impl を生成でき、split_x のような関数も中で使えるようになった。sqlx::FromRow と異なり指定したデータベース以外には使えなくなってしまうが、現実的に1つの struct を複数の異なる種類のデータベース間で使いまわしたい場面はまず無いだろう。

おまけ: darling

sqlx_with::FromRow という derive macro を実装するために darling というライブラリを初めて使った。proc macro を実装するための derive macro を提供していて、derive macro を実装するには FromDeriveInput を使うと必要な syn の値にすぐにアクセスできる struct が得られて便利だった。

C のヘッダファイルの解析に bindgen を濫用するアイデア

Rust には bindgen というツール、あるいはライブラリがあって binding を書くときに非常に重宝する。
https://rust-lang.github.io/rust-bindgen/
これを濫用すると C のヘッダファイルを解析してマクロの定数値を読み取ったり構造体のサイズを調べたりといったことが手軽に可能そう。

たとえば errno の名前と値とメッセージの一覧を知りたいとき、こんなかんじで errno.h の中身から定義を取り出すことができた。

lazy_static::lazy_static! {
    static ref MACROS: std::sync::Mutex<std::cell::RefCell<Vec<(String, i32)>>> = Default::default();
}

#[derive(Debug, Default)]
struct MacroCollector {}

impl bindgen::callbacks::ParseCallbacks for MacroCollector {
    fn int_macro(&self, name: &str, value: i64) -> Option<bindgen::callbacks::IntKind> {
        if name.starts_with('E') {
            MACROS
                .lock()
                .unwrap()
                .borrow_mut()
                .push((name.to_owned(), value as i32));
        }
        None
    }
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    use std::io::Write as _;

    bindgen::builder()
        .header_contents("wrapper.h", "#include <errno.h>")
        .parse_callbacks(Box::new(MacroCollector::default()))
        .generate()
        .expect("unable to collect macros");

    let out_dir = std::env::var("OUT_DIR")?;
    let mut file = std::fs::File::create(std::path::Path::new(&out_dir).join("errno.rs"))?;
    writeln!(file, "pub const ERROR_NUMBERS: &[(&str, i32, &str)] = &[")?;
    for (name, value) in MACROS.lock()?.borrow().iter() {
        let message = unsafe { std::ffi::CStr::from_ptr(libc::strerror(*value)).to_str()? };
        writeln!(file, "(\"{}\", {}, \"{}\"),", name, value, message)?;
    }
    writeln!(file, "];")?;
    Ok(())
}

当然こういう用途に使うものではないのでグローバル変数に結果を書き込むことになっているけど、build.rs 内ならまぁ許されるだろう。 本格的に解析するなら bindgen と同様に libclang を直接使ったほうがよさそうだけど、手軽に定数やシグネチャを拾うくらいなら bindgen を濫用するのが楽そう。

Axum を使ってみた感想

Axum は Tokio のチームから発表された Web アプリケーション向けのフレームワークです。
https://tokio.rs/blog/2021-07-announcing-axum
これを使って少し Web アプリを書いてみたりしたのでその感想を書いてみます。

Rust にもいくつか Web フレームワークがあって個人的には https://github.com/flosse/rust-web-framework-comparison#high-level-server-frameworks の表がしっくりきているんですが、async/await に対応していてハイパフォーマンスで書きやすいものといえば Actix Web が一番かなという状況でした。 ただ、Actix Web は HTTP のレイヤに hyper を利用しておらず独自の実装になっていて、Tokio や hyper をベースにしたライブラリが多い中でちょっと使いづらい面がありました。 そのためか最新の安定版でも Tokio v1 対応が済んでおらず、最近の Actix Web は実質的に beta 版を使うしかない状況になっています。 hyper ベースのフレームワークとしては warp も最近人気ですが、高レイヤの便利機能が不足していたり filter の概念が独特で型が複雑になりがちだったりで、個人的には Actix Web と比べると使いやすさはかなり落ちるという印象です。

そんな状況でリリースされた Axum は Actix Web にかなり似ている使い勝手を提供していて、一番の印象は Actix Web の対抗になれそうというものでした。 Tokio チームが開発しているので当然 Tokio をベースにしていて、hyper、tower、tracing といったデファクトとなりつつあるスタックの上に Axum は作られています。 gRPC サーバでは既に Tonic がデファクトになっていて、それと似たスタックの上に Web サーバを実装できるところも Actix Web より使いやすそうなところです。

実際に ISUCON11 の予選問題の初期実装を Actix Web 4 (beta) から Axum 0.4 に書き直してみたのがこれです https://github.com/isucon/isucon11-qualify/compare/main...eagletmt:rust-axum 。 ルーティングに関しては ISUCON11 予選では Actix Web のマクロを使って書いていたので一見差分が大きいですが、Actix Web にもマクロを使わない書き方があって、それと比べると Axum はかなり似ていると思います。 エラーの表現は Actix Web の ResponseError トレイトの代わりに IntoResponse トレイトを実装するようなかんじになっていて、ハンドラからエラーを返すときは Actix Web と同じように Result で返せばいいだけです。 ハンドラの書き方はリクエストの情報や共有データを extractor 経由で取り出して引数で受け取るという形になっていて、これも Actix Web と非常に似ています。Actix Web では app_data() で DB のコネクションプールのような共有データを登録して web::Data で受け取っていたのに対して、Axum では AddExtension layer として共有データを登録して extract::Extension で受け取るようになっていますし、パス内のパラメータの参照や JSON ボディのデシリアライズやクエリ文字列の取り出しなども同じように extractor 経由で行えます。 actix-session の CookieSession に相当するような便利なものはまだ無さそうだったのでそこは自作しました https://github.com/eagletmt/tower-cookie-store 。FromRequest トレイトを実装すればいいという点も非常に似ていますね。 ちなみにパフォーマンスについては少なくとも ISUCON11 予選問題の初期状態においては Axum のほうが若干上でした。

というかんじで、Actix Web と同じように使えて Actix Web の不満が一通り解消されているので、個人的には今後は Web フレームワークの第一候補として使っていこうと思えました。 まだリリースされたばかりで破壊的な変更もよくある https://github.com/tokio-rs/axum/blob/main/axum/CHANGELOG.md フェーズですが、今後にかなり期待できます。

2021年の思い出

しゃかいじんはちねんめ

去年は https://eagletmt.hateblo.jp/entry/2020/12/31/114259

仕事

去年は SRE グループを2つに分けてみたが、結果的にこれはうまくいかずに再度1つの SRE グループという形になった。失敗の原因はいくつか考えているが、ここは振り返りや反省の場ではないので書かないことにする。1つになった SRE グループで再びリード業をやりつつ、個人としては引き続きレガシーを倒していた。具体的にはレガシーとされていた VPC を消したり、同時に CentOS も消滅したり、Consul を廃止したり、これらの廃止のための諸々の移行作業といったかんじのことを主にやっていた。

今年はオフィスの移転が大きな変化だった。全く近場ではないみなとみらいへの移転となり、通勤にかかる時間が劇的に伸びた。 これと同時にリモートワークではなくオフィスにくるようにというメッセージが強化され、自分も週に1、2回オフィスに行っている。多くの通勤者とは逆方向の移動になるので電車に座れないとかはなく通勤に苦痛は無いんだけど、単純に時間がかかるので自分の時間を維持しようとすると勤務時間が足りなくなるという状態になっている。 かといって自分自身はリモートワーク縮小には否定的ではなくやや肯定よりのニュートラルな気持ちでいる。完全な廃止はされてほしくないけど、かといって全員にリモートワーク適正があるわけではないので、リモートワーク適正に関係なく人を集めたいなら最低限のラインを決めつつ選択性がいいのかなぁというところ。 自宅のほうがすべてを自分好みにできてオフィスよりも自分に合っている椅子、机、モニタ、室温で仕事できるし通勤に時間をかける必要も無いけど、仕事終わりに突発的に飲みながら話したりマリオカートライブホームサーキットが突然始まったりする良さも捨てがたい。 ただ、リモートワークを縮小するならなぜ移転先が都内ではなくみなとみらいなのかという点は納得してないが……

移転とリモートワーク縮小がきっかけなのか他の要因と重なってかは分からないけど、事実として自身のチームや身近なチームからの退職者が相次いだ。悲しい方向ではあるけど自分が中心に去年からやっていたレガシー倒しが現実的な効果を出すことになった。 そんなこともあって、今年の後半くらいからは採用関連で色々やってみたり現在の状況に適した組織構造について上長と話したりと人事関連 (?) にも少しずつかかわるようにしていった。 ソフトウェアエンジニアなんだからずっとコードを書いていたいという話題が Twitter では定期的に流れてくるけど、もちろんそういう志向の人がいるのは全く不思議じゃないし非難されるようなものではないけど、自分に何が向いてるのかなんて分からないので興味と機会があれば色々やってみるほうが自分は良いと思っている。そのために別の状況を求めて転職とかもよくある話だと思うけど、幸か不幸か会社の状況がどんどん変わっていくので、転職の手間をかけずに様々な経験をできている。 とはいえ去年と今年はだいぶ守りに偏ってしまっていて、去年はまぁ他のチームメイトに攻めてほしくて自分は守りに入ることにしたというところはあるけど、来年は死なない程度に守りを薄くしつつ自分も攻めたいですね。

引っ越し

今年の3月に引っ越しをした。旧居には社会人になったときから住んでいたので、7年間くらい住んでいたことになる。 オフィス移転とタイミングが近いのでそれに合わせての引っ越しだと思われがちだけど、実際は全く関係ない。オフィス移転しそうという噂は聞いていたけど時期も移転先も知らなくて、新居の契約を結ぶまであと数日というタイミングでオフィス移転の時期と移転先を知ることになっていた。なので当時そのまま契約するか結構迷ったけど、みなとみらい周辺で探してみても家賃相場は全然安くないどころかやや高いくらいで、結局そのまま契約してオフィスには1時間ちょっとくらいかけて通勤することになっている。

引っ越しのきっかけは収入がそれなりに増えてきたところに新型コロナで家にいる時間が増えたので家に金をかけたいと思ったことだった。 社会人になるときにオフィス近くで選んだところなので、まぁ狭いし設備もそんなによくないところに住んでいた。何なら学生時代よりも少し悪いくらいのところだった。 平日日中はオフィスに行ってゲーセンに寄ったり外食したりして帰ってくるような生活だと自宅が狭くてもそこまで不快じゃなかったし、契約更新のたびに少しずつ安くなっていったので引っ越すモチベーションがあまり上がっていなかったけど、リモートワーク中心で遊びに行ける先も少ない生活ではさすがにストレスが大きくて引っ越しすることにした。 バス、トイレ、洗面台が独立していて一定の広さがあって近くにゲーセンがあって…… みたいな条件で探すとオフィス付近には無いので家賃補助は早々に捨てる判断をして、ゲーセンの分布を見つつある程度知ってる土地から探していっていま住んでいるところに決めた。 許容範囲ギリギリくらいの家賃にはなってしまったが今のところ家計は大丈夫そうだし大いに満足している。 ただ、将来新型コロナが完全に収束して家にいる時間が減ったらまた違う条件で引っ越し先を探すかもしれない。

引っ越しをきっかけに家具にも少し興味を持てるようになってきた。少し快適になるようなものを買って置くような余裕ができたので、とりあえず実用面で良さそうなものを試していきたい。 雑なおたくグッズ置き場として https://www.amazon.co.jp/dp/B08BC71B9V とか良かった。有孔ボードで色々吊るせる上にスチール製なのでマグネットもつく。

趣味

音ゲー関連では CHUNITHM が無事虹レとなった。大型アップデートにより値は大きく変わったけど、虹レはそれより前に達成していたしレート値はほぼ +1 されていたので、順当に成長して虹レになったと言えると思う。最終的に MAX 16.16 まで上がったのは 120fps 化で見え方が変わった影響も大きいかもしれない。感覚的なところだと4鍵なら指押しが求められる階段や乱打をちゃんと押せるようになってきたと思う。5鍵以上は分からん。あとは餡蜜でとるというテクニックもできるようになってきた。 つい先日混沌もギリギリ S に乗せることができ MASTER 全譜面で S をとれたかと思いきや、大型アップデート時に追加された XL TECHNO -More Dance Remix- で S を取れずに泣いてます…… これどういう練習したらできるようになるの。 オンゲキのほうのレートは MAX 15.37 まで上がったので順調に上達はしてそうなんだけどなんかあんまり上手くなってる感が無いんだよな……

今年やったゲームで面白かったのは、マニフォールド ガーデン、メトロイド ドレッド、真・女神転生Ⅴ、アンリアルライフ、メルヘンフォーレスト、Tales of Arise、喫茶ステラと死神の蝶、PARQUET、異世界酒場のセクステットあたり。あとは一応ドーナドーナも今年分に含めておく。 リメイクも含めるとスーパーマリオ 3Dワールド + フューリーワールド、ゼルダの伝説 スカイウォードソード HD、ポケモン BDSP も。 マニフォールドガーデンは新鮮なパズルゲームで面白かった。3D 酔いはしやすいけど。 元々人気のあるシリーズだけど一作もやったことなくて今回初めてプレイして面白かったのがメトロイドドレッドと Tales of Arise。メトロイドドレッドは思ってたより死に覚えゲーだったけどやり直しがそこまで苦じゃないところが良かった。 Tales of Arise は PC にホリの GC コン繋いでやったけどストレス無く楽しめた。ストレスが無さすぎて戦闘が単調すぎるようにも感じたけど、難易度を上げたらまた変わるのかもしれない。 ドーナドーナは独立して感想を書いた https://eagletmt.hateblo.jp/entry/2021/01/19/024517 けどまじで面白かった。それだけに続編が絶望的になってしまったのが悲しい…… PARQUET と異世界酒場のセクステットは手軽に楽しめるギャルゲ、エロゲです。異世界酒場のセクステットは全年齢版を Steam で売って公式サイトで R-18 パッチを無料配布するという形式をとってるんだけど、これアリなんですね…… Steam で買えると楽なのでパクってほしいけど、全年齢版も同時に作ることになるのでフルプライスのエロゲでやるのは難しいか。

趣味開発では envchain 代替を書いたのが大きかったかな https://eagletmt.hateblo.jp/entry/2021/04/23/001333 。プライベートでは envwarden を使っているし仕事では envop を日常的に使っている。

イベント関連だと今年は少しずつ復活してきているように感じる。プリティーシリーズ系のライブは現地開催あったし、イロドリミドリのライブも現地があった。無声なのが悲しいけど配信との差でいうと音響が良いという差は大きいかも。 音響と言えば最後の VIRTUAFREAK にも行った。バーチャルに関係あるようなないような音で満ちていてよかったですね。 バーチャル関連だと HIMEHINA が元々すごく好きなんですがオンラインのみになってしまったライブもとても良かったですね…… 期間限定でいつ消えるか分からないけどダイジェスト版的なものが https://www.youtube.com/watch?v=b4JBZG7_UHA に残っている。 新型コロナのことがなければ藍の華は去年の全国ツアーの予定だったので、またそういう企画が出てくるようになってほしい。