TL;DR Go 1.25 から正式サポートされたtesting/synctestパッケージを使ってみた 「synctest.Run not supported with asynctimerchan」というメッセージとともにpanicした go mod edit -go=1.23 を実行すれば解決 背景 shogo82148/go-retry は指数的バックオフをよしなになってくれるライブラリーです。
shogo82148/go-retry Goで指数的バックオフをやってくれるgo-retryを書いた go-retry v1.2.0 リリースのお知らせ、ジェネリクスがやってきた go-retry v2 リリースのお知らせ は待ち時間を調整するテストを書くために、「実際にSleepをして経過時間を取得する」という方法を採用しています。
// テストのイメージ func Test(t *testing.T) { var policy = retry.Policy{ MinDelay: 100 * time.Millisecond, MaxDelay: time.Second, MaxCount: 3, } // 実際にSleepをして経過時間を取得する start := time.Now() _ = policy.Do(t.Context(), func() error { return errors.New("some error") }) d := time.Since(start) if d < 300*time.Millisecond { t.Errorf("want at least 300ms, got %s", d) } } このテストは実際に300ミリ秒Sleepしてしまうので、テストに必ず300ミリ秒かかってしまいます。 また実際のSleep時間はランタイムの状態によって変化してしまうため、 「300ミリ秒ちょうどSleepする」ことをテストしたいのに、実際にそのようなテストを書くことは難しいです。
この記事は、Perl Advent Calendar 2025 10日目の記事です。 9日目は@sognmuで「Text::Markdown::Discountのメンテナンスを引き継ぎました」でした。
背景 2025年12月10日時点の最新版のPerl 5.43.5(開発版)で、 サブルーチンの名前付きパラメーター呼び出し(Signatures Named Parameters)がサポートされました。 これは PPC0024 を実装したものです。
どういう機能かさっそく遊んでみましょう。
Perl 5.43.5 のビルド Signatures Named Parameters はまだ安定版のPerlには取り込まれていません。 使用するには開発版のPerlをビルドする必要があります。
plenv install 5.43.5 -Dusedevel plenv local 5.43.5 試してみる ビルドが完了したら実際に試してみましょう。
まだ実験的な(Experimental)機能という位置づけなので、今回検証した挙動は将来変更される可能性があります。 注意してください。
普通に呼び出し サブルーチンの仮引数部に :$alpha と書くと名前付きパラメーターとして扱われます。 サブルーチンの中からは $alpha という変数名でアクセス可能です。 呼び出し側からは alpha => "A" のように指定します。
use v5.43.5; use experimental qw(signature_named_parameters); sub foo (:$alpha, :$beta) { print "The value of alpha was $alpha\n"; print "The value of beta was $beta\n"; } foo(alpha => "A", beta => "B"); 出力:
この記事は、Perl Advent Calendar 2025 4日目の記事です。 3日目はid:hkoba501で「Claude Code は OO Modulino (オゥオゥ・モジュリーノ)も理解してくれる、ぽい!」でした。
YAPC::Fukuoka 2025 の余興でコードゴルフコンテストが開催されました。
コードゴルフコンテスト Anybatross YAPC::Fukuoka 2025 開催のお知らせ PerlAnybatross YAPC::Fukuoka 2025 コードゴルフとは、与えられた仕様に対してできる限り短いソースコードで解答するというゲームです。 今回はPerl、Ruby、Python、JavaScript、PHPなどで参加可能ですが、Perl Advent Calendarのネタのために、 個人的に一番馴染み深いPerlを使って参加しました。 結果、すべての問題をPerlでチャレンジした人の中では2位になることができました。
Hole 1 アルファベットのAやBにあるような、文字の中にある閉じた空間のことをカウンターといいます。
0〜9までの10種と、アルファベット大文字のA〜Zの26種、合計36種の文字やその他の記号を利用した文字列が渡されるので、カウンターの数を数えてください。アルファベット小文字は来ません。その他の記号のカウンターは数えなくてよいです。
1行ずつ数えて、その行までの累積個数と、その行での出現個数を出力してください。
https://perlbatross.kayac.com/contest/2025fukuoka/challenge/1/sample サンプルのコードが提供されているので、まずはここから始めていきましょう。
Score: +1065
use v5.40; use utf8; our $COUNTER_MAP = { '0' => 1, '1' => 0, '2' => 0, '3' => 0, '4' => 1, '5' => 0, '6' => 1, '7' => 0, '8' => 2, '9' => 1, 'A' => 1, 'B' => 2, 'C' => 0, 'D' => 1, 'E' => 0, 'F' => 0, 'G' => 0, 'H' => 0, 'I' => 0, 'J' => 0, 'K' => 0, 'L' => 0, 'M' => 0, 'N' => 0, 'O' => 1, 'P' => 1, 'Q' => 1, 'R' => 1, 'S' => 0, 'T' => 0, 'U' => 0, 'V' => 0, 'W' => 0, 'X' => 0, 'Y' => 0, 'Z' => 0, }; my $input = do { local $/; <STDIN> }; my @lines = split /\n/, $input; my @results; my $accumulated_count = 0; # 行ごとに処理する for my $line_num (0.
背景・目的 「マイナンバー.csvというファイルにチェックディジットの正しい12桁整数を大量に入れておくいたずらを考えた」という𝕏の発言を見て、
マイナンバーが保存された状態でコンピュータが故障したら修理にも出せない https://t.co/ZNK9KKtFkZ ので、絶対にメールでは送らないように言われたことがあったっけ。マイナンバー.csvというファイルにチェックディジットの正しい12桁整数を大量に入れておくいたずらを考えたけど実行してない
— Haruhiko Okumura (@h_okumura) November 3, 2025 そういえば、過去にマイナンバーの可能性のある数列をすべて列挙した人がいたよなーと思い出しました。 完全に二番煎じですが、自分でもやってみました。
前例 計算が終了 https://t.co/UivoSSaAJU pic.twitter.com/zm1ZznWWtA
— 弥生 水葉𝕏 (@yayoi_mizuha) November 25, 2024 マイナンバー一覧生成 C++よくわからずコンパイル通らなかったので、Goで再実装してみました。
shogo82148/go-generate-my-number package main import ( "bufio" "io" "os" ) func main() { w := bufio.NewWriterSize(os.Stdout, 1<<20) for i := range 100_000_000_000 { if err := checkDigit(w, i); err != nil { panic(err) } } if err := w.Flush(); err != nil { panic(err) } } func checkDigit(w io.
TL;DR shogo82148/actions-setup-mysql には「GitHub Actionsのコード」と「MySQLをビルドするためのスクリプト群」が一緒に入っていたのですが、 このたび「MySQLをビルドするためのスクリプト群」を以下のレポジトリーに移行することにしました。
shogo82148/build-mysql ユーザー側での対応は不要です。
背景・目的 shogo82148/actions-setup-mysql のビルド済みMySQLバイナリー置き場には紆余曲折ありました。 以下のブログ記事はPerlのものですが、MySQLも同じような道筋を辿っています。
Setup Perl GitHub Action を公開しました Setup Perl Environment Action のストレージを Azure Blob Storage に移行しました 紆余曲折あり最終的には GitHub Releases に保存することにしました。 GitHubネイティブの機能で安心感があるのと、なんたって無料なのが魅力的ですね。
v1.45.0 をリリースしたら、 v1.45.0の関連アセットに必要なバイナリーを全部突っ込む、という作戦で運用を続けてきました。 しかし、この方法では以下のような問題がありました。
リリースのたびにすべてのプラットフォームのビルドをし直さなければならない ひとつのリリースに含める事のできるアセットの上限 1000 が見えてきた というわけで、「MySQLをビルドするためのスクリプト群」を別レポジトリーに移行し、 shogo82148/actions-setup-mysql 自体のリリースサイクルとは別に管理するようにしました。
移行作業 shogo82148/build-mysql のセットアップ 「MySQLをビルドするための秘伝のスクリプト群」はすでに手元にあるので、スクリプト群をコピーするたけです。 完成したバイナリーは gh コマンドでアップロードします。
同じMySQLバージョンの再ビルドを行いたい(たとえば、将来リリースされるであろうubuntu-26.04版ビルドの追加とか)ケースも考えて、リリース名に mysql-8.4.6-20251026153058 のようにリリースした日付を入れておきます。 これは、変更不可リリースを有効化しており、過去のリリースを直接書き換えることができないためです。
shogo82148/actions-setup-mysql のダウンロードURL書き換え 毎回 shogo82148/build-mysql のリリースを検索するのも大変なので、 shogo82148/build-mysql の全リリースを gh api --paginate --slurp '/repos/shogo82148/build-mysql/releases?per_page=100' コマンドで取得し、整形したものをソースコードに埋め込んでいます。
[ { "arch": "arm64", "distribution": "mysql", "os": "darwin", "sha256": "874d49f26f0bcb1cdd186bf768d863cb1da227091609e5873e25255f19e46753", "url": "https://github.
背景・目的 弊社では依存性注入(DI)に google/wire を使用しています。 しかし、一部 Gopher 界隈で話題となったように 2025年8月25日 google/wire はアーカイブされてしまいました!
google/wire がアーカイブされたけどどうすればいいの? しばらく静観してきたのですが、そうも言ってられなくなったので移行先を考えることにしました。 軽く調べた感じでは uber-go/dig が依存関係も少なく、導入も楽そうです。 しかし google/wire でできたことが uber-go/dig に移行した途端できなくなっては困ります。
検証 そこで、弊社が google/wire で使っている機能が uber-go/dig でも実現できるのか、調査してみました。
依存関係の事前チェック 個人的に一番欲しいのが依存関係の事前チェックです。
たとえばプロバイダーの不足はよくあるミスだと思います。 google/wire は事前にコード生成を行うため、このようなミスに気が付きやすいです。 しかし uber-go/dig では実行時までエラーに気がつけません。
package main import ( "github.com/k0kubun/pp" "go.uber.org/dig" ) type foo struct { } func main() { c := dig.New() // foo のプロバイダーが登録されていない!!! if err := c.Invoke(func(foo *foo) { pp.Println(foo) }); err != nil { panic(err) // panic: missing dependencies for function "main".
背景・目的 ULIDを使っているプロジェクトを見かけました。 Go言語のULID実装は oklog/ulid が有名です。 見かけたプロジェクトでも oklog/ulid を利用していました。
しかし最新版 oklog/ulid v2.1.1 のソースコードを読んでみると色々と気になる実装があります。
まずはデフォルトのエントロピー源が math/rand であること。 math/rand は暗号論的に安全な乱数ではありません。 過去の採番履歴から、次に採番されるIDが予測されてしまう可能性があります。
さらに疑似乱数のSeedに現在時刻(time.Now().UnixNano())を使用している点。 1秒って10億ナノ秒しかないので、たとえば1秒間に約3.7万プロセスが一斉に起動するような大規模システムの場合、誕生日のパラドックス から50%の確率でSeedが衝突します。 意外と衝突確率高いと思いませんか? Seedが衝突したということは生成される乱数列も同じなので、ULIDが衝突する確率も高くなります。
最後に、Base32に含まれていない文字が入力された場合、Parse 関数はエラーを報告しないという点。 この場合どのようなULIDが返却されるかは未定義です。
Parse parses an encoded ULID, returning an error in case of failure. ErrDataSize is returned if the len(ulid) is different from an encoded ULID’s length. Invalid encodings produce undefined ULIDs. For a version that returns an error instead, see ParseStrict.
from https://pkg.go.dev/github.com/oklog/ulid/v2@v2.1.1#Parse
外部からの入力を受け付けるには心もとない挙動です。
実装 これらの点が気になったので、車輪の再発明ですが、自分でULIDの実装をしてみることにしました。
こんな罠に引っかかるのは僕だけだと思うんですが、一応記録として残しておきます・・・。
TL;DR 2025-08-09あたりから、GitHubがJWTのb64ヘッダーパラメーターを認識するようになった 具体的にはJWTのヘッダーに {"b64": false} が含まれている場合に、“A JSON web token could not be decoded, documentation_url: https://docs.github.com/rest" というエラーメッセージが返ってくる 原因 GitHub Appsにアクセスするのに、お勉強も兼ねて自前実装のJWTエンコーダーを使っていました。
https://github.com/shogo82148/goat JWSのペイロードは普通base64エンコードされていますが、base64エンコードをスキップする拡張が提案されています。
RFC 7797: JSON Web Signature (JWS) Unencoded Payload Option もちろん時前実装のJWTエンコーダーでも対応しました。
しかし、そこにバグがあったのです! payloadをbase64エンコードしているのにもかかわらず、{"b64": false} が設定された状態になっていました。
JWTのヘッダーの中身:
{ "cty": "JWT", "alg": "RS256", "b64": false } 実際のペイロードの内容と、ヘッダーの情報が食い違っており、要するにもとから壊れたJWTを送っていたのです。 今まではたまたまGitHubが受け入れてくれる状態でした。
GitHub 側の改修 おそらく b64 ヘッダーパラメーターをチェックするようになったのだと思います。 この改修により、今までは許容されていた {"b64": false} 付きのJWTが使えなくなりました。
対応 そもそも {"b64": false} のJWTは使用してはならない (MUST NOT) とRFC 7797 Section 7で明記されています。
For interoperability reasons, JSON Web Tokens [JWT] MUST NOT use “b64” with a “false” value.
TL;DR Goは x*y + z を math.FMA(x, y, z) にコンパイルする場合がある 再現性のある実行結果を得たい場合は float64(x*y) + z と書くか、明示的に math.FMA(x, y, z) を呼び出す 再現性は求めずに速度を出したい場合は x*y + z と書く FMAのバグ発生条件の謎 先日Goに math.FMA のバグ修正のパッチを送りました。
Goのmath.FMAの挙動を修正した ありがたいことにスッとマージしてもらえたのですが、実は発生条件にちょっと謎がありました。
検証の結果こんな感じです。
arm64のFMA命令: -0
arm64のmath.FMA: -0
x86_64のFMA命令: -0
x86_64のmath.FMA: 0
Pure Goで書かれた math.FMA の実行結果が変わるのが謎なんですよね 🤔
— f96fd3a0-bdb9-4f10-b69f-8f765c1d341c ICHINOSEShogo (@shogo82148) May 18, 2025 問題となったのは以下のコードです。
package main import ( "fmt" "math" ) var portableFMA = math.FMA func main() { fmt.Println(math.FMA(0x1p-1022, -0x1p-1022, 0)) fmt.Println(portableFMA(0x1p-1022, -0x1p-1022, 0)) } go version go1.
Goのコントリビューターになったぞ!
673856: math: fix portable FMA implementation when xy ~ 0, xy < 0 and z = 0 どんなバグがあって、どう修正したかを記録として残しておきます。
バグの内容 以下のコードを実行すると期待と異なる結果が返ってくる、というものです。
https://go.dev/play/p/DgFhrLf1CuF package main import ( "fmt" "math" ) var portableFMA = math.FMA func main() { fmt.Println(math.FMA(0x1p-1022, -0x1p-1022, 0)) fmt.Println(portableFMA(0x1p-1022, -0x1p-1022, 0)) } go version go1.24.3 linux/amd64 で実行すると、以下のような結果になります。
実際の出力:
-0 0 呼び出し方が違うだけで同じ関数を呼び出しているので、同じ結果が返ってきてほしいところです。
期待される出力:
-0 -0 そもそもFMAって何をする関数? そもそもFMAが何をする関数かというと、融合積和演算(Fused Multiply-Add, FMA) を計算する関数です。 $\mathrm{FMA}(x, y, z) = x \times y + z$ を高い精度で計算してくれます。