API 서버를 개발하고 배포 한 뒤에, 테스트를 진행하던 도중 아래와 같은 에러가 발생했다
Error: Cannot enqueue Query after fatal error.
at Protocol._validateEnqueue (/app/node_modules/mysql/lib/protocol/Protocol.js:212:16)
at Protocol._enqueue (/app/node_modules/mysql/lib/protocol/Protocol.js:138:13)
at Connection.query (/app/node_modules/mysql/lib/Connection.js:198:25)
at /app/models/receiveCardList.models.v1.js:604:14
at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
code: 'PROTOCOL_ENQUEUE_AFTER_FATAL_ERROR',
fatal: false
}
분명 지난주까지만 해도 발생하지 않던 에러였는데,
월요일에 출근 후 테스트를 하던 중 에러가 발생했다 !!!
로컬로 돌려보니 로컬에는 이상이 없길래, 에러코드를 검색하고 확인하던 도중 또 정상 작동 하는것을 확인했다.
지금 당장은 실제 운영하는 서비스는 아니고 테스트 단계라서 문제가 없지만,
추후에 실제 운영하는 서비스에서 이런 에러가 나면 안될 것 같아 에러에 대해 확인 해 봤당
해당 문제가 발생한 원인을 chatGPT 에게 물어보니 다음과 같이 알려줬다.
PROTOCOL_ENQUEUE_AFTER_FATAL_ERROR 오류
는 주로 Node.js와 MySQL을 사용할 때 발생하는 에러입니다. 이 에러는 Node.js 애플리케이션이 MySQL 데이터베이스에 연결하려고 할 때, 이전에 발생한 치명적인 오류(fatal error) 이후에 다시 요청을 시도할 때 발생합니다.
MySQL 연결이 끊어진 경우
: MySQL 서버와의 연결이 끊어진 후에 쿼리를 실행하려고 할 때 발생합니다.치명적인 오류가 발생한 경우
: 이전 쿼리나 명령어 실행 중에 치명적인 오류가 발생했을 때, 이 오류 이후에 새로운 쿼리를 보내려고 하면 이 에러가 발생합니다.잘못된 커넥션 관리
: MySQL 커넥션 풀이나 연결을 잘못 관리하여, 이미 문제가 발생한 연결을 다시 사용하려고 할 때 발생할 수 있습니다.
내가 Mysql DB를 이용해서 연결을 하고 있는데,
커넥션 문제가 발생한 것 같다.
이전까지는 문제가 없었기에 관련 자료를 더 찾아봤는데, 문제가 될 수 있는 원인으로는
1. 많은 요청으로 인한 과부화
2. 커넥션이 이뤄진 후 일정 시간동안 쿼리가 발송되지 않아 커넥션 끊김
이렇게 두가지가 대표적인 것 같다~~
나는 위에서 말했듯이 주말동안 쿼리를 사용하지 않다가
그 다음주에 테스트를 하던 도중 오류가 발생했기 때문에 2번째 이유가 원인인 것 같았다.
위 원인에 대한 해결 방법을 찾아보니 두가지가 있었다.
1. 활동하지 않는 커넥션을 끊을때까지 서버가 대기하는 시간 늘리기
2. 무의미한 더미 쿼리를 계속 보내면서 자동으로 커넥션이 끊어지는 것을 방지하기
난 이중에서 2번째 방식을 사용했다! 왜냐면 이전 프로젝트에서 이미 이 방법을 사용하고 있어서~~
일단 스케줄러를 이용해 1분마다 쿼리를 요청해야 하므로 스케줄러를 설치 해 줬다
npm i node-schedule
위 명령어로 설치 해 주면 된다.
그 다음, schedule 폴더 생성 후 mysql 상태를 체크하는 스케줄러를 넣을 js 파일을 생성한다.
나는 mysqlConnChk.js
파일을 생성 해 줬다.
이제 해당 파일에 상태 체크 코드 넣기!
const cron = require('node-schedule');
const pool = require('../db/mysqlPool');
cron.scheduleJob('*/1 * * * *', async function() {
try {
const query = `SELECT 1`;
const client = await pool.acquire(); // await를 사용하여 클라이언트를 획득
try {
client.query(query, (err, result, fields) => {
if (err) {
console.error(err);
} else {
console.log('mysqlConnChk success!!');
}
});
} finally {
pool.release(client); // finally 블록에서 항상 클라이언트를 릴리즈
}
} catch (err) {
const errObj = {
error: err,
message: err.message
};
console.error(errObj);
}
});
스케줄러가 1분마다 작동하게 해줬고,
SELECT 1
이라는 database와 관련 없는 무의미한 더미 쿼리를 보내도록 구현 해 뒀다.
그리고 스케줄러가 계속 작동 할 수 있도록 app.js 에 스케줄러 작동 코드를 넣어주면 된다
끝~