Record/Trubble Shooting

API 서버에 설치한 postgresql 서비스가 간헐적으로 죽는 문제 해결

범데이 2022. 8. 20. 12:06

1. 개요

며칠 전부터 내 AWS EC2서버의 postgresql 서비스가 아래와 같은 로그가 찍히면서 간헐적 종료되는 현상이 발생하였다.

 

무슨 문제로 종료되는지 찾기위해 이리저리 검색해보았지만.. 일부는 서버 리소스가 부족할 시에 발생한다고 하기도 하고, 아직은 근본적인 문제를 발견하진 못하였다.

 

 

현재 운영중인 게임앱의 랭킹정보 등 실제 데이터를 운용하고 있는 터라, 해당 문제의 해결이 필요하였다.

 

2. 해결방안1 (DB상태 확인 콘솔 앱 구현)

2.1 해결과정

 

첫번째 해결방안으로 아래와 같이 서버 상태 확인용 자체 콘솔 앱을 만들었다.

 

현재 나의 REST API서버에서 DB 연동을 위해 ORM(ORM이란? 포스팅참고) 으로 사용하는 sequelize 라이브러리에는 DB connection을 test하기위한 authenticate() 메서드를 제공한다. 

 

따라서 해당 메서드로 DB connection을 테스트하여, 성공 여부를 반환하여 앱에 표시해주는 역할을 구성해 주었다.

 

만일 DB 상태가 비정상이라면, "RESTART" 버튼을 클릭하여 postgresql서비스를 재실행하는 API를 구현하였다.

 

 

[dbManager.ts]

const { exec } = require("child_process");

...

export const restartPostgres = () => {
  return new Promise((resolve, reject) => {
    exec("sh ./restart_postgres.sh", (error: any, stdout: any, stderr: any) => {
    	...

      if (error !== null) {
        logger(`exec error: ${error}`);
        reject(error);
      } else {
        resolve(stdout);
      }
    });
  });
};

쉘 스크립트로 서비스를 재시작하게 하였으며, 쉘스트립트를 실행하기 위해선 NodeJS의 내장 API인 child_process모듈을 사용하였다.

 

 

[restart_postgres.sh]

 

 

 

 

2.2 결과

이로써 매일 AWS EC2서버에 접속하여 DB가 살았는지 죽었는지 확인한 후, DB를 재시작해줄 필요 없이 자체 제작 콘솔 앱을 켜서 DB상태를 확인하고 재실행 해줄 수 있게 되었다..

 

하지만 본인이 매번 DB상태를 일일히 확인할 수도 없는 노릇이고.. 구현해놓고 보니 차라리 이럴거라면 DB를 사용할때마다 체크하는 로직을 추가하면 되잖아?! 바로 해당 로직을 추가했다.

 

3. 해결방안2 (DB 상태 체크 로직 추가)

3.1 해결 과정

 

위에서 필요한건 이미 다 구현해놓았기 때문에 해결 과정이 복잡하지 않다.

구현된 DB재시작 메서드를 기존 DB를 사용하는곳에 끼워넣어 주었다.

 

 

아래는 실제 구현 코드의 일부를 발췌한 것이다.

 

[router.ts]

router.get("/", (req: any, res: any) => {
  myApp
    .getRecordList()
    .then((ret: any) => {
      res.json(ret);
    })
    .catch((err: any) => {
      if (err && err.code === cst.FailCode.DBError) {
        loggerErr("getRecordList() >> DBError! restart postgres...");
        dbManager
          .restartPostgres()
          .then(() => {
            myApp
              .getRecordList()
              .then((ret: any) => {
                res.json(ret);
              })
              .catch(() => {
                res.json(err);
              });
          })
          .catch(() => {
            res.json(err);
          });
      } else {
        res.json(err);
      }
    });
});

getRecordList()로 데이터를 조회하다가, DB에러가 발생하면 postgres를 restart한 후에 재 조회를 한다.

(재 조회까지 실패하면, DB에러라고 반환해주고, 만일 성공하면 성공한 데이터를 반환해준다.)

 

[myApp.ts]

export const getRecordList = () => {
  return new Promise((resolve, reject) => {
    console.log("getRecordList Start!!");

    db.tb_my_table
      .findAll({
      	...
      })
      .then((recordList: any) => {
        ...
      })
      .catch((reason: any) => {
        loggerErr("getRecordList() >> reason=", reason);
        res.success = false;
        res.code = cst.FailCode.DBError;
        res.message = reason;
        reject(res);
      });
  });
};

위는 getRecordList() 메서드의 일부이다.

tb_my_table에서 데이터를 조회하다가, 에러가 발생하면 DBError라고 반환해준다. 

 

3.2 결과

postgres 서비스가 죽어있는 상태에서 getRordList를 조회하는 API를 호출해 보았다.

 

아래 로그를 보면, getRecordList를 하다가 DB 에러가 나서 postgres를 restart한 후, 재 조회를 하여 정상적으로 데이터를 리턴하는 것을 확인할 수 있다.

 

 

그 후 postgres서비스의 상태를 확인해 보면, inactive (dead) 상태의 서비스가 active상태로 떠있음을 확인할 수 있다.

 

 

이제 일일히 콘솔 앱을 켜서 DB상태를 확인해준 후, 죽어있으면 RESTART해주는 작업은 하지 않아도 된다. ㅎㅎ

 

구현한 콘솔 앱은 앞으로 서버 상태가 정상적인지 간간히 확인하는 데에 사용할 것 같다.

 

 

 

 

 

궁금하신 점이나, 수정 및 보완할 점이 있다면 언제든지 댓글이나 방명록으로 남겨주세요! 감사합니다.

반응형