CPU spikes on insert/update when there's 'relatively large' number of documents in db

Hi, everyone. Apologies in advance for my poor English.
I’m new to both Meteor and MongoDB. Any help would be greatly appreciated.

I am trying to implement a chat application.
First when the page is loaded, subscribe the latest 20 messages. Then every time when a user scrolls up, load 20 more older messages.
----- Problem -----
lies when a new message is added(inserted).
The CPU spikes up, and if the insertion occurs several times in a row within a short period of time, the CPU usage reaches almost full percentage. (without multi-core enabling in a multi core instance, ie. single-core, it reaches almost 50 percent, when using meteorhacks:cluster packages, it reaches almost 99 percent.)
This didn’t happen when there are only about 4,000 documents in Chats collection.
With only about 20,000 docs, the webserver CPU struggles a lot. MongoDB CPU is calm like a sleeping baby.

----- What I tried or looked into -----

  1. Created MongoDB index (boardId, chatRoomId, createdAt) and added ‘$hint’ to publication query for find: no noticeable difference
  2. Tried adding ‘{validate: false}’ to the insert query since I am using SimpleSchema: no noticeable difference. not sure if this is applied, despite no error or exception.
  3. Tried adding ‘{w: 0}’ to the insert query and also added &w=0 in the MONGO_URL variable: no noticeable difference. not sure meteor 1.1.0.3 even supports this. I searched for {w: 1} literal, a lot of meteor packages define DB writeConcern {w: 1} internally.
  4. ‘Chat messages’ have to be served in different ways: In the main chat room, as described above, there will be the latest 20 messages, and scroll back for more. There is a ‘Files’ tab and Searched Messages tab, these tabs serve all messages with the matching keywords. These are from the same collection with Chat messages, albeit separate pub/subs. I checked the CPU with all the other pub/subs commented out, but no improvement in CPU.

Any idea, please?

  • specifications and code snippets are as follows:
    ----- Specifications and code snippets -----
  • Webserver: AWS EC2 m4.large

  • MongoDB Server: AWS EC2 m4.large

  • Meteor version: 1.3.x(updated from 1.1.0.3 as of June 18th, 2016, still the same)

  • Collection name: ‘Chats’ defined using SimpleSchema/attachSchema

  • Subscription: Using SubsManager

  • Subscription.get(‘chat’).subscribe(‘chats’, boardId, currentMsgCount, chatRoomId);

  • Publication:
    Meteor.publish(‘chats’, function (boardId, currentCount, chatRoomId) {
    check(boardId, String);

    if (!this.userId) {
    return this.stop();
    }

    var self = this;
    var ready = false;
    var cursor;

    if (!chatRoomId) {
    var chatRoom = ChatRooms.findOne({‘boardId’: boardId});
    chatRoomId = chatRoom ? chatRoom._id : ‘’;
    }

    if (currentCount == null) {
    cursor = Chats.find({$query: { boardId: boardId, chatRoomId: chatRoomId }, $hint: {boardId: 1, chatRoomId: 1, createdAt: -1}/, $orderBy: {createdAt: -1}/},
    {limit: 20 });
    } else {
    var count = 20;
    if (0 >= currentCount)
    currentCount = count;

      cursor = Chats.find({$query: { boardId: boardId, chatRoomId: chatRoomId }, $hint: {boardId: 1, chatRoomId: 1, createdAt: -1}/*, $orderBy: {createdAt: -1}*/},
          {skip: currentCount, limit: count });
    

    }

    var cursorHandle = cursor.observe({
    added: function (doc) {
    self.added(’_chats’, doc._id, doc);
    },
    removed: function (doc) {
    self.removed(’_chats’, doc._id);
    },
    changed: function (doc) {
    self.changed(’_chats’, doc._id, doc);
    }
    });

    self.onStop(function () {
    cursorHandle.stop();
    });

    ready = true;
    self.ready();
    });

  • Chat message insert:
    Chats.insert({
    localId: localId,
    boardId: chatRoom.boardId,
    contentType: chatContentType,
    message: message,
    userId: userId,
    chatRoomId: chatRoomId});