mongooseのfind query cursorは色々な回し方があるけどどれ使えばいいの?
1. cursorを自力で回す
const cursor = model.find().cursor()して、let doc = cursor.next()がnullになるまでforかwhileで回す
const cursor = model.find().cursor()して、for await (const doc of cursor) { }で回す
for await (const doc of model.find().cursor()) { }
for await (const doc of model.find()) { }
結論
1は現代のnode.js環境では使う必要ないが、とりわけ問題があるわけでもない
cursor回している途中で例外が起きた場合に、プロセスごと終了していいなら、4でいい
だいたいのbatch処理はこれshokai.icon
絶対に2、もしくは1を使うべきシチュエーションがある
cursorで回している処理の中で例外が起きてもプロセス終了できない場合
cursorを回している途中でreturnする可能性がある場合
これらは自前でcursor.close()する必要がある
つまりだいたいのwebサーバーでは2を使うべきshokai.icon
3は、問題があるわけではないが、4で書いたほうがシンプルになる
結局のところ全く同じ処理が行われる
以下で理由を説明していきますshokai.icon
調査ログ
必要なライブラリ全てローカルに集めて、Codex CLIにコード追わせるととマジでちゃんと読んで解説してくれるshokai.icon #Codex_CLIの感想 事前学習された知識ではなく、論拠としてコードが出てきて、人間が確認できる
これを定額プランでいつでも回せるのはすごすぎる
.cursor()なしで回す場合
lib/query.jsがlib/cursor/queryCursor.jsを返している
.cursor()で返ってくるのはlib/cursor/queryCursor.js
mongooseのlib/cursor/queryCursor.jsはmongodb npmのcursorをそのまま返しているわけではなく、薄いラッパーである アプリケーション側からfor awaitやnext()で呼び出すと、mongooseがさらにctx.cursor.next()を呼び出す
mongodb npmのcursorがそのままアプリケーション側まで露出しているわけではない
MongoDBサーバー側
cursorは一定時間でタイムアウトし、killする
TCP接続が切断すると、それに紐づいたcursorを即座にkillする
Node.jsアプリケーション側
上でまとめている通り、cursorは最後まで回さないとcloseされない
cursor回している途中で処理が打ち切られる可能性があり、しかしプロセスは終了せず動き続けるのであれば、try ~ finally等でcursor.close()する必要がある
コンストラクタで、abortListenerにthis.close()を登録している
cursorを最後まで回しきれなかった場合、closeされない
そういう実装がmongodb npmの中に無い
回している途中でエラーが発生したり、returnしたりするとcloseされないという事ですshokai.icon
finallyでcursor.close()するとよい
code:js
let cursor;
try {
cursor = model.find().cursor();
for await (const doc of cursor) {
// cursor回してdocuemntを処理
}
} catch (err) {
// 例外処理
} finally {
await cursor?.close(); // cursorが存在しない可能性もある
}
実装例
さらに深く