tl;dr
- MongoDBではv2.6から
cursor.maxTimeMS()
によって処理をタイムアウトさせられる
- 各ドライバから使用可能
- mongooseでは
mongoose.Query#maxTime
メソッドで指定可能
サーバ側で処理をタイムアウトさせるには
MongoDBでは、v2.6からサーバ側で処理の実行タイムリミットを設定できます。
処理のタイムリミットは、cursor.maxTimeMS()
メソッドを呼ぶことで設定できます。
この maxTimeMS
を設定すると、指定したタイムリミットを超えて検索やアップデートが実行された場合、その処理を中止します。
以下のようにコンソールで試せます:
db.collection.find({description: /August [0-9]+, 1969/}).maxTimeMS(50)
各ドライバでのmaxTimeMSの使用方法
各ドライバといっても調べたものだけを列挙しておきます。
PHP
例:
$cursor = $collection->find();
$cursor->maxTimeMS(2000);
try {
$results = iterator_to_array($cursor);
} catch (MongoExecutionTimeoutException $e) {
echo "query took too long!";
}
ちなみに、PHPにはMongoCursor::timeout
もあります。
このメソッドはクライアント側でタイムアウト処理を行うものです。
サーバ側では処理をキャンセルしないので、注意です。
node.js
こちらに詳しく書いてあります。
例:
var MongoClient = require('mongodb').MongoClient;
MongoClient.connect("mongodb://localhost:27017/test", function(err, db) {
// Get an aggregation cursor
var cursor = db.collection('data')
.find({"$where": "sleep(1000) || true"})
.maxTimeMS(50);
// Get alll the items
cursor.toArray(function(err, items) {
console.dir(err);
console.dir(items);
db.close();
});
});
mongooseでタイムアウトさせるには
MongoDBのnode.jsにおけるODM(Object Data Mapping)のmongooseでもmaxTimeMSに対応しています。
v3.8.13 から利用できるようになりました。
該当の変更は こちら。
実際には子分モジュールの mquery で実装されています。
使い方はシンプル:
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');
var Cat = mongoose.model('Cat', { name: String });
Cat.find({name: 'Zildjian' }).maxTime(1000).exec(function(err, docs) {
console.log(err); // --> { [MongoError: operation exceeded time limit] name: 'MongoError' }
});
operation exceeded time limit
というメッセージのエラーが得られます。
このように、mongoose.Query
オブジェクトを返すメソッドでmaxTime
メソッドが使えます。
Model#save
メソッド、Model#remove
では残念ながら使えないようです。
エラーを判別しにくい問題
Errorオブジェクトにはエラー種別を明示的に判別するコードがありません。
そのため、とても不安な方法ですがエラーメッセージで判別する方法しか見当たりません(他に良い方法をご存知の方がおられましたらぜひご教授ください)。
メッセージによるタイムアウト判別は以下のようになります:
if(err.message === 'operation exceeded time limit') {
// retry
}
なぜPHPにはMongoExecutionTimeoutException
があるのにnode.jsでは無いんだ?
参考