LabHub

Blog

Batch and End-of-Day Processing in Banking — The Architecture That Rules the Night

한국어English日本語

Introduction — A Bank Day Does Not End at Midnight

In the small hours, after branches close and mobile app users fall asleep, a bank data center goes through its busiest time of the day. Thousands of batch jobs flow along a dependency graph, computing interest, rolling loans into delinquency, producing reports, and closing the ledger. This whole process is called end-of-day (EOD) processing.

A question I hear often: in this real-time era, why still batch? This article starts with that answer and then draws the full picture of banking batch architecture: the scope of EOD processing, job schedulers and dependency management, high-volume techniques, the coexistence of online and batch, failure recovery, and modernization trends.

This article is a technical resource from a system architecture perspective, not advice on any institution's internal implementation or regulatory interpretation.

Why Batch Remains Central in Banking

Batch does not survive merely because of technical debt. Some workloads are inherently batch.

  1. Work that needs a reference point in time: Interest accrual, asset classification, and reports must apply uniformly to all accounts as of a specific moment. With an ever-flowing stream and no reference point, you cannot even define "interest based on today's closing balance."
  2. Regulatory reporting is daily-grained: Most supervisory reports, central bank submissions, and deposit insurance outputs require business-day snapshots.
  3. The structure of reconciliation and settlement: Settlement with external institutions (clearing houses, card networks, securities depositories) is a protocol of file exchange and daily finalization — batch by nature.
  4. Processing efficiency: Accruing interest for ten million accounts is tens of times more efficient as set-based bulk processing than as individual online transactions.

In other words, batch versus real-time is not an either-or choice. It is a division of roles: work that needs a reference point goes to batch; work that needs immediacy goes online.

The Scope of EOD — What Happens at Night

The representative workloads processed in the EOD window are as follows.

WorkloadDescriptionCharacteristics
Interest accrualDaily interest calculation and accumulation per deposit/loan accountAll accounts, compute-intensive
Value date processingFinal attribution of after-cutoff transactions to business daysTied to date cut-over
Delinquency rolloverTransition of overdue loans to delinquent status, penalty interest startState machine, regulation-dependent
Asset quality classificationReclassification into normal/precautionary/substandard etc.Full loan book re-evaluation
Maturity processingTerm deposit maturity payout/rollover, loan due-date handlingDiverse product rules
Fees and settlementDaily fee aggregation, settlement file generation for external bodiesExternal interfaces
GL postingAggregation of the transaction ledger into the General LedgerBalanced-entry gate
Reports and DW loadRegulatory report generation, data warehouse ETLRuns after close is finalized

The key point is that these workloads have ordering dependencies. Today's transactions must be finalized before interest accrual; asset classification follows delinquency rollover; GL posting is possible only after all ledger movements are done.

Batch Architecture — Schedulers and the Dependency DAG

No human can run thousands of jobs in order, so a job scheduler launches jobs along a dependency graph (DAG).

[EOD dependency DAG (simplified)]

  Online cutoff declared
  Finalize today's transactions ──┬─────────────┐
        │                         │             │
        ▼                         ▼             ▼
  Interest accrual          Fee aggregation  Maturity processing
        │                         │             │
        ▼                         │             │
  Delinquency rollover ◀──────────┘             │
        │                                       │
        ▼                                       │
  Asset quality classification ◀────────────────┘
  Ledger verification (balanced entries) ── gate: halt everything on failure
  GL posting ──▶ Report generation ──▶ DW load
  Date cut-over ──▶ Online resumes

Traditionally, banks have used commercial enterprise schedulers (Control-M, TWS/IWS, AutoSys, JobScheduler families, and so on), and more shops now adopt open source such as Airflow. A comparison of perspectives:

PerspectiveCommercial schedulersAirflow and other open source
Dependency modelJob/calendar centric, operator-friendly GUICode-based DAGs, developer-friendly
Business-day calendarStrong built-in holiday/business-day calendarsBuild yourself or use plugins
Restart and exception handlingEasy immediate intervention from ops consoleTask-level retry, UI intervention possible
Audit and authorizationMature controls at the level finance demandsRBAC possible but needs configuration
Cost and scalingHigh license costFree license, requires ops capability

