Cosmic Module

O

Qubits of DPK

March 20, 2026

Core Open Source

The Analogy

Every night after the bank closes, a cleaning crew comes in and does all the work that can't be done while customers are present:
  • Calculate everyone's daily interest
  • Check who hasn't paid their EMI
  • Apply late fees
  • Classify overdue loans
  • Prepare tomorrow's reports
This is COB — Close of Business. It's a batch job that runs every night and processes the entire loan and savings portfolio.

Why COB Exists

Real-time processing of millions of loans every time a payment is due is expensive and slow. Instead:
  • During the day: human actions (deposits, repayments, approvals)
  • At night (COB): automated processing (interest, penalties, delinquency)

Business Date vs System Date

This is critical to understand.
System Date: The actual current date on the server (e.g., 20 March 2026)
Business Date: The "bank's working date" — can be set independently
Why separate?
  • If the server goes down on 15 March and comes back on 17 March, COB for 15 and 16 still needs to run
  • In tests, you set the business date to simulate different dates without actually waiting
javascript
QUBITS OF DPK
1PUT /fineract-provider/api/v1/businessdate
2Body: { "type": "COB_DATE", "date": "15 March 2025" }
COB always runs for the COB_DATE (business date), not the system date.

How COB Works — Spring Batch

Fineract uses Spring Batch for COB.

Layman: What is Spring Batch?

Spring Batch is a framework for processing large amounts of data in chunks. Instead of loading 10 million loans into memory at once, it processes them in batches of 100.
javascript
QUBITS OF DPK
1Job
2 └── Step 1: Loan COB
3      ├── ItemReader:    Read 100 loans from DB
4      ├── ItemProcessor: Run COB steps on each loan
5      └── ItemWriter:    Save results to DB
6      └── Repeat until all loans processed

The COB Steps — What Runs Each Night

For EACH active loan, these steps run in order:

Loan Locking — Critical Concept

Why Locks Exist

During COB, if a teller processes a repayment on a loan at the same time COB is applying interest, data corruption can occur.
Solution: COB locks each loan before processing it. Any API call trying to modify a locked loan gets rejected with an error.

In Code

java
QUBITS OF DPK
1// LoanAccountLockRepository stores locked loans
2@Entity
3@Table(name = "m_loan_cob_account_lock")
4public class LoanAccountLock {
5    private Long loanId;
6    private LockOwner lockOwner;  // LOAN_COB_CHUNK_PROCESSING or LOAN_INLINE_COB
7    private LocalDateTime lockPlacedOn;
8}

Inline COB

If a teller tries to process a repayment on a loan that hasn't had COB run yet today, Fineract runs COB inline (synchronously) before allowing the action:
javascript
QUBITS OF DPK
1Teller: POST /loans/42/transactions?command=repayment
2
3Fineract checks: Has COB run for loan 42 today?
4NoRun inline COB for loan 42 first
5Then process the repayment

COB Infrastructure

Partitioning — Parallel Processing

For a bank with 1 million loans, COB can't run on one thread. Fineract partitions loans by ID range:
javascript
QUBITS OF DPK
1Partition 1: Loan IDs 1100,000Thread 1
2Partition 2: Loan IDs 100,001200,000Thread 2
3Partition 3: Loan IDs 200,001300,000Thread 3
4...
All partitions run in parallel → 10x faster.

Batch Manager vs Batch Worker Mode

Fineract can run in split mode:
  • Batch Manager: Schedules and coordinates the COB job, splits into partitions
  • Batch Worker: Runs the actual processing for assigned partitions
This is tested in your PR #5658 (InstanceModeIntegrationTest)!

Scheduler — Triggering COB

COB is triggered by the Quartz Scheduler at a configured time (usually midnight).
java
QUBITS OF DPK
1// Jobs are configured in database: m_job table
2// shortName = "LOAN_COB"
3// cronExpression = "0 0 0 * * ?"  ← runs at midnight every day

Triggering Manually (for testing)

javascript
QUBITS OF DPK
1POST /fineract-provider/api/v1/jobs/short-name/LOAN_COB?command=executeJob
This is exactly what SchedulerJobHelper.runSchedulerJobByShortName() does in your tests!

Key Jobs in the Scheduler

The Business Date Fast-Forward Pattern

In integration tests, you often need to simulate time passing (e.g., to trigger a missed payment penalty). You use fastForwardTime():
java
QUBITS OF DPK
1// Advance business date from March 1 to April 1
2// running COB for each day
3schedulerJobHelper.fastForwardTime(
4    LocalDate.of(2025, 3, 1),   // start
5    LocalDate.of(2025, 4, 1),   // end
6    "Loan COB",                  // job to run each day
7    responseSpec
8);
This loops: set COB date = March 1 → run COB → set COB date = March 2 → run COB → ... → March 31.

Mentor Cheat Sheet