|
1 | 1 | import { Injectable, Logger } from '@nestjs/common'; |
2 | 2 | import { InjectRepository } from '@nestjs/typeorm'; |
3 | | -import { Repository } from 'typeorm'; |
| 3 | +import { MoreThanOrEqual, Repository } from 'typeorm'; |
4 | 4 | import { UserAchievement } from './entities/user-achievement.entity'; |
5 | 5 |
|
6 | 6 | /** |
@@ -62,25 +62,45 @@ export class AchievementsNotificationsService { |
62 | 62 |
|
63 | 63 | /** |
64 | 64 | * Send batch notifications for achievements unlocked today |
| 65 | + * Processes achievements in bounded chunks so a large backlog is not |
| 66 | + * loaded or sent in a single tight loop. |
65 | 67 | */ |
66 | 68 | async sendBatchNotifications(): Promise<number> { |
| 69 | + const BATCH_SIZE = 100; |
| 70 | + |
67 | 71 | try { |
68 | 72 | const today = new Date(); |
69 | 73 | today.setHours(0, 0, 0, 0); |
70 | 74 |
|
71 | | - const achievements = await this.userAchievementRepository.find({ |
72 | | - where: { |
73 | | - unlockedAt: new Date(), |
74 | | - notificationSent: false, |
75 | | - }, |
76 | | - relations: ['user', 'achievement'], |
77 | | - }); |
78 | | - |
79 | 75 | let sentCount = 0; |
80 | | - |
81 | | - for (const userAchievement of achievements) { |
82 | | - await this.sendAchievementUnlockedNotification(userAchievement); |
83 | | - sentCount++; |
| 76 | + let skip = 0; |
| 77 | + let keepProcessing = true; |
| 78 | + |
| 79 | + while (keepProcessing) { |
| 80 | + const achievements = await this.userAchievementRepository.find({ |
| 81 | + where: { |
| 82 | + unlockedAt: MoreThanOrEqual(today), |
| 83 | + notificationSent: false, |
| 84 | + }, |
| 85 | + relations: ['user', 'achievement'], |
| 86 | + take: BATCH_SIZE, |
| 87 | + skip, |
| 88 | + }); |
| 89 | + |
| 90 | + if (achievements.length === 0) { |
| 91 | + keepProcessing = false; |
| 92 | + continue; |
| 93 | + } |
| 94 | + |
| 95 | + for (const userAchievement of achievements) { |
| 96 | + await this.sendAchievementUnlockedNotification(userAchievement); |
| 97 | + sentCount++; |
| 98 | + } |
| 99 | + |
| 100 | + skip += achievements.length; |
| 101 | + |
| 102 | + // Stop when a chunk returns fewer than the batch size (no more pending). |
| 103 | + keepProcessing = achievements.length === BATCH_SIZE; |
84 | 104 | } |
85 | 105 |
|
86 | 106 | this.logger.log(`Sent ${sentCount} achievement notifications`); |
|
0 commit comments