【问题标题】:Batch Write to Firestore with Admin SDK使用 Admin SDK 批量写入 Firestore
【发布时间】:2020-03-05 19:13:51
【问题描述】:

我需要读取一个非常大的 ASCII 平面文件(150 万行)。它基本上是制造商的零件清单。我想使用 Firestore 来托管它。

作为 .csv 文件,它的大小为 250GB。我能够使用 Windows PowerShell 将其转换为 JSON 文件,现在它的重量超过 1GB。

如何将这些数据导入 Firestore?我认为 Admin SDK 和批量写入将是可行的方法。因此,我完成了所有设置并组装了一个节点脚本,但 Firestore 的 Admin SDK 文档很薄。

我的节点脚本在下面,但它抛出错误FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory

var admin = require("firebase-admin");
var serviceAccount = require("./--------------------------.json");
var fs = require('fs');
var myCsvFile = "./global.csv"
var parse = require('csv-parse');
require('should');

admin.initializeApp({
  credential: admin.credential.cert(serviceAccount),
  databaseURL: "https://g--------b.firebaseio.com"
});

var firestore = admin.firestore();
var writeBatch = firestore.batch();
var myRef = firestore.collection("foo").doc();
var obj = {};


fs.createReadStream(myCsvFile)
    .pipe(parse({delimiter: '|',relax_column_count:true,quote: ''}))
    .on('data', function(csvrow) {
        if(csvrow[1]){
            obj.family = csvrow[1];
        }
        if(csvrow[2]){
            obj.series = csvrow[2];
        }
        if(csvrow[3]){
            obj.sku = csvrow[3];
        }
        if(csvrow[5]){
            obj.description = csvrow[5];
        }
        if(csvrow[7]){
            obj.price = csvrow[7];  
        }
        writeBatch.set(myRef, obj);
    })
    .on('end',function() {
      writeBatch.commit()
    });

【问题讨论】:

  • 嘿罗恩。关闭 Firestore 本身似乎非常不太可能。如果你能做到,请告诉我,我欠你一个。 :-) 你最有可能在你自己的笔记本电脑上耗尽内存,这(虽然很烦人)也是相当非破坏性的。所以总的来说,试一试,如果你卡住了,请回复你尝试过的和出了什么问题。
  • @FrankvanPuffelen “致命错误:CALL_AND_RETRY_LAST 分配失败 - JavaScript 堆内存不足” ...这是 150 万条记录(sku、描述、系列、系列、价格)。 ...我想知道是否可以在我的机器上创建 JSON 文件,我可以一次上传它 - 而不是尝试批量写入每一行...?
  • 嗯...该库看起来应该是流式传输的,但显然您的内存仍然不足。您可能想查看我们的实时数据库流式导入库,该库可以流式传输您提供的 JSON,而不是一次性读取它。
  • 哦,等等,我现在看到您只提交了.on('end' 中的批处理。这意味着您正在从整个 CSV 中构建一个批次。当您向其中添加了几百行/文档时,您需要保留一个计数器来记录您添加到批处理中的项目数量并将其提交到 .on('data')。
  • 这是每个文档。您是否将所有 CSV 数据写入一个文档?因为那样你肯定会很快遇到每个文档 1MB 的限制。

标签: javascript node.js firebase google-cloud-firestore


【解决方案1】:

每秒可写入 500 个条目。因此,关键是将.commit 的速率限制为每秒1 次,并将batch.set 的速率限制为每次提交低于500。我使用 aynch/await 作为速率限制器以及将.set 推入.batches 的递增数组的promise.all 样式模式。

哦,最后一件事 - 我必须通过 --max-old-space-size 开关告诉 Node 使用更多内存。

下面的脚本是从High Tech Telecom复制过来的:

var admin = require("firebase-admin");
var serviceAccount = require("./your-firebase-project-service-account-key.json");
var fs = require('fs');
var csvFile = "./my-huge-file.csv"
var parse = require('csv-parse');
require('should');

admin.initializeApp({
  credential: admin.credential.cert(serviceAccount),
  databaseURL: "https://your-project.firebaseio.com"
});

var firestore = admin.firestore();
var thisRef;
var obj = {};
var counter = 0;
var commitCounter = 0;
var batches = [];
batches[commitCounter] = firestore.batch();

fs.createReadStream(csvFile)
    .pipe(
      parse({delimiter: '|',relax_column_count:true,quote: ''})
  )
    .on('data', function(csvrow) {
      if(counter <= 498){
          if(csvrow[1]){
              obj.family = csvrow[1];
          }
          if(csvrow[2]){
              obj.series = csvrow[2];
          }
          if(csvrow[3]){
              obj.sku = csvrow[3];
          }
          if(csvrow[4]){
              obj.description = csvrow[4];
          }
          if(csvrow[6]){
              obj.price = csvrow[6];  
          }
          thisRef = firestore.collection("your-collection-name").doc();
          batches[commitCounter].set(thisRef, obj);
          counter = counter + 1;          
      } else {
          counter = 0;
          commitCounter = commitCounter + 1;
          batches[commitCounter] = firestore.batch();
      }
    })
    .on('end',function() {
      writeToDb(batches);
    });

function oneSecond() {
  return new Promise(resolve => {
      setTimeout(() => {
          resolve('resolved');
      }, 1010);
  });
}

async function writeToDb(arr) {
  console.log("beginning write");
  for (var i = 0; i < arr.length; i++) {
      await oneSecond();
      arr[i].commit().then(function () {
          console.log("wrote batch " + i);
      });
  }
  console.log("done.");
}

【讨论】:

    【解决方案2】:

    在规定的限制内批量写入不会对您的情况造成任何问题。我认为您目前没有其他选择。

    【讨论】:

    • 这是 150 万行或行。我已经找到了节点脚本,重新获取了每行中的特定字段。现在我只需要把它放到 Firestore 中。
    【解决方案3】:

    一句话:你从不重置 obj (obj={}),所以基本上你一直插入相同的记录。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-08-03
      • 1970-01-01
      • 2021-09-25
      • 2020-07-11
      • 2021-08-21
      • 2018-12-10
      • 2021-12-07
      • 2018-04-26
      相关资源
      最近更新 更多