2010年11月29日月曜日

MongoDB その2

さて, redis のときと同じドメイン・クラスのインスタンスを, 同じように1万個作って, MongoDBに入れます.

一個ずつ flush:true すると34秒くらい, まとめて flush:true すると3秒くらい. こちらはまぁ, 許容範囲, というか Grails らしい遅さだ:-)

redis の場合に較べれば, とりあえずまともに動いているっぽい.

ただし, flush:true する前に永続化された (と思っている) インスタンスにアクセスしても (例えばfind) 答えは返ってきません...

とは言え, MongoDBだったら, 別に Grails のドメイン・クラスと inconsequential を経由するほどのことではない, という気もしないでもなかったりして(^_-)

2010年11月28日日曜日

MongoDB その1

どうも GORM for Redis の実装がうまくない, Spring 内の inconsequential がダメのような気がする, SpringSource から無用のプレッシャがあるんじゃないか...

と言うところで, 今度は GORM for MongoDB を試してみることにします.

まずは GORM for MongoDB のドキュメントを訳しましたよ.


なるほど, なるほど...

2010年11月20日土曜日

redis その4

GORM for Redis を使ってみたところ, 頭を傾げざるを得ない結果になってしまいました (redis その2).

そこで今度は Grails プラグインを経由せずに Groovy から直接 redis にアクセスしてみます.

とは言っても, redis のプロトコルをそのまま使うのは辛いので, GORM for Redis でも使われている Jedis (http://github.com/xetorthio/jedis) という Java クライアント API から使います.

ちなみに Jedis に Groovy 用に (とーっても) 薄い皮を被せた Gedis (http://github.com/xetorthio/gedis) と言うものもあるのですが, これは Jedis の最新 (1.4.x) でない版 (1.3.x) を要求するので止めておきます.

GORM for Redis と同じようにオブジェクト (Map) を Redis の hash, インデクスを Redis の set にマッピングしてみます.

public void testSomething() {

def redis = new Jedis('localhost')

assert redis

println redis.info()

redis.flushDB()

println redis.info()

def now = new Date()

println now

(0..9999).each { i ->

redis.hmset("jp.co.metabolics:redistest:Person:$i" as String,

[surname:"YAMADA$i" as String, forename:"Masaki$i" as String, dateOfBirth:now as String, gender:true as String])

redis.sadd("jp.co.metabolics:redistest:Person:id:$i" as String, "$i" as String)

redis.sadd("jp.co.metabolics:redistest:Person:surname:YAMADA$i" as String, "$i" as String)

redis.sadd("jp.co.metabolics:redistest:Person:forename:Masaki$i" as String, "$i" as String)

}

println new Date()

assert redis.exists('jp.co.metabolics:redistest:Person:8267')

assert redis.type('jp.co.metabolics:redistest:Person:8267') == 'hash'

assert redis.hget('jp.co.metabolics:redistest:Person:8267', 'surname') == 'YAMADA8267'

def i = redis.sinter('jp.co.metabolics:redistest:Person:surname:YAMADA8267')

assert i.contains('8267')

println new Date()

redis.save()

println new Date()

}


当然のことながら, 1万件の insert に対してほんの2秒ほど (GORM for Redisより二桁小さい) しか掛かりません...

2010年9月18日土曜日

redis その3

一目で分かるRedis



一目で分かる Grails から Redis へのマッピング



ただし, 実際には今のところキーに非欧米文字は使えないようで, 適当なエスケープ/エンコードが必要です

2010年9月12日日曜日

redis その2

さて, 淡々といきますよ.

redis本体をダウンロード, $make; $make test します.

Grailsプロジェクトを作り, GORM for redisプラグインをインストールします.

maven経由でいろいろなjarが付いてきます.
  • grails-datastore-gorm-redis-1.0.0.M1.jar
  • grails-datastore-gorm-1.0.0.M1.jar
  • spring-datastore-web-1.0.0.M1.jar
  • spring-datastore-core-1.0.0.M1.jar
  • spring-datastore-redis-1.0.0.M1.jar
  • jedis-1.0.0-RC4.jar
最後のjedisは, Javaのredisクライアントですが, その他はバージョン番号から見て, このGORM for redisの一味 (inconsequentialってやつですかい?) のようです.

さて, 何はともあれ, redisにつなぐGrailsアプリケーションを作ってみます. 取りあえず, grails側, redis側共にいっさい手は入れず, デフォルトのまま, grailsのintegration testを動かすことにします.

redisは $ ./redis-server で走り始めます. 5秒ごとのハートビートが鬱陶しいですが, 動かし始めると最初は役に立ちます.

ドメイン・クラスはこんな単純なもの. 関連も何も持ちませんが, 姓, 名についてインデクスを貼ってみました.

class Person {
static mapWith = "redis"
String surname
String surnameKana
String forename
String forenameKana
Date dateOfBirth
Boolean gender

static mapping = {
surname index:true
forename index:true
}
}

見て分かるとおり, Hibernateと共存した上で, このドメイン・クラスについてはredisを使うようにしています.

さて, 適当に1万件ほど作って, save()してみましょう. 20秒ほどで終了. なるほど.

と思いきや, この後に count()しようとすると, その時点で200秒 (!!) ほど待たされます. その間, redis側ではせっせとファイルに書き込んでいるようです. (redisではない) どこかにキャッシュされていただけなのですかね.

最初から1万件を一件ごとに save(flush:true) してみると, 230秒. だいたい理屈は合ってます:-) ちなみにこの時点でDBファイルの大きさは3MBほどになっています.

うーん. じゃあ今度は同様に10万件ほど書き込んでみると...

... 何時間か動いた挙げ句落ちました orz

そろそろ, 自分の手が何をやっているか, 頭が理解していないとやばい気がしてきました. GORM for redisにもまだまずいところはありそうですが, まずはredisについてもう少し掘ってみましょうか...

2010年9月11日土曜日

redis その1

ここ十数年, あらゆるデータ・ストレージがSQLに偏っていくのを苦々しく見ていた山田です.

80年代前半はSQLもまだ成熟していませんでしたが, リレーショナルなデータベースの理論には面白いところもありました. BSD版UNIXには, UCBで作られたIngresが付いてきましたよね. Ingresはその後, いろいろあってPostgreSQLへとつながっていくわけですが, 当時はRDBMSを本番のデータ・ストレージに使うなんてとんでもない! という (今とはまったく逆の) 雰囲気でした.

問題はリレーショナルなモデルそのものというよりは, SQLとか, 「データ中心モデリング」などという考え方にあるというのは, 後々に分かってくることです.

当時メイン・フレームやミニコンはともかく, Unixなどで何をデータ・ストレージに使っていたかというと, ファイル・システム (それ以前のファイル・システムに較べて, Unixのそれはそれなりに良くできていたのです) やBerkley DB (これはその後ライセンスが難しくなり, 使いにくかった) だったわけです. Berkley DBはいわゆるkey-value storeですね. 今はオラクルのものになってしまいましたが...

その後オブジェクトの時代になります. オブジェクトにはオブジェクトの容れ物 (OODB) があり, とても面白く, ある面では使いにくかった. その1つにGemStoneがあり, これは今vmwareに買収されて, 新しい局面を迎えています.

OODBは広く普及することはなく, ORマッパが広まっていきます. 中でもその初期に, NeXTが提供していたEOF (Enterprise Object Framework) は, 今のような, 単純にクラスと表を対応付けるだけのものとは微妙に違って, 表をオブジェクト的に活用する, (癖はあるものの) 興味深いORマッパでした.

当時は, 大量の不定型なデータを扱うようなものを作っていたので, 「全部オブジェクト・ネットワークのまま, メモリに入れとけばいいじゃん」とか, 「RDBMSったって, 巨大な表が1つだけあればいいんだよ」とか, 「所詮はみんなハッシュ表なんだから, そんなストレージを作ろうよ」とか言っては, DBAといわれる人たちに白眼視されていたわけです.

さて, 時は流れ, noSQLと言われるものがもてはやされる時代になりました. 温故知新. すべてのものをRDBMSに入れようとするアーキテクチャとか, DOAなどという馬鹿なものは消えてしまえばいいのにと心から思っている今日この頃です.

そこでRedisという, vmwareがサポートするオープン・ソースなkey-value storeが2.0になり, Grailsでもサポートされたのを機に, 何回かに分けて見ていこうと思います.

とりあえず, Grailsのプラグイン GORM for Redis のユーザ・ガイドを訳してみました.


ごく短いものですが, これを見るだけでRedis, 及びそのGrailsへの統合がおよそどのようなものか, 見当が付くと思います.

この後, Redis本体を調べつつ, プラグインのコードを読んだり, Grailsで実際的なモデルやコードを書きながら, 「表」じゃない, データ・モデルを探っていこうと思います. Redis本体に直接言及するわけではなく, 外堀から征くのがうちらしいやり方だ:-)

