PersistenceGeneration

English page

Kahua Release

kahua-web Release

Security Advisory

Event Log

Documentation

For developers

Site info

Related Site

永続クラスの履歴管理

永続的なオブジェクトは、プロセスよりも長生きする。 そのため、あるバージョンのプログラムで作った永続オブジェクトを、 別のバージョンのプログラムで取り出して変更したりすることは 必然的に起こり得る。

何らかの異なるバージョンのインスタンス間の変換規則は 与えられているとして (例えば、同名のスロットなら活かす、 追加されたスロットなら:init-valueで初期化、など)、 データベースにあるインスタンスをどのようにアップデート してゆけば良いだろうか。

最も簡単なものは、プログラムが自分の持つ永続クラス定義 (データベーススキーマとも考えられる) とデータベースにある 永続インスタンスの違いを見つけた時に、永続インスタンスを 自分の持つ定義に合わせてアップデートする、という方法である。 普通はプログラムの更新はインクリメンタルであると考えられるので、 この方法でも大抵はうまくゆく。

しかし、実際の運用では、上記の方法で問題が生じる場合がある。 サーバのような長期間に渡って走るプログラムがあったり、 非常に多種のプログラムがデータベースにアクセスするケースだ。 そのような環境では、「永続クラス定義(データベーススキーマ) を変更した際に、全てのプログラムを同時に変更するのが不可能である」 ということが起こり得る。

例えば、サーバを止められる時間が非常に限られている場合。新しい スロットを追加してデータにアップデートをかけたくても、 既に走っているサーバは古いクラス定義のままである。また、 永続クラス定義をインハウスライブラリとして提供していて、 たくさんの人がそれを使ったツールを書いているような環境では、 全てのツールを一斉にアップデートすることは非現実的である。

このような環境で、上記の簡単なアップデート戦略を取ると、 新しい定義を用いるプログラムと古い定義を用いるプログラムが 交互にデータベースにアクセスした際に、永続インスタンスが その都度変換されることになる。これは性能的に 不利なだけでなく、変換の際に情報が落ちる可能性もあり、 好ましくない。

kahua.persistenceでは、永続クラス定義の変更履歴を自動的に 管理することで、モノトニックなスキーマ更新と、任意のバージョンの プログラムがデータベースに透過的にアクセスすることを可能にしている。

永続クラスの世代

永続クラスの定義はデータベースに記録されている。 定義が変更される度に、各定義にユニークな世代番号が割り当てられている。 データベースは、明示的にpurgeされない限り、全ての世代の定義を保持する。

(但し、データベースが保持するのは永続クラス定義のうち、 永続スロット (:allocation :persistentなもの)のみである。 非永続スロットの定義変更はクラスの世代には影響を与えない。)

プログラムがデータベースにアクセスすると、 プログラムが持つクラス定義(in-memory class)と データベースの持つクラス定義(in-db class)が比較される。 in-memory classがin-db classの最新の世代に一致すれば、 問題はない。

in-memory classがin-db classの最新の世代に一致しない場合、 考えられるケースは二つある。

  • in-memoryがin-dbよりも古い定義を使っている
  • in-memoryがin-dbよりも新しい定義を使っている

この二つは、定義の比較だけでは区別できない。たまたま、 最新の定義が過去の定義と一致する場合もあるからである。

そのため、永続クラスのメタクラス <kahua-persistent-meta>には、 source-idというスロットが用意してある。一般に、 永続クラス定義が変更される度に更新されるような文字列を与えることが 想定されている。CVSを使っている場合は$Revision$等のキーワード置換を 用いることができる。

source-idは必ずしも永続クラス定義に1対1に対応している必要はない。 一般のワークフローでは、定義を変更し、テストしてからチェックインする という流れになるであろうから、テスト時点では同一のsource-idで 異なる定義が用いられることになる。また、チェックインした時点で 定義は同じでもsource-idが異なったものになる。 データベースは、source-idと定義の世代番号とをmany to manyの関係で保持している。

