Skip to content

Commit 668107a

Browse files
Tolerblancsummersummerwhyezcolin2
committed
feat: 크론잡을 통해 레디스를 컨슘하도록 구현
- 아직 엣지 해야함 Co-authored-by: Summer Min <[email protected]> Co-authored-by: ez <[email protected]>
1 parent 82fbe39 commit 668107a

File tree

1 file changed

+142
-33
lines changed

1 file changed

+142
-33
lines changed

apps/backend/src/tasks/tasks.service.ts

Lines changed: 142 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
import { Injectable, Logger } from '@nestjs/common';
22
import { 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';
49
import { DataSource } from 'typeorm';
510
import { 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
/**
916
TODO 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

Comments
 (0)