Add fallback mechanism for notification messages#57
Conversation
Test Results80 tests 80 ✅ 1s ⏱️ Results for commit 25d23ae. ♻️ This comment has been updated with latest results. |
There was a problem hiding this comment.
Pull request overview
This PR refactors notification message construction to explicitly represent fallback usage (particularly for CLOCK_OUT) and adds a dedicated business metric to observe fallback-based dispatches, while also removing the deprecated PUBLIC_HOLIDAY notification type.
Changes:
- Changed
NotificationMessageBuilder.buildMessageto return aNotificationMessageBuildResult(message + fallback metadata) and added fallback logging/reasons forCLOCK_OUT. - Added
moa.notification.dispatch.fallbackmetric inNotificationDispatchService, and updated metrics tests accordingly. - Removed
PUBLIC_HOLIDAYfromNotificationTypeand related sync logic, and added new unit tests for the message builder.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/test/kotlin/com/moa/service/notification/NotificationMessageBuilderTest.kt | New unit tests covering static messages and CLOCK_OUT fallback scenarios. |
| src/test/kotlin/com/moa/service/notification/NotificationDispatchServiceMetricsTest.kt | Updated to new builder return type and added fallback metric assertion. |
| src/main/kotlin/com/moa/service/notification/NotificationSyncService.kt | Removed PUBLIC_HOLIDAY branch from notification sync logic. |
| src/main/kotlin/com/moa/service/notification/NotificationMessageBuilder.kt | Implemented build result object, fallback handling, and logging for earnings failures/zero. |
| src/main/kotlin/com/moa/service/notification/NotificationDispatchService.kt | Added fallback counter metric and increments based on builder result. |
| src/main/kotlin/com/moa/entity/notification/NotificationType.kt | Removed deprecated PUBLIC_HOLIDAY enum value. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| val result = notificationMessageBuilder.buildMessage(notification, publicHolidays) | ||
| if (result.fallbackUsed) { | ||
| fallbackCounter(typeName, result.fallbackReason ?: REASON_UNKNOWN).increment() | ||
| } | ||
| val data = result.message.toData() |
| val earnings = try { | ||
| notificationEarningsService.calculateTodayEarnings( | ||
| notification.memberId, | ||
| notification.scheduledDate, | ||
| publicHolidays, | ||
| ) | ||
| } catch (e: Exception) { | ||
| log.error( | ||
| "Fallback message used — earnings calculation failed for notification {}, member {}", | ||
| notification.id, | ||
| notification.memberId, | ||
| e, | ||
| ) | ||
| return fallbackResult(notification, REASON_EARNINGS_ERROR) | ||
| } |
| data class NotificationMessageBuildResult( | ||
| val message: NotificationMessage, | ||
| val fallbackUsed: Boolean, | ||
| val fallbackReason: String? = null, | ||
| ) |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.
Comments suppressed due to low confidence (1)
src/main/kotlin/com/moa/service/notification/NotificationDispatchService.kt:92
- 여기서
buildMessage()가InterruptedException을 던지면catch (e: Exception)에 잡혀서 FAILED 처리 후 계속 진행됩니다(인터럽트는 복원되더라도 중단되지 않음). 인터럽트는 종료/취소 신호이므로catch (e: InterruptedException)을 먼저 두고Thread.currentThread().interrupt()후 그대로 전파(또는 즉시 루프/메서드 종료)하도록 분리해 주세요.
val data = result.message.toData()
tokens.forEach { dispatchItems.add(DispatchItem(notification, it.token, data)) }
} catch (e: Exception) {
notification.status = NotificationStatus.FAILED
failedCounter(typeName, REASON_BUILD).increment()
| if (earnings == BigDecimal.ZERO) { | ||
| return CLOCK_OUT_FALLBACK_BODY | ||
| log.warn( | ||
| "Fallback message used — zero earnings for notification {}, member {}", | ||
| notification.id, | ||
| notification.memberId, |
| private fun messageFallbackCounter(type: String, reason: String): Counter = Counter.builder(METRIC_MESSAGE_FALLBACK) | ||
| .description("메시지 빌드 시 fallback으로 대체된 알림 수 (FCM 발송 성공 여부와 별개)") | ||
| .tag("notification_type", type) | ||
| .tag("reason", reason) | ||
| .register(meterRegistry) | ||
|
|
| CLOCK_OUT("퇴근 했어요!", "오늘 ₩%s 벌었어요."), | ||
| PAYDAY("오늘은 월급날이에요!", "한 달 동안 고생 많으셨어요."), | ||
| PUBLIC_HOLIDAY("오늘은 공휴일이에요!", "오늘 하루 푹 쉬세요."); | ||
| PAYDAY("오늘은 월급날이에요!", "한 달 동안 고생 많으셨어요."); |
This pull request refactors the notification message building and dispatch logic to improve reliability and observability, especially around fallback messages for the
CLOCK_OUTnotification type. It introduces structured handling of fallback scenarios, adds new business metrics, and updates tests accordingly.Notification message building and fallback handling:
NotificationMessageBuilderso thatbuildMessagenow returns aNotificationMessageBuildResultobject, which indicates whether a fallback message was used and provides a reason for the fallback. Fallbacks are triggered if earnings calculation fails or results in zero, and appropriate log messages are recorded. ([[1]](https://github.com/Nexters/moa-server/pull/57/files#diff-750c9487eb0c7a589d052a95259d29a7e0f82d1c006b3329d2e8966d8348d631R17-R90),[[2]](https://github.com/Nexters/moa-server/pull/57/files#diff-750c9487eb0c7a589d052a95259d29a7e0f82d1c006b3329d2e8966d8348d631R101-R106))PUBLIC_HOLIDAYnotification type, as it is no longer supported. ([[1]](https://github.com/Nexters/moa-server/pull/57/files#diff-76f94a9e8f0d1e27f14fe9aa624ecc3a176fd8c0e644f50c429cba8c038a3915L9-R9),[[2]](https://github.com/Nexters/moa-server/pull/57/files#diff-a4471b363cfe710951bc6d28225422f05ad7ecee6a6c66333f556309c40278f7L68))Business metrics and observability:
moa.notification.dispatch.fallbackto count the number of notifications sent using fallback messages, tagged by notification type and fallback reason. ([[1]](https://github.com/Nexters/moa-server/pull/57/files#diff-71afcf80ed94a794e6ae8f9c091fc7f7a9dcbb67f0c91834fea11d0ec298958aR41-R46),[[2]](https://github.com/Nexters/moa-server/pull/57/files#diff-71afcf80ed94a794e6ae8f9c091fc7f7a9dcbb67f0c91834fea11d0ec298958aL76-R86),[[3]](https://github.com/Nexters/moa-server/pull/57/files#diff-71afcf80ed94a794e6ae8f9c091fc7f7a9dcbb67f0c91834fea11d0ec298958aR118-R122))Testing improvements:
NotificationMessageBuilderTestfor comprehensive coverage of message building scenarios. ([[1]](https://github.com/Nexters/moa-server/pull/57/files#diff-636c19d5e8b613173bf8e5bc9f996fea880c093fb89823b0decbb742332158e6L66-R65),[[2]](https://github.com/Nexters/moa-server/pull/57/files#diff-636c19d5e8b613173bf8e5bc9f996fea880c093fb89823b0decbb742332158e6R113-R136),[[3]](https://github.com/Nexters/moa-server/pull/57/files#diff-9db9a528eb9f839e5706e1d91352d15a922ed372dd7ea965d451c9e0e65b2605R1-R73))Other updates:
NotificationMessageBuilderto aid debugging and monitoring. ([src/main/kotlin/com/moa/service/notification/NotificationMessageBuilder.ktR17-R90](https://github.com/Nexters/moa-server/pull/57/files#diff-750c9487eb0c7a589d052a95259d29a7e0f82d1c006b3329d2e8966d8348d631R17-R90))These changes increase the robustness of notification dispatching by ensuring that fallback scenarios are handled transparently and are observable through metrics and logs.