MOSS OutOfMemory の怪
IA-32 系のプロセッサを搭載したマシンでは、1 プロセスが参照可能なアドレス空間は、4GB(2^32=4294967296) だ。
そのアドレス空間の上位 2G がカーネルモード用、下位 2G がユーザーモード用。
MOSS のモジュールが既に 300MB 位を利用しているため、IA-32 系の環境では、約 1.7GB のアドレス空間の中で、MOSS のカスタマイズを行う必要がある。
(*) MOSS がユーザーモードアドレス空間の拡張に対応しているかは未確認。
(*) メモリの仕組みについては、「第4回 メモリー管理のキー技術「仮想メモリー」を知る」を参考のこと。
1.7GB、筆者にとっては途方もない数値。実際、筆者が構築してきたシステムでこのアドレス空間を食い潰したものは皆無だ。
しかし、それは、MOSS の開発者を悩ませた。
MOSS のリストは、DB の テーブルと類似している。よって、オンラインバッチ処理という発想も当然の様に実装される。
バッチ処理でその問題は発生した。約 20,000 件ほどのリストアイテムを foreach で回し、値を変更する処理だ。
昨今では、DB 上のレコードを約 20,000 件参照する位の処理で、メモリを気にすることはほとんど無い。しかし、それは、開発者側から見た類似構造である MOSS のリストでは、そうもいかなかった。テストデータのリストアイテムを MOSS のリストに入れ、バッチ処理プログラムを実行。実行中、MOSS のワーカープロセスは、メモリをぐんぐんと消費し、2GB に到達、ついには、例外「OutOfMemory」が発生。
MOSS のオブジェクトモデルの内部実装は良くは分からないが、おそらく、MOSS のオブジェクトモデルを利用して、リストアイテムを参照すると、DB からアイテムの各メタデータを収集し、オブジェクトとして返却する。この際、値のコピーを行って、DB のコネクションを切断していると推測される。というか最近のスクラッチ開発でも、このような方式が定石となっている。DB へコネクションを確立したままレコードを参照することはほぼ行わない。古くは、ADO の切断レコードセットという方式も同様であるが、複数のレコードを SQL 文で取得し、レコードに対応するオブジェクトをインスタンス化し、データを任意のコレクションに格納後、DB へのコネクションを切断するという方式だ。
今、ほぼ行わないと表現したのは、現在でも、DB の情報をメモリに展開し、オブジェクトとして扱わないケースがあるからだ。それがまさにバッチ処理である。
バッチ処理では、トランザクションの分離、膨大なデータに対する処理の早さが求められるため、しばしば利用している DB にネイティブな方式で実現される。筆者が関わったシステムでも、ストアドプロシージャーでバッチ処理を組むことが多々あった。筆者の考えでは、バッチ処理であっても上位 API で組むべきと考えているが、SLA を実現するためには、ネイティブな方式をとらざるを得なかった。
では、MOSS でバッチ処理を行うためには、SQL Server に直接アクセスすべきか?であるが、これは、MOSS の DB テーブル構造の仕様が公開されていないため、ノンサポートのシステムとなってしまう。これでは、パッケージを入れた意味が無くなってしまう。
選択枠としては、MOSS のオブジェクトモデルを利用し、バッチ処理を実装するしか方法は無い。
そこで、筆者の経験上、以下の点に注意して実装を行う必要がある。
1. メモリの利用を最低限に抑える
2. トランザクションの加味した設計とする
1 は、悲しきかな高級言語ではあるが、調査を行った所、Best Practices: Using Disposable Windows SharePoint Services Objectsにベストプラクティスとして方式が掲載されている。ここに記載されている方法で、メモリをこまめに開放するように実装する。特に、MOSS ライブラリでは、ファイルのバイナリをメモリに展開するため、巨大なファイルを参照する際には注意が必要だ。
2 については、非常に難しい問題である。トランザクションの分離レベルとスコープが課題となる。分離レベルとしては、同時実行性の加味が必要で、同期処理方式の十分な検討、設計が必要である。スコープでは、複数のリソースを更新するような処理であれば、各処理が失敗した場合の挙動を定義する必要がある。ロングトランザクションによるコミット、ロールバック方式の検討、設計が必要となる。
また、MOSS カスタマイズを行う場合、アーキテクトを担当する方は、Best Practices: Common Coding Issues When Using the SharePoint Object Modelも参考にした方が良いだろう。
そのアドレス空間の上位 2G がカーネルモード用、下位 2G がユーザーモード用。
MOSS のモジュールが既に 300MB 位を利用しているため、IA-32 系の環境では、約 1.7GB のアドレス空間の中で、MOSS のカスタマイズを行う必要がある。
(*) MOSS がユーザーモードアドレス空間の拡張に対応しているかは未確認。
(*) メモリの仕組みについては、「第4回 メモリー管理のキー技術「仮想メモリー」を知る」を参考のこと。
1.7GB、筆者にとっては途方もない数値。実際、筆者が構築してきたシステムでこのアドレス空間を食い潰したものは皆無だ。
しかし、それは、MOSS の開発者を悩ませた。
MOSS のリストは、DB の テーブルと類似している。よって、オンラインバッチ処理という発想も当然の様に実装される。
バッチ処理でその問題は発生した。約 20,000 件ほどのリストアイテムを foreach で回し、値を変更する処理だ。
昨今では、DB 上のレコードを約 20,000 件参照する位の処理で、メモリを気にすることはほとんど無い。しかし、それは、開発者側から見た類似構造である MOSS のリストでは、そうもいかなかった。テストデータのリストアイテムを MOSS のリストに入れ、バッチ処理プログラムを実行。実行中、MOSS のワーカープロセスは、メモリをぐんぐんと消費し、2GB に到達、ついには、例外「OutOfMemory」が発生。
MOSS のオブジェクトモデルの内部実装は良くは分からないが、おそらく、MOSS のオブジェクトモデルを利用して、リストアイテムを参照すると、DB からアイテムの各メタデータを収集し、オブジェクトとして返却する。この際、値のコピーを行って、DB のコネクションを切断していると推測される。というか最近のスクラッチ開発でも、このような方式が定石となっている。DB へコネクションを確立したままレコードを参照することはほぼ行わない。古くは、ADO の切断レコードセットという方式も同様であるが、複数のレコードを SQL 文で取得し、レコードに対応するオブジェクトをインスタンス化し、データを任意のコレクションに格納後、DB へのコネクションを切断するという方式だ。
今、ほぼ行わないと表現したのは、現在でも、DB の情報をメモリに展開し、オブジェクトとして扱わないケースがあるからだ。それがまさにバッチ処理である。
バッチ処理では、トランザクションの分離、膨大なデータに対する処理の早さが求められるため、しばしば利用している DB にネイティブな方式で実現される。筆者が関わったシステムでも、ストアドプロシージャーでバッチ処理を組むことが多々あった。筆者の考えでは、バッチ処理であっても上位 API で組むべきと考えているが、SLA を実現するためには、ネイティブな方式をとらざるを得なかった。
では、MOSS でバッチ処理を行うためには、SQL Server に直接アクセスすべきか?であるが、これは、MOSS の DB テーブル構造の仕様が公開されていないため、ノンサポートのシステムとなってしまう。これでは、パッケージを入れた意味が無くなってしまう。
選択枠としては、MOSS のオブジェクトモデルを利用し、バッチ処理を実装するしか方法は無い。
そこで、筆者の経験上、以下の点に注意して実装を行う必要がある。
1. メモリの利用を最低限に抑える
2. トランザクションの加味した設計とする
1 は、悲しきかな高級言語ではあるが、調査を行った所、Best Practices: Using Disposable Windows SharePoint Services Objectsにベストプラクティスとして方式が掲載されている。ここに記載されている方法で、メモリをこまめに開放するように実装する。特に、MOSS ライブラリでは、ファイルのバイナリをメモリに展開するため、巨大なファイルを参照する際には注意が必要だ。
2 については、非常に難しい問題である。トランザクションの分離レベルとスコープが課題となる。分離レベルとしては、同時実行性の加味が必要で、同期処理方式の十分な検討、設計が必要である。スコープでは、複数のリソースを更新するような処理であれば、各処理が失敗した場合の挙動を定義する必要がある。ロングトランザクションによるコミット、ロールバック方式の検討、設計が必要となる。
また、MOSS カスタマイズを行う場合、アーキテクトを担当する方は、Best Practices: Common Coding Issues When Using the SharePoint Object Modelも参考にした方が良いだろう。
コメント (0)
コメントの投稿