MongoDB のレプリカセットを意識して findOneAndUpdate を Primary DB に負荷が掛かりにくいように利用する

MongoDB のレプリカセットを意識して、Primary DB へのクエリ発行数が多くならないように Mongoose の findOneAndUpdate と findOne を組み合わせて、データベース負荷を下げた話をご紹介します。

mongoose | マングース

findOneAndUpdate の使いドコロ

findOneAndUpdate だけの場合

findOneAndUpdate は必ず Primary DB へクエリが発行されます。

なので、以下のような最初の1回だけ document を作成するけど、2回目以降は findOne で済むような処理でも Primary DB へ発行されるので Secondary DB を有効活用できません。

WishListConfig.findOneAndUpdate(
  {
    user: userId
  },
  {
    user: userId
  },
  {
    upsert: true,
    new: true
  },
  (err, doc) => callback(err, doc)
);

findOneAndUpdate と findOne を組み合わせた場合

findOneAndUpdate だけだと常に Primary DB へクエリが発行されてしまうので、findOne でワンクッション挟むようにしましょう。

2回目以降は必ず findOne で document が取得できるので Secondary DB を有効活用できます。更新する必要もない findOneAndUpdate の無駄なクエリが Primary DB へ発行されることもありません。

// `findOne` is executed in the secondary db at first
// because `findOneAndUpdate` is executed in the primary db.
WishListConfig.findOne({
  user: userId
})
  .read('secondaryPreferred')
  .exec((err, doc) => {
    if (err || doc) {
      return callback(err, doc);
    }
 
    return WishListConfig.findOneAndUpdate(
      {
        user: userId
      },
      {
        user: userId
      },
      {
        upsert: true,
        new: true
      },
      (err, doc) => callback(err, doc)
    );
  });

以上、MongoDB の Primary DB が高負荷になったので、Primary へのクエリ発行数を減らす工夫をしてみた現場からお送りしました。