2017-10-07 11 views
5

私は、APIコールを実行し、オフセットを介して巨大なデータベースからJSONを順番に要求する関数をコーディングしています。 JSONレスポンスが解析され、その後のデータがCloud Firestoreサーバーにアップロードされます。Nodejs、Cloud Firestoreアップロードタスク - エラー:ソケットがハングアップする

Nodejs(ノード6.11.3)&最新Firebase管理SDK

情報が完全にコンソールに予想されるように解析され、そして印刷されています。

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

と時折::データは、しかし、私たちのFirestoreデータベースにアップロードしようとすると、コンソールにエラーメッセージをスパムされ

Auth error:Error: read ECONNRESET

のforeach関数は、ダウンロードしたJSONやプロセスからアイテムを収集しますFirestoreデータベースにアップロードする前のデータ。各JSONには、forEach関数を通過するための1000項目のデータ(1000ドキュメント分)があります。アップロードセットが終了する前に関数が繰り返されると、これが問題になる可能性があることを理解していますか?

私はコーディング初心者であり、この機能の制御フローが最良ではないことを理解しています。ただし、コンソールが印刷するエラーに関する情報は見つかりません。ソケットのハングアップに関する情報はたくさんありますが、Authエラーのセクションには何も見つかりません。

私は、firebase-adminsdkアカウントを使用するデータベースにアクセスするための認証情報として、生成されたサービスアカウントJSONを使用しています。私たちのデータベースに対する読み書きルールは現在、あらゆるアクセスを可能にするために開かれています(実際のユーザーでは開発されていません)。

ここ

は私の機能です:

Firebase初期&ゼロ-INGの

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') 
     } 
    }; 

を設定する機能&を実行

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; 

をオフセット私たちは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(); 

からの応答の終わりに達していない場合はHTTPリクエスト&再実行機能を任意の助けいただければ幸いです!ソケット100までの1000年からの誤差が少ないハングアップ -

UPDATE

私はちょうど私が一度に引くと、その後の機能を使用して、一度にアップロードJSONの行を変更してみました頻繁に、それは間違いなくデータベースの過負荷のためです。

それぞれのfor each配列の反復が前の反復が完了するのを待ってから開始するのが理想的です。

UPDATE#2

私は非同期モジュールをインストールしていると私は現在、一度に1本の文書のアップロードを実行するためにasync.eachSeries機能を使用しています。中途半端にアップロードされたすべてのエラーは表示されなくなりますが、機能には非常に時間がかかります(158,000文書の場合、約9時間)。

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

今私の機能が長すぎるを取っているかのように思え:一定期間後にデータベースがこのエラーを返し、また

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() 
    }); 
}); 

:私の更新されたループのコードが実行さカウンターで、これです...長くないのではなく。誰かが明示的なエラーなしでこれをより速く走らせる方法に関する助言を持っていますか?

答えて

2

このループの一部としての書き込み要求は、Firestoreのクォータを単純に超えていたため、サーバーは大部分を拒否していました。

この問題を解決するために、一度に50個ほどのチャンクでアップロードするリクエストを変換しました。プロミスは次のチャンクアップロードに移行するタイミングを確認します。

答えはここに掲載されました - >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

関連する問題