0

데이터베이스에서 스레드 (Gmail 대화) ID를 가져온 다음 각 스레드 ID에 대한 모든 데이터를 Google API에 요청하는 기능이 있습니다. 스레드 객체를 받으면 데이터베이스에 저장합니다. 이 메시지는 ~ 1k 개의 메시지가있는받은 편지함에서는 정상적으로 작동합니다. 그러나 100,000 개가 넘는 메시지가있는 계정에서 작동하는지 확신 할 수 없습니다.막대한 양의 콜백이 스크립트를 중단 시키거나 충분한 RAM을 사용할 수있을 때마다 계속할 것인가?

이제 컴퓨터에서 메모리가 부족 해지면 충분한 RAM을 다시 사용할 수있을 때마다 콜백 함수가 계속 실행됩니까? 나는 (일부 지점에서 다시 실행 전체 스크립트를하고 마지막으로 끝난 곳에서 신선한 RAM을 계속?)이 부분 별 역할을 다하기 위해이 코드를 수정해야

function eachThread(auth) { 
    var gmail = google.gmail('v1'); 

    MongoClient.connect(mongoUrl, function(err, db){ 
    assert.equal(null, err); 
    var collection = db.collection('threads'); 
    // Find all data in collection and convert it to array 
    collection.find().toArray(function(err, docs){ 
     assert.equal(null, err); 
     var threadContents = []; 
     // For each doc in array... 
     for (var i = 0; i < docs.length; i++) { 
     gmail 
     .users 
     .threads 
     .get({auth:auth,'userId':'me', 'id':docs[i].id}, function(err, resp){ 
      assert.equal(null, err); 
      threadContents.push(resp); 
      console.log(threadContents.length); 
      console.log(threadContents[threadContents.length - 1].id); 
      var anotherCollection = db.collection('threadContents'); 
      anotherCollection.updateOne(
      {id: threadContents[threadContents.length - 1].id}, 
      threadContents[threadContents.length - 1], 
      {upsert:true}, 
      function(err, result){ 
       assert.equal(null, err); 
       console.log('updated one.'); 
      }); 
      if (threadContents.length === docs.length) { 
      console.log('Length matches!'); 
      db.close(); 
      } 
     });//end(callback(threads.get)) 
     }//end(for(docs.length)) 
    });//end(find.toArray) 
    });//end(callback(mongo.connect)) 
}//end(func(eachThread)) 
+0

RAM 때문인지는 모르겠지만 SQL to MongoDB 도구에서 부분적으로 구현하는 것을 기억합니다.파트별로 파트를 테스트하기 전에 파트 별 파트의 버전이 * 2 빠릅니다. – DrakaSAN

+0

@DrakaSAN 나는 똑같이했다. 나는 수백만 개의 행을 가진 mySQL 데이터베이스를 가지고 있었고 SQL에서 mongo로 부분적으로 마이그레이션 한 CRON을 만들었습니다. 그러나 그것은 PHP였습니다. 이 콜백 세계는 시간이 오래 걸리더라도 일회성 실행으로 더 잘 작동 할 수 있다고 생각합니다. 작업을 수행하는 데 필요한 것입니다. – Kunok

+1

당신이 할 수있는 일은'threadContents'를 피하고'resp' 자체를 삽입하는 것입니다. 또한 루프 내에서'anotherCollection'을 생성하는 것은 반복되는 동일한 객체이므로 아무런 의미가 없습니다. 그럼 당신은 확실히 숫양에 어떤 문제가 없을 것입니다. – sed

답변

2

: 귀하의 경우 MongoDB

이 같은 https://mongodb.github.io/node-mongodb-native/2.0/tutorials/streams/

뭔가 일을해야 find 방법에 스트림을 제공한다. 또한 루프 내부의 모든 요소에서 동일한 개체를 인스턴스화하지 않습니다.

다음은 메모리가 부족하지 않은 코드의 예입니다.하지만 끝나면 콜백을받지 않을 것이라는 점을 잊지 말아야합니다. 그렇게하고 싶다면 promises/비동기.

// Fire-and-forget type of function 
// Will not run out of memory, GC will take care of that 
function eachThread(auth, cb) { 
    var gmail = google.gmail('v1'); 

    MongoClient.connect(mongoUrl, (err, db) => { 
    if (err) { 
     return cb(err); 
    } 

    var threadsCollection = db.collection('threads').find(); 
    var contentsCollection = db.collection('threadContents'); 

    threadsCollection.on('data', (doc) => { 
     gmail.users.threads.get({ auth: auth, 'userId': 'me', 'id': doc.id }, (err, res) => { 
     if (err) { 
      return cb(err); 
     } 

     contentsCollection.updateOne({ id: doc.id }, res, { upsert: true }, (err, result) => { 
      if (err) { 
      return cb(err); 
      } 
     }); 
     }); 
    }); 

    threadsCollection.on('end',() => { db.close() }); 
    }); 
} 
+0

