2013-03-28 4 views
2

MongoDB + Mongoose와 함께 NodeJS 사용.NodeJS 비동기 동작 처리

우선 비동기 비 차단 코드의 장점을 알고 있습니다. 그래서 콜백을 처리합니다. 그러나 마침내 나는 다음과 같은 문제에 직면했다.

사용자가 언제든지 호출 할 수있는 함수가 있다고 가정 해 보겠습니다. 그리고 슈퍼 번개 속도 사용자가 거의 동시에 두 번 호출 할 수도 있습니다. 물론

function do_something_with_user(user_id){ 
    User.findOne({_id:user_id}).exec(function(err,user){ // FIND QUERY 
     // Do a lot of different stuff with user 
     // I just cannot update user with a single query 
     // I might need here to execute any other MongoDB queries 
     // So this code is a set of queries-callbacks 
     user.save() // SAVE QUERY 
    }) 
} 

그것은 다음과 같이 실행합니다 : QUERY 찾기, QUERY 찾기, SAVE QUERY, SAVE QUERY는

이 완전히 (쿼리를 저장 QUERY 찾기, QUERY, SAVE QUERY를 찾아야한다 응용 프로그램의 논리를 나누기). 그래서 나는 특정 사용자를 위해 전체 함수를 "잠그"는 비동기 행동을 막기로 결정했다. 그래서 함수 코드 내부는 여전히 비동기이다.

var lock_function_for_user = {} 

function do_something_with_user(user_id){ 
    if(!lock_function_for_user[user_id]){ 
     lock_function_for_user[user_id] = true 
     User.findOne({_id:user_id}).exec(function(err,user){ 
      // Same code as above 
      user.save(function(){ 
       lock_function_for_user[user_id] = false 
      }) 
     }) 
    } else { 
     setTimeout(function(){ 
      do_something_with_user(user_id) 
     },100) // assuming that average function execution time is 100ms in average 
    } 
} 

제 질문은 다음과 같습니다. 좋은 연습, 좋은 해킹 또는 불량 해킹입니까? 해킹이 잘못 되었다면 다른 해결책을 제공해주세요. 특히 NodeJS 프로세스를 확장하고 실행할 때이 솔루션이 제대로 작동하지 않을지 의심 스럽습니다.

답변

2

이것은 매우 나쁜 습관입니다. 타이머를 사용하여 코드의 흐름을 제어해서는 안됩니다.

여기의 문제를 원 자성이라고합니다. 찾기 - 저장, 찾기 - 저장이 필요한 경우 어떻게 든이 작업을 묶어야합니다 (트랜잭션). 사용하는 소프트웨어에 따라 다릅니다. redis에는 multi 및 exec 명령이 있습니다. mongodb에는 findAndModify()가 있습니다. 다른 해결책은 색인을 사용하는 것입니다. 같은 필드를 두 번 저장하려고하면 오류가 발생합니다. 몽구스의 schemaType에서 "진정한 독특한":과 : 속성, "진정한 인덱스"를 사용

var schema = mongoose.Schema ({ 
    myField: { type: String, index: true, unique: true, required: true }, 
}); 

이 필요한 것입니다 : Mongodb - Isolate sequence of operations - Perform Two Phase Commits. 그러나 많은 거래를해야하는 경우 mongodb을 선택하는 것이 최선이 될 수 없다는 점을 고려하십시오.

+0

답이 맞습니다. 좋은 습관을 언급합니다. 그러나, 내가 생각하기 - 생각하기 - 생각하기 - 기억해 - SAVE-SAVE (문서 찾기, 무언가 찾기, 다른 문서 찾기, 무언가 실행, 둘 다를 저장). 여기 findAndModify를 사용할 수없는 것 같습니다. 색인 생성을 사용할 수 없다고 생각합니다. Mongo가 두 번째 세이브에서 에러를 던지면 나는 첫 번째 세이브를 롤백 할 수 있습니다. 그렇다면 무엇을합니까? 어떻게 함수를 다시 호출하려고합니까? 따라서 색인을 생성하더라도 문제는 여전히 존재합니다. – igorpavlov

+0

다음 웹 페이지 확인 : http://docs.mongodb.org/manual/tutorial/isolate-sequence-of-operations/. 2 단계 커밋을 수행하십시오. –

+0

마지막으로, 여기서 내가 필요한 것. 고맙습니다. Redis-MongoDB 하이브리드를 사용할 것입니다. Redis는 비 차단이기 때문에 어쨌든, 당신은 매우 도움이됩니다. 그래서 나는 Redis를 위해 multi를 사용하고 Mongo를 위해 고립을 사용할 것이다. – igorpavlov

0

당신은 RAM을 낭비하고 싶지, 그래서 그것과는 별도로

delete lock_function_for_user[user_id] 

lock_function_for_user[user_id] = false 

를 대체하지 않습니다 당신은 just be optimistic and retry if a conflict happens 수 있습니다. 잠금을 해제하고 물건이 잘못되었을 때 DB가 알아 ​​차릴 수 있는지 확인하십시오 (이 경우 다시 시도하십시오). 물론 이러한 충돌이 실제로 얼마나 자주 발생하는지에 따라 어떤 방법이 더 나은지 알 수 있습니다.

+0

감사합니다. 물론 나는 delete를 사용할 것이고 이것은 매우 좋은 지적이다. 그러나, 내가 언급 한 아래의 대답에서 DB가 이상한 것을 발견하고 오류를 처리하면 함수를 다시 시작하는 방법을 알 수 없습니다. 나는 사용자 요청을 잃고 싶지 않다. 내 목표는 단지 ​​쓸데없는 글에서 데이터베이스를 보존하는 것이 아니라 사용자 요청을 보존하고 다시 시도하는 것이다. 그건 질문입니다 : "재시도하는 법?". 그리고 우리가 확장 할 때,이 오류는 꽤 자주 발생할 수 있다고 가정 해 봅시다. – igorpavlov

+0

@igorpavlov 이런 무늬를 사용하는 것은 어떻습니까? 'performChange (document_id, function (document) {...; returns changedDocument;})'그러면 performChange는 오류가 발생하면 다시 자신을 호출 할 수 있습니다. 기본적으로 새 함수에 동일한 변경 사항을 적용하여 재 시도 할 수 있어야하므로 기존 문서를 인수로 사용하여 새 함수를 반환하는 JS 함수로 변경 사항을 표현할 수 있어야합니다. – thejh

+0

함수 self를 호출하면 방대한 양의 DB 쿼리가 발생할 수 있습니다. 이전 함수 호출의 쿼리가 끝날 때 : 10ms, 1000ms? 거대한 혼란을 일으킬 수 있습니다.BTW, .save (err, saved_document)와 동일하게 보입니다. {/ * 저장된 문서로 메모하기 */ – igorpavlov