본문 바로가기
Back Side/Module

[node-cron] 메모리에 저장되는 스케줄러를 Redis에 저장해서 영구보관하자

by developerBeluga 2024. 2. 2.
728x90
반응형

 

 

 

 

문제 발생

수강생이 강의 신청을 하면 강의일로부터 하루 전에 예약 메시지를 보내야 한다.

수강생이 많을수록 예약 메시지도 많아진다.

 

node-cron으로 스케줄러를 통해 강의일로부터 하루 전에 예약 메시지를 보내고 있었는데 애플리케이션 단에서 수정을 하게 됐다.

서버에서 도커로 돌아가던 애플리케이션을 다운시키고 이미지를 풀 받은 다음 다시 올렸다.

그러자 예약 메시지들이 오지 않는다.

왜일까?

 

그건 제목에도 적었듯이 node-cron이 메모리에 스케줄러를 저장시키기 때문이다.

즉, 도커를 다시 시작하자 메모리가 리셋되면서 스케줄러가 없어진거다 🤯🤯🤯

 

 

 

 

해결법으로 Redis에 저장하자

꼭 Redis가 아니어도 된다.

다른 데이터베이스를 사용해도 되는데 난 Redis를 사용했다.

 

그렇다면 Redis에 무엇을 저장해야할까?

복원할 때 필요한 스케줄러 시간과 스케줄러 코드 자체를 저장하면 된다고 생각했다.

 

틀렸다 ㅋ

스케줄러 코드 자체를 Redis에 저장하는 것은 보안과 용량에 문자가 있다.

 

정답은 스케줄러 코드가 돌아갈 수 있는 메타데이터들만 Redis에 저장하는 것이다.

그렇다면 코드는?

애플리케이션에 복원 코드를 따로 둬야 한다.

 

	// Redis 저장 하는 함수
    const setupAndSaveCronJob = (name, metadata, jobFuncion) => {
      // 스케줄러 작업 설정
      const job = cron.schedule(metadata.time, jobFuncion, {
        scheduled: true,
        timezone: 'Asia/Seoul',
      });

      // 스케줄러 설정을 Redis에 저장
      client.set(`testOne:${name}`, JSON.stringify(metadata), err => {
        if (err) {
          console.log(`Error saving job ${name} to Redis:`, err);
        } else {
          console.log(`Job ${name} saved to Redis`);
        }
      });

      return job;
    };

    // 스케줄러 작업 설정
    setupAndSaveCronJob(name, metadata, async () => {
 		(스케줄러 코드 작성)
		(복원 코드와 동일)
        
        // 스케줄러가 잘 되었을 경우 레디스에서 삭제
        client.del(`smsTimeOne:${this.param[3].value}`);
    });

코드를 이렇다. 

여기에서 신경 써줘야 하는 건 스케줄러가 잘 되었을 경우 레디스에서 삭제하는 것이다.

레디스에서 삭제를 안 할 경우 계속해서 살아있기 때문에 애플리케이션을 다시 시작할 때 문제가 발생할 수 있다.

 

메타데이터의 경우 복원 코드에서 쓰일 것을 생각해서 객체에 저장하면 된다.

그럼 인제 복원을 시켜보자.

 

 

 

복원시키자

const redis = require('redis');
export const client = redis.createClient({
  url: process.env.REDIS_URL,
  password: process.env.REDIS_PASSWORD,
});

client.connect();
client.on('connect', () => {
  console.log('Redis client connected');
});

client.on('error', err => {
  console.error('Redis client could not connect:', err);
  client.quit();
});

애플리케이션 최상단 파일(대체로 app.ts)에 Redis 기본 셋팅을 해준다.

 

client.on('ready', async () => {
  console.log('redils ready!');
  
  // Redis에서 스케줄링 정보 불러오기
  const keys = await client.keys('testOne:*');
  for (const key of keys) {
    const jobDataStr = await client.get(key);
    const jobData = JSON.parse(jobDataStr);
 
    // 스케줄러 작업
    cron.schedule(
      jobData.time,
      async () => {
        console.log('cron.schedule: ', jobData.time);
        
        (위에서 작성한 코드와 동일한 복원코드 작성)

        // 스케줄러 완료시 해당 데이터 삭제
        client.del(key);
      },
      {
        scheduled: true,
        timezone: 'Asia/Seoul',
      },
    );
  }

});

복원코드에선 Redis에 있는 모든 데이터를 동일한 key로(=textOne:*) 분기를 타서 반복문으로 돌아 스케줄러 작업을 다시 해준다.

이렇게 하면 날아간 스케줄러를 다시 똑같이 작업해줄 수 있다.

 

여기에서도 스케줄러 완료시 해당 Redis 데이터를 삭제 해야한다.

 

혹시 여기에서 복원해야하는 코드가 n개 있다면 함수화해서 예쁘게 작성하시길.

 

 

 

 

 

 

 

 

fin.

 

 

 

728x90
반응형

댓글