2017-10-07 72 views
5

我正在編寫一個函數,該函數運行API調用並通過偏移量從大型數據庫按順序請求JSON。解析JSON響應,然後將其中的後續數據上傳到我們的Cloud Firestore服務器。Nodejs,雲Firestore上傳任務 - 身份驗證錯誤:錯誤:套接字掛斷

的NodeJS(節點6.11.3)&最新火力地堡聯繫SDK

預期的信息進行解析,並打印到控制檯完美。當數據試圖但是上傳到我們公司的FireStore數據庫,控制檯垃圾郵件與錯誤信息:

Auth error:Error: socket hang up

(node:846) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: -Number-): Error: Getting metadata from plugin failed with error: socket hang up

偶爾:

Auth error:Error: read ECONNRESET

在foreach功能收集來自下載JSON和流程的項目上傳到Firestore數據庫之前的數據。每個JSON最多可以有1000個數據項(1000個文檔值)通過forEach函數。我知道,如果在上傳設置完成之前重複該功能,這可能是一個問題?

我是一個編碼新手,並且明白這個函數的控制流並不是最好的。但是,我無法找到有關控制檯打印錯誤的任何信息。我可以找到有關套接字掛起的大量信息,但沒有在驗證錯誤部分中找到。

我使用生成的服務帳戶JSON作爲憑據訪問我們的數據庫,該數據庫使用firebase-adminsdk帳戶。我們對數據庫的讀/寫規則目前是開放的,以允許任何訪問(因爲我們正在開發沒有真正的用戶)。

這裏是我的功能:

火力地堡初始化&零偏移-ING

const admin = require('firebase-admin'); 
    var serviceAccount = require("JSON"); 
    admin.initializeApp({ 
    credential: admin.credential.cert(serviceAccount), 
    databaseURL: "URL" 
    }); 
    var db = admin.firestore(); 
    var offset = 0; 
    var failed = false; 

運行功能&設置HTTP頭

var runFunction = function runFunction() { 
    var https = require('https'); 
    var options = { 
     host: 'website.com', 
     path: (path including an offset and 1000 row specifier), 
     method: 'GET', 
     json: true, 
     headers: { 
      'content-type': 'application/json', 
      'Authorization': 'Basic ' + new Buffer('username' + ':' + 'password').toString('base64') 
     } 
    }; 

運行HTTP請求&重新運行該功能,如果我們沒有從API

if (failed === false) { 
     var req = https.request(options, function (res) { 
      var body = ''; 
      res.setEncoding('utf8'); 
      res.on('data', function (chunk) { 
       body += chunk; 
      }); 
      res.on('end',() => { 
       console.log('Successfully processed HTTPS response'); 
       body = JSON.parse(body); 
       if (body.hasOwnProperty('errors')) { 
        console.log('Body ->' + body) 
        console.log('API Call failed due to server error') 
        console.log('Function failed at ' + offset) 
        req.end(); 
        return 
       } else { 
        if (body.hasOwnProperty('result')) { 
         let result = body.result; 
         if (Object.keys(result).length === 0) { 
          console.log('Function has completed'); 
          failed = true; 
          return; 
         } else { 
          result.forEach(function (item) { 
           var docRef = db.collection('collection').doc(name); 
           console.log(name); 
           var upload = docRef.set({ 
            thing: data, 
            thing2: data, 
           }) 
          }); 
          console.log('Finished offset ' + offset) 
          offset = offset + 1000; 
          failed = false; 
         } 
         if (failed === false) { 
          console.log('Function will repeat with new offset'); 
          console.log('offset = ' + offset); 
          req.end(); 
          runFunction(); 
         } else { 
          console.log('Function will terminate'); 
         } 
        } 
       } 
      }); 
     }); 
     req.on('error', (err) => { 
      console.log('Error -> ' + err) 
      console.log('Function failed at ' + offset) 
      console.log('Repeat from the given offset value or diagnose further') 
      req.end(); 
     }); 
     req.end(); 
    } else { 
     req.end(); 
    } 
    }; 
    runFunction(); 

任何幫助將不勝感激達到響應的結束!

UPDATE

我只是試圖改變JSON的,我拉的時間和使用功能,在隨後的時間上傳行 - 從1000下降到100座掛斷誤差不頻繁出現,所以肯定是由於數據庫超載。

