6

API 呼び出しを実行し、巨大なデータベースからオフセットを介して順番に JSON を要求する関数をコーディングしています。JSON レスポンスが解析され、その後のデータが Cloud Firestore サーバーにアップロードされます。

Nodejs (Node 6.11.3) & 最新の Firebase Admin SDK

情報は期待どおりに解析され、コンソールに完全に出力されます。ただし、データを Firestore データベースにアップロードしようとすると、コンソールに次のエラー メッセージが表示されます。

認証エラー:エラー: ソケットがハングアップしました

(ノード:846) UnhandledPromiseRejectionWarning: 未処理の promise 拒否 (拒否 ID: -Number-): エラー: プラグインからのメタデータの取得がエラーで失敗しました: ソケットがハングアップしました

そして時折:

認証エラー: エラー: ECONNRESET の読み取り

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

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

生成されたサービス アカウント JSON を資格情報として使用して、firebase-adminsdk アカウントを使用するデータベースにアクセスしています。データベースの読み取り/書き込みルールは現在、すべてのアクセスを許可するために開かれています (実際のユーザーなしで開発中であるため)。

これが私の機能です:

Firebase の初期化とオフセットのゼロ化

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

API からの応答の最後に到達していない場合は、HTTP 要求を実行して関数を再実行します。

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

どんな助けでも大歓迎です!

アップデート

一度にプルし、その後関数を使用して一度にアップロードする JSON の行を変更しようとしました - 1000 から 100 まで。

理想的には、各 forEach 配列の反復が前の反復が完了するのを待ってから開始すれば完璧です。

更新 #2

async モジュールをインストールしました。現在、async.eachSeries 関数を使用して、一度に 1 つのドキュメントのアップロードを実行しています。アップロード中のすべてのエラーは消えますが、機能が完了するまでには非常に長い時間がかかります (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()
    });
});

また、一定期間が経過すると、データベースは次のエラーを返します。

(ノード:16168) UnhandledPromiseRejectionWarning: 未処理の promise 拒否 (拒否 ID: -Number-): エラー: データストア操作がタイムアウトしたか、データが一時的に利用できませんでした。

私の関数は時間がかかりすぎているようです... 十分な長さではありません。エラーなしでこれをより速く実行する方法について誰かアドバイスがありますか?

4

3 に答える 3