MongoSessionEndedError: Session expired and cannot be utilized

I have been working on creating new documents from existing ones within a collection in my database. Specifically, I am flattening a field from the original document during this process. Initially, everything ran smoothly using mongosh but now that I need to execute the script on a gcp function, I'm encountering some challenges. The error arises when attempting to close the connection:

MongoExpiredSessionError: Cannot use a session that has ended

As a result, the document is not added to the new collection. Interestingly, removing DB.close() allows the documents to be added successfully, however, the program does not complete.

I made sure to place await statements where necessary for interactions with the DB, but unfortunately, it did not resolve the issue. Below is the code structure: The main function calls connectMongo, which establishes a connection to the MongoDB and initiates the execute function. The execute function retrieves documents from the original collection and passes them individually to the flatten function. The flatten function is responsible for the flattening operation and adding the new document to the new collection.

Here's an overview of the code setup: Print statements within the code work regardless of whether DB.close() is included or not.

const {MongoClient} = require('mongodb');
const uri = "mongodb://<myURI>"


const DB = new MongoClient(uri);
var collections;

//---------------------------------------------------------------------------------------------
execute = async function(db){
    
    docs = await db.collection('observations6').find()
    
    await docs.forEach(flatten)
    
}
//---------------------------------------------------------------------------------------------

flatten = async function(myDoc){

    forms = myDoc.properties.forms
    for(var i = 0; i < forms.length; i++){

        FormObj = {
            parent_id : myDoc._id+'_form_'+i,
            eventId: myDoc.eventId,
            userId: myDoc.userId,
            deviceId: myDoc.deviceId,
            createdAt: myDoc.createdAt,
            lastModified : myDoc.lastModified,
            type: myDoc.type,
            geometry: myDoc.geometry,
            favoriteUserIds: myDoc.favoriteUserIds,
            states: myDoc.states,
            attachments: myDoc.attachments,
            formId: forms[i]['formId'],
        }
        console.log('Created form object')
        field_count = 0
        retry = 0

        while (retry <= 10){
            if (!forms[i].hasOwnProperty('field'+field_count)){
                field_count += 1
            }
            retry += 1
        }
        console.log(`Did ${field_count} retry(s)`)
        while(forms[i].hasOwnProperty('field'+field_count)){
            FormObj['field'+field_count] = forms[i]['field'+field_count]
            field_count+=1
        }
        console.log('Added field properties')
        
        parent_id = myDoc._id + '_form_' + i
        await collections.collection('observations6_flatten').updateOne({parent_id},{$set: {FormObj}}, {upsert:true})

        console.log('Added object to collection')

    }
    
}
//---------------------------------------------------------------------------------------------
async function connectMongo() {
     
    try {
        // Connect to the MongoDB cluster
        await DB.connect();
 
        // Make the appropriate DB calls
        collections = await DB.db('magedb')
        //console.log(collections)
        await execute(collections)
       
    } catch (e) {
        console.error(e);
    } 
    
}

//---------------------------------------------------------------------------------------------
//Main call
main = async function(){
    await connectMongo()
    .then(()=>{
        DB.close()
    })
}

main()

The script returns the following errors:

aortiz@WW-593 MINGW64 ~/Documents/GitHub/engineering_etl (main)
$ node flattening/flattening.js
Created form object
Did 11 retry(s)
Added field properties
Created form object
Did 0 retry(s)
Added field properties
...
C:\Users\aortiz\Documents\GitHub\engineering_etl\node_modules\mongodb\lib\sessions.js:655
...

If I exclude DB.close(), the output adds objects to the collection but fails to exit.

This discrepancy indicates that the error occurs during the line:

await collections.collection('observations6_flatten').updateOne({parent_id},{$set: {FormObj}}, {upsert:true})

It raises questions about why the connection terminates before reaching this point.

Answer №1

If you're struggling with promises and async/await syntax, it's crucial to understand how they work in the context of the event loop. You can learn more about this by checking out resources like MDN's guide on promises and gaining insights into the event loop here.

The code snippet:

await docs.forEach(flatten)

might not function as expected.

