web-dev-qa-db-ja.com

Node.jsとmongodbでタイムアウトを処理する

私は現在、いくつかのコードが次のシナリオに対してどのように立ち向かうかをテストしています。

  • Node.jsアプリケーションが開始され、mongodbへの接続が正常に確立されます
  • 接続が正常にセットアップされた後、mongodbサーバーは停止し、後続のすべてのリクエストは失敗します

これを行うために、公式ドライバーを使用する次のコードを取得しました(ここにあります: https://github.com/mongodb/node-mongodb-native ):

MongoClient.connect('mongodb://localhost:27017/testdb', function(err, db) {
app.get('/test', function(req, res) {
    db.collection('users', function (err, collection) {
        console.log(err);
        if (err) {
            // ## POINT 1 ##
            // Handle the error
        }
        else {
            collection.find({ 'username': username }, { timeout: true }).toArray(function(err, items) {
                console.log(err);
                if (err) {
                    // ## POINT 2 ##
                    // Handle the error
                }
                else {
                    if (items.length > 0) {
                        // Do some stuff with the document that was found
                    }
                    else {
                        // Handle not finding the document
                    }
                }
            }); 
        }
    });
});

});

リクエストの処理中にmongodbサーバーが実行されなくなったため、## POINT 1 ##または## POINT 2 ##とラベルを付けたポイントのいずれかで、次のようなエラーが返されると想定しました。タイムアウト;ただし、これは当てはまりません。

さまざまな設定(カーソルのタイムアウトを明示的に許可する設定を含む)を試しましたが、どのような方法でも有効にできないようです。私が試したすべての構成で、Node.jsはfind()操作がコールバックするのを待つだけで、決してコールバックしません。

Mongodbを実行する前にNode.jsアプリケーションを起動すると、接続コールバックでエラーが正常にキャッチされますが、その後接続が切断されると、で処理されないようです。とにかく。

欠落している設定がありますか、または接続が確立された後に終了している接続を検出する方法がありませんか?

編集:明確にするために、findメソッドで使用されるユーザー名変数は実際には私の完全なコードで宣言されています。この投稿に入れたコードは構造を説明するための縮小版ですおよびエラーチェック。

18
rastating

UPD:
この投稿に基づくと、ここで行っているのと同じことを行う修正がデプロイされているようです。これがすでにnpm(15.10.13)内にあるかどうかはわかりません。 https://github.com/mongodb/node-mongodb-native/issues/1092#ref-commit-2667d1

いくつかの調査の後、私はそこで何が起こっているのかを理解することができました:
データベースを処理するメソッド(検索、更新、挿入など)を呼び出すたびに、独自のIDを持つカーソルが作成され、後で呼び出されるようにDbのEventEmitterに登録されます。その間、同じCallBackStore内の_notRepliedオブジェクトに自分自身を登録します。

しかし、接続が閉じられると、_notRepliedカーソルを反復処理し、エラーまたはタイマー付きのロジックでトリガーするものを見つけることができませんでした(まだどこかにある可能性があります)。だから私はなんとか小さな回避策を書くことができました、それはDBがcloseイベントを出すときにエラーでカーソルを強制的にトリガーします:

new mongodb.Db('testdb', new mongodb.Server('localhost', 27017, { }), { safe: true }).open(function (err, db) {
  if (!err) {
    db.on('close', function() {
      if (this._callBackStore) {
        for(var key in this._callBackStore._notReplied) {
          this._callHandler(key, null, 'Connection Closed!');
        }
      }
    });

    // ...

  } else {
    console.log(err)
  }
});

MongoClientの代わりに最初のアプローチを使用することをお勧めします。理由はいくつかあります。たとえば、接続を閉じてから.findを呼び出すと、コールバックでエラーが適切にトリガーされますが、MongoClientではトリガーされません。

MongoClientを使用している場合:

MongoClient.connect('mongodb://localhost:27017/testdb', function(err, db) {
  if (!err) {
    db.on('close', function() {
      if (this._callBackStore) {
        for(var key in this._callBackStore._notReplied) {
          this._callHandler(key, null, 'Connection Closed!');
        }
      }
    });

    // ...

  } else {
    console.log(err);
  }
});

これは何をしますか?接続が閉じられると、すべての_notRepliedカーソルを反復処理し、エラーConnection Closed!でそれらのイベントをトリガーします。

テストケース:

items.find({ }).toArray(function(err, data) {
  if (!err) {
    console.log('Items found successfully');
  } else {
    console.log(err);
  }
});
db.close();

これにより、データベース接続が強制的に閉じられ、以前に処理したcloseイベントがトリガーされ、カーソルが確実に閉じられます。

UPD:GitHubにIssueを追加しました: https://github.com/mongodb/node-mongodb-native/issues/1092 これに関して彼らが言うことを確認します。

11
moka

私は同じ問題を抱えていて、グーグルからこのページを見つけました。しかし、あなたが選んだ答えは問題を解決しませんでした、そしてそれはあなたと同じです、this._callBackStoreは使うことができません

しかし、私はMongoをラップしようとしました、そしてそれはうまくいくようです

var MongoClient = require('mongodb').MongoClient;

var mongo = {};
mongo.init = function() {
  MongoClient.connect('mongodb://localhost:27017/testdb', function(err, db) {
    if (err) {
      mongo.DB = '';
    } else {
      mongo.DB = db;
    }
    db.on('close', function() {
      mongo.DB = '';
    });
    db.on('reconnect', function() {
      mongo.DB = db;
    });
  }
}
                      
mongo.getdb = function(callback) {
    if (mongo.DB) {
      callback(null, mongo.DB);
    } else {
      callback('can not connect to db', null);
    }
}
module.exports = mongo;

最初にサーバーを起動し、init()します

そして、あなたはそれを要求して使用することができます

mongo.getdb(function(err, db) {
  if (err) {
    console.log(err);
  } else {
    db.collection('user').find({'xxx':'xxx'}).toArray(function(err, items) {
      console.log(items);
    });
  }
});
1
user3134009

さらに調査したところ、上記のシナリオのように「オフライン」タイムアウトを指定できないようです。指定できる唯一のタイムアウトは、非アクティブの10分後にカーソルをタイムアウトするようにサーバーに通知するものですが、上記のシナリオのように、サーバーへの接続がダウンしています。これは機能しません。

参考までに、ここで情報を見つけました: https://github.com/mongodb/node-mongodb-native/issues/987#issuecomment-1891526 私が主な貢献者の1人であると信じていた人プロジェクトに。

0
rastating

HapiとMongodb(マングースなし)でAPIを作成しています。特徴:

  1. Mongo dbが利用可能な場合にのみ、APIリクエストへの応答を開始します
  2. サイクル中にmongoが死んだ場合は、応答を停止します
  3. モンゴが再び利用可能になったら再起動します
  4. すべてのリクエストに対して単一の接続を維持する

他の回答とこの投稿からのいくつかのアイデアを組み合わせる https://productbuilder.wordpress.com/2013/09/06/using-a-single-global-db-connection-in-node-js/ 私のアプローチはこれです:

server.js

Utilities.initializeDb(() => {
    server.start((err) => {
        if (err) throw err;
        console.log('Server running at:', server.info.uri);
    });
}, () => {
    server.stop((err) => {
        if (err) throw err;
        console.log('Server stopped');
    });
});

Utilities.js

"use strict";

const MongoClient = require('mongodb').MongoClient;
const MongoUrl = 'mongodb://localhost:27017/db';

export const Utilities = {
    initializeDb: (next, onCrash) => {

        const ConnectToDatabase = (params) => {
            MongoClient.connect(MongoUrl, (err, db) => {
                if (err !== null) {
                    console.log('#t4y4542te Can not connect to mongo db service. Retry in 2 seconds. Try #' + params.retry);
                    console.error(err);
                    setTimeout(() => {
                        ConnectToDatabase({retry: params.retry + 1});
                    }, 2000);
                } else {

                    db.on('close', () => {
                        onCrash();
                        console.log('#21df24sf db crashed!');
                        ConnectToDatabase({retry: 0});
                    });
                    global.db = global.db || db;
                    next();
                }
            });
        };

        ConnectToDatabase({retry: 0});

    }
};

データベース接続をグローバルスペースにエクスポートしています。最善の解決策ではないように感じますが、db接続がすべてのモジュールにparamとして渡され、それがさらにひどいプロジェクトがありました。必要な場所にdb接続をインポートするモジュラーアプローチがあるはずですが、私の状況ではほとんどどこでも必要なので、ほとんどのファイルにそのincludeステートメントを記述する必要があります。このAPIは、dbへの接続がないと意味がないので、グローバルスペースで何かが魔法のように飛ぶことに反対している場合でも、最善の解決策になると思います。

0
Lukas Liesis