11import { Injectable , Logger } from '@nestjs/common' ;
22import { Cron , CronExpression } from '@nestjs/schedule' ;
3- import { RedisService } from '../redis/redis.service' ;
3+ import {
4+ RedisEdge ,
5+ RedisPage ,
6+ RedisNode ,
7+ RedisService ,
8+ } from '../redis/redis.service' ;
49import { DataSource } from 'typeorm' ;
510import { InjectDataSource } from '@nestjs/typeorm' ;
6- import { Page } from 'src/page/page.entity' ;
11+ import { Page } from '../page/page.entity' ;
12+ import { Node } from '../node/node.entity' ;
13+ import { Edge } from '../edge/edge.entity' ;
714
815/**
916TODO Coordinate
@@ -58,33 +65,15 @@ export class TasksService {
5865 this . logger . log ( '스케줄러 시작' ) ;
5966 // 시작 시간
6067 const startTime = performance . now ( ) ;
61- const keys = await this . redisService . getAllKeys ( 'page:*' ) ;
62-
63- Promise . allSettled (
64- keys . map ( async ( key : string ) => {
65- const redisData = await this . redisService . get ( key ) ;
66- // 데이터 없으면 오류
67- if ( ! redisData ) {
68- throw new Error ( `redis에 ${ key } 에 해당하는 데이터가 없습니다.` ) ;
69- }
70-
71- const { title, content, emoji } = redisData ;
72-
73- const updateData : Partial < {
74- title : string ;
75- content : any ;
76- emoji : string ;
77- } > = { } ;
78-
79- if ( title ) updateData . title = title ;
80- if ( content ) updateData . content = JSON . parse ( content ) ;
81- if ( emoji ) updateData . emoji = emoji ;
82-
83- // 업데이트 대상이 없다면 리턴
84- if ( Object . keys ( updateData ) . length === 0 ) return ;
85- this . migrate ( key , updateData ) ;
86- } ) ,
87- )
68+ const pageKeys = await this . redisService . getAllKeys ( 'page:*' ) ;
69+ const nodeKeys = await this . redisService . getAllKeys ( 'node:*' ) ;
70+ // const edgeKeys = await this.redisService.getAllKeys('edge:*');
71+
72+ Promise . allSettled ( [
73+ ...pageKeys . map ( this . migratePage . bind ( this ) ) ,
74+ ...nodeKeys . map ( this . migrateNode . bind ( this ) ) ,
75+ // ...edgeKeys.map(this.migrateEdge.bind(this)),
76+ ] )
8877 . then ( ( results ) => {
8978 const endTime = performance . now ( ) ;
9079 this . logger . log ( `총 개수 : ${ results . length } 개` ) ;
@@ -101,10 +90,130 @@ export class TasksService {
10190 } ) ;
10291 }
10392
104- async migrate (
105- key : string ,
106- updateData : Partial < { title : string ; content : any ; emoji : string } > ,
107- ) {
93+ async migratePage ( key : string ) {
94+ const redisData = ( await this . redisService . get ( key ) ) as RedisPage ;
95+ // 데이터 없으면 오류
96+ if ( ! redisData ) {
97+ throw new Error ( `redis에 ${ key } 에 해당하는 데이터가 없습니다.` ) ;
98+ }
99+
100+ const { title, content, emoji } = redisData ;
101+
102+ const updateData : Partial < Page > = { } ;
103+
104+ if ( title ) updateData . title = title ;
105+ if ( content ) updateData . content = JSON . parse ( content ) ;
106+ if ( emoji ) updateData . emoji = emoji ;
107+
108+ // 업데이트 대상이 없다면 리턴
109+ if ( Object . keys ( updateData ) . length === 0 ) return ;
110+ const pageId = parseInt ( key . split ( ':' ) [ 1 ] ) ;
111+
112+ // 트랜잭션 시작
113+ const queryRunner = this . dataSource . createQueryRunner ( ) ;
114+ try {
115+ await queryRunner . startTransaction ( ) ;
116+
117+ // 갱신 시작
118+ const pageRepository = queryRunner . manager . getRepository ( Page ) ;
119+
120+ // TODO : 페이지가 없으면 affect : 0을 반환하는데 이 부분 처리도 하는 게 좋을 듯...?
121+ await pageRepository . update ( pageId , updateData ) ;
122+
123+ // redis에서 데이터 삭제
124+ await this . redisService . delete ( key ) ;
125+
126+ // 트랜잭션 커밋
127+ await queryRunner . commitTransaction ( ) ;
128+ } catch ( err ) {
129+ // 실패하면 postgres는 roll back하고 redis의 값을 살린다.
130+ this . logger . error ( err . stack ) ;
131+ await queryRunner . rollbackTransaction ( ) ;
132+ updateData . title &&
133+ ( await this . redisService . setField ( key , 'title' , updateData . title ) ) ;
134+ updateData . content &&
135+ ( await this . redisService . setField ( key , 'content' , JSON . parse ( content ) ) ) ;
136+ updateData . emoji &&
137+ ( await this . redisService . setField ( key , 'emoji' , updateData . emoji ) ) ;
138+
139+ // Promise.all에서 실패를 인식하기 위해 에러를 던진다.
140+ throw err ;
141+ } finally {
142+ // 리소스 정리
143+ await queryRunner . release ( ) ;
144+ }
145+ }
146+
147+ async migrateNode ( key : string ) {
148+ const redisData = ( await this . redisService . get (
149+ key ,
150+ ) ) as unknown as RedisNode ;
151+ // 데이터 없으면 오류
152+ if ( ! redisData ) {
153+ throw new Error ( `redis에 ${ key } 에 해당하는 데이터가 없습니다.` ) ;
154+ }
155+
156+ const updateData : Partial < Node > = {
157+ x : Number ( redisData . x ) ,
158+ y : Number ( redisData . y ) ,
159+ } ;
160+
161+ // 업데이트 대상이 없다면 리턴
162+ if ( Object . keys ( updateData ) . length === 0 ) return ;
163+ const nodeId = parseInt ( key . split ( ':' ) [ 1 ] ) ;
164+
165+ // 트랜잭션 시작
166+ const queryRunner = this . dataSource . createQueryRunner ( ) ;
167+ try {
168+ await queryRunner . startTransaction ( ) ;
169+
170+ // 갱신 시작
171+ const nodeRepository = queryRunner . manager . getRepository ( Node ) ;
172+
173+ // TODO : 페이지가 없으면 affect : 0을 반환하는데 이 부분 처리도 하는 게 좋을 듯...?
174+ await nodeRepository . update ( nodeId , updateData ) ;
175+
176+ // redis에서 데이터 삭제
177+ await this . redisService . delete ( key ) ;
178+
179+ // 트랜잭션 커밋
180+ await queryRunner . commitTransaction ( ) ;
181+ } catch ( err ) {
182+ // 실패하면 postgres는 roll back하고 redis의 값을 살린다.
183+ this . logger . error ( err . stack ) ;
184+ await queryRunner . rollbackTransaction ( ) ;
185+ await this . redisService . setField ( key , 'x' , updateData . x . toString ( ) ) ;
186+ await this . redisService . setField ( key , 'y' , updateData . y . toString ( ) ) ;
187+
188+ // Promise.all에서 실패를 인식하기 위해 에러를 던진다.
189+ throw err ;
190+ } finally {
191+ // 리소스 정리
192+ await queryRunner . release ( ) ;
193+ }
194+ }
195+
196+ async migrateEdge ( key : string ) {
197+ const redisData = await this . redisService . get ( key ) ;
198+ // 데이터 없으면 오류
199+ if ( ! redisData ) {
200+ throw new Error ( `redis에 ${ key } 에 해당하는 데이터가 없습니다.` ) ;
201+ }
202+
203+ const { title, content, emoji } = redisData ;
204+
205+ const updateData : Partial < {
206+ title : string ;
207+ content : any ;
208+ emoji : string ;
209+ } > = { } ;
210+
211+ if ( title ) updateData . title = title ;
212+ if ( content ) updateData . content = JSON . parse ( content ) ;
213+ if ( emoji ) updateData . emoji = emoji ;
214+
215+ // 업데이트 대상이 없다면 리턴
216+ if ( Object . keys ( updateData ) . length === 0 ) return ;
108217 const pageId = parseInt ( key . split ( ':' ) [ 1 ] ) ;
109218
110219 // 트랜잭션 시작
0 commit comments