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入門とかを書くかもしれません。需要があれば。。。