時計を壊せ

駆け出してからそこそこ経ったWebプログラマーの雑記

Goの並列テストが何並列で実行されるのかを知りたい

Goの並列テストそのもにについては、Mercari Engineer Blogで@yoshiki_shibataさんによって解説されているこの記事が有名かと思います。

engineering.mercari.com

並列に実行されるということは、これはレースコンディション問題に対するテストケースの作成にも利用できるわけです。 sync.Cond などを使わずにできるので便利ですね。

とはいえ、何並列まで同時に実行してくれるのかが分からないと、このような用途ではちょっと困ります。

たとえば、適当な数のテストを並列で走らせるとして、実際の並列実行数を並列テストの数が下回るぶんには正常に実行できますが、それを大きく上回るとデッドロックを起こす場合があります。

そして、実際の並列実行数は最初に貼った記事のとおり-parallelオプションかあるいはそのデフォルト値であるGOMAXPROCSによるので、すなわち環境依存で失敗しえるテストになってしまいます。 環境依存で失敗しえると本当の失敗を見逃しやすくなってしまうのでよろしくありません。

これを解決するためには、小さな数に並列テスト数を固定してしまう手もありますが、あまり小さな並列数に固定してしまってはレースコンディションが起きそうな状況を引き起こせる確率が低く、また十分に低くなければ前述のように実行環境依存でらデッドロックを引き起こしてしまいかねません。

かといって並列実行数を得られるインターフェイスは提供されていなさそうです。

じゃあ無理やり取るかというわけで暴力です。暴力は全てを解決する……。

func mustGetParallelCount() int {
    tp := flag.CommandLine.Lookup("test.parallel").Value.String()

    parallel, err := strconv.Atoi(tp)
    if err != nil {
        panic(err)
    }

    return parallel
}

こんな感じで取れます。flagはCommandLineという変数にグローバルなコマンドライン引数の処理結果を持っているのでそこからLookupすれば取れるという寸法です。

これを使って並列テスト数を調整したり、場合によってはテストケースごとSkipするなどすればよさそうです。めでたしめでたし。

とはいえ、若干筋悪っぽい感じもするのもっとで良いアイデアあったら教えて下さい。

WEB+DB Press Vol.119のPerl Hackers Hubに寄稿しました

WEB+DB PRESS vol.119の表紙
WEB+DB PRESS vol.119

Perl Hackers Hubも第64回ということでキリが良いですね。 個人的にはありがたいことに3度目のPerl Hackers Hub掲載です。

今回は「少しマニアックなPerlのテクニック」ということでPerlにまつわる少しニッチなTips集のようなものを書かせていただきました。

特殊変数を使って短くシンプルにコードを書き上げるテクニックであったり、Perl組み込み関数のsyscallを使って任意のシステムコールを呼び出す方法などを紹介します。 もっとPerlを使いこなしたい!と思っている方へのヒントとなるような内容を届けられたらと思っております。 もちろん、CPANモジュールを使わないことを推奨するわけではなく、あくまでもPerl本体の機能だけでもここまでのことができるぞという紹介になっております。

Dockerコンテナに潜ってDockerfileのデバッグをしたり、FaaSでprintデバッグをしたりすることも多い昨今ですが、まだまだオンプレミスの環境やその流れで構築されたIaaSの環境も多いことでしょう。 そのような環境でいざというときにログやデータを調査するための道具が少し足りない!というときに、CPANモジュールなどの外部パッケージのインストールが気軽にできる環境というのはそう多くはありません。

そのような環境で、外部パッケージに頼らず、多くの環境に最初からインストールされているPerlそのものの機能を使いこなすことができると、その際の解決策の選択肢の幅が広がります。 Perlそのものの機能も使いこなせれば、CPANモジュールなどの外部パッケージのインストールをする必要もなく、より素早く問題を解決できる場面もあることでしょう。 その際に役に立ったり、あるいはそのヒントになるようなものを提供出来たらと思っています。

そういうわけで、今回のテーマを書かせていただきました。 明日10/24に発売となりますので、もし良ければ書店等でお買い上げいただき、読んで頂けますと幸いです。

編集を担当して頂いた技術評論社の稲尾さんをはじめ、直接監修頂いた牧さん、ほか勤務先など各方面の関係者の方々にも様々な面で協力してもらい助けて頂きました。 この場を借りて改めて御礼申し上げます。ありがとうございました。

TypeScriptでタプル型の順列を得たい

たとえば、 [1, 2, 3] というタプル型があった場合に [1, 2, 3] | [1, 3, 2] | [2, 1, 3] | [2, 3, 1] | [3, 1, 2] | [3, 2, 1] みたいな組み合わせが欲しい。 これは順序を指定するようなケースに型制約を持たせるときに役立ち、io-tsなどを使ってType Guard関数を生成すれば外部入力に対しても型エラーが捕捉できる。

サッとググった感じ、自分のググりパワーが足りないのかうまいソリューションがみつからなかったので、あーでもないこーでもないとやって諦めてベタッと書いたところこんな感じになった。