Either way, the non-negotiable requirements are the same.

High-Volume Techniques — Partitioning, Parallelism, Chunks

The fundamentals for batches that process tens of millions of rows are three.

  1. Partitioning: Split the workload into non-overlapping ranges (account number ranges, hashes, branches) and run them in parallel.
  2. Chunk processing: Instead of committing per record, read-process-write in chunks of N records before each commit, reducing transaction overhead.
  3. Prefer set-based operations: Use SQL bulk operations (INSERT SELECT, bulk UPDATE) wherever possible instead of row-by-row loops.

The skeleton of an interest accrual job in Spring Batch looks like this.

@Configuration
public class InterestAccrualJobConfig {

    @Bean
    public Job interestAccrualJob(JobRepository jobRepository, Step partitionedStep) {
        return new JobBuilder("interestAccrualJob", jobRepository)
                .start(partitionedStep)
                .build();
    }

    // Partition by account number range and run gridSize partitions in parallel
    @Bean
    public Step partitionedStep(JobRepository jobRepository,
                                Step accrualStep,
                                AccountRangePartitioner partitioner,
                                TaskExecutor taskExecutor) {
        return new StepBuilder("partitionedAccrualStep", jobRepository)
                .partitioner("accrualStep", partitioner)
                .step(accrualStep)
                .gridSize(8)
                .taskExecutor(taskExecutor)
                .build();
    }

    // Chunk-oriented processing: commit after reading/calculating/writing 1000 records
    @Bean
    public Step accrualStep(JobRepository jobRepository,
                            PlatformTransactionManager txManager,
                            JdbcPagingItemReader<Account> reader,
                            InterestCalculator processor,
                            JdbcBatchItemWriter<AccrualResult> writer) {
        return new StepBuilder("accrualStep", jobRepository)
                .<Account, AccrualResult>chunk(1000, txManager)
                .reader(reader)
                .processor(processor)
                .writer(writer)
                .faultTolerant()
                .skipLimit(0)          // financial batch: fail and investigate, do not skip
                .build();
    }
}
public class AccountRangePartitioner implements Partitioner {
    @Override
    public Map<String, ExecutionContext> partition(int gridSize) {
        Map<String, ExecutionContext> result = new HashMap<>();
        long min = 1_0000_0000L, max = 9_9999_9999L;
        long size = (max - min) / gridSize + 1;
        for (int i = 0; i < gridSize; i++) {
            ExecutionContext ctx = new ExecutionContext();
            ctx.putLong("minAccountNo", min + size * i);
            ctx.putLong("maxAccountNo", Math.min(min + size * (i + 1) - 1, max));
            result.put("partition" + i, ctx);
        }
        return result;
    }
}

Design points:

Coexistence of EOD and Online — Center-Cut and Date Cut-Over

The central challenge of the 24-hour banking era is: transfers keep arriving during close. The mechanisms that handle this are center-cut processing and the date cut-over sequence.

Center-cut is a batch-driven approach that pushes high volumes through the online transaction path. For example, a payroll file of 100,000 credits is received as a batch, and internally the system fires online transfer transactions at high speed. Because the online path is reused, limit checks and balance checks remain in one place.

Date cut-over proceeds roughly in the following sequence.

[Date cut-over sequence (simplified)]

  23:50  Pre-cutoff notice      : announce restrictions on some channels
  23:55  Start queueing new txns: arrivals from now go to next-day value queue
  00:00  Logical date switch    : system business day D → D+1
         ├─ Queued transactions: resume online processing with value date D+1
         │   (the account ledger stays live — zero-downtime switch)
  00:05  Snapshot of day-D finalized txns → EOD batch starts
  02:00  Ledger verification and GL posting complete
  04:00  Reports and DW load complete, EOD declared finished

Important design decisions:

Batch Performance Optimization — Indexes and Bulk Operations

Most batch performance problems come from a batch full-scanning a schema designed for online.

-- Set-based bulk interest accrual instead of a row-by-row loop (example)
INSERT INTO interest_accruals (account_id, business_date, accrual_amount)
SELECT b.account_id,
       DATE '2026-06-13',
       ROUND(b.balance * r.daily_rate, 0)
