2017-02-14 6 views
0

'firebase-admin'SDK 버전 4.1.0을 사용하는 노드 js에서 데이터베이스의 메시지 대기열 참조를 수신하고 메시지를 처리 ​​한 후 수신 대기하는 수신기가 있습니다. 큐 참조에서 제거하려고합니다.현재 참조를 수정하면 최대 스택 크기가 충돌을 초과합니다.

스크립트를 시작하기 전에 대기열에있는 특정 수의 레코드 (내 컴퓨터에서 1354 개)보다 큰 레코드가있는 경우 스크립트는 최대 호출 스택 초과 오류로 인해 충돌합니다.

이상한 점은 스크립트 시작 전에 대기열에 1354+ 값이있을 때만 발생한다는 것입니다. 이보다 낮 으면 문제가 사라집니다.

왜 이런 일이 발생하는지 모르지만 스냅 숏 참조에서 개체를 수정/제거하려고 할 때만 발생합니다. 여기

는 코멘트에 표시된 문제 영역과 독립적 인 mcve입니다 :

var admin = require("firebase-admin"); 

var serviceAccount = require("<ADMIN JSON FILE PATH GOES HERE>"); 

admin.initializeApp({ 
    credential: admin.credential.cert(serviceAccount), 
    databaseURL: "<FIREBASE URL GOES HERE>" 
}); 

var ref = admin.database().ref(); 

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 

// the number of messages to generate for the queue. when this is >= 1354 (on my machine) the program crashes, if it's less than that, 
// it works perfectly fine; your tipping point may vary 
var amount = 1354; 
// message payload to deliver to the queue <amount> times 
var payload = {}; 

// message generation loop 
for (i = 0; i < amount; i++) { 
    var message = {msg: "hello"}; 
    payload['message-queue/' + ref.push().key] = message; 
} 

// add the generated messages simultaneously to message-queue 
ref.update(payload).then(function() { 

    // 'on child added' listener that causes the crash of the program when there are 1354+ pre-existing messages in the queue prior to application start 
    ref.child('message-queue').on('child_added', function(snapshot) { 

     var msgKey = snapshot.key; 
     var msgContents = snapshot.val().msg 

     // do something with msgContents (e.g. sanitize message and deliver to some user's message-received node in the firebase) 

     // ***THIS*** is what causes the crash. if you remove this line of code, the program does not crash. it seems that any 
     // modification/removal to/of the current <msgKey> node does the same 
     ref.child('message-queue').child(msgKey).remove(); 
    }); 
}); 

을 그리고 여기 충돌의 스택 추적입니다 : 당신이하지 비록

FIREBASE WARNING: Exception was thrown by user callback. RangeError: Maximum call stack size exceeded 

    at RegExp.exec (native) 
    at RegExp.test (native) 
    at tc (<MY_PROJECT_PATH>\node_modules\firebase-admin\lib\database\database.js:63:86) 
    at ub (<MY_PROJECT_PATH>\node_modules\firebase-admin\lib\database\database.js:60:136) 
    at vb (<MY_PROJECT_PATH>\node_modules\firebase-admin\lib\database\database.js:43:1228) 
    at Xb.h.remove (<MY_PROJECT_PATH>\node_modules\firebase-admin\lib\database\database.js:52:44) 
    at Xb.h.remove (<MY_PROJECT_PATH>\node_modules\firebase-admin\lib\database\database.js:52:136) 
    at Xb.h.remove (<MY_PROJECT_PATH>\node_modules\firebase-admin\lib\database\database.js:52:136) 
    at Xb.h.remove (<MY_PROJECT_PATH>\node_modules\firebase-admin\lib\database\database.js:52:136) 
    at Xb.h.remove (<MY_PROJECT_PATH>\node_modules\firebase-admin\lib\database\database.js:52:136) 

