Skip to content

Add fallback mechanism for notification messages#57

Merged
subsub97 merged 2 commits into
mainfrom
feat/notification-fallback
May 27, 2026
Merged

Add fallback mechanism for notification messages#57
subsub97 merged 2 commits into
mainfrom
feat/notification-fallback

Conversation

@subsub97

Copy link
Copy Markdown
Collaborator

This pull request refactors the notification message building and dispatch logic to improve reliability and observability, especially around fallback messages for the CLOCK_OUT notification type. It introduces structured handling of fallback scenarios, adds new business metrics, and updates tests accordingly.

Notification message building and fallback handling:

  • Refactored NotificationMessageBuilder so that buildMessage now returns a NotificationMessageBuildResult object, 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))
  • Removed handling and references to the PUBLIC_HOLIDAY notification 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:

  • Added a new metric moa.notification.dispatch.fallback to 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:

  • Updated and added unit tests to verify the new fallback logic and metric counting, including a new test class NotificationMessageBuilderTest for 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:

  • Improved logging for fallback scenarios in NotificationMessageBuilder to 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.

Copilot AI review requested due to automatic review settings May 27, 2026 12:34
@github-actions

github-actions Bot commented May 27, 2026

Copy link
Copy Markdown

Test Results

80 tests   80 ✅  1s ⏱️
12 suites   0 💤
12 files     0 ❌

Results for commit 25d23ae.

♻️ This comment has been updated with latest results.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.buildMessage to return a NotificationMessageBuildResult (message + fallback metadata) and added fallback logging/reasons for CLOCK_OUT.
  • Added moa.notification.dispatch.fallback metric in NotificationDispatchService, and updated metrics tests accordingly.
  • Removed PUBLIC_HOLIDAY from NotificationType and 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.

Comment on lines +82 to +86
val result = notificationMessageBuilder.buildMessage(notification, publicHolidays)
if (result.fallbackUsed) {
fallbackCounter(typeName, result.fallbackReason ?: REASON_UNKNOWN).increment()
}
val data = result.message.toData()
Comment on lines +39 to +53
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)
}
Comment on lines +102 to +106
data class NotificationMessageBuildResult(
val message: NotificationMessage,
val fallbackUsed: Boolean,
val fallbackReason: String? = null,
)
@subsub97 subsub97 requested a review from Copilot May 27, 2026 13:08
@subsub97 subsub97 merged commit ad6cc2f into main May 27, 2026
4 checks passed
@subsub97 subsub97 deleted the feat/notification-fallback branch May 27, 2026 13:14

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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()

Comment on lines 57 to +61
if (earnings == BigDecimal.ZERO) {
return CLOCK_OUT_FALLBACK_BODY
log.warn(
"Fallback message used — zero earnings for notification {}, member {}",
notification.id,
notification.memberId,
Comment on lines +41 to +46
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("오늘은 월급날이에요!", "한 달 동안 고생 많으셨어요.");
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants