DBA的な視点から見たgit

git は現在分散バージョン管理システムの中では最も使われているものだと思いますが、サーバ管理やネットワークだけやってるエンジニアからすると、git は開発者が使うもので、なんか難しそう、と思って敬遠しがちです。実際僕も昔は git なんてコマンド打ちたくありませんでした。しかし、分散バージョン管理システムというのは、複数人でシステムを運用(not 開発)する際にもとても役に立ちます。このエントリでは、いわゆる一般的な git の解説とは違った視点から、git の良さを書いてみたいと思います。

ちなみに、git の基本的なアイデアについてはこちらのエントリが分かりやすいと思います。

複数人でファイルを編集するなら

別にバージョン管理すべきものはソースコードだけではなく、例えばサーバの設定ファイルなんかも管理すべきものです。etckeeperなんてのがありますが、これでなくても例えば最近なら継続的プロビジョニングとして Puppet や Chef を使っている方が多いと思いますが、それらのプロビジョニングの手続きや配置するファイルなんかは git で管理すべきです。

なぜか?たかが設定ファイルやちょっとしたスクリプトにそんなのいらんでしょ?僕もそう思っていました。でも、1 つのファイルを複数人で編集するというのは wiki の 1 ページを複数人で更新するようなもので、保存しようとしたら他の人が先に保存してて衝突して保存できなかったり、下手すると前の人の更新を上書きして消し去ってしまったりします。これは複数人で 1 つのリソースを同時に更新するなら当然起こりうる現象であり、並列処理の文脈では当たり前の様にこれをどうやってうまくハンドリングするかが考えられているわけです。

その最たるものがデータベースですね。昨今のデータベースは同時処理性能はかなり重要です。同時にレコード A とレコード B を更新しようとした場合、MySQL なら 1 レコードでもテーブル全体をロックしてしまう MyISAM よりも、更新するレコードだけ(正確には違いますが)をロックする InnoDB を使った方が、処理にかかる時間は早いことになります。1 レコードを複数人が更新しようとする場合にも、早い者勝ちでロックを取って更新する悲観ロック方式と、実際に更新する時に自分より前に更新がないことをアトミックに確認して更新する楽観ロック方式みたいなのがあったりしますね。また、1 人が複数の更新をアトミックに行うためのトランザクションという仕組みもあります。

そう、インフラやってる人は、一つのリソースに対して複数人が更新をするという事象は当たり前に見ていて、当たり前にうまくハンドリングしているわけです。だったら設定ファイルだってちゃんとハンドリングしてあげないと、データベースの一貫性がぶっ壊れるのと同じ様に、ファイルがおかしなことになってしまいます。データというものに関して複数人の更新を管理する時にデータベースを使うように、ファイルに対して複数人の更新を管理するために使うのがバージョン管理システムです。特に中央に何かしら master となるレポジトリを持つ subversion や一般的な git の使い方はいわゆるデータベースに近いものがあります。

データベースのアナロジー

で、git の何が良いかというと、僕の大好きな InnoDB の様に、とても実践的でバランスのいい管理システムなんですよね。

更新する時に逐一リモートレポジトリを更新する必要のある Subversion などは、トランザクションのないデータベースみたいな感じです。自分だけが見えればいい小さい更新もみんなに丸見えになってしまいますし、それが他の人の作業に即座に影響を与えてしまいます。MyISAM がまさにそれですが、確かにシンプルで分かりやすいんですが、ちょっと複雑なシステムになってくると厳しくなってきます。

git のローカルコミットは、トランザクショナルデータベースで言うところの、トランザクション内の更新みたいなものです。他の人、もしくは他のブランチからはその更新は見えないので、REPEATABLE READ みたいな感じですね。しかも面白いことに、ローカルの作業ディレクトリでは、トランザクションを走らせて確定をしないままに、別のトランザクションを開始することもできるわけです。それが git の branch です。手元ではいつでもいくつでもトランザクションを開始できるわけですね。

で、トランザクションを確定する処理が push に相当します。手元でトランザクションを開始した時には当然共有ロックは取得していないので、この更新は楽観ロック的なイメージで一貫性を保ちます。git が自動で素直に更新を確定できない(楽観ロックのバージョンチェックに失敗するみたいな)場合、push はできずエラーが返ってきます。無理やり更新するには git push -f するわけですが、これは一貫性を壊す行為なのでオススメできません。楽観ロックよろしく、もう一度トランザクションを開始(HEAD を merge)して同じ処理を行なって再チャレンジすればうまくいきます。

他にもデータベースには、バックアップと Write Ahead Log(WAL)を使った Point-in-Time Recovery(ある時点のデータを復元する)という機能というか処理があるんですが、git はレポジトリ全体の状態を commit 毎にハッシュ値を作って記録しているので、いつでも好きな時に任意の状態にレポジトリ全体を復元できます。更に任意の状態からトランザクションを開始することもできます。ちなみにハッシュ値にエイリアスをつけることができて tag と呼ばれています。ソフトウェアのリリース時にタグを打っておくと、戻したり比較する時に楽ですね。

そして、git というより github になりますが、pull request という機能も素晴らしいです。これは、自分では更新できない(もしくは直接更新したくない)データベースを更新したい時に、更新権限のある人に依頼するための効率良い仕組みになります。そう、本番のデータベースを触れない人が DDL の投入を DBA に依頼するような感じですね!その際に、diff が確認できるのは当然として、ウェブの画面でコメントのやり取りを行ったり commit に対して質問したりして、実際の反映前に処理の内容を明確にすることができますし、後からその経緯を参照できます。また、実際反映する時はボタンをぽちっとなとするわけですが、当然その間にも master は更新され続けているので、最初に pull request した時とは状況が変わっています。その場合でも git は 3-way-merge なので、かなり賢く merge してくれますし、ちょっと乖離しすぎてる場合には改めて最新の状態からトランザクションを再度開始(HEAD を merge)してコミットすることで pull request をフレッシュにすることもできます。

まとめ

というわけで 1 つのファイルを複数人で編集するなら、データベース相当のものをつかうべきで、git は実践的にはとてもよくできているシステムだと思います。特にウェブの開発をする人にとってデータベースの使い方が必修科目であるのと同様に、ウェブのオペレーションをする人にとってはバージョン管理の使い方は必修科目だと僕は思います。ので、これをふまえて、非開発者向けの git 入門とかを書くかもしれません。需要があれば。。。