<MY_PROJECT_PATH>\node_modules\firebase-admin\lib\database\database.js:63 
(d="0"+d),c+=d;return c.toLowerCase()}var zc=/^-?\d{1,10}$/;function tc(a){retur 
n zc.test(a)&&(a=Number(a),-2147483648<=a&&2147483647>=a)?a:null}function Ac(a){ 
try{a()}catch(b){setTimeout(function(){N("Exception was thrown by user callback. 
",b.stack||"");throw b;},Math.floor(0))}}function Bc(a,b,c){Object.definePropert 
y(a,b,{get:c})}function Cc(a,b){var c=setTimeout(a,b);"object"===typeof c&&c.unr 
ef&&c.unref();return c};function Dc(a){var b={},c={},d={},e="";try{var f=a.split 
("."),b=bb(hc(f[0])||""),c=bb(hc(f[1])||""),e=f[2],d=c.d||{};delete c.d}catch(g) 
{}return{wg:b,Ge:c,data:d,mg:e}}function Ec(a){a=Dc(a);var b=a.Ge;return!!a.mg&& 
!!b&&"object"===typeof b&&b.hasOwnProperty("iat")}function Fc(a){a=Dc(a).Ge;retu 
rn"object"===typeof a&&!0===y(a,"admin")};function Gc(a,b,c){this.type=Hc;this.s 
ource=a;this.path=b;this.children=c}Gc.prototype.Jc=function(a){if(this.path.e() 
)return a=this.children.sub 

RangeError: Maximum call stack size exceeded 
    at RegExp.exec (native) 
    at RegExp.test (native) 
    at tc (<MY_PROJECT_PATH>\node_modules\firebase-admin\lib\database\database.js:63:86) 
    at ub (<MY_PROJECT_PATH>\node_modules\firebase-admin\lib\database\database.js:60:136) 
    at vb (<MY_PROJECT_PATH>\node_modules\firebase-admin\lib\database\database.js:43:1228) 
    at Xb.h.remove (<MY_PROJECT_PATH>\node_modules\firebase-admin\lib\database\database.js:52:44) 
    at Xb.h.remove (<MY_PROJECT_PATH>\node_modules\firebase-admin\lib\database\database.js:52:136) 
    at Xb.h.remove (<MY_PROJECT_PATH>\node_modules\firebase-admin\lib\database\database.js:52:136) 
    at Xb.h.remove (<MY_PROJECT_PATH>\node_modules\firebase-admin\lib\database\database.js:52:136) 
    at Xb.h.remove (<MY_PROJECT_PATH>\node_modules\firebase-admin\lib\database\database.js:52:136) 

<MY_PROJECT_PATH>> 

<MY_PROJECT_PATH>> 

<MY_PROJECT_PATH>> 

답변

1

그것을 처리 할 때 remove()에 대한 호출은 여전히 ​​async/promise 기반이고 실행 컨텍스트를 생성합니다. 약속 컨텍스트는 상당히 크며 여기에 스택이 부족한 것은 놀랄만 한 것이 아닙니다. 이와 같은 패턴이 실제로 필요하다면 업데이트를 일괄 적으로 처리 할 수 ​​있습니다 - child_added에 값을 "삭제 예정"배열에 삽입 한 다음 그 배열을 한 번에 별도의 작업으로 배열을 처리 할 때까지 처리하십시오 빈. 도움이 될 수있는 BlueBird (http://bluebirdjs.com/) 라이브러리의 배열 및 약속을 사용하기위한 도우미 메소드가 많이 있습니다 (예 : map/mapSeries).

이것은 실제로 Firebase 문제는 아닙니다. 다른 모든 VM (PHP, Java 등)도 처리 할 스택 크기 제한이 있습니다. 대부분의 다른 사람들과 마찬가지로, V8의는 조정하고, 필요하면, 당신은 다음과 같은 명령을 사용하여 쿼리 (및 조정) 할 수 있습니다

node --v8-options | grep -B0 -A1 stack_size 

그러나 나는 당신의 가장 좋은 방법은 당신의 스택 사용을 최소화하기 위해 프로그램을 구성하는 것입니다 생각 이 삭제 패턴. 스택 크기를 늘리면 항상 "지금 충분히 큰가요?"라는 질문을 할 수 있습니다. 문제.

0

스택에 할당 된 메모리를 최소화하십시오. 예를 들어 정적 배열 대신 동적 배열을 사용하는 것이 좋습니다.