2017-10-07 5 views
5

Ich bin eine Funktion, die API-Aufrufe und JSON von einer riesigen Datenbank in Folge über Offsets anfordert codiert. Die JSON-Antwort wird analysiert und anschließend werden die nachfolgenden Daten auf unseren Cloud Firestore-Server hochgeladen.Nodejs, Cloud Firestore Hochladen Aufgaben - Auth Fehler: Fehler: Socket auflegen

NodeJS (Node 6.11.3) & Neueste Firebase Admin SDK

Die Informationen analysiert wird, wie erwartet, und druckt die perfekt Konsole.

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

und gelegentlich: Wenn die Daten versucht jedoch zu unserer Firestore-Datenbank zu laden, wird die Konsole mit der Fehlermeldung spammed

Auth error:Error: read ECONNRESET

Die forEach Funktion die Elemente aus dem heruntergeladenen JSON sammelt und Prozesse die Daten vor dem Hochladen in die Firestore-Datenbank. Jeder JSON enthält bis zu 1000 Datenelemente (1000 Dokumente), die die ForEach-Funktion durchlaufen. Ich verstehe, dass dies ein Problem sein könnte, wenn die Funktion wiederholt wird, bevor der Upload-Satz fertig ist?

Ich bin ein Codierungsneuling und verstehe, dass der Kontrollfluss dieser Funktion nicht der beste ist. Ich kann jedoch keine Informationen zu dem Fehler finden, den die Konsole druckt. Ich kann viele Informationen über Sockets aufhängen, aber keine auf der Auth-Fehler-Abschnitt.

Ich verwende ein generiertes Service-Konto JSON als Berechtigungsnachweis für den Zugriff auf unsere Datenbank, die das firebase-adminsdk-Konto verwendet. Unsere Lese-/Schreibregeln für die Datenbank sind derzeit offen, um jeglichen Zugriff zu erlauben (da wir uns in der Entwicklung befinden, ohne echte Benutzer).

Hier ist meine Funktion:

Firebase Initialisierung & Offset-Null-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; 

die Funktion & Lauf Einstellung HTTP-Header

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

das Laufen HTTP Request & Wieder läuft die Funktion, wenn wir nicht das Ende der Antwort von der API erreicht

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

Jede Hilfe wäre sehr dankbar!

UPDATE

Ich habe versucht, nur die Zeilen von JSON zu ändern, die ich zu einer Zeit ziehen und anschließend zu einem Zeitpunkt laden mit der Funktion - weniger von 1000 bis 100. Der Sockel aufzulegen Fehler häufig, so ist es definitiv wegen Überlastung der Datenbank.

Idealerweise wäre es perfekt, wenn jede ForAtach-Array-Iteration auf den Abschluss der vorherigen Iteration wartet, bevor sie beginnt.

UPDATE # 2

ich das Asynchron-Modul installiert haben, und ich bin derzeit die async.eachSeries Funktion zu einem Zeitpunkt ein Dokument Upload auszuführen. Alle Fehler in der Mitte des Uploads verschwinden - die Funktion wird jedoch unglaublich lange dauern (ungefähr 9 Stunden für 158.000 Dokumente). Meine aktualisiert Schleife Code ist dies mit einem Zähler implementiert:

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

Auch nach einer gewissen Zeit gibt die Datenbank diesen Fehler:

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

Es scheint, als ob jetzt meine Funktion dauert zu lange ... statt nicht lang genug. Hat jemand einen Ratschlag, wie man diesen Lauf ohne angegebene Fehler schneller machen kann?

Antwort

2

Die Schreibanforderungen als Teil dieser Schleife überschritten einfach die Quote von Firestore - daher wies der Server die Mehrheit von ihnen zurück.

Um dieses Problem zu lösen, konvertierte ich meine Anfragen zum Hochladen in Stücke von 50 oder so auf einmal, mit Zusagen, die bestätigen, wann ich zum nächsten Chunk-Upload übergehen muss.

Die Antwort wurde hier geschrieben ->Iterate through an array in blocks of 50 items at a time in node.js, und die Vorlage für meinen Arbeits Code ist wie folgt:

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

das Datenfeld durchfahren zu upload - upload (Daten) verwendet wird; und posten Sie Ihren Upload-Code für jedes Element in uploadDataChunk innerhalb des setTimeout-Blocks (vor der resolve() -Zeile) innerhalb der Funktion chunk.map.

0

Ich kam um chaining the promises in the loop mit einer Wartezeit von 50 Millisekunden dazwischen.

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

Sie könnten die einzelnen Versprechen, die von jedem firestore docSet zurückgegeben werden, besser verwenden. Ich löse Versprechungen auf, so dass mein Code nur auf den nächsten Upload-Chunk übergeht, wenn alle vorherigen Chunks hochgeladen wurden. Entfernt Wartezeiten zwischen Iterationen. – Hendies

Verwandte Themen