매개 변수로'cb'를 추가했습니다. 그것은 기본적으로 정의 될 필요가 없거나 어딘가에 정의해야하는 콜백 변수입니까? – Kunok

+0

오류 처리를위한 cb입니다. 'eachThread ('my auth', (err) => {if (err) {console.error (err)}})'여기 예제가 있습니다. – sed

+0

이 오류가 발생합니다 :'TypeError : cb가 함수가 아닙니다' – Kunok

1

Now what I am asking, once a machine runs out of memory, will it break or will it continue executing callback functions whenever enough RAM is available again?

를 메모리가 부족하면, OS가 프로세스를 중단시킵니다. Linux에서는 OOM (Out of Memory)이 표시됩니다. 그래서 예, 깨질 것입니다.

이러한 시나리오에서는 스트림이나 생성기를 사용하여 처리해야하는 데이터 덩어리 만 메모리에 보관할 수 있습니다. 당신이 모든 것을 얻을 및 배열에 밀어하지 않을 경우 당신은 메모리가 부족하지 않습니다

var collection = db.collection('threads'); 
var cursor = collection.find() 

cursor.on('data', function(doc) { 
    gmail 
    .users 
    .threads 
    .get({auth:auth,'userId':'me', 'id': doc.id}, function(err, resp) { 
    ... 
    }) 
}) 
1

async.mapLimit하여 for 루프는 일부 기능에 의해 부품을 추가 한 정도로되어 교체. 또한 anotherCollection 생성을 collection과 함께 이동하도록 자유를 가져 왔습니다. 연결을 여는 것이 한 번 수천 번 열지 않으면 hundre를 여는 것보다 낫습니다.

또한 assert.equalcallback(err)으로 바 꾸었습니다. async의 함수는 모든 것을 멈추어야한다는 것을 이해하고 예외를 던지는 대신 완전히 종료 할 수있게합니다.

편집 :

@chernando는 언급으로, collection.find().toArray를 사용하여 RAM에 전체 컬렉션을 가져옵니다. 부분적으로 파트를 수행하는 더 좋은 방법은 데이터를 스트리밍하거나 DB에 청크로 데이터를 제공하도록 요청하는 것입니다.

이 버전에서는 collection.find().toArray을 문제없이 작동시키기에 충분한 RAM이 있다고 가정합니다.

나중에 시간이있을 때 주석에서 언급 한 도구를 적용하여 나중에 다시 볼 수 있습니다.

var async = require('async'); 

function eachThread(auth) { 
    var gmail = google.gmail('v1'), 
     limit = 100; //Size of the parts 

    MongoClient.connect(mongoUrl, function(err, db){ 
    assert.equal(null, err); 
    var collection = db.collection('threads'), 
     anotherCollection = db.collection('threadContents'); 
    // Find all data in collection and convert it to array 
    collection.find().toArray(function(err, docs){ 
     assert.equal(null, err); 
     var threadContents = []; 
//Change here 
     async.mapLimit(docs, limit, (doc, callback) => { 
     gmail 
     .users 
     .threads 
     .get({auth:auth,'userId':'me', 'id':docs[i].id}, function(err, resp){ 
      if(err) { 
      return callback(err); 
      } 
      threadContents.push(resp); 
      console.log(threadContents.length); 
      console.log(threadContents[threadContents.length - 1].id); 
      anotherCollection.updateOne(
      {id: threadContents[threadContents.length - 1].id}, 
      threadContents[threadContents.length - 1], 
      {upsert:true}, 
      function(err, result){ 
       if(err) { 
       console.error(err); 
       } else { 
       console.log('updated one.'); 
       } 
       callback(err); 
      }); 
     });//end(callback(threads.get)) 
//Change here 
     }, (error) => { 
     if(error) { 
      console.error('Transfert stopped because of error:' + err); 
     } else { 
      console.log('Transfert successful'); 
     } 
     });//end(async.mapLimit) 
    });//end(find.toArray) 
    });//end(callback(mongo.connect)) 
}//end(func(eachThread)) 
+2

'collection.find(). toArray'가 당신의 RAM을 열정적으로 소비한다는 것을 알아 두십시오. –

+0

@chernando : 정말 좋은 점 – DrakaSAN

+0

좋아, 나중에 다시 올 때까지이 대답을 계속 주시 할 것입니다. – Kunok

관련 문제