FROM eod_balance_snapshot b
JOIN product_rates r ON r.product_code = b.product_code
WHERE b.snapshot_date = DATE '2026-06-13'
  AND b.balance > 0;

However, the more set-based you go, the weaker the traceability of "why did this account get this amount." Storing the calculation basis (applied rate, reference balance) alongside the result helps greatly with audits.

Failure Handling — Partial Reprocessing and Idempotent Design

Half of batch design is building a restart button the operator can press with confidence when a job dies at 2 a.m.

Three principles:

  1. Idempotent re-execution: Running the same job with the same parameters twice must yield the same result. For interest accrual, explicitly choose either "delete today's accruals, then recompute" or "block duplicates with an account-plus-date unique constraint."
  2. Checkpoint-based partial reprocessing: Chunk commit points are checkpoints, so completed chunks are skipped on restart. Spring Batch JobRepository manages this state.
  3. Corrections go through correction jobs: The moment an operator fixes data with ad hoc SQL, idempotency and audit trails break. Corrections must also run as jobs that leave records.
-- Idempotency pattern: account+date unique constraint plus cleanup on re-run
ALTER TABLE interest_accruals
    ADD CONSTRAINT uq_accrual UNIQUE (account_id, business_date);

-- Restart pre-step: delete today's unfinalized accruals (finalized flag protected)
DELETE FROM interest_accruals
WHERE business_date = DATE '2026-06-13'
  AND finalized = false;

Jobs interfacing with external institutions are trickier still. If a settlement file was already sent but the job was recorded as failed, a re-run could send the file twice. Split transmission into create-verify-send-confirm stages, with an idempotency key (file name plus date) specifically for the send stage.

Monitoring — Batch SLAs and Delay Alerts

EOD has an absolute deadline: complete before the morning opening of business. For monitoring, delay prediction matters more than failure detection.

Batch Modernization — Near-Real-Time and Cloud

Modernization does not mean turning all batch into real-time. Realistic directions are:

  1. Separate calculation from finalization: Pre-compute interest incrementally during online hours (near-real-time aggregation) and perform only the finalization at close, shrinking the EOD window.
  2. Event-driven transitions: Work with clear triggers, like delinquency rollover (due date passed), can move from a once-a-day batch to events. Consistency with regulatory reporting reference points still requires daily finalization, though.
  3. Batch on the cloud: Moving the execution platform to Kubernetes Job/CronJob, AWS Batch, or managed Airflow buys elastic parallelism (4 nodes normally, 16 at month-end). In exchange, business-day calendars, restart controls, and data adjacency (network latency to the database) become your design problems.
  4. There is no accounting without a close: In any architecture, the accounting requirement to finalize numbers as of a point in time does not go away. The goal of modernization is not eliminating the close but shortening and stabilizing the close window.

Testing — You Cannot Trust a Batch Without Volume

Batches that behave at ten thousand records die at ten million. The core of test strategy is production-scale data.

An Operations Scenario — Responding to a Delayed Close

Let me close the main body with a fictional but typical incident scenario.

[Scenario: interest accrual job delayed]

 01:40  Accrual job normally takes 40 min → at 60 min, only 55% done
 01:45  Delay alert fires. On-call assesses critical path impact
        → at this rate GL posting lands at 04:30; will miss opening
 01:50  Root cause: new product launched yesterday grew target
        accounts by 30% + stale statistics changed the plan
        (index scan → full scan)
 02:00  Decision: stop job → refresh statistics → checkpoint restart
        (parallelism 8 → 12, within a pre-validated ceiling)
 02:10  Restarted. Completed partitions skipped; only remainder runs
 03:05  Accrual done, downstream jobs proceed automatically
 04:10  EOD completes normally. Follow-up: add batch impact review
        to the product launch checklist; promote the statistics
        refresh job into the upstream sequence

What this scenario shows is that incident response capability ultimately comes from everyday design — checkpoints, idempotent restarts, adjustable parallelism, and critical path visibility.

