alberto-art3ch commented on PR #5951:
URL: https://github.com/apache/fineract/pull/5951#issuecomment-4769288191
> **Orphaned‑lock cleanup is weaker than Loan's and may unlock un‑processed
accounts.** Loan's `deleteOrphanedLocksForProcessedAccounts` only removes a
lock when it can prove the account was actually processed:
>
> ```
> WHERE lck.error IS NULL
> AND lck.lockPlacedOnCobBusinessDate IS NOT NULL
> AND lck.lockOwner IN :lockOwners
> AND EXISTS (SELECT l FROM Loan l
> WHERE l.id = lck.loanId
> AND l.lastClosedBusinessDate =
lck.lockPlacedOnCobBusinessDate) ← key guard
> ```
>
> The Savings query
(`SavingsAccountLockRepository.removeOrphanedLocksForProcessedAccounts`) drops
that correlated `EXISTS`: `delete ... where lck.lockPlacedOnCobBusinessDate is
not null and lck.error is null and lck.lockOwner = SAVINGS_COB_CHUNK_PROCESSING`
>
> Without the `lastClosedBusinessDate == lockPlacedOnCobBusinessDate` check,
"no error recorded" is treated as "successfully processed." If a chunk fails in
a path that doesn't write an error onto the lock (worker/JVM crash, a skip that
only logs, any exception that bypasses
`ChunkProcessingSavingsItemListener.updateLockError`), this tasklet will still
delete the lock and silently treat the account as done — exactly the case
Loan's `EXISTS` clause was added to prevent. I'd add the same correlated check
against `SavingsAccount.lastClosedBusinessDate`. (Minor, related: Loan cleans
both COB lock owners via `COB_LOCK_OWNERS`; Savings only handles
`SAVINGS_COB_CHUNK_PROCESSING` — acceptable while inline COB isn't implemented,
but worth a note.)
>
> **Duplication**
>
> The framework already has generalized COB base types that Loan extends;
Savings re‑implements them as standalone copies rather than
reusing/parameterizing them:
>
> * `SavingsCOBPartitioner` re‑inlines all of `CommonPartitioner` (the
`isEmpty→stopJob`, `StopWatch`, partition‑to‑context logic) instead of
extending it.
> * `SavingsContextAwareTaskDecorator` is a byte‑for‑byte copy of the
generic `ContextAwareTaskDecorator` (no savings‑specific logic).
> * `SavingsItemReader.filterRemainingData` duplicates
`BeforeStepLockingItemReaderHelper.filterRemainingData`.
> * `SavingsLockingService/SavingsLockingServiceImpl` duplicate
`LockingService/AbstractLockingService` instead of extending it. (Partly
justified: the shared AbstractLockingService hardcodes the loan_id column, so
reuse needs that column parameterized — but the cleaner fix is to generalize
the base, not fork it.)
> * `UnlockProcessedSavingsTasklet` re‑implements
`UnlockProcessedAccountsTasklet<T>` with a direct repository call instead of
going through the `AccountLockService<T>` abstraction.
>
> One cosmetic note: the partitioner stores the COB parameter under a new
`SavingsCOBConstant.SAVINGS_COB_PARAMETER` key rather than the shared
`COBConstant.COB_PARAMETER`; it's internally consistent (reader and apply‑lock
read the same key), just inconsistent with the shared constant.
We have applied some enhancements on this, and now we have:
- **Orphaned-lock cleanup hardened.**
_SavingsAccountLockRepository_.removeOrphanedLocksForProcessedAccounts now uses
the same correlated EXISTS guard as _Loan_, only deleting a lock when
_SavingsAccount.lastClosedBusinessDate_ == lock.lockPlacedOnCobBusinessDate.
This prevents un-processed accounts (worker/JVM crash, skip-without-error) from
being silently treated as done. The lock owner is now parameterized (IN
:lockOwners) to align with Loan and stay future-proof.
- **Deduplication**. ContextAwareTaskDecorator was moved into
_fineract-cob_ (_org.apache.fineract.cob.common)_ and is now shared by Loan,
Savings and Working-Capital-Loan; the byte-for-byte
_SavingsContextAwareTaskDecorator_ copy was removed. The filterRemainingData
logic was extracted into a shared, parameterized RemainingDataFilter so the
Loan helper and SavingsItemReader no longer duplicate it.
Some points to be consider, maybe in other PR
- Generalize CommonPartitioner (currently hardcoded to
retrieveLoanCOBPartitions / COBConstant.COB_PARAMETER) so
_SavingsCOBPartitioner_ can extend it instead of re-inlining it.
- Parameterize the loan_id column in _AbstractLockingService_ /
_LockingService_ so _SavingsLockingService_ can reuse the base instead of
forking it.
- Route _UnlockProcessedSavingsTasklet_ through the generic
_AccountLockService_<T> / _UnlockProcessedAccountsTasklet_<T> abstraction.
- Cosmetic: reconcile SavingsCOBConstant.SAVINGS_COB_PARAMETER with the
shared COBConstant.COB_PARAMETER key.
- Once inline COB cleanup is wired to the batch tasklet, extend
orphaned-lock cleanup to also cover SAVINGS_INLINE_COB_PROCESSING.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]