2024 年の振り返り
携わったアプリのリリース
結構タイトなスケジュールではあったものの何とかリリース出来た。というのは小並感の感想としては良かった。
いつもながらスマホアプリのリリースはどうしてこうもギリギリになってしまうか...良くも悪くも慣れてしまったのであんまり思うことはないのだけれど...
リリース時のゴタゴタでどうしても機能を提供することが優先にはなってしまうので、運用フェーズに入ったらユニットテストを出来る環境の整備だったり関連してユニットテストがしにくいコアな機能の改修だったりが発生する定期って感じだった。
来世はコアな機能に関してはユニットテストを先んじて書いておけるくらいの余裕と先見の明がほしいところ。TestContainers を導入したのだけれど、(カスタマイズ次第)で DB など単一になって汚しがちな物がテストケース毎に分離できるのは「なんかよくわからないけどテストが壊れました」が防げる可能性が上がった感触はあるので良きだった。トレードオフとしては、コンテナを複数立ち上げることによるリソース不足がシビアになったこと、 DB のマイグレーション済みのコンテナを使わなかったので毎度マイグレーションを実行する必要が出てきて全てのユニットテストが完了するまでの時間が増えたこと。このあたりは改善の余地があるかなと思うので今後のチャレンジになりそう。
DFrame
今まではバックエンドの負荷試験は JMeter か、自作のツールで対応してた(しかも C# じゃない)が、やはり C#er としては「DFrame を使わないなんてあり得ない...!!!」というお気持ちでいっぱいだったので遅ればせながら使わせていただいた。
ちょっとしたことではあるが API サーバーのプロジェクトと同じソリューションに含められるので使い慣れた IDE で開発できるし、書き慣れた C#, .NET の記述方法でワークロード(ワークロード)が作れるというのは魅力的だった。勿論 const なども共有できるわけで結果的に開発効率は良かったように思う。
ただ、気になったというか自分の使い方があまり良くなかったと感じたのは Ramp-Up の部分。事前調査はあまりせずに使い始めたが DFrame の実装的には Repeat モードでワークロードが 1 周すると並列数を増やせる。という使い方でコレが自分の使い方と上手くかみ合わなかった。自分は特定の API のみに負荷をかけてスループットを計測するのではなく、1シナリオをなるべく人間の実際に操作に近い状況、例えば入会〜チュートリアルの終了させ主要な機能をループするような形式かつ、RPS を本番想定負荷する為に要求毎に Sleep などを仕込んでいたため 1ワークロードが 30 min とか 1h かかる方法でワークロードを作成していた。そのため、DFrame の想定の Ramp-Up を用いると想定負荷まで乗せきろうとすると単純に時間がかってしまう。逆に総実行時間を減らすために初手の並列数を増やすとRamp-Up の意味が無くなり瞬間的に過剰な RPS, QPS をたたき出してサービスが止まる...という悲しいことになってしまった。
一応ワークロードを実際の人間の操作に近い状況にしたかった意図としては本番リリース時のデータ量の偏りや、Read/Write の偏りによって起きうる障害というの検知し修正したかったのとゲームの特性上やりこむことで機能が解放されることはままあり、事前にデバッグ目的として機能を解放しない方針で取り組んだからである。時間がある許すならスループットを計測する...でも良いとは思う。
Ramp-Up の部分は、ほんの少しカスタマイズして最大の並列数と Ramp-Up が終了するまでの時間を与えることで時間経過と共に徐々に並列数を増やすような実装を加えて対応した。
あとは、Blazor の UI は最初こそ使っていたのだが、ワークロードの使い方が DFrame の想定している利用方法ではなかったので最終的には Azure Monitor など他のツール類で観測、計測などを行っていた。
とはいえ気になった(というか自分が悪い)点はそのくらいで、良い意味でめちゃくちゃ機能があるわけでもなく必要があればソースコードを読めばいい規模感で使いやすかったので非常に満足だった。
Azure
自分の少ないインフラ経験の中で最長タイに触ることになった Azure 。
去年も同じことを思ったのだが、アプリケーションエンジニアには優しいなぁと思う。結構よしなに必要なサービスやコンポーネントをボタンクリックで作ってくれるし WebApps とか作成は本当に楽なのでオススメ。
小ネタだが Reserved Instance (RI) を買いすぎてもいくらかは払い戻ししてくれるのでその点は良心的だなぁーと思っている。
わがままとしては DB について明るくないのでアレだが、New SQL なサービス (Cosmos 以外)出してほしいなと思う。個人的には TiDB とか Spanner 使いたくなる。
SQL Server 周り
負荷試験時に色々と学ぶことが多かった印象。SQL Server を利用したアプリケーションをリリースしたのが今回が人生において始めてだったもので...
特に記憶に残っているのがラッチ競合 。細かいところまで理解が未だに追いついてはいないが、パーティションの分割をしたり ID の連番発行をやめたりこねくり回して対応したのが良い思い出。(自分が手を動かしたわけじゃないが)
ZOZO さん関連のブログには非常に助けられました。
https://techblog.zozo.com/entry/sqlserver-incident-management-2020
https://qiita.com/tenn25/items/09cb190620da1af08836
Redis 周り
久しぶりちょっとしたランキング機能を作った際に色々と考えることがあり...それが同率判定。
世の中的にも色々やり方があるようで...
https://qiita.com/aoyagikouhei/items/7c59a335006940e5eb4f
https://www.knocknote.co.jp/blog/ゲームのイベントランキングで同一スコアを取り/
bit 演算や、小数点部分を使って同値だった場合の判断基準を添えておく...というは一理あるとは思うがそれだと有効桁数が機能によって変わってしまう大きく減ってしまうおそれと逆にスコアがでかいと対応仕切れないので何か方法はないかなぁーと思っていた次第。
先のブログのにも記載があるが Redis の ドキュメント には以下のような記載がある。
If B and A are two elements with a different score, then A > B if A.score is > B.score.
If B and A have exactly the same score, then A > B if the A string is lexicographically greater than the B string. B and A strings can't be equal since sorted sets only have unique elements.
訳
BとAが異なるスコアを持つ2つの要素である場合、A.scoreが> B.scoreであれば、A>Bとなる。 BとAが全く同じスコアである場合、Aの文字列がBの文字列よりも辞書的に大きければ、A>Bとなる。 ソートされた集合は一意な要素しか持たないので、BとAの文字列が等しくなることはありえない。
簡単な同率判定なら上記を上手く使えばいい。と思って実装した。
つまり、同値だった場合は member の文字列順になるのわけなのでスコアをごにょごにょ変更する必要が無い...と。CloudStructures とかを利用すると member は class だった場合はカスタマイズしてなければ JsonSerialize した文字列がそのままので JsonPropertyOrder
とかをつければ意図した順番での評価も一応可能(文字列上大きいかの判定なので必要なら値を反転するなどは必要)
この方法による面倒や制約としては ZREM
や ZADD
するときに class だと登録した member (≒ class のプロパティ)にしないと適切に処理できない。という点。なので、PlayerId など可能な限り不変的な値を用いた方が実装的には楽だし、変動がある時刻を比較対象として登録したい場合は1回の Redis 上値の更新で ZREM
で値を消して ZADD
で新しい member で登録し直す...のような手間が発生するので微妙かもしれない。
自分の中での実装方法のオプションが一つ増えたのは良い経験だった
- 不変性の高いデータによる同率判定なら、member を上手く使う(桁数は落ちない)
- 常に置き換わる可能性があり、有効桁数を落としても問題ないなら、bit 演算なり小数点部分を上手く使う
- 上記いずれもが該当しないなら、同値の際は DB などから値を取得してアプリケーションコードで頑張る
概ねランキングの同率判定を考える場合は 2 になる気もしている。が、手札は多い方がいい(はず)
CDKTF
Bicep で IaC していたが、構文を覚えるのが地味に面倒だった(実際はそこまで大変じゃ無いかも)のと C#er としては C# で書きたい(暴論)ので、試験的に CDKTF を導入できたのは良かった。
ドキュメントや世の中的なナレッジはそこまでないのだけれど、Terraform 関連のドキュメントを見れば察しはつくので慣れればそこまで大変じゃ無いのかもしれない。
アプリケーションエンジニアがインフラまで管理する環境下においては書き慣れたプログラミング言語で定義できるのは一定の利があると思っているし、Terraform 形式に出力することも可能なので未来に Terraform に詳しいインフラ専門部隊が立ち上がったときにパスしやすい環境にもなり得るので、今後も機会があれば触っていきたい。
.NET 9 にアップデート
STS だろうが関係無い。常に最新に上げることで次バージョンへのアップデート時の対応項目数は減るし毎回のごとく .NET はバージョンが上がる度にパフォーマンスが良くなるなので上げておいて損はないと思っている。2024 年のうちに携わっているアプリを .NET 9 に出来たのは良かった。が、地味に躓いた箇所があるのでメモを記載(尚、具体的な原因がわかっていないものもある)
32bit/64bit だと挙動が変わる
https://github.com/dotnet/runtime/issues/108969 この issue と同じことが Google Billing Library で発生してわりと肝を冷やした。2025/01/03 時点では .NET 9 にはバックポートされてないようなのでコメントにあるように DOTNET_JitEnablePhysicalPromotion=0
を付与するか 64 bit にすれば回避できる。
Functions Isolated の挙動?が変わった?
.NET の話なのか、azure-functions-dotnet-worker なのかまだは追い切れていない話。
Middleware なので Singleton やろと言われればそうなのだが、 .NET 8 までは以下のように Scoped なサービスなどを Middleware のコンストラクタでインジェクションしても動作していたが、 .NET 9 からはランタイムエラーになる(Singleton な物に Scoped なものがインジェクションされようとしているよ...みたいなエラー)
public class TestMiddleware(HogeService hogeService) : IFunctionsWorkerMiddleware
{
public async Task InvokeAsync(FunctionContext context, FunctionExecutionDelegate next)
{
hogeService.Hoge();
await next(context);
}
}
public static class IServiceCollectionsExtensions
{
public static IServiceCollection AddService(this IServiceCollection services)
{
services.TryAddScoped<HogeService>();
return services;
}
}
public class HogeService
{
public void Hoge()
{
Console.WriteLine("Hoge");
}
}
修正方法は色々あるとは思うのだが、なるべくコードを改変しない形式で修正する場合は FunctionContext
に、InstanceServices
というプロパティがいるのでそこから取得すれば良い。
public class TestMiddleware : IFunctionsWorkerMiddleware
{
public async Task InvokeAsync(FunctionContext context, FunctionExecutionDelegate next)
{
var hogeService = context.InstanceServices.GetRequiredService<HogeService>();
hogeService.Hoge();
await next(context);
}
}
メモツールを Obsidian へ
時代の流れに便乗して Notion から Obsidian に本格的に移動した。
自分は大して機能を使っていないが気に入っているプラグインとしては obsidian-marp-slides, obsidian-git, obsidian-digital-garden あたり。
ちょっとしたプレゼン資料を Obsidian で纏めて Marp を使ってスライドとして展開したり、メモがてら取っておいた技術ネタを纏めて Digital Garden として公開したりなど「文字に起こす」作業のベースとして使いまわしの良さが気に入っている。ついでに git 管理も出来るので仕事上いろんな PC やデバイスで作業することがあるので、ひとまず git 管理出来ていると clone すればメモする環境が準備出来るの良い点かなと思っている。obsidian-git
が、スマホのサポートがほとんど出来ないのが残念だが、 GitHub を直接見ればいいし、たぶんきっと時間がいつか解決してくれるだろう...と思っている。
あとは、TODO というかタスク管理をする方法で自分の手に馴染む物があると最高かなといったところ。