Back Side/Module

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

developerBeluga 2024. 2. 2. 16:29
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
반응형