Skip to content

Commit 6033a74

Browse files
committed
feat: redis -> postgres 영속화 트랜잭션 적용
1 parent cd85bce commit 6033a74

File tree

1 file changed

+58
-65
lines changed

1 file changed

+58
-65
lines changed
Lines changed: 58 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import { Injectable, Logger } from '@nestjs/common';
22
import { Cron, CronExpression } from '@nestjs/schedule';
33
import { 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()
68
export 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

Comments
 (0)