MongoDBのcursor
下記の感じでなんとなくでMongoDBのcursorを使っているけど、今までちゃんと公式ドキュメントを読んだ事がなかったので改めてドキュメントを読んでみた。
var cursor = db.users.find(); while (myCursor.hasNext()) { printjson(cursor.next()); }
現行バージョン:
https://docs.mongodb.com/manual/tutorial/iterate-a-cursor/
過去バージョン:
https://docs.mongodb.com/v3.0/core/cursors/
仕様概要
軽く読んだ感じ次の仕様になっているらしい。
cursor.next()
を使ってループする実装にしていても、実は毎回MongoDBサーバーからデータを取得している訳ではない(ネットワーク負荷がボトルネックになってパフォーマンスが出ないので)。MongoDBドライバは実は裏でMongoDBサーバーから小分けにデータを受け取っていて、
cursor.next()
はその小分けのデータから1ドキュメントずつ返しているだけ。小分けで受け取っていたデータがなくなったらMongoDBサーバーに問い合わせて小分けのデータを補充する。(裏でMongoDBドライバがMongoDB Wire Protocolを使ってデータ取得やらなんやらしているが、それをMongoDBドライバが隠匿している)MongoDBドライバがどれくらい小分けにして受け取るかというと、一番最初は101個のドキュメントをまとめて取得し、それ以降は16MBずつ取得する。この挙動は
batchSize()
とlimit()
を使って制御する事が可能。
※3.4より前のバージョンだとちょっと挙動が違うMongoDBドライバは、MongoDBサーバーから小分けにデータを貰ってくるが、MongoDBサーバーはその瞬間の最新データを返す。したがってデータの更新が重なると1回のカーソルのループで、同じレコードが複数回返ってくる事がある。
※insert,updateでnatural orderが変わった時に複数回返ってくると思われる
※ただし前述の通り、結果が101ドキュメント以下なら複数回返ってくるのは起こり得ないはず上記の、同じレコードが複数回返ってくる問題は
cursor.snapshot()
を使って回避する事ができる。
という事で、cursorを使っていても最新のデータが返ってくるケースがあるっぽい(もちろん古いデータが返ってくることもある)。
個人的に注意が必要だと思ったのは下記の点。
- cursorから返ってくるドキュメントは、
find()
した瞬間のドキュメントが返ってくることもあれば、そうでないこともある - cursorをループしていると、同じドキュメントが複数回返ってくることがある
概念図
文章だけだとわかりづらいので図にしてみる。
+---------+ +---------+ +--------+ | MongoDB | | MongoDB | | Client | | Server | | Driver | | | +-+-------+ +----+----+ +------+-+ | | | | | find() | | OP_QUERY <---------------+ <----------------+ | | 101 documents | | +----------------> cursor | | +---------------> | | | | | cursor.next() | | <---------------+ | | document | | +---------------> | | | | | cursor.next() | | <---------------+ | | document | | +---------------> : : : 101個documentを取得する : : : | | cursor.next() | | OP_GET_MORE <---------------+ <----------------+ | | some documents | | +----------------> document | | +---------------> | | | | | cursor.next() | | <---------------+ | | document | | +---------------> | | |
補足
- MongoDBサーバーとMongoDB Driverは
MongoDB Wire Protocol
を使ってやり取りを行う。 - (言語は何でもいいけど)例えばPHPからMongoDBクラスを使ってMongoDBサーバーと通信を行うとき、裏では
MongoDB Wire Protocol
を使っている。 - PHPからすると、MongoDBからcursorを取得して、cursorから 1 documentずつドキュメントを受け取るように実装したとしても、実はある程度の塊でMongoDBサーバーからMongoDB Driverが複数のドキュメントを受け取っていて、MongoDB Driverがそれを 1 documentずつ返している。
参考資料
2018初夏、Visual Studio Codeに夢中
エディタなんて好きなものを使えばいいと思うし、他人に押し付けるものでもないし、なんなら自分はIDE派でもあるんだけど、最近Visual Studio Code(vscode)を気に入って使っている、という話。
Microsoftの印象
自分にとってMicrosoftといえば悪いイメージしかなかったんだけど、最近はむしろ頑張ってるイメージが強い。なんなら軽く応援している。
きっとその要因の一つはvscodeだと思う。
vscodeがいい感じ
自分は最近少しだけGo言語を書いている。
Go言語を書いている人はvscodeを使っている人が多いと思う(超主観)。もちろん公式にはvimとかGoLandとかも紹介されているけど、自分はvscodeを気に入って使っている。
少し前までは色々なエディタを試してはしっくりこないくて乗り換える、というのを何年か繰り返していたけど、これをきっかけにvscodeでもういいか、という気になった。
golangでKVSサーバーを実装してみた
golangの勉強がてらKVSのDBサーバーを雑に実装してみた。
DBの実装自体はbuntdbというライブラリをそのまま使っているので、あまり大した事はやっていないけど。
実装したDBサーバーについて
全体構成
大体こんな感じで実装されている。
- DBサーバーを起動して8888ポートで待ち受ける。
- clientが8888に接続しにくる。
- DBサーバーはclientを受け入れてgoroutineを起動する。
- groutineではclientからデータが送信されてくるのを待ち受ける。
- clientから送られた文字列を溜め込んで、コマンドとして解釈可能なレベルまで文字列が溜まるのを待つ。
- コマンドとして解釈可能なら、それを使ってDBエンジンにデータをRead/Writeしに行く。
その他
- 軽く試した感じRedisよりもRead/Writeが速かった。これはBuntDBがredisよりも速い実装だから当たり前だけど。※index貼っていないので貼るともっと速くなるかも。
- goroutineとchannelをうまく使いこなせていないのでgoroutineがリークしてるかもしれないのと、適当な実装がたくさんある。