Skip to content

πŸ”€ :: (#880) 코루틴 μ·¨μ†Œ μ „νŒŒλ₯Ό μœ„ν•œ runcatching κ°œμ„ #892

Merged
uson1004 merged 11 commits intodevelopfrom
feature/880-코루틴-μ·¨μ†Œ-μ „νŒŒλ₯Ό-μœ„ν•œ-runcatching-κ°œμ„ 
Apr 13, 2026

Hidden character warning

The head ref may contain hidden characters: "feature/880-\ucf54\ub8e8\ud2f4-\ucde8\uc18c-\uc804\ud30c\ub97c-\uc704\ud55c-runcatching-\uac1c\uc120"
Merged

πŸ”€ :: (#880) 코루틴 μ·¨μ†Œ μ „νŒŒλ₯Ό μœ„ν•œ runcatching κ°œμ„ #892
uson1004 merged 11 commits intodevelopfrom
feature/880-코루틴-μ·¨μ†Œ-μ „νŒŒλ₯Ό-μœ„ν•œ-runcatching-κ°œμ„ 

Conversation

@uson1004
Copy link
Copy Markdown
Member

@uson1004 uson1004 commented Apr 9, 2026

κ°œμš”

μž‘μ—…μ‚¬ν•­

μΆ”κ°€ 둜 ν•  말

Summary by CodeRabbit

  • Refactor
    • Standardized cancellable exception handling across network, data, and repository layers by replacing legacy catch wrappers with a cancellable wrapper.
    • Made session-cache cleanup non-cancellable during sign-out/withdraw flows.
    • Updated background widget and datastore flows to handle coroutine cancellation more explicitly for more predictable behavior.
  • Bug Fixes
    • Improved cancellation propagation to prevent unintended swallowing of cancellation events.

@uson1004 uson1004 requested a review from ashxom April 9, 2026 16:32
@uson1004 uson1004 self-assigned this Apr 9, 2026
@uson1004 uson1004 added the performance μ„±λŠ₯을 κ°œμ„  ν•  경우 label Apr 9, 2026
@uson1004 uson1004 added the refactor μ½”λ“œ λ¦¬νŒ©ν† λ§ ν•  경우 label Apr 9, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 9, 2026

No actionable comments were generated in the recent review. πŸŽ‰

ℹ️ Recent review info
βš™οΈ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7258b2b2-49f6-4344-a6ef-1c582332d054

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between 0ff85aa and 29fe41a.

πŸ“’ Files selected for processing (1)
  • core/widget/src/main/kotlin/team/aliens/dms/android/core/widget/MealWorker.kt
🚧 Files skipped from review as they are similar to previous changes (1)
  • core/widget/src/main/kotlin/team/aliens/dms/android/core/widget/MealWorker.kt

πŸ“ Walkthrough

Walkthrough

Replaced suspend-run-catching helpers with a new cancellation-aware runCatchingCancellable, removed suspendRunCatching, migrated call sites across core, data, network, and feature layers, and added the shared-exception dependency; some paths were refactored to preserve NonCancellable cache-clearing behavior and explicit cancellation handling.

Changes

Cohort / File(s) Summary
New Utility
shared/exception/src/main/java/team/aliens/dms/android/shared/exception/util/RunCatchingCancellable.kt
Added suspend inline fun <T> runCatchingCancellable(...) that rethrows CancellationException and returns Result.failure for other exceptions.
Removed Utility
shared/exception/src/main/java/team/aliens/dms/android/shared/exception/util/SuspendRunCatching.kt
Removed suspendRunCatching helper.
Global replacements (network/data layers)
network/.../datasource/*, data/.../repository/*
Replaced suspendRunCatching/suspendRunCatching-like helpers and runCatching usages with runCatchingCancellable across many network/data datasource and repository implementations; some flows refactored to use fold or explicit try/catch to preserve cancellation semantics.
Core utilities & handlers
core/datastore/.../TransformHandler.kt, core/network/.../NetworkExceptionMapper.kt
Converted exception wrappers from runCatching to runCatchingCancellable and adjusted success/failure handling to avoid swallowing cancellations.
Workers / Widgets / Device
core/widget/.../MealWorker.kt, core/device/.../DeviceDataStoreDataSourceImpl.kt
Switched to runCatchingCancellable; refactored error handling to fold/try–catch and added explicit CancellationException handling with NonCancellable updates where applicable.
Session cache cleanup
data/.../auth/repository/AuthRepositoryImpl.kt, data/.../student/repository/StudentRepositoryImpl.kt
Extracted clearSessionCaches() and run it inside withContext(NonCancellable); wrapped calls with runCatchingCancellable.
Feature ViewModels
feature/.../FindIdViewModel.kt, .../OnboardingViewModel.kt, .../AdjustProfileViewModel.kt, .../EnterSchoolVerificationCodeViewModel.kt, .../EnterSchoolVerificationQuestionViewModel.kt
Replaced runCatching with runCatchingCancellable for repository/network calls; isolated extraction/handling in a few places to maintain previous semantics.
Build files
core/widget/build.gradle.kts, data/build.gradle.kts
Added dependency on shared exception module: implementation(project(ProjectPaths.Shared.EXCEPTION)).

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant VM as ViewModel
  participant Repo as Repository
  participant Run as runCatchingCancellable
  participant API as Network/Data

  VM->>Repo: call suspend operation
  Repo->>Run: execute block
  Run->>API: invoke network/database call
  API-->>Run: success / throws
  alt Success
    Run-->>Repo: Result.success(value)
    Repo-->>VM: return success result
  else Exception (non-cancellation)
    Run-->>Repo: Result.failure(error)
    Repo-->>VM: propagate failure / fold handling
  else CancellationException
    Run-->>API: rethrow CancellationException
    API-->>Repo: cancellation propagated
    Repo-->>VM: cancellation propagated
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested labels

setting

Suggested reviewers

  • parkuiery

Poem

🐰 I hop through code with nimble paws,
catching faults with gentle laws.
If coroutines sleep and run away,
I let cancellations hop on their way.
Other errors? I bundle and relay.

πŸš₯ Pre-merge checks | βœ… 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 1.61% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
βœ… Passed checks (2 passed)
Check name Status Explanation
Description Check βœ… Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check βœ… Passed The PR title describes a core improvement to error-handling utilities (enhancing runCatching for coroutine cancellation propagation), which directly aligns with the main changeset introducing runCatchingCancellable and refactoring exception handling across the codebase.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
πŸ“ Generate docstrings
  • Create stacked PR
  • Commit on current branch
πŸ§ͺ Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/880-코루틴-μ·¨μ†Œ-μ „νŒŒλ₯Ό-μœ„ν•œ-runcatching-κ°œμ„ 

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❀️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
feature/src/main/kotlin/team/aliens/dms/android/feature/signup/viewmodel/EnterSchoolVerificationQuestionViewModel.kt (1)

46-63: ⚠️ Potential issue | 🟑 Minor

Move loading reset to finally to handle cancellation safely.

After this migration, cancellation no longer goes through onFailure, so isLoading may remain true if the call is cancelled mid-flight.

Suggested fix
     internal fun onNextClick() {
         viewModelScope.launch {
             setState { it.copy(isLoading = true) }
-            runCatchingCancellable {
-                networkSchoolDataSource.examineSchoolVerificationQuestion(
-                    schoolId = UUID.fromString(currentSignUpData.schoolId),
-                    answer = uiState.value.schoolVerificationAnswer,
-                )
-            }.onSuccess {
-                setState { it.copy(isLoading = false) }
-                sendEffect(
-                    EnterSchoolVerificationQuestionSideEffect.MoveToEnterEmail(
-                        signUpData = currentSignUpData.copy(
-                            schoolAnswer = uiState.value.schoolVerificationAnswer,
-                        )
-                    )
-                )
-            }.onFailure {
-                setState { it.copy(isLoading = false) }
-                sendEffect(EnterSchoolVerificationQuestionSideEffect.ShowErrorSnackBar)
-            }
+            try {
+                runCatchingCancellable {
+                    networkSchoolDataSource.examineSchoolVerificationQuestion(
+                        schoolId = UUID.fromString(currentSignUpData.schoolId),
+                        answer = uiState.value.schoolVerificationAnswer,
+                    )
+                }.onSuccess {
+                    sendEffect(
+                        EnterSchoolVerificationQuestionSideEffect.MoveToEnterEmail(
+                            signUpData = currentSignUpData.copy(
+                                schoolAnswer = uiState.value.schoolVerificationAnswer,
+                            )
+                        )
+                    )
+                }.onFailure {
+                    sendEffect(EnterSchoolVerificationQuestionSideEffect.ShowErrorSnackBar)
+                }
+            } finally {
+                setState { it.copy(isLoading = false) }
+            }
         }
     }
πŸ€– Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/src/main/kotlin/team/aliens/dms/android/feature/signup/viewmodel/EnterSchoolVerificationQuestionViewModel.kt`
around lines 46 - 63, The isLoading flag reset belongs in a finally block so
cancellation (which no longer triggers onFailure) always clears loading; in
EnterSchoolVerificationQuestionViewModel wrap the runCatchingCancellable call in
a try { ... } catch { ... } finally { setState { it.copy(isLoading = false) } }
(keep success path sending
EnterSchoolVerificationQuestionSideEffect.MoveToEnterEmail and failure sending
ShowErrorSnackBar) and remove the duplicated setState { isLoading = false } from
the onSuccess/onFailure handlers so loading is cleared reliably even on
cancellation.
πŸ€– Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@core/widget/src/main/kotlin/team/aliens/dms/android/core/widget/MealWorker.kt`:
- Around line 69-84: The widget can remain stuck in MealInfo.Loading because
runCatchingCancellable rethrows CancellationException and onFailure won't run on
cancellation; modify MealWorker.kt to explicitly handle cancellation after
setting Loading by wrapping the fetch in a try/catch that catches
CancellationException, calls setWidgetState(glanceIds, MealInfo.Unavailable) (or
restores previous state) and then rethrows the CancellationException, and keep
the existing runCatchingCancellable block for other failures so non-cancellation
errors still hit onFailure.

In
`@data/src/main/kotlin/team/aliens/dms/android/data/meal/repository/MealRepositoryImpl.kt`:
- Around line 24-32: The fold in MealRepositoryImpl.kt currently maps any
non-CannotSaveMealException to a new CannotSaveMealException, which wrongly
wraps CannotFindMealException; change the onFailure handling for
updateMeal(date) so that if exception is CannotFindMealException you return
Result.failure(exception) (preserving the original exception), if it's
CannotSaveMealException return Result.failure(exception) as before, and only
wrap other unexpected exceptions as CannotSaveMealException(); reference the
updateMeal call and the exception types CannotFindMealException and
CannotSaveMealException when making this adjustment.

---

Outside diff comments:
In
`@feature/src/main/kotlin/team/aliens/dms/android/feature/signup/viewmodel/EnterSchoolVerificationQuestionViewModel.kt`:
- Around line 46-63: The isLoading flag reset belongs in a finally block so
cancellation (which no longer triggers onFailure) always clears loading; in
EnterSchoolVerificationQuestionViewModel wrap the runCatchingCancellable call in
a try { ... } catch { ... } finally { setState { it.copy(isLoading = false) } }
(keep success path sending
EnterSchoolVerificationQuestionSideEffect.MoveToEnterEmail and failure sending
ShowErrorSnackBar) and remove the duplicated setState { isLoading = false } from
the onSuccess/onFailure handlers so loading is cleared reliably even on
cancellation.
πŸͺ„ Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
βš™οΈ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5e468e34-fd14-44aa-8e15-e1cf4e87992c

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between 4c9a798 and d8800d3.

πŸ“’ Files selected for processing (26)
  • core/datastore/src/main/java/team/aliens/dms/android/core/datastore/util/TransformHandler.kt
  • core/device/src/main/kotlin/team/aliens/dms/android/core/device/datastore/DeviceDataStoreDataSourceImpl.kt
  • core/network/src/main/java/team/aliens/dms/android/core/network/util/NetworkExceptionMapper.kt
  • core/widget/build.gradle.kts
  • core/widget/src/main/kotlin/team/aliens/dms/android/core/widget/MealWorker.kt
  • data/build.gradle.kts
  • data/src/main/kotlin/team/aliens/dms/android/data/auth/repository/AuthRepositoryImpl.kt
  • data/src/main/kotlin/team/aliens/dms/android/data/meal/repository/MealRepositoryImpl.kt
  • data/src/main/kotlin/team/aliens/dms/android/data/student/repository/StudentRepositoryImpl.kt
  • feature/src/main/kotlin/team/aliens/dms/android/feature/findid/ui/FindIdViewModel.kt
  • feature/src/main/kotlin/team/aliens/dms/android/feature/onboarding/viewmodel/OnboardingViewModel.kt
  • feature/src/main/kotlin/team/aliens/dms/android/feature/profile/viewmodel/AdjustProfileViewModel.kt
  • feature/src/main/kotlin/team/aliens/dms/android/feature/signup/viewmodel/EnterSchoolVerificationCodeViewModel.kt
  • feature/src/main/kotlin/team/aliens/dms/android/feature/signup/viewmodel/EnterSchoolVerificationQuestionViewModel.kt
  • network/src/main/kotlin/team/aliens/dms/android/network/auth/datasource/NetworkAuthDataSourceImpl.kt
  • network/src/main/kotlin/team/aliens/dms/android/network/file/datasource/NetworkFileDataSourceImpl.kt
  • network/src/main/kotlin/team/aliens/dms/android/network/meal/datasource/NetworkMealDataSourceImpl.kt
  • network/src/main/kotlin/team/aliens/dms/android/network/notice/datasource/NetworkNoticeDataSourceImpl.kt
  • network/src/main/kotlin/team/aliens/dms/android/network/notification/datasource/NetworkNotificationDataSourceImpl.kt
  • network/src/main/kotlin/team/aliens/dms/android/network/point/datasource/NetworkPointDataSourceImpl.kt
  • network/src/main/kotlin/team/aliens/dms/android/network/remains/datasource/NetworkRemainsDataSourceImpl.kt
  • network/src/main/kotlin/team/aliens/dms/android/network/student/datasource/NetworkStudentDataSourceImpl.kt
  • network/src/main/kotlin/team/aliens/dms/android/network/user/datasource/NetworkUserDataSourceImpl.kt
  • network/src/main/kotlin/team/aliens/dms/android/network/voting/datasource/NetworkVotingDataSourceImpl.kt
  • shared/exception/src/main/java/team/aliens/dms/android/shared/exception/util/RunCatchingCancellable.kt
  • shared/exception/src/main/java/team/aliens/dms/android/shared/exception/util/SuspendRunCatching.kt
πŸ’€ Files with no reviewable changes (1)
  • shared/exception/src/main/java/team/aliens/dms/android/shared/exception/util/SuspendRunCatching.kt

Comment thread core/widget/src/main/kotlin/team/aliens/dms/android/core/widget/MealWorker.kt Outdated
Comment on lines +24 to +32
return updateMeal(date).fold(
onSuccess = { Result.success(it) },
onFailure = { exception ->
when (exception) {
is CannotSaveMealException -> Result.failure(exception)
else -> Result.failure(CannotSaveMealException())
}
},
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟑 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check if CannotSaveMealException accepts a cause parameter

# Find and display the CannotSaveMealException class definition
fd -t f -e kt "CannotSaveMealException" --exec cat {}

Repository: team-aliens/DMS-Android

Length of output: 185


🏁 Script executed:

#!/bin/bash
# Check the parent class MealException
fd -t f -e kt "MealException" --exec cat {}

Repository: team-aliens/DMS-Android

Length of output: 430


🏁 Script executed:

#!/bin/bash
# Find and display the updateMeal method implementation
rg -t kt "fun updateMeal" -A 20

Repository: team-aliens/DMS-Android

Length of output: 93


🏁 Script executed:

#!/bin/bash
# Find updateMeal method implementation
rg "fun updateMeal" -A 20

Repository: team-aliens/DMS-Android

Length of output: 1953


🏁 Script executed:

#!/bin/bash
# Find the MealRepositoryImpl file
fd -t f "MealRepositoryImpl.kt" --exec cat {}

Repository: team-aliens/DMS-Android

Length of output: 2031


Handle CannotFindMealException separately instead of wrapping it as CannotSaveMealException.

When updateMeal() throws CannotFindMealException (line 42), it gets caught by the else clause and incorrectly wrapped as CannotSaveMealException(), misrepresenting the actual failure reason to callers and logs.

Proposed fix
         return updateMeal(date).fold(
             onSuccess = { Result.success(it) },
             onFailure = { exception ->
                 when (exception) {
                     is CannotSaveMealException -> Result.failure(exception)
+                    is CannotFindMealException -> Result.failure(exception)
-                    else -> Result.failure(CannotSaveMealException())
+                    else -> Result.failure(CannotSaveMealException())
                 }
             },
         )
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return updateMeal(date).fold(
onSuccess = { Result.success(it) },
onFailure = { exception ->
when (exception) {
is CannotSaveMealException -> Result.failure(exception)
else -> Result.failure(CannotSaveMealException())
}
},
)
return updateMeal(date).fold(
onSuccess = { Result.success(it) },
onFailure = { exception ->
when (exception) {
is CannotSaveMealException -> Result.failure(exception)
is CannotFindMealException -> Result.failure(exception)
else -> Result.failure(CannotSaveMealException())
}
},
)
πŸ€– Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@data/src/main/kotlin/team/aliens/dms/android/data/meal/repository/MealRepositoryImpl.kt`
around lines 24 - 32, The fold in MealRepositoryImpl.kt currently maps any
non-CannotSaveMealException to a new CannotSaveMealException, which wrongly
wraps CannotFindMealException; change the onFailure handling for
updateMeal(date) so that if exception is CannotFindMealException you return
Result.failure(exception) (preserving the original exception), if it's
CannotSaveMealException return Result.failure(exception) as before, and only
wrap other unexpected exceptions as CannotSaveMealException(); reference the
updateMeal call and the exception types CannotFindMealException and
CannotSaveMealException when making this adjustment.

@uson1004 uson1004 merged commit 729aab8 into develop Apr 13, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

performance μ„±λŠ₯을 κ°œμ„  ν•  경우 refactor μ½”λ“œ λ¦¬νŒ©ν† λ§ ν•  경우

Projects

None yet

Development

Successfully merging this pull request may close these issues.

코루틴 μ·¨μ†Œ μ „νŒŒλ₯Ό μœ„ν•œ runCatching κ°œμ„ 

2 participants