MDN documentation clarifies that forEach is synchronous and behaves the same whether or not 'await' is used. It essentially iterates over all items in the array simultaneously, queuing up tasks without blocking subsequent operations under await execute(collections).

Consequently, await connectMongo() followed by closing the DB connection prior to any potential control being taken by

await collections.collection('observations6_flatten').updateOne
, might present issues.

To address this, consider employing Promise.all:

execute = async function(db){
    
    docs = await db.collection('observations6').find().toArray();
    
    return await Promise.all(docs.map(flatten))    
} 

or manually iterating through the documents:

execute = async function(db){
    
    docs = await db.collection('observations6').find().toArray();
    
    for(const doc of docs) { 
        await flatten(doc)
    }    
} 

Using Promise.all initiates simultaneous updates and resolves the promise after processing all items within the array. On the other hand, the for-await loop updates one document at a time, which may result in slower performance.

A functional example showcasing the usage of Promise.all: here

Displayed console output:

{ _id: new ObjectId("63ada717dc7041cce1c67a86") }
Created form object
Did 0 retry(s)
Added field properties
{ _id: new ObjectId("63ada723dc7041cce1c67a87") }
Created form object
Did 0 retry(s)
Added field properties
{ _id: new ObjectId("63ada725dc7041cce1c67a88") }
Created form object
Did 0 retry(s)
Added field properties
Added object to collection
...

Answer №2

Experience the difference between await and .then() by delving into Promise Chaining

const {MongoClient} = require('mongodb');
const uri = "mongodb://<myURI>"
const client = new MongoClient(uri);

async function connectMongo() {
    try {
        await client.connect();
        console.log('Successfully connected to the server');

        const collections = await client.db('magedb');
        console.log('Collections', collections);
        await execute(collections);
    } catch (e) {
        console.error(e);
    } 
}

async function execute(myDoc) {
    forms = myDoc.properties.forms
    for(var i = 0; i < forms.length; i++){

        FormObj = {
            parent_id : myDoc._id+'_form_'+i,
            eventId: myDoc.eventId,
            userId: myDoc.userId,
            deviceId: myDoc.deviceId,
            createdAt: myDoc.createdAt,
            lastModified : myDoc.lastModified,
            type: myDoc.type,
            geometry: myDoc.geometry,
            favoriteUserIds: myDoc.favoriteUserIds,
            states: myDoc.states,
            attachments: myDoc.attachments,
            formId: forms[i]['formId'],
        }
        console.log('Form object created')
        field_count = 0
        retry = 0

        while (retry <= 10){
            if (!forms[i].hasOwnProperty('field'+field_count)){
                field_count += 1
            }
            retry += 1
        }
        console.log(`Did ${field_count} retry(s)`)
        
        while(forms[i].hasOwnProperty('field'+field_count)){
            FormObj['field'+field_count] = forms[i]['field'+field_count]
            field_count+=1
        }
        console.log('Added field properties')
        
        parent_id = myDoc._id + '_form_' + i;

        try {
            await client.db().collection('observations6_flatten').updateOne({parent_id},{$set: {FormObj}}, {upsert:true});
            console.log('Object added to collection');
        } catch (error) {
            throw error; // intentional crash
        }

    }
    
}

connectMongo()
  .then(console.log)
  .catch(console.error)
  .finally(() => client.close());

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 Backbone model destruction URL fails to include the model's ID when trying to delete

I'm facing an issue in my app where I need to delete a model from a collection using "this.model.destroy" in my view, but it triggers a 405 response and the response URL doesn't include the model's id. According to the Backbone documentation ...

Where can I find the previous version of three.js? What is causing the incompatibility between the old and new versions of

Why is my app facing issues with the updated version of three.js? Can I find the previous version of three.js and why isn't the new version compatible? ...

Tips for utilizing the "get" method in React to submit a form

Is there a way to submit a form using the "get" method to an external URL in react? Similar to how it is done in HTML like in this example: https://www.example.com/form But instead, to a third party URL...? Can I achieve something like this? The goal is ...

Interactive image grid with adjustable description field per image upon selection

My goal is to create a grid of images with a single text field below the grid. This text field should display the description of the image that was last clicked. The grid is implemented using floating divs within a main div, as shown in the code snippet be ...