Resource Contention Between Batch and Online — Isolation Strategies

In a non-stop close, batch and online compete for the same database. Without isolation, a heavy batch makes online response times spike, and in the worst case lock waits time out channel transactions.

Practical isolation tools, in escalating order:

ToolDescriptionWhere it applies
Temporal isolationSchedule windows away from online peaksThe baseline, but limited with 24-hour channels
Resource isolationDedicated batch connection pools, session capsPrevents DB connection exhaustion
Read separationRun read-only batches on read replicasReports, DW loads
Lock minimizationShort transactions, chunk commits, optimistic processingLedger-updating batches
Priority controlDemote batch sessions via DB resource managersProtection when batch spills into peak hours

Two patterns deserve special caution.

  1. Long-running batch transactions: Processing a million rows in one transaction explodes undo pressure and lock-hold time. Chunk commits are an online-protection device before they are a performance technique.
  2. Batches touching the same rows as online: If interest accrual updates account rows that online transfers also update, design row lock ordering and lock timeouts explicitly. Give the batch side short timeouts plus retries, so the batch never becomes the long lock holder.

Job Design Standards — What Makes a Batch Operable

To operate thousands of jobs, consistency of standards matters more than the quality of any single job. The skeleton of a job design standard every organization should have:

[Job execution summary log example (structured)]

  job=DEP-INTACC-D-010  business_date=2026-06-13
  status=COMPLETED      exit_code=0
  read_count=10482917   write_count=10482917  skip_count=0
  started=01:12:03      ended=01:54:41        duration=42m38s
  partition_count=8     restart=false

When these five lines accumulate daily, they become the source data for throughput trend analysis and delay prediction. Without this standard, every incident burns time decoding a different log format per job.

Beyond EOD — Month-End, Quarter-End, Year-End

Larger closing cycles stack on top of EOD. Month-end (EOM) runs monthly interest settlement, fee billing, and monthly reports; quarter-end finalizes asset quality and provisioning; year-end (EOY) performs annual closing and carry-forward.

Days where cycles overlap — December 31 stacks EOD, EOM, quarter-end, and EOY all at once — are the tricky ones to design.

[Stacked closing cycles (example: December 31)]

  EOD (daily close)
   └─ EOM (month-end)   : December interest settlement, monthly reports
       └─ EOQ (quarter-end): asset classification final, provisions
           └─ EOY (year-end) : annual closing, carry-forward, annual reports

  Execution order: EOD → EOM → EOQ → EOY (each gates the next)

Pitfalls and Anti-Patterns

Finally, a collection of anti-patterns repeatedly observed in banking batch.

  1. System clock dependence: Jobs deriving the business date from the current time internally. They can never be re-run for yesterday. The business date is always a parameter.
  2. Implicit dependencies: Time-based dependencies like "job A usually ends at 1:00, so start job B at 1:30." On the day A is late, B processes empty data. Dependencies must be declared as precedence relations.
  3. Non-restartable intermediate state: A job that TRUNCATEs then loads a staging table dies midway, and a restart begins from a half-empty table. Check that state is self-contained at every step boundary.
  4. Infinite retries: Infinite retry on external institution failures drains your own queues and threads when the counterparty is down. Set retry ceilings and a handover point to manual intervention.
  5. Alert floods: Sending every job failure to the same channel buries the truly critical alerts. Separate alert severities for critical path jobs versus ordinary jobs.
  6. The "run once" correction script: Unrecorded one-off SQL fixes come back as reconciliation breaks at the next close. Corrections are jobs too, with records.
  7. Small data in test environments: A performance test passed with ten thousand rows guarantees nothing. Execution plans change with data volume.

Design Checklist

Closing Thoughts

Banking batch is not legacy technology; it is the most efficient form of implementing the accounting requirement to finalize numbers as of a reference point. The essence of good batch architecture is not a fancy framework — it is explicit dependencies, idempotent restarts that assume failure, and a restart button the 2 a.m. operator can press with confidence. If you want to shrink the EOD window, do not try to remove batch; first consider moving calculation earlier and leaving only finalization in the close.

References

Comments

No comments yet.

Sign in to leave a comment