ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [SW 정글 108일차] 나만의 무기 16일차 (Error: read ECONNRESET)
    기타/SW 사관학교 정글 2021. 11. 19. 00:52
    더보기

    오늘은 오후 12시에 하루를 시작했다.

    이유는 오늘 오후 1시 30분에 3차 발표(시작 발표를 1차라고 생각하면 3번째)가 예정되어있었고 그에 맞추어 우리가 무엇을 하려는지 어느정도의 결과물을 보여줘야해서 오전 6시를 넘기면서 개발을 하고 잠에 들러 갔기 때문이다.

    6시간 정도 잤지만 그래도 몸이 피곤했다.

    일단은 일어나서 씻은 후에 점심을 먹고 발표에 참여하러 강의실에 나왔다.

    발표를 한 후 운영진님들에게 들은 피드백은 모든 팀들에게 해주는 공통적인 것과 발표 flow에 대한 것이였다.

    더 이상 프로젝트 기회에 대한 피드백은 들어오지 않았고 조금 더 디테일한 부분들을 얘기해주셨다.

     

    모든 팀의 발표를 끝마치고 한 주동안 어땠는지 팀원들과 KPT회의를 진행했다.

    KPT회의는 지난 주에 처음을 진행했었고 일주일에 한 번씩 하기로 팀과의 약속이 된 상태였다.

    팀원 전체적으로 한 주동안의 팀 프로젝트 진행에 대해 만족하는 모습을 보였고 나 또한 지난주에 비해 굉장히 만족도가 올라갔다.

    지난 주에는 개발을 위한 공부시간을 가지고 개발을 시작한 지 몇일 안되어 KPT를 진행한 것이여서 프로젝트 완성에 대한 불안감과 좋은 결과물이 나올 수 있을지에 대한 불안감이 높았다.

    하지만, 한 주를 모든 팀원이 개발에 몰두하였고 그에 대한 결과물이 눈에 보이니까 앞으로의 방향이 흐트러지지 않고 명확하다면 좋은 결과물을 볼 수 있을 것같다는 생각이 들었다.

    KPT회의를 마치고서는 저녁을 먹은 후에 다시 개발을 하기 위해 컴퓨터 앞에 앉았다.

     

     

     

    내가 해결해야할 것은 다음과 같다.

    어제 저녁부터 사이트에 접속 시에 mysql connection 과정에서 error가 나오면서 서버가 종료되는 현상을 발견했는데 오늘 발표를 위한 데모를 준비하기 위해서 테스트하면서도 mysql connection 과정에서 error가 발생하여 데모가 안되면 어떡할까 걱정을 했지만 일단 데모는 잘 마무리 됐다.

    그리고 저녁을 먹고 조장님에게 error log를 받았다.

    Error: read ECONNRESET
        at TCP.onStreamRead (node:internal/stream_base_commons:220:20)
        --------------------
        at Protocol._enqueue (/Users/jahunseo/Documents/jungle/raising-alien-creatures/server/node_modules/mysql/lib/protocol/Protocol.js:144:48)
        at Connection.query (/Users/jahunseo/Documents/jungle/raising-alien-creatures/server/node_modules/mysql/lib/Connection.js:198:25)
        at /Users/jahunseo/Documents/jungle/raising-alien-creatures/server/routes/main.js:8:20
        at Layer.handle [as handle_request] (/Users/jahunseo/Documents/jungle/raising-alien-creatures/server/node_modules/express/lib/router/layer.js:95:5)
        at next (/Users/jahunseo/Documents/jungle/raising-alien-creatures/server/node_modules/express/lib/router/route.js:137:13)
        at Route.dispatch (/Users/jahunseo/Documents/jungle/raising-alien-creatures/server/node_modules/express/lib/router/route.js:112:3)
        at Layer.handle [as handle_request] (/Users/jahunseo/Documents/jungle/raising-alien-creatures/server/node_modules/express/lib/router/layer.js:95:5)
        at /Users/jahunseo/Documents/jungle/raising-alien-creatures/server/node_modules/express/lib/router/index.js:281:22
        at Function.process_params (/Users/jahunseo/Documents/jungle/raising-alien-creatures/server/node_modules/express/lib/router/index.js:335:12)
        at next (/Users/jahunseo/Documents/jungle/raising-alien-creatures/server/node_modules/express/lib/router/index.js:275:10) {
      errno: -54,
      code: 'ECONNRESET',
      syscall: 'read',
      fatal: true
    }

    일단은 에러문을 구글링했다.

    ECONNRESET는 TCP 연결의 반대쪽(AWS RDS)에서 갑자기 연결을 끊은 것이다.

    왜 갑자기 끊은 것일까...

    더 error log를 읽어보라고 해서 일단 아래 에러문을 구글링해봤다.

    TCP.onStreamRead (node:internal/stream_base_commons:220:20) -> 해결방법을 얻지는 못함..

     

    그래서 맨 아래 나온 errno: -54, code: 'ECONNRESET', syscall: 'read', fatal: true을 그대로 구글링했다.

    그리고 아래의 웹사이트에서 해결방법을 제시해주었다.

    https://stackoverflow.com/questions/62416274/econnreset-error-crashing-nodejs-application

    First of all, you should catch the error, so you app can handle it properly and doesn't crash when mysql connection is closed for any odd reason. Try either with connection.on('error', ...) or with try-catch blocks.

    For keeping an open connection, you should either reconnect on close. Or simply use mysql's pooling connection, which handles automatic reconnection very well, with a single code change.

    PS: Pooling multiple connections is a generally good idea for async apps, like servers, but it's safe to maintain a single connection via pooling (connectionLimit : 1) just for automatic reconnection itself.

    PPS: Mysql's inactivity timeout can be configured in server's my.cnf

    첫 번째는 error를 잡아서 서버가 죽지 않도록하라는 것인데 솔직히 정확히 어디서 error가 발생하는지 잘모르겠어서 이 방법을 사용하기 위해서는 error발생지점을 알아야한다.

    error 발생지점은 에러문에 다 나와있지만 내가 짠 코드인 main.js 이후의 지점들은 mysql npm 코드여서 해석하기 힘들다...

    그리고 main.js에서 conncection.query()는 error가 발생 시에 error를 log에 찍고 return해주어서 서버는 죽지않도록 해주었다.

     

     

    그래서 다음 방법인 pooling connection을 사용해보는 것이 나에게 좋을 것같다는 느낌을 받았다.

    mysql 모듈 공식문서를 봤는데 pooling connections를 지원해준다.

    하지만 그냥 '그래, pooling connections 사용하자'라고 넘어가기에는 '지금 코드에서 어느 지점이 문제여서 에러가 발생한 것이지?'라는 궁금증을 해결하고 싶었고 pooling connections를 도입해도 내가 마주한 에러를 확실히 잡을 수 있다는 보장도 안섰다.

    그리고 저 error가 모두 각자의 local로 서버를 켜서 localhost에 접속 시에 나는 것도 아니고 항상 나는 것도 아니여서 '왜 불규칙적으로 error가 발생할까?'에서 더 궁금증이 유발됐다.

     

     

     

    그래서 pooling connection을 설정하기 전에 더 찾아보며 생각해봤다.

    내 코드는 에러를 발생시키 않도록 짜진 것일까?

    일단은 api 코드가 담긴 main.js에서 쿼리를 던지는데 쿼리를 던지기 전에 명시적으로 connection.connect()를 해주지 않았고 쿼리가 끊나고 나서도 connection.end()를 해주지 않았다.

    이것이 문제가 될 수 있을까?

    일단은 connection.connect()이다.

    npm에서 제시하는 것은 아래처럼 코드를 짜는 것이다.

    https://www.npmjs.com/package/mysql

    connection.connect(function(err) {
      if (err) {
        console.error('error connecting: ' + err.stack);
        return;
      }
     
      console.log('connected as id ' + connection.threadId);
    });

    명시적으로 connection.connect()를 선언하는 것을 추천하지만 connection은 암묵적으로(implicitly) 아래처럼 쿼리를 던질 때 설립된다.

    connection.query('SELECT 1', function (error, results, fields) {
      if (error) throw error;
      // connected!
    });

    그래서 나는 connection.connect()를 명시적으로 선언하지 않았다.

    그래도 일단은 추천해준대로 코드를 리팩토링 해봐야겠다.

     

     

    그러면 다음으로 connection.end()이다.

    일단 connection.end()를 해주지 않은 이유는 어차피 우리는 계속 db 커넥션을 맺을 것이니까 end()로 끊는 것보다는 연결을 유지하게 놔두면 connection 과정(three handshake)의 지연시간(+end 시의 four handschake)이 줄어드니까 더 좋지 않을까 해서이다.

    (근거없는 생각...)

     

    다시 한 번 정확한 근거로 생각해보기 위해 connection.end()의 필요성을 검색해보았다.

    일반적으로 'Connection'이라 함은 DB에 접속 -> SQL 문을 날리고 -> 결과를 받은 후 -> 연결 종료의 플로우를 따른다고 한다. 결과를 받은 후 커넥션을 닫지 않으면 리소스를 불필요하게 낭비한다고 한다.

    같은 application에서 추후에 query를 날릴 때 connection이 재사용안되는 것인가?

    https://stackoverflow.com/questions/14087924/cannot-enqueue-handshake-after-invoking-quit

    그리고 위 stackoverflow 답변에서 connection.end()를 해주지 않는 것은 좋지 않은 방법이라고 한다.

    그래서 다수의 인원을 수용하기에는 pooling connection을 사용하는 것이 좋다고 한다.

     

     

    '그래, connection.end()를 명시해주지 않은 것은 좋지 않구나'생각은 들지만 근본적으로 문제를 해결하지는 못했다.

    connection.end()를 해주지 않아서 불규칙적으로 에러가 발생하는 것은 이상하다.

    모두가 에러를 마주쳐야하고 그러면 이전부터 계속 에러가 났어야하는데...

    무엇이 문제일까....

    일단은 pooling connection을 공부하고 리팩토링해야겠다.

    또 에러가 발생한다면 그 때 더 알아봐야겠다.

     


    <참고 자료>

    https://www.npmjs.com/package/mysql

    https://github.com/knex/knex/issues/2443

    https://stackoverflow.com/questions/17245881/how-do-i-debug-error-econnreset-in-node-js

    https://knight76.tistory.com/entry/%EC%9B%B9-%EC%84%9C%EB%B9%84%EC%8A%A4-HTTPS-%EC%97%B0%EA%B2%B0%EC%8B%9C-%EC%86%8C%EC%BC%93-%EB%81%8A%EA%B9%80-%ED%98%84%EC%83%81-%EB%B0%8F-%ED%95%B4%EA%B2%B0%EC%82%AC%EB%A1%80

    https://velog.io/@somm12/Nodejs-Error-read-ECONNRESET-at-TCP.onStreamRead

     

    댓글

Designed by Tistory.