2010年5月28日金曜日

柔軟なエンティティの拡張

uehajさんの最近の一連の "DCIらぶ" な投稿を読んでいて, 大昔, Grailsでこんなプラグインを書いて遊んでいたのを思い出しました.

一つは動的に役割を与えるものです.

例えば

class Person { String name; String type }

というクラスがあったとします.

def john = new Person(name:"john", type:"contracted")

とすると, john は契約社員なので契約期間など契約社員特有のプロパティにアクセスできます.

john.fromDate = new Date("2010/6/1")

一方

def paul = new Person(name:"paul", type:"official")

とすると, paulは正社員なので給料など正社員特有のプロパティにアクセスできます.

paul.salary = 1200

これは実はmissingPropertyを利用して, typeの値に応じてそれぞれクラスContractedPerson, OfficialPersonのプロパティにアクセスできるようにしているだけなんですね.

typeの値によって, 動的にインスタンスごとに異なる役割を持たせることができるわけです.

もう一つはドメインクラスの定義を変えずに, ビュー (プロパティ・セット) を追加していくものです.

class Person { String name; static allowView = true }

としておくと

def george = new Person(name:"george")
george['contact'].tel = "0123-456-789"

これも前の例と同じようにmissingPropertyを利用して, クラスPersonContactにアクセスするようにしているだけです.

ビューであることを明示するために george['contact'] とかしていますが, もちろん普通にgeorge.contactと書いても同じです.

grailsのドメイン・クラスに対して, 単純にmixinしたり, categoryを追加しても駄目なんで, こんなことしていたわけですね...

2010年2月6日土曜日

Grailsのドメイン・クラスでnullな値はvalidateしてもnullable制約しかチェックされない. 同様にblankな文字列はblank制約しかチェックされない. 空の値を対象とする制約なんてあり得ないよね, と思えば, まぁ妥当性もないわけではないが...

「こういう場合にはnullを許す」みたいなカスタム制約を書きたいときには, 別の手を考えなくちゃいけないことになる. 面倒くさい.

2010年1月7日木曜日

もう使っている人も多いと思いますが, bashでgrailsコマンドの補完をしてくれるスクリプト. MacOSX用と書いていますが, 特にMacOSXには依存していないんじゃないかな.


grailsプロジェクト内 (create-domain-class, ..) と外 (create-app, ...) では補完されるコマンドが違っていたり, なかなかよくできてると思います.

同じ人が作った (補完スクリプトのオリジナル作者は別の人ですけど) もう一つ, これは面白い!


# 珍しくreblogしてしまいました