type Permutations2<T extends readonly any[]> = [T[0], T[1]] | [T[1], T[0]];
type Permutations3<T extends readonly any[]> =
  | [T[0], ...Permutations2<[T[1], T[2]]>]
  | [T[1], ...Permutations2<[T[0], T[2]]>]
  | [T[2], ...Permutations2<[T[0], T[1]]>];
type Permutations4<T extends readonly any[]> =
  | [T[0], ...Permutations3<[T[1], T[2], T[3]]>]
  | [T[1], ...Permutations3<[T[0], T[2], T[3]]>]
  | [T[2], ...Permutations3<[T[0], T[1], T[3]]>]
  | [T[3], ...Permutations3<[T[0], T[1], T[2]]>];
type Permutations<T extends readonly any[]> = {
  2: Permutations2<T>;
  3: Permutations3<T>;
  4: Permutations4<T>;
}[T["length"] extends  2 | 3 | 4 ? T["length"] : never];

もっとうまくできそうな気がするが無限再起と判定されたり難しかった。n行ぶん記述すれば良いので雪だるま式に記述量が増えることもないが、添字を書き間違えたら変なタプル型が混じることになるのは微妙っぽい。 無限再起を回避する方法はboost-tsの実装とか参考にしたけど定数ぶんまでのパターンしかサポートしないということにするしかないのだろうか。

もっとうまく書けるぜ!ってひといたら教えて下さい。

これだった(教えてもらった):

susisu.hatenablog.com

やっぱ素直に再帰すると無限再帰扱いでエラーになるんだなぁ。 ベタに書くのも悪くないだろうか。

こういうのもあると教えてもらった:

github.com

シンプルでよさそうだけど無限再帰エラーにかからないのはobject型(っていうのかこれ?)をつかっているからだろうか?

Google App Engineでローカル開発をするときにdispatch.yamlをもとにReverse Proxyしてくれるツールを書いた

これです

github.com

なぜ作ったのか

dispatch.yamldispatch.xml はGoogle App Engine(以下GAE)のFrontendでルールベースでL7 HTTP Reverse Proxyしてくれるものです。

cloud.google.com

これはMicroservicesをやる上では大変便利なものになっています。
一方で、これをローカルで動かす手段が少なくとも自分の知る限りはありませんでした。

なので、いままでは各サービスを連携して動作検証したいときは、GAEにデプロイしてしまうか、
あるいは各サービスをそれぞれ別々のポートで立ち上げながら、各サービスに何らかの方法でそれらのマッピング情報を入れてサービス間で連携が可能なようにするなど工夫をする必要がありました。

別々のポートで立ち上げるところまではよしとしても、めんどくさいのでその後の各サービスには統一的にアクセスしたいわけです。
しかし、dispatch.yamldispatch.xml をnginx.confなどに移植してnginxを立ち上げたりなんてのもまためんどくさい。ぐぬぬ。

せめて dispatch.yamldispatch.xml をそのまま読んでよしなにReverse Proxyしてくれるサーバーがあると楽そうです。 ということで作りました。

使い方

README.mdにあるとおりですが、こういう感じで使えます。

$ (cd default; dev_appserver.py --port=8081 | tee -a dev.log) &
$ (cd mobile-backend; dev_appserver.py --port=8082 | tee -a dev.log) &
$ (cd static-backend; dev_appserver.py --port=8083 | tee -a dev.log) &
$ gae-dispatcher-emulator -c dispatch.yaml -s default:localhost:8081 -s mobile-frontend:localhost:8082 -s static-backend:localhost:8083

-c で渡した dispatch.yaml-s で渡しているサービスの待受情報をもとによしなにやってくれるというわけです。

これでデフォルトの localhost:3000gae-dispatcher-emulator

実際はforemanなどと組み合わせて使うのがおすすめです。

インストール

go製なので go get でいけます。

$ go get -u github.com/karupanerura/gae-dispatcher-emulator/...

また、Github Releasesにてバイナリの配布もしてますのでそちらもご利用いただけます。

Releases · karupanerura/gae-dispatcher-emulator · GitHub

macOSであればこういう感じでインストールできるでしょう:

$ curl -sfL -o ~/bin/gae-dispatcher-emulator https://github.com/karupanerura/gae-dispatcher-emulator/releases/download/v0.3.0/gae-dispatcher-emulator_darwin_amd64
$ chmod +x ~/bin/gae-dispatcher-emulator

実装

dispatch.yaml などを読めることを除けば素直なReverse Proxyとして動くと思います。

具体的には、X-Forwarded-For ヘッダを処理したりRFC 2616 - Hypertext Transfer Protocol -- HTTP/1.1をもとにhop-by-hopなものとして定義されているヘッダフィールドを取り除くなどのことをしています。
このあたりはlib/Plack/App/Proxy.pm - metacpan.org の実装を参考にしました。

一応APIも公開していますのでforkして追いかけるのはつらそうだけど表面だけ変えたいとかあればお使いください(ただしインターフェースは突然変わるかもしれません):

godoc.org

なにか気になること、困ったこと、要望などあればお気軽にissueやTwitterなどでお知らせください。