承诺并批量发送到数据库

时间:2017-04-04 14:43:08

标签: javascript mongodb mongoose promise when-js

我目前正在解析一个js对象的列表,这些对象一个接一个地插入到db中,与Node.js大致类似:

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

问题在于,当列表大小变得非常大(~3000项)时,并行解析所有项目的内存很重。使用promise库添加并发限制并且不会耗尽内存(当/ guard时)非常容易。

但我也想优化db upserts,因为mongodb提供了bulkWrite函数。由于无法一次解析和批量写入所有项目,我需要将原始对象列表拆分为较小的集合,这些集合使用promises并行解析,然后该集合的结果数组将传递给promisified bulkWrite。如果列出项目,则会对剩余的集合重复此操作。

我很难绕过如何构建较小的promises集,以便我只能在一时间执行一组parseSomeItems-BulkUpsertThem(类似于Promise.all([set1Bulk] [set2Bulk] ]),其中set1Bulk是另一个并行解析器Promises数组?),任何伪代码帮助都会受到赞赏(但如果有所不同,我会使用它。)

2 个答案:

答案 0 :(得分:1)

如果使用mongoose和底层nodejs-mongodb-driver,它看起来像这样:



const saveParsedItems = items => ItemCollection.collection.bulkWrite( // accessing underlying driver
   items.map(item => ({
      updateOne: {
           filter: {id: item.id}, // or any compound key that makes your items unique for upsertion
           upsert: true,
           update: {$set: item} // should be a key:value formatted object
      }
   }))
);


const parseAndSaveItems = (items, offset = 0, limit = 3000) => { // the algorithm for retrieving items in batches be anything you want, basically
  const itemSet = items.slice(offset, limit);
  
  return Promise.all(
    itemSet.map(parseItem) // parsing all your items first
  )
    .then(saveParsedItems)
    .then(() => {
      const newOffset = offset + limit;
      if (items.length >= newOffset) {
        return parseAndSaveItemsSet(items, newOffset, limit);
      }
      
      return true;
    });
};

return parseAndSaveItems(yourItems);




答案 1 :(得分:1)

第一个答案看起来很完整。然而,这里有一些其他的想法浮现在脑海中。

作为一个hack-around,你可以在下一次写操作执行之前在写操作的回调中调用超时函数。这可以使您的CPU和内存在调用之间中断。即使在调用之间添加一毫秒,如果总共有3000个写入对象,则只增加3秒。

或者您可以对insertObjects数组进行分段,并将它们发送到自己的批量写入器。