Performing simultaneous document queries within a single API in MongoDB

I am currently working with an API written in typescript and attempting to execute parallel queries for the same document by using Promise.allSettled. However, I have noticed that it is performing poorly and seems to be running sequentially instead of in parallel. Is there a way to improve performance and actually run parallel queries on the same document within the same connection for MongoDB? Below is the code snippet:

console.time("normal");
let normal = await ContentRepo.geBySkillIdWithSourceFiltered(
    [chosenSkillsArr[0].sid!],
    readContentIds,
    body.isVideoIncluded,
    true,
    true
);
console.timeEnd("normal");

console.time("parallel");
const parallel = await Promise.allSettled(
    chosenSkillsArr.map(async (skill: IScrapeSkillDocument) => {
        const result = await ContentRepo.geBySkillIdWithSourceFiltered(
            [skill.sid!],
            readContentIds,
            body.isVideoIncluded,
            true,
            true
        );
    })
);
console.timeEnd("parallel");

You can find the relevant function being called below:

async geBySkillIdWithSourceFiltered(
    skillIds: string[],
    contentIds: string[],
    isVideoIncluded?: boolean,
    isCuratorIdFilter?: boolean,
    activeSourceFilter?: boolean
): Promise<IContentWithSource[]> {
    try {
        console.time(`single-${skillIds}`);
        var contents = await ContentM.find({
            $and: [
                { "skills.skillId": { $in: skillIds } },
                { recordStatus: true },
                isCuratorIdFilter ? { curatorId: 0 } : {},
                isVideoIncluded ? {} : { type: contentTypeNumber.read },
                { _id: { $nin: contentIds } },
            ],
        }).exec();
        var items: IContentWithSource[] = [];
        var sourceIds = new Set<string>();
        contents.forEach((content) => {
            if (!this.isEmpty(content.sourceId)) {
                sourceIds.add(content.sourceId!);
            }
        });
        var sources: any = {};
        var sourcesArr = await new SourceRepo().getByIds(
            Array.from(sourceIds)
        );
        sourcesArr.forEach((source) => {
            sources[source._id] = source;
        });

        if (activeSourceFilter) {
            contents
                .map((i) => i.toJSON() as IContentWithSource)
                .map((k) => {
                    if (sources[k.sourceId!].isActive) {
                        k.source = sources[k.sourceId!];
                        items.push(k);
                    }
                });
        } else {
            contents
                .map((i) => i.toJSON() as IContentWithSource)
                .map((k) => {
                    k.source = sources[k.sourceId!];
                    items.push(k);
                });
        }
        console.timeEnd(`single-${skillIds}`);

        return items;
    } catch (err) {
        throw err;
    }
}

Here are the results from running the code:

single-KS120B874P2P6BK1MQ0T: 1872.735ms
normal: 1873.934ms
single-KS120B874P2P6BK1MQ0T: 3369.925ms
single-KS440QS66YCBN23Y8K25: 3721.214ms
single-KS1226Y6DNDT05G7FJ4J: 3799.050ms
parallel: 3800.586ms

Answer №1

It appears that the parallel version of your code is running more functions simultaneously.

// The regular version
let normal = await ContentRepo.geBySkillIdWithSourceFiltered(
    [chosenSkillsArr[0].sid!],
    readContentIds,
    body.isVideoIncluded,
    true,
    true
);


// The code within the parallel version:
chosenSkillsArr.map(async (skill: IScrapeSkillDocument) => {
        const result = await ContentRepo.geBySkillIdWithSourceFiltered(
            [skill.sid!],
            readContentIds,
            body.isVideoIncluded,
            true,
            true
        );
    })
[chosenSkillsArr[0].sid!], vs  chosenSkillsArr.map()

In the parallel version, you are placing the function call (

ContentRepo.geBySkillIdWithSourceFiltered
) inside a loop. This can cause it to run slower.

Regarding executing promises in parallel:

Both Promise.all and Promise.allSettled allow for awaiting multiple promises. They do not dictate the order of resolution or guarantee that computations are running concurrently. Their purpose is simply to ensure all promises are handled.

You cannot manually enforce promise execution parallelism

Check out this fascinating article discussing parallelism, Promise.All, and how browser Node.js API differs from Node.js API installed on your machine with respect to parallelism.

The conclusion from the article states:

JavaScript runtime is single-threaded. We don't have access to threads in JavaScript. Even with a multi-core CPU, you can't execute tasks in parallel using JavaScript. However, browsers/Node.js utilize C/C++ (!) where they have thread access. Hence, they can achieve parallelism.

Additional Note:

There is one subtle distinction:

  1. Promise.all: Resolves only when all promises resolve; otherwise, it will reject with the first error encountered.

  2. Promise.allSettled: Will always resolve with an array containing information about resolved and rejected promises.

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

What is the best way to display multiple HTML files using React?

Looking to develop a web application using React that consists of multiple HTML pages. For instance, login.html and index.html have been created and linked to URIs through the backend - resulting in localhost:8080/login and localhost:8080/index. However, R ...

