I am trying to create new documents from existing ones in one of my database’s collections. I’m flattening one of the fields from the original document. The process worked perfectly when using mongosh, but now I have to run this script on a gcp function. So I need to have everything inside the script.
I managed to connect to the db, add the flattened document to the new collection, but when I go to close the connection it gives me the error:
MongoExpiredSessionError: Cannot use a session that has ended
And it never adds the document to the new collection.
I have figured out that when I remove the DB.close()
from the code, it adds the documents but the program never finished/exits.
I have placed each await document where it interacts with the DB, but it has not been of much help. Here is my full code. The main function calls the connectMongo which connects to the mongo db and runs the execute function. This execute functions takes the documents from the original collection and passes them one by one to the flatten function. The flatten function does the flattening plus the adding of the new document to the new collection.
Here is the code:
The print statements at all cases (w/ & w/out DB.close()
) work and print out each one.
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()
This is the error it returns
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
Created form object
Did 1 retry(s)
Added field properties
Created form object
Did 0 retry(s)
Added field properties
Created form object
Did 0 retry(s)
Added field properties
Created form object
Did 0 retry(s)
Added field properties
Created form object
Did 0 retry(s)
Added field properties
C:UsersaortizDocumentsGitHubengineering_etlnode_modulesmongodblibsessions.js:655
return new error_1.MongoExpiredSessionError();
^
MongoExpiredSessionError: Cannot use a session that has ended
at applySession (C:UsersaortizDocumentsGitHubengineering_etlnode_modulesmongodblibsessions.js:655:16)
at Connection.command (C:UsersaortizDocumentsGitHubengineering_etlnode_modulesmongodblibcmapconnection.js:281:53)
at C:UsersaortizDocumentsGitHubengineering_etlnode_modulesmongodblibsdamserver.js:210:18
at Object.callback (C:UsersaortizDocumentsGitHubengineering_etlnode_modulesmongodblibcmapconnection_pool.js:327:13)
at ConnectionPool.processWaitQueue (C:UsersaortizDocumentsGitHubengineering_etlnode_modulesmongodblibcmapconnection_pool.js:506:33)
at C:UsersaortizDocumentsGitHubengineering_etlnode_modulesmongodblibcmapconnection_pool.js:183:37
at processTicksAndRejections (node:internal/process/task_queues:78:11) {
[Symbol(errorLabels)]: Set(0) {}
}
When I remove the DB.close()
it returns
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
Created form object
Did 1 retry(s)
Added field properties
Created form object
Did 0 retry(s)
Added field properties
Created form object
Did 0 retry(s)
Added field properties
Created form object
Did 0 retry(s)
Added field properties
Created form object
Did 0 retry(s)
Added field properties
Added object to collection
Added object to collection
Added object to collection
Added object to collection
Added object to collection
Added object to collection
Added object to collection
and never finished/exits the script
EDIT
Now that I am looking better at the output when I remove DB.close()
, I see that the Added Object to collection
print statement gets printer and when I have the DB.close()
it stops just before then. So this must mean that the error is ocurring on the line:
await collections.collection('observations6_flatten').updateOne({parent_id},{$set: {FormObj}}, {upsert:true})
But what exactly is going on that the connection ends before this point?
2
Answers
As mentioned in comments, you really need to crack on promises and async/await syntax https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises. I’d recommend to learn it in context of event loop: https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop, https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/ etc.
This
Doesn’t do what you think it does.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach is a synchronous call. It works identical with and without
await
. It iterates the docs array addingflatten
function to the queue for all items at once and resolves the "promise", i.e. returns control back to the callingawait execute(collections)
without waiting for any promises inside.await execute(collections)
is the last statement in the function, so once this promise is resolved, it returns control back to the callerWhich closes the connection before any
await collections.collection('observations6_flatten').updateOne
has a chance to take control.You need to either use https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all:
or loop through the docs explicitly:
The
Promise.all
start all updates at once, and resolves the promise whenflatten
for all items are resolved. The for-await loop is much slower us it updates one doc at a time.Full working example with
Promise.all
(I simplified irrelevant flattening logic): https://replit.com/@blex18/74891663mongoexpiredsessionerror-cannot-use-a-session?v=1Console output:
await is different that .then() read here if you want to learn more about Promise Chaining