これはGo3 Advent Calendar 2019の2日目の記事です。
昨日はimotyさんの「とにかく要素一覧の取得が高速な、要素が削除可能であるリストを実装する」でした。
昨年後半から仕事で Go を書く機会が増え、使用している DB ライブラリ GORM を使う事が多かったので
いちユーザーの視点から今年の動きについてつらつらと書きたいと思う。
昨日はimotyさんの「とにかく要素一覧の取得が高速な、要素が削除可能であるリストを実装する」でした。
昨年後半から仕事で Go を書く機会が増え、使用している DB ライブラリ GORM を使う事が多かったので
いちユーザーの視点から今年の動きについてつらつらと書きたいと思う。
GORM v1.9.2 以前
GORM は Go の ORM のひとつで、使い方などについては日本語での紹介記事も複数あるため改めて書く事もないが、リポジトリスター数の多さなど知名度の面では Go ORM の中では上位に入るのではないだろうか。
…が、自分が仕事で使い始めてからは PR の消化も滞り気味で、2018年11月にリリースされた v1.9.2 を最後に新しいバージョンも出ず、
少し触っているだけでバグではないかという挙動もいくつか見られたため PR を出してみたりもしたが見てもらえる望みも薄そう…という状況だった。
作者 jinzhu さんのモチベーションが下がったのか時間がとれなくなったのか、
もしくは元から広く使われる想定はなかったが何かの拍子で知名度やユーザーが増えたのかは不明だが、OSS としては微妙な印象があった。
(勝手な想像だが、個人的には前者ではないかと思っている。
ちゃんとした?公式サイトもあり v2.0 というマイルストーンも作られていたようだが、いつしかこのマイルストーンも更新されなくなってしまった…)
月に1回程度、思い出したかのように jinzhu さんが PR をマージしたりコメントしたりしていたが、
その基準もよく分からず、思い立ったタイミングで目についたものに反応している程度にしか見えなかった。
GORM v1.9.3 以降
そんな中、emirb さんという方がメンテナとして加わったようで、凄い勢いで溜まっていた PR をマージして v1.9.3 が出たのが2019年4月。結果的に大型アップデートとなった。
個人的に v1.9.3 で入って嬉しかったのが以下の2つ。
- optimize getColumnAsArray
- Preloadを使用して関連テーブルのデータを読み取る際に複数レコードが同じ外部キーに紐づいていた場合、
Where句に重複して指定されていたのが解消された。
- Better log output int8, int, int16, int32, int64, float32, float64, bool.
- 発行されるSQLをログ出力する際に数値や真偽値もクオーテーションされており、そのままコピペして実行しようとするとSQLエラーになっていたのが解消された。
こちらは自分でもPRを出そうとして、すでに出ていたのに気づいた。
これはこれで良いのか感がある…。
今では v1.9.11 まで上がっているが、上がるタイミングも粒度も謎、という状況になっている。
しかし、全く動きがない状態よりは良いだろう。
GORM へのコントリビュート
GORM を使っている中で気になる点も見つかり、いくつか PR を出した。- Don't set NULL if timestamp column is Primary Key
- MySQLで日付型を主キーにしていた場合、(実際のDDLには不要なのに)
gorm:"primary_key;not null"
とNOT NULL制約も指定しないと
テーブル作成に失敗する問題を修正した。 - Handle syntax to specify an index prefix length
gorm
タグでインデックスを指定する際、プレフィクスサイズ付きで指定できるようにした。- Fix function name of comment
- Uncapitalize error strings
- この2つは単なるリファクタリング。
- Fix CallbackProcessor.Get() for removed or replaced same name callback
- これは別の不具合を修正しようとテストを書いていて気づいた問題。
ちょっと調べただけで不具合っぽい挙動もまだまだ残っているので、コントリビュートチャンスはいくつも眠っているのではないだろうか。
(メンテナのお二人に反応がもらえるかが最もハードルが高い気がする…)
issue が増え続け誰も反応しないのも何とかしたかったので、サッと読んですぐ答えられそうなものはコメントしたりもしていた。
#2408, #2414, #2426, #2473, #2581, #2613, #2665, #2703, #2740
結果、いくつかの issue を解決しクローズできたようではあるが、新しく立てられる issue の数に対しては微々たるものだろう。
気づいてコメントしたのが issue が立てられてからだいぶ経っていて、「もう使うのやめたんで…」といったコメントと共に閉じられたものもあり良い状況とは言えない。
色々な Go ORM, これからの Go ORM
contracts (generics) 導入以前の Go の ORM は、言語的な制約に対する策のひとつとして何でもかんでも引数を
interface{}
として受けて柔軟な書き方ができるが、型の恩恵が受けられず間違った呼び出し方をすればランタイムエラーになってしまう、というものがある。
GORM はこの方針を採っており、ちゃんと使った事はないがパッと調べてみた限り gorp や sqlx なども似た方向性のように見える。
しばらく仕事で使ってみた身としては、型の恩恵が受けられずランタイムエラーに苦しめられ、
Go で書いているはずなのに何故こんな…となる事が少なくないため、個人的にはあまり良い印象が無い。
また別の策として、各テーブルそれぞれにきっちり型を定義した関数・メソッドを用意する、
ただし自力でボイラープレートを書くのはしんどいので DDL を食わせてコード自動生成をするというものがあるだろう。
SQLBoiler というライブラリがその方向性のように見えるので、機会があれば使ってみたい。
go-swagger や gomock など、ORM に限らず Go はジェネリクスのような気の利いた言語機能が無い事をコード自動生成で補う方向に進化していると思っていて、
この方向性のほうが筋が良いのではないだろうか。
いずれ contracts を活用した新機軸の Go ORM が爆誕するのではないかと期待しているが、
Go の言語機能として導入されたバージョンがリリースされ、浸透し、それを活用したライブラリが誕生するのはまだまだ先の話になりそうな気がする。
まとめに代えて、GORM に思う事
今までGoでの開発を3社経験したが、全社Webアプリケーションフレームワークに Gin、ORM に GORM という構成だった。スター数や知名度などからメジャーな ORM 感を醸し出しているが、使えば使うほど粗が見えてしまい、
次は別の DB ライブラリを選択したほうがいいのでは?いやそもそも素の
database/sql
だけで十分なのでは?いやいやどうせ SELECT した結果を struct にバインドするような処理は必要だから、薄い DB ライブラリは欲しくなるのでは?と考えを巡らせる事がある。
GORM を使っていて思うのは、前述の通り型に守られている感があまりなく、柔軟に書けるメリットよりも
実行時にエラーが発生したり想定と異なる挙動をしたりして原因解明に苦しめられるデメリットが目立ってしまう。
更にこれは GORM 特有の問題だと思う(そうであってほしい)のだが、
error を返したり panic してくれればまだ良くて、
厄介な事に内部で発生した error を握りつぶしてゼロ値として扱ったり無視して値を設定しなかったりするところもある。
設定したつもりの where 句の値が無視されてSQLが発行されたりすると、重大な不具合につながる事もあるだろう。
また、『改訂2版 みんなのGo言語』などでスター数の多い ORM の1つとして xorm や gorp などと比較されることがあるが、
実行速度や機能面で他の ORM に後塵を拝する事が多い印象もある。
とはいえ、仕事では今後も GORM と付き合い続ける予感はしているので、引き続き動向を追ったり貢献できるところはしていければと思う。
良くない点ばかり挙げてしまったが、シンプルな使い方をすれば開発速度を上げるのにとてもお世話になっているのも確かだ。
(ORM を導入しているようなプロダクトで複雑なクエリを書かなければいけない状況に陥った時点で
データ構造や処理の流れが妥当かを再考する必要があるのかもしれない。)
明日はtchsskさんの「golang.org/x/tools/go/analysis を使ってコードを自動修正する」です。