11import { Injectable , Logger } from '@nestjs/common' ;
22import { Cron , CronExpression } from '@nestjs/schedule' ;
33import { RedisService } from '../redis/redis.service' ;
4- import { PageService } from '../page/page.service' ;
4+ import { DataSource } from 'typeorm' ;
5+ import { InjectDataSource } from '@nestjs/typeorm' ;
6+ import { Page } from 'src/page/page.entity' ;
57@Injectable ( )
68export class TasksService {
79 private readonly logger = new Logger ( TasksService . name ) ;
810 constructor (
911 private readonly redisService : RedisService ,
10- private readonly pageService : PageService ,
12+ @ InjectDataSource ( ) private readonly dataSource : DataSource ,
1113 ) { }
1214
1315 @Cron ( CronExpression . EVERY_10_SECONDS )
@@ -16,78 +18,69 @@ export class TasksService {
1618 // 시작 시간
1719 const startTime = performance . now ( ) ;
1820 const keys = await this . redisService . getAllKeys ( 'page:*' ) ;
19- Promise . all (
20- keys . map ( async ( key ) => {
21- const { title, content } = await this . redisService . get ( key ) ;
21+ console . log ( keys ) ;
22+ Promise . allSettled (
23+ keys . map ( async ( key : string ) => {
24+ const redisData = await this . redisService . get ( key ) ;
25+ // 데이터 없으면 오류
26+ if ( ! redisData ) {
27+ throw new Error ( `redis에 ${ key } 에 해당하는 데이터가 없습니다.` ) ;
28+ }
29+
30+ const { title, content } = redisData ;
31+
32+ const updateData : Partial < { title : string ; content : any } > = { } ;
33+
34+ if ( title ) updateData . title = title ;
35+ if ( content ) updateData . content = JSON . parse ( content ) ;
2236 const pageId = parseInt ( key . split ( ':' ) [ 1 ] ) ;
23- if ( title === undefined ) {
24- const jsonContent = JSON . parse ( content ) ;
25- await this . pageService . updatePage ( pageId , {
26- content : jsonContent ,
27- } ) ;
28- } else if ( content === undefined ) {
29- await this . pageService . updatePage ( pageId , {
30- title,
31- } ) ;
32- } else {
33- const jsonContent = JSON . parse ( content ) ;
34- await this . pageService . updatePage ( pageId , {
35- title,
36- content : jsonContent ,
37- } ) ;
37+ // title과 content 중 적어도 하나가 있을 때 업데이트 실행
38+ if ( Object . keys ( updateData ) . length > 0 ) {
39+ // 트랜잭션 시작
40+ const queryRunner = this . dataSource . createQueryRunner ( ) ;
41+ try {
42+ await queryRunner . startTransaction ( ) ;
43+
44+ // 갱신 시작
45+ const pageRepository = queryRunner . manager . getRepository ( Page ) ;
46+ await pageRepository . update ( pageId , updateData ) ;
47+
48+ // redis에서 데이터 삭제
49+ await this . redisService . delete ( key ) ;
50+
51+ // 트랜잭션 커밋
52+ await queryRunner . commitTransaction ( ) ;
53+ } catch ( err ) {
54+ // 실패하면 postgres는 roll back하고 redis의 값을 살린다.
55+ this . logger . error ( err . stack ) ;
56+ await queryRunner . rollbackTransaction ( ) ;
57+ title && ( await this . redisService . setField ( key , 'title' , title ) ) ;
58+ content &&
59+ ( await this . redisService . setField ( key , 'content' , content ) ) ;
60+
61+ // Promise.all에서 실패를 인식하기 위해 에러를 던진다.
62+ throw err ;
63+ } finally {
64+ // 리소스 정리
65+ await queryRunner . release ( ) ;
66+ }
3867 }
39- await this . redisService . delete ( key ) ;
4068 } ) ,
4169 )
42- . then ( ( ) => {
70+ . then ( ( results ) => {
4371 const endTime = performance . now ( ) ;
44- this . logger . log ( `갱신 개수 : ${ keys . length } 개` ) ;
72+ this . logger . log ( `총 개수 : ${ results . length } 개` ) ;
73+ console . log ( results ) ;
74+ this . logger . log (
75+ `성공 개수 : ${ results . filter ( ( result ) => result . status === 'fulfilled' ) . length } 개` ,
76+ ) ;
77+ this . logger . log (
78+ `실패 개수 : ${ results . filter ( ( result ) => result . status === 'rejected' ) . length } 개` ,
79+ ) ;
4580 this . logger . log ( `실행 시간 : ${ ( endTime - startTime ) / 1000 } 초` ) ;
4681 } )
4782 . catch ( ( err ) => {
4883 this . logger . error ( err ) ;
4984 } ) ;
50- // scan stream을 가져온다.
51- // const stream = this.redisService.createStream();
52- // let totalCount = 0;
53- // stream.on('data', (keys) => {
54- // console.log(keys);
55- // totalCount += keys.length;
56- // stream.pause();
57- // Promise.all(
58- // keys.map(async (key) => {
59- // const { title, content } = await this.redisService.get(key);
60- // if (title === null) {
61- // const jsonContent = JSON.parse(content);
62- // await this.pageService.updatePage(parseInt(key), {
63- // content: jsonContent,
64- // });
65- // } else if (content === null) {
66- // await this.pageService.updatePage(parseInt(key), {
67- // title,
68- // });
69- // } else {
70- // const jsonContent = JSON.parse(content);
71- // await this.pageService.updatePage(parseInt(key), {
72- // title,
73- // content: jsonContent,
74- // });
75- // await this.redisService.delete(key);
76- // }
77- // }),
78- // )
79- // .then(() => {
80- // stream.resume();
81- // })
82- // .catch((err) => {
83- // this.logger.error(err);
84- // stream.resume();
85- // });
86- // });
87- // stream.on('end', () => {
88- // const endTime = performance.now();
89- // this.logger.log(`갱신 개수 : ${totalCount}개`);
90- // this.logger.log(`실행 시간 : ${(endTime - startTime) / 1000}초`);
91- // });
9285 }
9386}
0 commit comments