Currently seeking user coordinates for Vue implementation

I recently started using Vue and I'm working on capturing the lat/long of a user to be used in other functions within Vue. Currently, I am retrieving the coordinates and plan to utilize them in an API but for now, I am just logging them. Although I c ...

Faulty toggle functionality within a JavaScript-created accordion panel

HTML I'm looking to add a FAQ question within the div with the class "section-center" <body> <section class="questions"> <div class="title"> <h2>FAQ SECTION</h2> < ...

Hide dropdown when clicked outside of it

Can someone help me modify my JavaScript code so that when I click on a new dropdown, it closes the previous one and keeps only the current one open? function manageDropdowns() { var dropdowns = document.querySelectorAll('.dropdown:not(.is-hove ...

Guide on incorporating an Angular directive to substitute an element under specific conditions

My goal is to create a directive that replaces an anchor element with a div element under certain conditions. The content of the anchor element should remain unchanged and be included within the new element. The concept involves having an attribute on the ...

techniques for presenting tabular data in a JavaScript modal

Hey there! I'm looking for a way to show table formatted data in a JavaScript popup. Do you know if it's possible to display table formatted data in a JavaScript popup using ASP.NET and C#? ...

How to retrieve headers using Express.js middleware

My middleware creates authentication API keys that are then added to the header. const loger = require("easy-loger"); require('dotenv').config(); function authMiddleware(req, res, next){ const appApiKey = process.env.API_KEY; l ...

The items in my array have been replaced with new objects

I am facing an issue with storing objects in an array within a function. Every time the function is called, a new object is received and I want to add it to the existing array without overwriting the previous objects. This way, all the objects can be acc ...

Map checkboxes aren't updating after an array update following a refactor to react hooks

I recently transformed a class component into a function component using hooks. However, I am facing an issue where the checkboxes within a specific mapping are not updating with the checked value. The onChange handler is firing and updating the array corr ...

Is your dropdown menu malfunctioning with jQuery? (Chromium)

I am currently working on a website that requires the options in the second dropdown menu to change based on the selection made in the first dropdown menu. However, despite the fact that the browser is recognizing the javascript file, it doesn't seem ...

Steps to reopen MongoDB Compass on a Windows computer

After installing version 1.20.5 of MongoDB (mongodb-compass-1.20.5-win32-x64) on my Windows machine, I noticed that it automatically launches itself upon installation. However, once I close the application, I am unable to find its icon on the desktop to ...

Personalized validation using Bootstrap V5

Currently, I am using the default Bootstrap V5 form validator and I am interested in finding a way to create a custom parameter that must be checked for the input to be considered valid. Specifically, I want users to input their license plate, which should ...

I'm encountering an issue with the React state where it seems to be endlessly nesting

I am new to React and I seem to be encountering an issue where every time I trigger a click event, the state object continues to nest. The code snippet below is part of the App component. const [state, setstate] = useState('') useEffect(() =& ...

Can you explain the distinction between using `new ObjectId()`, `new ObjectId`, and `ObjectId()` in

Consider this initial definition in a file: const ObjectId = mongoose.Types.ObjectId; Which method should you choose and why? // 1 new ObjectId; // 2 new ObjectId(); // 3 ObjectId(); The official documentation recommends using new ObjectId. Person ...

Filter out specific fields from an object when populating in MongoDB using the aggregate method

Is there a way to use the populate() function in MongoDB to exclude specific fields like email and address, and only retrieve the name? For example: const results = await Seller.aggregate(aggregatePipeline).exec(); const sellers = await Seller.populate(re ...

Modify the CSS preferences using an object that has been obtained

After the user selects options from a form, I am trying to update my CSS using the settings received from an object. However, I am unsure of how to implement this layout change. Here is a glimpse of my template: div class="btn btn-primary" ng-click= ...

Issue: Switching between concealing and revealing is not functional

body { margin: 0; } a{ text-decoration: none; color: lightseagreen; } a:hover { color: lightgray; } /* full bar*/ .navbar { display: flex; justify-content: space-between; align-items: center; background-color: white; p ...