理想情況下,如果每個forEach數組迭代在開始之前等待先前的迭代完成,那將是完美的。

更新#2

我已經安裝了異步模塊和我目前使用的async.eachSeries功能在每次執行一個文件上傳。上傳中的所有錯誤消失 - 但是該功能將花費大量時間完成(對於158,000個文檔,大約需要9小時)。我更新的循環代碼是這樣的,以實現一個計數器:

async.eachSeries(result, function (item, callback) { 
    // result.forEach(function (item) { 
    var docRef = db.collection('collection').doc(name); 
    console.log(name); 
    var upload = docRef.set({ 
     thing: data, 
     thing2: data, 
    }, { merge: true }).then(ref => { 
     counter = counter + 1 
     if (counter == result.length) { 
      console.log('Finished offset ' + offset) 
      offset = offset + 1000; 
      console.log('Function will repeat with new offset') 
      console.log('offset = ' + offset); 
      failed = false; 
      counter = 0 
      req.end(); 
      runFunction(); 
     } 
     callback() 
    }); 
}); 

而且,一段時間後,數據庫返回此錯誤:

(node:16168) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: -Number-): Error: The datastore operation timed out, or the data was temporarily unavailable.

看來,如果現在我的作用時間過長...而不是不夠長。有沒有人有任何建議如何使這個運行更快,沒有錯誤?

回答

2

作爲此循環的一部分的寫入請求超過了Firestore的配額 - 因此服務器拒絕了其中的大部分配額。

爲了解決這個問題,我一次將大約50個左右的項目轉換成上傳請求,Promise確認何時移到下一個塊上傳。

答案被張貼在這裏 - >Iterate through an array in blocks of 50 items at a time in node.js,爲我的工作代碼模板是如下:

async function uploadData(dataArray) { 
    try { 
    const chunks = chunkArray(dataArray, 50); 
    for (const [index, chunk] of chunks.entries()) { 
     console.log(` --- Uploading ${index + 1} chunk started ---`); 
     await uploadDataChunk(chunk); 
     console.log(`---Uploading ${index + 1} chunk finished ---`); 
    } 
    } catch (error) { 
    console.log(error) 
    // Catch en error here 
    } 
} 

function uploadDataChunk(chunk) { 
    return Promise.all(
    chunk.map((item) => new Promise((resolve, reject) => { 
     setTimeout(
     () => { 
      console.log(`Chunk item ${item} uploaded`); 
      resolve(); 
     }, 
     Math.floor(Math.random() * 500) 
    ); 
    })) 
); 
} 

function chunkArray(array, chunkSize) { 
    return Array.from(
    { length: Math.ceil(array.length/chunkSize) }, 
    (_, index) => array.slice(index * chunkSize, (index + 1) * chunkSize) 
); 
} 

通過對uploadData傳遞數據陣列 - 使用uploadData(數據);並將每個項目的上傳代碼發佈到chunk.map函數內的setTimeout塊(位於resolve()行之前)內的uploadDataChunk中。

0

我通過chaining the promises in the loop解決了這個問題,每次之間等待50毫秒。

function Wait() { 
    return new Promise(r => setTimeout(r, 50)) 
} 

function writeDataToFirestoreParentPhones(data) { 
    let chain = Promise.resolve(); 
    for (let i = 0; i < data.length; ++i) { 
     var docRef = db.collection('parent_phones').doc(data[i].kp_ID_for_Realm); 
     chain = chain.then(()=> { 
      var setAda = docRef.set({ 
       parent_id: data[i].kf_ParentID, 
       contact_number: data[i].contact_number, 
       contact_type: data[i].contact_type 
      }).then(ref => { 
       console.log(i + ' - Added parent_phones with ID: ', data[i].kp_ID_for_Realm); 
      }).catch(function(error) { 
       console.error("Error writing document: ", error); 
      }); 
     }) 
     .then(Wait) 
    } 
} 
+0

你可能會更好地使用每個firestore docSet返回的個體承諾。我解決了這個問題中的承諾,以便當所有前面的塊已經上傳時,我的代碼只傳遞到下一個上傳塊。消除了在迭代之間等待的需要。 – Hendies