in-memory classとin-db classの定義が一致しない場合、 まず、in-memory classが持つsource-idに対応する世代番号(複数であることもある) の定義がデータベースから取り出され、それがin-memory classの定義と 比較される。一致するものがあった場合は、 データベースの世代の方が進んでいるとみなす。 一致するものが無い場合や、source-idに対応する世代番号が無い場合は in-memory classの方が新しい定義であるとみなし、 新たな世代番号を割り当てる。

永続インスタンスの変換

各永続インスタンスは、データベースに記録される際に、 それがどの世代の永続クラス定義に従って書かれたものかを保持している。

永続インスタンスがプログラムに読まれる際に、in-db instanceの 世代とin-memory classの世代が異なっている場合は、in-memory classの 定義に合うようにインスタンスが変換される。 (変換規則の詳細は後述する)

in-memoryでのインスタンスが変更を受けた場合、トランザクションの コミット時にその変更がデータベースに反映されるが、 ここでin-memoryとin-dbの世代差によって次の操作が行われる。

  • in-memoryの世代がin-dbの世代と同じか、新しい場合: in-memoryの世代の永続クラス定義を使って書き込まれる
  • in-memoryの世代がin-dbの世代より古い場合: in-memoryのインスタンスが、読まれた時とは逆の変換を受け、 in-dbの世代の定義を使って書き込まれる

このことから、データベース上の永続インスタンスの世代は 単調に増加する。従って、上に述べたような、永続インスタンスが ピンポンのように変更を受け続けるようなことは起こらない。

デフォルトのインスタンス変換規則

デフォルトのインスタンス変換規則は、クラス再定義の際の インスタンスアップデートプロトコルに似ている。 すなわち、同名のスロットの値が保持され、 追加されたスロットは:init-valueの値で初期化される。 但し、削除されたスロットの値は捨てられるのではなく、 インスタンス中の特殊なエリアに保存される。

変換する世代が2つ以上離れている場合は、1世代毎に変換が 適用される。

削除スロットの値を保持しておくことで、例えば次のような操作が 可能になっている。

  • in-dbの世代の方が新しく、スロットXを追加しているとする。 プログラムがそのインスタンスを変換する際に、スロットXの値は 見えなくなる。しかし、そのインスタンスが変更を受け、データベースに 書き戻される際に、スロットXの値は復元される。
  • 一度スロットXの削除した定義を行い、その後でスロットXを 復活させた。スロットXを削除する前の世代のインスタンスにアクセスした 場合、最初の世代の変換途中でスロットXは消えるが、次の世代の 変換によってその値は復元される。

現状の実装での制限

永続オブジェクトのキーをメソッドkey-ofで与えることになっているが、 キー計算の方法は世代間で変えることはできない。 これを変えると、過去の世代のインスタンスにはアクセスできなくなる。

システムの方ではキーの計算法が変わったかどうかは検出しない(できない) ので、プログラマが注意する必要がある。

Kahua 0.8で追加されたインデックススロット機能を使うと(0.8ではefsデータベースでしか使用できないが)、

(define-class <a-class> (<kahua-persistent-base>)
  ((a :init-keyword :a :allocation :persistent :index :unique)
   (b :init-keyword :b :allocation :persistent :index :any)
   (c :init-keyword :c :allocation :persistent)))

と定義されるクラスのインスタンスに対して、スロットaやbの値で問い合わせを行うことができる。

(make-kahua-collection <a-class> :index '(a . "foo"))
=> #<make-kahua-collection>
(make-kahua-collection <a-class> :index '(b . "bar"))
=> #<make-kahua-collection>

ただし、このあたりのインタフェースは、実装をしながら練り込んでいるところなので、将来的に 変更される可能性もある。

Copyright (c) 2003-2007 Kahua Project Contact | About Us