MongoDB Bucket upsert performance

Viewed 275

TL;DR: My upserts for the bucket model are 10x slower than an insert of the same document in a more granular format.

Background: We store IoT data inside a PSS MongoDB replica set. Currently, we have a small model that saves each sensor value in one document in the following format:

{
  _id: <ObjectId>,
  Time: <DateTime>,
  Value: <Object>,
  InternalId: <string>
}

With our current server (512GB RAM, 64 threads, NVMe disks), we are observing 200k+ reads/writes per second, combined. Documents are on average 120 bytes in size, totaling 8 billion documents for 7 days of data, which consume roughly 1TB in storage.

We are now moving towards a bucket model to improve storage efficiency. Currently, I've am using the suggested model of:

{
  _id: <ObjectId>,
  BlockStartTime: <DateTime>,
  BlockEndTime: <DateTime>,
  InternalId: <String>,
  LastModified: <DateTime>,
  Telemetry: [
    Key: <DateTime>,
    Value: <Object>,
    ...
  ]
}

This model is very efficient in RAM usage and storage. However, all writes are very slow. I use the following to insert a telemetry record:

db.Telemetry.BulkWrite([
    {
        updateOne: {
            filter: { InternalId: <Guid>, BlockStartTime: <DateTime>},
            update: {
                $currentDate: { LastModified: true },
                $setOnInsert: { BlockEndTime: <DateTime>},
                $push: { Telemetry: { $each: [{ Key: <DateTime>, Value: <Object>}] }}
            },
            upsert: true
        }
]

The above BulkWrite typically contains 25 - 775 queries, almost always containing one key value pair to insert into "Telemetry".

Performance has dropped from 100K+ writes/s, to 5k/s - 10k/s updates.

I've tried the following:

  • Removing the $push, thereby only setting the LastModified date. Writes improved to 15k/s. This was to identify whether the upserts were the bottleneck
  • Altering batch sizes from 25 updates, to 775 updates, to 1500 updates, with no luck
  • Optimizing the query to use indexes fully. I can confirm that .explain() explains that indexes are hit only once, and only one document is returned/fetched
  • Server CPU (less than 20%) and write tickets are normal (always more than 120, with a max of 127)
  • Inserting a bucket for each telemetry value as a test of insert vs upsert. Performance increased 3x from upserts
  • Testing on a 4GB RAM server. Oddly enough, performance was marginally lower on the small server
  • Removing the possibility of update conflicts by using a simulator that sends telemetry every 15 seconds, spread over 400K unique InternalIds, sequentially

My question: Is upsert the way to go for data ingestion? I can understand a 50% drop in performance (upsert needs to do find, then update or insert), but is a 10x drop normal? Am I missing something with my ingestion strategy? Should I perhaps try to only store in the bucket format as a roll-up strategy, maybe one hour after ingestion? We need to use MongoDB for this client

0 Answers
Related