2017-10-07 3 views
5

API 호출을 실행하고 거대한 데이터베이스에서 JSON을 오프셋을 통해 순서대로 요청하는 기능을 코딩하고 있습니다. JSON 응답이 파싱 된 다음 내부의 후속 데이터가 Cloud Firestore 서버에 업로드됩니다.Nodejs, Cloud Firestore 업로드 작업 - 오류 : 소켓 끊기

Nodejs (노드 6.11.3) & 최신 중포 기지 관리 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

때때로 :

Auth error:Error: read ECONNRESET

foreach는 기능이 다운로드 된 JSON 및 프로세스에서 아이템을 수집하는 데이터는 그러나 우리의 경우 FireStore 데이터베이스에 업로드하려고 할 때, 콘솔은 오류 메시지가 스팸입니다 데이터를 Firestore 데이터베이스에 업로드하기 전에 각 JSON에는 forEach 함수를 통해 전달할 수있는 최대 1000 개의 데이터 항목 (1000 개의 문서 값)이 있습니다. 업로드 설정이 완료되기 전에 함수가 반복되면 문제가 될 수 있다는 것을 알고 있습니다.

저는 코딩 초보자이며이 기능의 제어 흐름이 좋지 않다는 것을 알고 있습니다. 그러나 콘솔에서 인쇄하는 오류에 대한 정보를 찾을 수 없습니다. 소켓 끊기에 대한 정보는 많이 있지만 인증 오류 섹션에는 정보가 없습니다.

저는 firebase-adminsdk 계정을 사용하는 데이터베이스에 액세스하기 위해 생성 된 서비스 계정 JSON을 자격 증명으로 사용하고 있습니다. 데이터베이스에 대한 우리의 읽기/쓰기 규칙은 현재 모든 사용자가 액세스 할 수 있도록 열려 있습니다 (실제 사용자가없는 개발 단계에 있음). & 제로 보내고

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 요청 &는 재 실행 기능은 우리가 어떤 도움을 크게 감상 할 수

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

는 API에서 응답의 끝에 도달하지 않은 경우! 소켓 100까지 1000에서 오류가 작다 끊지 -

UPDATE

난 그냥 한 번에 당겨 이후 기능을 사용하여 한 번에 업로드 JSON의 행을 변경하려고했습니다 빈번하므로 데이터베이스가 과부하 된 것입니다.

각 forEach 배열 반복이 이전 반복이 시작되기 전에 완료 될 때까지 기다리는 것이 이상적입니다.

내가 비동기 모듈을 설치 한 나는 현재 async.eachSeries 한 번에 하나 개의 문서 업로드를 수행하는 기능을 사용하고 2

UPDATE 번호. 중간 업로드 오류는 모두 사라지지만 기능을 완료하는 데는 미친 시간이 걸립니다 (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

나는이 사이에 각각 50 밀리 초의 대기 시간을 가지고 chaining the promises in the loop에 이르렀다.

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

관련 문제