Make ProcessDueRecurringExpenses atomic with withTransaction #12
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Context
Review of PR #10 (
feat/3-implement-recurring-interactors) flagged thatProcessDueRecurringExpenses.awaitperformsexpenseDao.insert(...)andrecurringDao.update(...)outside a@Transactionboundary (app/src/main/java/dev/achmad/ledgerr/domain/recurring/interactor/ProcessDueRecurringExpenses.kt:27-32).If the process is killed (or a coroutine is cancelled) between those two calls, the template's
nextDueDateis not advanced and the next call will re-process the same template → duplicate expense in the user's list. There is no idempotency key on the generatedExpense(the only link back to the template isrecurringExpenseId), so the duplicate is invisible and persists.The codebase already uses
database.withTransaction { }forClearAllData(perdocs/03-function-todos.md:181), so the pattern is established.What to do
AppDatabaseintoProcessDueRecurringExpenses. The cleanest shape is to drop the two DAO constructor params and resolve them from the database inside the transaction block:androidx.room.withTransaction.app/src/main/java/dev/achmad/ledgerr/di/DomainModule.ktto pass the database:factory { ProcessDueRecurringExpenses(get()) }(theAppDatabaseis already asingleinDataModule)../gradlew assembleDebugsucceeds.Trade-off
ClearAllData.ProcessDueRecurringExpensesno longer takes DAOs directly. Per AGENTS.md, interactors normally take DAOs and not the database —ClearAllDatais the existing exception. Acceptable for an interactor that needs cross-DAO atomicity.Acceptance
ProcessDueRecurringExpenses.awaitruns the per-row insert + advance loop insidedatabase.withTransaction { }di/DomainModule.ktfactory updated accordingly./gradlew assembleDebugsucceedsImplementation rule
Per
AGENTS.md— do not start implementation without explicit user sign-off on this issue. When working, check for related issues in the remote repo first.Closing — addressed as a review comment on PR #10 (#10) instead of a separate issue.