Instructions for adding a class when a li is clicked and replacing the previous class on any other li in Vue 2

Even though I successfully used jQuery within a Vue method to achieve this task, I am still searching for a pure Vue solution. My goal is to remove a class from one list item and then add the same class to the clicked list item. Below is the code snippet w ...

Tips for displaying text within an input field labeled "password" and then reverting back to a password display after entering text

I am working with a login form that includes a password field: <input type="password" name="password" id="password" value="Password" maxlength="40" required style="width: 130px; font-size: 10px;" /> Currently, the password field displays "******** ...

Remove multiselect label in PrimeNG

I am attempting to change the multiselect label of selected items and replace it with a static default label. Here is what it currently shows: https://i.sstatic.net/qBNHG.png This is what I have tried: .p-multiselect-label { visibility: collapse; ...

Customizing material-ui styles within a nested component

I am looking to modify the position of the expandIcon in an ExpansionPanel by changing the right attribute: <ExpansionPanel> <ExpansionPanelSummary expandIcon={<ExpandMoreIcon />}> <Typography className={classes.heading}&g ...

Leverage ngFor to loop through a "highly intricate" data structure

In my project, I have stored data in a JSON file structured as follows: { "name": { "source1": ____, "source2": ____, "source3": ____ }, "xcoord": { "source1": ____, "source2": ____, "source3": _ ...

Angular's asynchronous HTTP request allows for non-blocking operations when

I'm currently working with Angular 6, and I've encountered an issue while updating my resource instance in the following way: this.user = api.getUser(); public getUser(): User { const myHeader = new HttpHeaders({ 'Authorization' ...

Using JQuery to search for and remove the id number that has been clicked from a specific value

I am in need of assistance with deleting the clicked id number from an input value using jQuery. I am unsure of how to proceed with this task and would appreciate some guidance. To simplify my request, let me explain what I am trying to accomplish. Take ...

Switching out one block of HTML with another in JavaScript is a powerful way to dynamically update

I am working on creating a feature on a webpage where clicking a button will change the HTML code inside a specific div. Essentially, I want to be able to update the content of a div by simply clicking a link. Here are two different sets of HTML code: Co ...

Having trouble with my jQuery .hover() code not running as expected

Whenever I hover over my divs, I want them to change color. However, the code doesn't seem to be working as expected when I try to do so. I suspect that the issue might be related to the z-index property used in the class that I am trying to hover ove ...

Guide to generating dynamic arrays in JavaScript when a button is clicked

I am working on a JavaScript program where I dynamically generate buttons and div tags, as well as retrieve data from a local JSON file. My goal is to implement a new feature where clicking a button will create an array with the same name as the button, al ...

Unlocking password in Meteor: A step-by-step guide

Can I decrypt this specific type of password that was created by a Meteor application? https://i.sstatic.net/UCdg8.png I'm considering using the same login credentials (email and password) for my C# application. https://i.sstatic.net/pDew2.png ...

What is the process of importing jQuery types into Ember using TypeScript?

Is there a specific way to import the JQuery.Event type for use in a click function? I've searched on various websites but haven't found any clear instructions. Appreciate your help. Thank you. ...

The combination of TypeScript decorators and Reflect metadata is a powerful tool for

Utilizing the property decorator Field, which adds its key to a fields Reflect metadata property: export function Field(): PropertyDecorator { return (target, key) => { const fields = Reflect.getMetadata('fields', target) || []; ...

Customized Error Handling Function for Ajax Requests

I have a function that works perfectly, but I need to add six more buttons without repeating code. I want each callback to be customizable, with different text for each scenario (e.g. displaying "Please Log In" if the user is not an admin). How can I make ...

Height of inline-block list items in ul is not properly adjusted

I am working on a horizontal navigation bar using inline-block for the li tags. Here is the code snippet: <ul> <li><a href="#">HOME</a></li> <li><a href="#">FEATURES</a></li> <li><a href ...

Enhance your workflow with Visual Studio Code by incorporating multiple commands

Embarking on my journey to create my first VSC extension by following this tutorial. Within the "extension.ts" file resides a simple hello world command. My ambition is to introduce another command called git_open_modified_files, however, the tutorial la ...

The value of the progress bar in Bootstrap Vue cannot be retrieved using a function

I have implemented a progress bar using Bootstrap Vue. Here is the HTML code: <b-progress :value="getOverallScore" :max=5 variant="primary" animated></b-progress> The getOverallScore function calculates an average value based on three differe ...

The element will only show up upon resizing (in Safari web browser)

I am facing a problem with a button styled using the class btn-getInfo-Ok <button class="btn-getInfo-Ok">Hello</button> in my style.css file .btn-getInfo-Ok { color:white !important; margin: 0 auto !important; height:50px; bottom:0; ...

How can you modify a button in Ionic 2 based on the login status, using a modal to redirect to a different page once authenticated?

I have a button on my main page that is supposed to display 'Log out' when the user is currently logged in, and 'Log in' when there is no active user session. Clicking on the login button opens a modal. After successful login, the user ...