Bulk database upserts with promises

Currently, I am working on parsing a list of JavaScript objects and upserting them to the database one by one using Node.js. The process typically looks like this:

return promise.map(list,
    return parseItem(item)
        .then(upsertSingleItemToDB)
    ).then(all finished!)

The challenge arises when dealing with large lists (around 3000 items), as parsing all the items in parallel consumes too much memory. To address this issue, I incorporated a concurrency limit within the promise library to prevent running out of memory (using 'when/guard').

However, I believe there is room for optimization in the database upsert process, especially since MongoDB offers a bulkWrite function. Given that parsing and bulk writing all items simultaneously is not feasible, I plan to divide the original object list into smaller sets. Each set will be parsed using promises in parallel, and the resulting array from each set will then undergo a promisified bulkWrite operation. This process will continue for the remaining sets of list items.

I am struggling to figure out how to structure these smaller sets of promises to ensure that only one set of parseSomeItems-BulkUpsertThem is executed at a time. Perhaps something like Promise.all([set1Bulk][set2Bulk]) could work, where set1Bulk represents another array of parser Promises run in parallel. Any pseudo code assistance would be greatly appreciated (I'm using 'when' if that information makes a difference).

Answer №1

If you are using mongoose along with the nodejs-mongodb-driver, your code may resemble the following:

const saveProcessedItems = items => ItemCollection.collection.bulkWrite( 
   items.map(item => ({
      updateOne: {
           filter: {id: item.id}, 
           upsert: true,
           update: {$set: item} 
      }
   }))
);


const processAndSaveItems = (items, start = 0, batchSize = 3000) => { 
  const batch = items.slice(start, start + batchSize);
  
  return Promise.all(
    batch.map(parseItem) 
  )
    .then(saveProcessedItems)
    .then(() => {
      const newStart = start + batchSize;
      if (items.length >= newStart) {
        return processAndSaveItems(items, newStart, batchSize);
      }
      
      return true;
    });
};

return processAndSaveItems(yourItems);

Answer №2

While the initial answer seems comprehensive, there are alternative approaches to consider.

One possible solution is to implement a timeout function within the callback of your write operation to introduce a pause before proceeding with the next write action. Even a minimal delay, such as one millisecond between calls, can alleviate strain on your CPU and Memory. For instance, if you have 3000 write operations in total, adding a 1-millisecond break would only extend the process by 3 seconds overall.

Another strategy could involve dividing your array of insertObjects into smaller chunks and directing them to distinct bulk writers for increased efficiency.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The error encountered with react createRef was caused by a faulty implementation

Here is the revised question post completing the answer In this particular code snippet, I have encountered an issue where my file browser opens correctly upon submission, however, the updated state is not reflected when I click the final submit button. ...

Storing form data in a file using React

I am trying to create a feature in my react component where instead of submitting a form and sending the data, I want to write this data to a file for later use. Is it achievable using vanilla JS or should I consider using a library? This is how my handl ...

Mongoid fails to properly update the position field

Looking for help to update the Attachinary::File position field My tech stack includes ruby 2.5.0, rails 5.2.2, mongoid, 'jquery-ui-rails', and a custom Attachinary for images upload. The custom Attachinary can be found here. Here is a snippet ...

What is the proper method for transforming an Excel column containing JSON format data into a JavaScript object?

I am facing an issue with converting data from an excel sheet to json format. While the other columns convert successfully, one specific column containing json data is not being converted properly and instead gets treated as a string. Using JSON.parse() on ...

Could not find reference to Google (error occurred following ajax request)

I encountered an error message when attempting to use the Google Map after making an ajax call. ReferenceError: google is not defined The issue arises when I include the link "<script type="text/javascript" src="http://maps.google.com/maps/api/js?sen ...

Having trouble choosing files on Mobile devices, whether it's IOS or Android

I'm having trouble getting an image upload to change the background of a table automatically. It works fine on Desktop computers, but not on IOS or Android. Does anyone have any advice on how to make it work across all devices? Thanks in advance. cons ...

Is the code for uploading multiple images not functioning as expected?

My Photo Table is Column Name Data Type Constraint PhotoID Int Primary key,auto increment PhotoName Varchar(100) ExtName Varchar(100) PhotoType Varchar(100) PhotoSize Int TempleID Int Foreign key with templ ...

Is an array considered sparse when defined as 'array[i]=undefined'?

Are the effects of delete array[i] and array[i] = undefined identical? I'm aware that using the former can result in a sparse array - but does the latter have the same outcome? ...

Access the values within an array located inside a data object by utilizing ng Repeat function

Attempting to utilize ng repeat for extracting values from an array. Below is the HTML code: <ion-list ng-repeat="item in locationresult"> <ion-item > <ion-checkbox ng-repeat="innerItem in item.loc[$index]"> ...

JavaScript incorrectly constructs date strings

Hey there, I've been attempting to create a JavaScript date object using a string but for some reason, it's consistently off by one day. Every time I try, it ends up showing the wrong day. Below is the code snippet in question: var date = new Da ...

Take out property that was placed in the "style" tag

One particular static HTML file that I have consists of the following code: <style> p { overflow-wrap: break-word; word-break: break-word; hyphens: auto; } </style> <div id="wrapper"> <p>Insert text here ...

The SCSS styling was eliminated with the help of @nuxtjs/tailwindcss

In my Nuxt project, I am using @nuxtjs/tailwindcss and vue-formulate. I have defined some styles for vue-formulate in a SCSS file which work perfectly when running nuxt --spa. However, when I try to generate the project using nuxt generate and serve it, ...

Battle between Comet and Ajax polling

I'm looking to develop a chat similar to Facebook's chat feature. Using Comet would require more memory to maintain the connection. There seems to be a latency issue when using Ajax polling if requests are sent every 3-4 seconds. Considering t ...

Following the submission of a POST request, an error occurred stating: "Unable to assign headers once they have been sent to

I'm having trouble figuring out what's wrong with my code. Whenever I send a post request, I get an error message saying "Cannot set headers after they are sent to the client". My model includes a comment schema with fields for user, content, and ...

Is it necessary for me to master React in order to code with React Native?

As I move on to learning React and/or React Native after mastering Angular, it feels like a natural progression in my development journey. My understanding is that React Native could streamline the process of building Android/iOS native apps within one pr ...

Tips for replacing default arrow icons with 'Previous' and 'Next' buttons in a Material-UI pagination element

I've been struggling to find a solution with my code provided below. Despite multiple attempts, the issue remains unresolved. import React from "react"; import { gridPageCountSelector, gridPageSelector, gridPageSizeSelector, useGridA ...

Bringing in data using .json files in a react native environment with Redux

I have developed a fitness app and I am utilizing Redux to store all the sets and workouts. Currently, I have manually entered all the exercises into Redux data for testing purposes. However, I now have all the exercises stored in a .json file and I want t ...

Error: ajax is not defined and needs to be declared (repeated twice)

Currently, I have a form that requires processing using Ajax. <script type="text/javascript" src="http://code.jquery.com/jquery-1.9.1.js"></script> <div class="column1"> <form class="form box" action="javascript:networkCheck();" ...

Unleashing the power of XPath and wildcards in AJAX

Can anyone explain why the variable objProperties, which contains an xpath with a wildcard, is coming up empty in this scenario? function getXMLServerObject (httpType, cmd, isAsync) { var object = new Array(); $.ajax({ type: httpType, ...

The removal of the Lodash library from node modules is not occurring

In an effort to reduce bundle size, I made the decision to replace all lodash methods with core js methods in my Vuejs project. Despite attempting various npm uninstall commands such as: npm uninstall lodash npm uninstall lodash --save npm uninstall lodas ...