At a glance
- Client: one of the largest commercial banks, with European offices and state participation
- System: the bank's payroll platform - issuing, maintaining and cancelling agreements, plus payroll card issue, payroll accounting and dismissed-employee registries
- Stakes: the system pays employees of Europe's largest companies and remits part of their pay to tax authorities. A failure means millions of people do not receive their money
- Load tested: 10,000 registries per hour, with individual registries up to 9,000 records
- Coverage: 60+ business cases, 16 resource-heavy interface operations, up to 12 transaction statuses per registry tracked through the process model
- Built for the project: 29 emulators standing in for 7 adjacent bank systems, and 43 test registry generators written in Python
- Stack under test: Oracle DBMS, Oracle BPM Suite, WebLogic
- Tools: LoadRunner, Apache JMeter, Java, BPEL
Why a payroll system is unusually hard to load test
Money moving from a company to an employee's account is not one transaction. It is a registry that travels through several processing stages, arrives through different integration channels, and is checked at every step. Along the way a registry can hold any of twelve statuses defined by the business process model.
That status model is the difficulty. A load test that only measures throughput will happily report success while registries silently land in the wrong state - and in a payroll system the wrong state means someone is not paid. So the testing had to follow status transitions for every registry, not just count completions.
The bank's goal was accordingly specific: eliminate the risk of payroll card service disruption, payroll accounting delays and failures during registry processing.
What we built
Off-the-shelf load generation could not cover a system this integrated, so a large part of the engagement was engineering:
- 29 emulators reproducing the behaviour of seven adjacent bank systems, so the payroll platform could be loaded realistically without those systems being available
- 43 test registry generators in Python, producing the different registry types the system receives from different sources
- Additional load generated through Java and BPEL alongside JMeter and LoadRunner
How the testing ran
Each release carried at least three functional changes, and each release was tested against the previous one so the effect of every change was visible. Changes that failed their check did not ship - the comparison was a gate, not a report.
The test scope per release covered more than 60 business cases: receiving and processing registries from different channels and sources, generating reports, working with agreements, activating cards. Sixteen interface operations known to be resource-hungry - activation, obtaining document and task lists - were re-tested every time.
Two things were measured that a generic load test would miss: the processing time of a large registry (up to 9,000 records) and the average registry processing time under a load of 10,000 registries per hour. Throughout, the team tracked business-process status transitions for every registry and reported status defects as first-class findings.
What the testing delivers
The outcome here is prevention, and it is worth stating plainly rather than dressing as a percentage:
- Registry status defects were detected by tracking every registry's business-process status transitions through the run.
- DBMS behaviour was tracked with Oracle Automatic Workload Repository, with monitoring on Telegraf, InfluxDB and Grafana plus purpose-written Python and Go scripts.
- Releases carrying performance degradation are caught before production. For a payroll system that is the whole point: the failure mode is people not being paid on time, and the cost is financial and reputational at once.
- PFLB has run load testing for financial systems for more than five years, and this is the class of failure that work exists to prevent.
Questions this engagement answers
How do you load test a system where correctness matters as much as speed?
By tracking the business-process state of every object under load - here, up to twelve statuses per registry - and treating a wrong status as a defect equal to a slow response.
What if the surrounding systems aren't available for testing?
You build them. This engagement required 29 emulators covering seven adjacent bank systems before realistic load was possible at all.
How much test data does a payroll load test need?
Enough variety to match every registry type and channel - here 43 separate generators, because a single synthetic registry shape would have tested one path and missed the rest.
Can performance testing gate a release?
It did here: each release was compared against the previous one, and changes that failed the comparison were excluded from the release.
Paying people on time is not a performance nice-to-have
If your system has a date it cannot miss - payroll, settlement, reporting - the useful question is not whether it is fast, but what state everything is in when it is under load.


