adamsaghy commented on PR #5951:
URL: https://github.com/apache/fineract/pull/5951#issuecomment-4809616915
@alberto-art3ch Please consider the below recommendations:
<head></head><h3 class="text-text-100 mt-3 -mb-1 text-[1.125rem] font-bold"
style="caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0); font-style: normal;
font-variant-caps: normal; letter-spacing: normal; orphans: 2; text-align:
start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2;
word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-line: none;
text-decoration-thickness: auto; text-decoration-style: solid;">PR #5951
(Savings COB) — does it match Loan COB?</h3><p class="font-claude-response-body
break-words whitespace-normal" style="caret-color: rgb(0, 0, 0); color: rgb(0,
0, 0); font-style: normal; font-variant-caps: normal; font-weight: 400;
letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px;
text-transform: none; white-space: normal; widows: 2; word-spacing: 0px;
-webkit-text-stroke-width: 0px; text-decoration-line: none;
text-decoration-thickness: auto; text-decoration-style: solid;">Short
answer:<span class="Apple-con
verted-space"> </span><strong>no, not in the way you want.</strong><span
class="Apple-converted-space"> </span>The Spring Batch<span
class="Apple-converted-space"> </span><em>step topology</em><span
class="Apple-converted-space"> </span>is a faithful port, but the PR does<span
class="Apple-converted-space"> </span><strong>not</strong>reuse the generic COB
framework that Loan COB already sits on — it re-introduces monolithic,
copy‑pasted implementations under<span class="Apple-converted-space">
</span><code class="bg-text-200/5 border border-0.5 border-border-300
text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px
text-[0.9rem]">Savings*</code><span class="Apple-converted-space">
</span>names. On top of that, several pieces are missing or wrong enough that
the job can't compile, start, or actually do anything.</p><h4
class="text-text-100 mt-2 -mb-1 text-base font-bold" style="caret-color: rgb(0,
0, 0); color: rgb(0, 0, 0); font-style: normal; font-variant-caps:
normal; letter-spacing: normal; orphans: 2; text-align: start; text-indent:
0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px;
-webkit-text-stroke-width: 0px; text-decoration-line: none;
text-decoration-thickness: auto; text-decoration-style: solid;">The core
problem: it duplicates instead of reusing the existing generic layer</h4><p
class="font-claude-response-body break-words whitespace-normal"
style="caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0); font-style: normal;
font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans:
2; text-align: start; text-indent: 0px; text-transform: none; white-space:
normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;
text-decoration-line: none; text-decoration-thickness: auto;
text-decoration-style: solid;">Loan COB was already refactored into
entity‑agnostic bases in the<span class="Apple-converted-space">
</span><strong><code class="bg-text-200/5 border border-0.5 border-border-
300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px
text-[0.9rem]">fineract-cob</code></strong><span class="Apple-converted-space">
</span>module, and the Loan classes are thin (30–60 line) subclasses. The PR
ignored all of them and re-implemented the pre‑refactor monoliths. Verified
mappings:</p><div class="overflow-x-auto w-full px-2 mb-6" style="caret-color:
rgb(0, 0, 0); color: rgb(0, 0, 0); font-style: normal; font-variant-caps:
normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align:
start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2;
word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-line: none;
text-decoration-thickness: auto; text-decoration-style: solid;">
PR's new Savings class | Existing generic base it should extend (in
fineract-cob) | What Loan does
-- | -- | --
SavingsCOBPartitioner (~107 lines) | cob.common.CommonPartitioner |
LoanCOBPartitioner extends CommonPartitioner (58 lines)
SavingsLockingServiceImpl (~117 lines) | cob.domain.AbstractLockingService |
LoanLockingServiceImpl extends AbstractLockingService (57 lines)
ApplySavingsLockTasklet (~108 lines) | cob.tasklet.ApplyCommonLockTasklet |
ApplyLoanLockTasklet extends ApplyCommonLockTasklet (47 lines)
UnlockProcessedSavingsTasklet |
cob.tasklet.UnlockProcessedAccountsTasklet<T>+ AccountLockService<T> |
UnlockProcessedLoansTasklet extends UnlockProcessedAccountsTasklet<>
ChunkProcessingSavingsItemListener(~103 lines) |
cob.listener.AbstractLoanItemListener<T>(already generic) |
ChunkProcessingLoanItemListener extends AbstractLoanItemListener<Loan> (39
lines)
SavingsItemReader | cob.service.BeforeStepLockingItemReaderHelper |
LoanItemReader delegates to it (44 lines)
new SavingsLockOwner enum | existing cob.domain.LockOwner | reuses LockOwner
</div><p class="font-claude-response-body break-words whitespace-normal"
style="caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0); font-style: normal;
font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans:
2; text-align: start; text-indent: 0px; text-transform: none; white-space:
normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;
text-decoration-line: none; text-decoration-thickness: auto;
text-decoration-style: solid;">The uniform approach you're after already
exists. Savings COB should shrink to ~6 thin subclasses + supply
entity-specific table/SQL/lock-owner/parameter keys, exactly like Loan and
Working‑Capital‑Loan do. Where a base is "Loan"‑named but already generic
(<code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000
whitespace-pre-wrap rounded-[0.4rem] px-1 py-px
text-[0.9rem]">AbstractLoanItemListener<T></code>,<span
class="Apple-converted-space"> </span><code class="bg-text-200/5 border border-
0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem]
px-1 py-px text-[0.9rem]">AbstractLoanItemReader<T></code>), rename it to
a neutral<span class="Apple-converted-space"> </span><code class="bg-text-200/5
border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap
rounded-[0.4rem] px-1 py-px text-[0.9rem]">Abstract*</code>/<code
class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000
whitespace-pre-wrap rounded-[0.4rem] px-1 py-px
text-[0.9rem]">Common*</code><span class="Apple-converted-space"> </span>as
part of this PR.</p><h4 class="text-text-100 mt-2 -mb-1 text-base font-bold"
style="caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0); font-style: normal;
font-variant-caps: normal; letter-spacing: normal; orphans: 2; text-align:
start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2;
word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-line: none;
text-decoration-thickness: auto; text-de
coration-style: solid;">Verified findings, most severe first</h4><ol
class="[li_&]:mb-0 [li_&]:mt-1 [li_&]:gap-1
[&:not(:last-child)_ul]:pb-1 [&:not(:last-child)_ol]:pb-1 list-decimal
flex flex-col gap-1 pl-8 mb-3" style="caret-color: rgb(0, 0, 0); color: rgb(0,
0, 0); font-style: normal; font-variant-caps: normal; font-weight: 400;
letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px;
text-transform: none; white-space: normal; widows: 2; word-spacing: 0px;
-webkit-text-stroke-width: 0px; text-decoration-line: none;
text-decoration-thickness: auto; text-decoration-style: solid;"><li
class="font-claude-response-body whitespace-normal break-words
pl-2"><strong><code class="bg-text-200/5 border border-0.5 border-border-300
text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px
text-[0.9rem]">UnlockProcessedSavingsTasklet:44</code><span
class="Apple-converted-space"> </span>won't compile</strong><span
class="Apple-converted-space"> </spa
n>— calls<span class="Apple-converted-space"> </span><code
class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000
whitespace-pre-wrap rounded-[0.4rem] px-1 py-px
text-[0.9rem]">savingsAccountLockRepository.removeOrphanedLocksForProcessedAccounts()</code>,
but that method does not exist on<span class="Apple-converted-space">
</span><code class="bg-text-200/5 border border-0.5 border-border-300
text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px
text-[0.9rem]">SavingsAccountLockRepository</code><span
class="Apple-converted-space"> </span>(it only has<span
class="Apple-converted-space"> </span><code class="bg-text-200/5 border
border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap
rounded-[0.4rem] px-1 py-px text-[0.9rem]">removeLockByOwner()</code><span
class="Apple-converted-space"> </span>/<span class="Apple-converted-space">
</span><code class="bg-text-200/5 border border-0.5 border-border-300
text-danger-000 whitespace-pre-wrap round
ed-[0.4rem] px-1 py-px text-[0.9rem]">existsBy…</code>). The generic<span
class="Apple-converted-space"> </span><code class="bg-text-200/5 border
border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap
rounded-[0.4rem] px-1 py-px
text-[0.9rem]">AccountLockService.removeOrphanedLocksForProcessedAccounts()</code><span
class="Apple-converted-space"> </span>that Loan uses<span
class="Apple-converted-space"> </span><em>does</em><span
class="Apple-converted-space"> </span>exist — reuse it. (Note<span
class="Apple-converted-space"> </span><code class="bg-text-200/5 border
border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap
rounded-[0.4rem] px-1 py-px text-[0.9rem]">removeLockByOwner</code><span
class="Apple-converted-space"> </span>deletes rows where<span
class="Apple-converted-space"> </span><code class="bg-text-200/5 border
border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap
rounded-[0.4rem] px-1 py-px text-[0.9rem]">error IS NOT NULL</code><
span class="Apple-converted-space"> </span>— the opposite of orphan
cleanup.)</li><li class="font-claude-response-body whitespace-normal
break-words pl-2"><strong>Lock table is never created</strong><span
class="Apple-converted-space"> </span>—<span class="Apple-converted-space">
</span><code class="bg-text-200/5 border border-0.5 border-border-300
text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px
text-[0.9rem]">SavingsAccountLock</code><span class="Apple-converted-space">
</span>maps to<span class="Apple-converted-space"> </span><code
class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000
whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">@Table(name =
"m_savings_account_locks")</code>, but no Liquibase changelog creates that
table (<code class="bg-text-200/5 border border-0.5 border-border-300
text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px
text-[0.9rem]">grep</code><span class="Apple-converted-space"> </span>fi
nds none; Loan ships<span class="Apple-converted-space"> </span><code
class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000
whitespace-pre-wrap rounded-[0.4rem] px-1 py-px
text-[0.9rem]">0039_add_loan_account_locks.xml</code>). First<span
class="Apple-converted-space"> </span><code class="bg-text-200/5 border
border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap
rounded-[0.4rem] px-1 py-px text-[0.9rem]">applyLock</code>→ SQL error on every
tenant.</li><li class="font-claude-response-body whitespace-normal break-words
pl-2"><strong><code class="bg-text-200/5 border border-0.5 border-border-300
text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px
text-[0.9rem]">batchJdbcTransactionTemplate</code><span
class="Apple-converted-space"> </span>bean is undefined</strong><span
class="Apple-converted-space"> </span>— injected with<span
class="Apple-converted-space"> </span><code class="bg-text-200/5 border
border-0.5 border-border-300 text-da
nger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px
text-[0.9rem]">@Qualifier("batchJdbcTransactionTemplate")</code><span
class="Apple-converted-space"> </span>in<span class="Apple-converted-space">
</span><code class="bg-text-200/5 border border-0.5 border-border-300
text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px
text-[0.9rem]">SavingsCOBWorkerConfiguration</code>,<span
class="Apple-converted-space"> </span><code class="bg-text-200/5 border
border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap
rounded-[0.4rem] px-1 py-px
text-[0.9rem]">ApplySavingsLockTasklet:54</code>,<span
class="Apple-converted-space"> </span><code class="bg-text-200/5 border
border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap
rounded-[0.4rem] px-1 py-px
text-[0.9rem]">ChunkProcessingSavingsItemListener</code>, but no such bean
exists anywhere. Loan uses the existing<span class="Apple-converted-space">
</span><code class="bg-text-200/5 border border-0.5 border-b
order-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px
text-[0.9rem]">requiresNewTransactionJdbcTemplate</code><span
class="Apple-converted-space"> </span>(<code class="bg-text-200/5 border
border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap
rounded-[0.4rem] px-1 py-px
text-[0.9rem]">JdbcTransactionConfig:51</code>,<span
class="Apple-converted-space"> </span><code class="bg-text-200/5 border
border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap
rounded-[0.4rem] px-1 py-px text-[0.9rem]">PROPAGATION_REQUIRES_NEW</code>).
→<span class="Apple-converted-space"> </span><code class="bg-text-200/5 border
border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap
rounded-[0.4rem] px-1 py-px
text-[0.9rem]">NoSuchBeanDefinitionException</code><span
class="Apple-converted-space"> </span>at startup.</li><li
class="font-claude-response-body whitespace-normal break-words
pl-2"><strong>Retry contract broken in<span class="Apple-converted-space
"> </span><code class="bg-text-200/5 border border-0.5 border-border-300
text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px
text-[0.9rem]">ApplySavingsLockTasklet</code></strong><span
class="Apple-converted-space"> </span>— gates retries on<span
class="Apple-converted-space"> </span><code class="bg-text-200/5 border
border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap
rounded-[0.4rem] px-1 py-px
text-[0.9rem]">contribution.getStepExecution().getCommitCount()</code>(diff
line 729) instead of the persisted attempt counter<span
class="Apple-converted-space"> </span><code class="bg-text-200/5 border
border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap
rounded-[0.4rem] px-1 py-px text-[0.9rem]">ApplyCommonLockTasklet</code><span
class="Apple-converted-space"> </span>uses (<code class="bg-text-200/5 border
border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap
rounded-[0.4rem] px-1 py-px text-[0.9rem]">executionContext.getLong(getApply
LockAttemptsKey())</code>, lines 88–99).<span class="Apple-converted-space">
</span><code class="bg-text-200/5 border border-0.5 border-border-300
text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px
text-[0.9rem]">getCommitCount()</code><span class="Apple-converted-space">
</span>is committed-chunk count, not failed-lock attempts, so<span
class="Apple-converted-space"> </span><code class="bg-text-200/5 border
border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap
rounded-[0.4rem] px-1 py-px text-[0.9rem]">> 3</code><span
class="Apple-converted-space"> </span>is typically never true → tasklet
returns<span class="Apple-converted-space"> </span><code class="bg-text-200/5
border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap
rounded-[0.4rem] px-1 py-px text-[0.9rem]">CONTINUABLE</code><span
class="Apple-converted-space"> </span>and can spin instead of failing after 3
attempts. Disappears entirely if you extend the base.</li><li class=
"font-claude-response-body whitespace-normal break-words
pl-2"><strong>Stayed-locked detection is a no-op</strong><span
class="Apple-converted-space"> </span>—<span class="Apple-converted-space">
</span><code class="bg-text-200/5 border border-0.5 border-border-300
text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px
text-[0.9rem]">RetrieveSavingsIdServiceImpl.findAllStayedLockedByCobBusinessDate</code><span
class="Apple-converted-space"> </span>returns<span
class="Apple-converted-space"> </span><code class="bg-text-200/5 border
border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap
rounded-[0.4rem] px-1 py-px text-[0.9rem]">List.of()</code><span
class="Apple-converted-space"> </span>with comment<span
class="Apple-converted-space"> </span><em>"implemented when we add the query…
lock table doesn't exist yet"</em>. So<span class="Apple-converted-space">
</span><code class="bg-text-200/5 border border-0.5 border-border-300
text-danger-000 whitespace-pre-
wrap rounded-[0.4rem] px-1 py-px
text-[0.9rem]">StayedLockedSavingsTasklet</code><span
class="Apple-converted-space"> </span>can never emit<span
class="Apple-converted-space"> </span><code class="bg-text-200/5 border
border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap
rounded-[0.4rem] px-1 py-px
text-[0.9rem]">SavingsAccountsStayedLockedBusinessEvent</code>.</li><li
class="font-claude-response-body whitespace-normal break-words
pl-2"><strong>Job has no business steps → stops immediately</strong><span
class="Apple-converted-space"> </span>— there is zero<span
class="Apple-converted-space"> </span><code class="bg-text-200/5 border
border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap
rounded-[0.4rem] px-1 py-px text-[0.9rem]">implements
SavingsCOBBusinessStep</code><span class="Apple-converted-space"> </span>and
no<span class="Apple-converted-space"> </span><code class="bg-text-200/5 border
border-0.5 border-border-300 text-danger-000 whitespace-pre-w
rap rounded-[0.4rem] px-1 py-px
text-[0.9rem]">m_batch_business_steps</code><span
class="Apple-converted-space"> </span>seed for<span
class="Apple-converted-space"> </span><code class="bg-text-200/5 border
border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap
rounded-[0.4rem] px-1 py-px
text-[0.9rem]">SAVINGS_CLOSE_OF_BUSINESS</code>.<span
class="Apple-converted-space"> </span><code class="bg-text-200/5 border
border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap
rounded-[0.4rem] px-1 py-px text-[0.9rem]">SavingsCOBPartitioner</code><span
class="Apple-converted-space"> </span>calls<span class="Apple-converted-space">
</span><code class="bg-text-200/5 border border-0.5 border-border-300
text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px
text-[0.9rem]">stopJobExecution()</code><span class="Apple-converted-space">
</span>when steps are empty, so every run stops without processing
anything.</li><li class="font-claude-response-body whitespace-norm
al break-words pl-2"><strong>No<span class="Apple-converted-space">
</span><code class="bg-text-200/5 border border-0.5 border-border-300
text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px
text-[0.9rem]">job</code><span class="Apple-converted-space"> </span>row →
never scheduled</strong><span class="Apple-converted-space"> </span>— no
migration inserts a "Savings COB"<span class="Apple-converted-space">
</span><code class="bg-text-200/5 border border-0.5 border-border-300
text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px
text-[0.9rem]">ScheduledJobDetail</code><span class="Apple-converted-space">
</span>(Loan ships<span class="Apple-converted-space"> </span><code
class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000
whitespace-pre-wrap rounded-[0.4rem] px-1 py-px
text-[0.9rem]">0037_add_loan_cob_job_data.xml</code>). The bean exists but the
scheduler never triggers it and it's invisible in the Scheduler Jobs
API.</li><li class=
"font-claude-response-body whitespace-normal break-words pl-2"><strong><code
class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000
whitespace-pre-wrap rounded-[0.4rem] px-1 py-px
text-[0.9rem]">PartitionedJob</code><span class="Apple-converted-space">
</span>enum not extended</strong><span class="Apple-converted-space"> </span>—
still only<span class="Apple-converted-space"> </span><code
class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000
whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">LOAN_COB</code>.
A stuck<span class="Apple-converted-space"> </span><code class="bg-text-200/5
border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap
rounded-[0.4rem] px-1 py-px text-[0.9rem]">SAVINGS_COB</code><span
class="Apple-converted-space"> </span>execution fails<span
class="Apple-converted-space"> </span><code class="bg-text-200/5 border
border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4r
em] px-1 py-px text-[0.9rem]">existsByJobName</code>, so<span
class="Apple-converted-space"> </span><code class="bg-text-200/5 border
border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap
rounded-[0.4rem] px-1 py-px
text-[0.9rem]">StuckJobExecutorServiceImpl</code><span
class="Apple-converted-space"> </span>treats it as a plain tasklet job —
stuck-job recovery diverges from Loan.</li></ol><p
class="font-claude-response-body break-words whitespace-normal"
style="caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0); font-style: normal;
font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans:
2; text-align: start; text-indent: 0px; text-transform: none; white-space:
normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px;
text-decoration-line: none; text-decoration-thickness: auto;
text-decoration-style: solid;">Also worth fixing:<span
class="Apple-converted-space"> </span><code class="bg-text-200/5 border
border-0.5 border-border-300 text-da
nger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px
text-[0.9rem]">ApplySavingsLockTasklet</code>/<code class="bg-text-200/5 border
border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap
rounded-[0.4rem] px-1 py-px
text-[0.9rem]">ChunkProcessingSavingsItemListener</code><span
class="Apple-converted-space"> </span>mutate a shared<span
class="Apple-converted-space"> </span><code class="bg-text-200/5 border
border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap
rounded-[0.4rem] px-1 py-px text-[0.9rem]">TransactionTemplate</code><span
class="Apple-converted-space"> </span>via<span class="Apple-converted-space">
</span><code class="bg-text-200/5 border border-0.5 border-border-300
text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px
text-[0.9rem]">setPropagationBehavior(...)</code><span
class="Apple-converted-space"> </span>per call (diff 450, 766) under the
multi-threaded worker step — a data race the Loan path avoids by using a
pre-configured
bean; and<span class="Apple-converted-space"> </span><code
class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000
whitespace-pre-wrap rounded-[0.4rem] px-1 py-px
text-[0.9rem]">AbstractSavingsItemReader</code><span
class="Apple-converted-space"> </span>throws raw<span
class="Apple-converted-space"> </span><code class="bg-text-200/5 border
border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap
rounded-[0.4rem] px-1 py-px text-[0.9rem]">RuntimeException</code><span
class="Apple-converted-space"> </span>instead of a typed not-found exception
(loses meaning in the recorded lock stacktrace).</p><h4 class="text-text-100
mt-2 -mb-1 text-base font-bold" style="caret-color: rgb(0, 0, 0); color: rgb(0,
0, 0); font-style: normal; font-variant-caps: normal; letter-spacing: normal;
orphans: 2; text-align: start; text-indent: 0px; text-transform: none;
white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width:
0px; text-decoration-line: none; te
xt-decoration-thickness: auto; text-decoration-style:
solid;">Recommendation</h4><p class="font-claude-response-body break-words
whitespace-normal" style="caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0);
font-style: normal; font-variant-caps: normal; font-weight: 400;
letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px;
text-transform: none; white-space: normal; widows: 2; word-spacing: 0px;
-webkit-text-stroke-width: 0px; text-decoration-line: none;
text-decoration-thickness: auto; text-decoration-style: solid;">Reframe the PR
around the existing<span class="Apple-converted-space"> </span><code
class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000
whitespace-pre-wrap rounded-[0.4rem] px-1 py-px
text-[0.9rem]">fineract-cob</code><span class="Apple-converted-space">
</span>generics rather than parallel copies: extend<span
class="Apple-converted-space"> </span><code class="bg-text-200/5 border
border-0.5 border-border-300 text-danger-000 whites
pace-pre-wrap rounded-[0.4rem] px-1 py-px
text-[0.9rem]">CommonPartitioner</code>,<span class="Apple-converted-space">
</span><code class="bg-text-200/5 border border-0.5 border-border-300
text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px
text-[0.9rem]">AbstractLockingService</code>,<span
class="Apple-converted-space"> </span><code class="bg-text-200/5 border
border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap
rounded-[0.4rem] px-1 py-px text-[0.9rem]">ApplyCommonLockTasklet</code>,<span
class="Apple-converted-space"> </span><code class="bg-text-200/5 border
border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap
rounded-[0.4rem] px-1 py-px
text-[0.9rem]">UnlockProcessedAccountsTasklet<T></code>,<span
class="Apple-converted-space"> </span><code class="bg-text-200/5 border
border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap
rounded-[0.4rem] px-1 py-px
text-[0.9rem]">AbstractLoanItemListener<T></code><span class="Apple-c
onverted-space"> </span>(rename it),<span class="Apple-converted-space">
</span><code class="bg-text-200/5 border border-0.5 border-border-300
text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px
text-[0.9rem]">BeforeStepLockingItemReaderHelper</code>, and reuse<span
class="Apple-converted-space"> </span><code class="bg-text-200/5 border
border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap
rounded-[0.4rem] px-1 py-px text-[0.9rem]">LockOwner</code>/<code
class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000
whitespace-pre-wrap rounded-[0.4rem] px-1 py-px
text-[0.9rem]">AccountLockService</code>. Then add the missing artifacts Loan
ships as standard — the lock-table migration, the<span
class="Apple-converted-space"> </span><code class="bg-text-200/5 border
border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap
rounded-[0.4rem] px-1 py-px text-[0.9rem]">job</code><span
class="Apple-converted-space"> </span>row, the business-st
ep seed, at least one<span class="Apple-converted-space"> </span><code
class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000
whitespace-pre-wrap rounded-[0.4rem] px-1 py-px
text-[0.9rem]">SavingsCOBBusinessStep</code><span
class="Apple-converted-space"> </span>impl, and the<span
class="Apple-converted-space"> </span><code class="bg-text-200/5 border
border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap
rounded-[0.4rem] px-1 py-px text-[0.9rem]">PartitionedJob</code><span
class="Apple-converted-space"> </span>enum entry. That gets you the uniform,
single-maintenance-point COB you're describing, and every future COB fix
applies to both account types at once.</p>
--
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]