How to do bulk upsert to MongodB asynchronously to avoid "Exception in callback of async function: Error: Future resolved more than once" error?


#1

I have a for loop to do mongodB which reads in from JSON array:

for (var i = 0; i < lengthOfArray; i++) {  
    
    FutureTasks.upsert({
                    number: contentObj.records[i].number,
                    assignment_group: contentObj.records[i].assignment_group
                }, {
                    // Modifier
                    $set: {
                        userid: contentObj.records[i].assigned_to_userid,
                        username: contentObj.records[i].assigned_to,
                        start_date: modifiedStartTimeString,
                        end_date: modifiedEndTimeString
                    }
                }
                ,
                function (err, doc) {
                    if (err) {
                        console.log('error inside upsert : ', err.reason);
                        future.throw(err);
                    } else {
                        console.log('doc inside upsert : ', doc);
                        future.return(doc);
                    }
                }
}

But this causes a “Exception in callback of async function: Error: Future resolved more than once”,so how to do a forEach and upsert without a for loop?


#2
try {
  contentObject.records.forEach((record) => {
    FutureTasks.upsert(<...>);
  });
} catch(e) {
  return future.throw(e);
} finaly {
  return future.return('all is ok');
}

#3

Hi UMrzafod thank you, not sure why now database is not produced, nor error being produced?

Give data is of this type:

{ records: 
   [ { number: 'CHG012345',
       u_change_type: 'Routine',
      u_owner: 'Seb',
     },
    { number: 'CHG067890',
       u_change_type: 'Routine',
      u_owner: 'Seb',
  ]
}

Also, the upsert content would now be

i.e

userid: contentObj.record.assigned_to_id

etc

or alternatively I can pass by the entire collections to be consumed by another async function once it is ready?

i.e return future.return(FutureTask)

or return future.return(FutureTask.find().fetch())??

EDIT: Managed to iterate using forEach, but not sure how to return collections asynchronously


#4

All collection operations on the server side are running in a Fiber, so they are async by default. Can you clarify your task. I cant understand what you watta do.
You have an array of documnts to upsert. Are you going to upsert and then have an array of those documents from db?


#5

Hi mrzafod, so any CRUD actions on the MongodB collections is already to be read by another function BEFORE the function returns? If that’s the case why need to return a future or promise?

I am trying to do a HTTP.get in JSON array and then store into MongodB for persistence, and any changes in next HTTP.get will be upserted in MongodB and ready to be read from another service…


#6

I’m probably missing something here, but I think you’re over-thinking this. Meteor gives you synchronous HTTP and MongoDB operations, so you only need something like this if you want your MongoDB data to be ready as soon as the loop completes:

try {
  contentObj.records.forEach(doc => {
    FutureTasks.upsert({
      number: doc.number,
      assignment_group: doc.assignment_group
    }, {
        // Modifier
        $set: {
          userid: doc.assigned_to_userid,
          username: doc.assigned_to,
          start_date: modifiedStartTimeString,
          end_date: modifiedEndTimeString
        }
      });
  });
} catch (err) {
  throw new Meteor.Error('error inside upsert : ', err.message);
}

No need to set up your own Futures - they’re built in already.


#7

Thanks Rob, no wonder when I followed the same design flow with another function with non MongodB calls I was no getting any issues.