Performance

This page summarises the results of performance testing conducted on openFHIR Enterprise. Since version 3.0.0, all performance measurements run on a dedicated, Terraform-provisioned Hetzner Cloud fleet: openFHIR alone on a server sized to each scenario, the database on its own server, and Apache JMeter driving load from a third server over a private network — a topology representative of real customer deployments.

All tests run under sustained concurrent load using Apache JMeter with 20 threads, a 30-second ramp-up, and 100 iterations per operation type (toFHIR and toOpenEHR) — 20 000 requests per scenario. The test dataset covers five mapping domains from the MII KDS profile set: Laborbericht, Fall, Procedure, Medikationsverabreichung, and a mixed set — 95 distinct request payloads per direction.

Note

Detailed breakdown of performance test available at https://open-fhir.com/performance-results

Test environment

One server per tier (Hetzner dedicated-vCPU CCX line, location fsn1), connected over a private network:

There is no docker CPU or memory pinning: each application server is itself the resource limit, so JVM ergonomics and GC sizing see the real machine. The memory axis is explored via JVM heap sizes per box, capped at ~75 % of box RAM so the OS and off-heap memory keep headroom. The default matrix is 7 configurations (2|2g 2|4g 4|8g 4|12g 8|8g 8|16g 8|24g) × {postgres, mongo} = 14 scenarios.

Two properties of the environment to keep in mind when reading the numbers:

  • Latencies include the private-network round trip (~0.2–0.5 ms) between the load generator and the app, and between the app and the database — as they do in a real deployment.

  • Dedicated vCPUs are still SMT threads on a shared physical host — good, though not laboratory-grade, isolation.

Test matrix

Measured with openfhir/openfhir-enterprise (version corresponds to version tied to this documentation). Fourteen scenarios, varying vCPU count, JVM heap, and database backend; every scenario completed all 20 000 requests with an error rate of ≤ 0.1 %.

vCPU

Heap

DB

Mean (ms)

Median (ms)

P90 (ms)

P95 (ms)

2

2 GB

MongoDB

1 605

1 167

3 231

4 342

2

2 GB

PostgreSQL

1 828

1 322

3 667

4 928

2

4 GB

MongoDB

1 545

1 121

3 084

4 126

2

4 GB

PostgreSQL

1 781

1 299

3 570

4 794

4

8 GB

MongoDB

321

75

925

1 358

4

8 GB

PostgreSQL

478

169

1 264

1 922

4

12 GB

MongoDB

316

78

923

1 312

4

12 GB

PostgreSQL

500

173

1 331

2 043

8

8 GB

MongoDB

23

13

39

52

8

8 GB

PostgreSQL

51

22

77

146

8

16 GB

MongoDB

23

13

39

54

8

16 GB

PostgreSQL

52

22

79

163

8

24 GB

MongoDB

25

13

42

58

8

24 GB

PostgreSQL

44

22

69

123

Throughput and peak load

Sustained throughput and median latency observed during each run:

vCPU

Heap

DB

Throughput (req/s)

Median (ms)

2

2 GB

MongoDB

84.3

1 167

2

2 GB

PostgreSQL

77.8

1 322

2

4 GB

MongoDB

87.5

1 121

2

4 GB

PostgreSQL

78.5

1 299

4

8 GB

MongoDB

168.2

75

4

8 GB

PostgreSQL

164.9

169

4

12 GB

MongoDB

170.0

78

4

12 GB

PostgreSQL

160.6

173

8

8 GB

MongoDB

199.1

13

8

8 GB

PostgreSQL

193.6

22

8

16 GB

MongoDB

193.7

13

8

16 GB

PostgreSQL

183.8

22

8

24 GB

MongoDB

185.2

13

8

24 GB

PostgreSQL

185.2

22

The highest sustained throughput observed was 199 req/s (8 vCPU / 8 GB heap / MongoDB). Under the tested 20-thread load profile the engine saturates around 165–170 req/s at 4 vCPU and 185–199 req/s at 8 vCPU, while latency keeps improving with core count — additional CPUs beyond 4 are better justified by latency SLO requirements than by raw throughput.

Analysis

CPU is the dominant factor. The step from 2 to 4 vCPU cuts mean latency ~5× and doubles throughput; 4 to 8 vCPU cuts mean latency a further ~10–14× (321 ms → 23 ms with MongoDB) and collapses the tail — P95 drops from 1 358 ms to 52 ms. Mapping is a CPU-bound workload; adding cores scales both latency and throughput up to the tested range.

Heap size has no measurable effect. At every box size, larger heaps perform within a few percent of the smallest one (8 GB vs 24 GB heap at 8 vCPU: identical 13 ms median; throughput differences within run-to-run noise). The engine does not benefit from heap beyond the baseline sizes — provision RAM for the OS and off-heap headroom, and spend the budget on vCPUs instead.

MongoDB leads PostgreSQL at every size. Median latency is 13 ms vs 22 ms at 8 vCPU and 75–78 ms vs 169–173 ms at 4 vCPU; throughput converges at 8 vCPU. The gap is most pronounced in the mid-tier, making MongoDB the better choice when running at 4 vCPU.

toFHIR and toOpenEHR behave comparably under sufficient resources. Both directions converge from 4 vCPU upward, indicating the engine itself is not the bottleneck at that resource level.

Recommendations

  • Minimum production configuration: 4 vCPU / 8 GB RAM, MongoDB — 168 req/s sustained throughput with a 75 ms median and 1.4 s P95.

  • Latency-sensitive deployments: 8 vCPU — 13 ms median / 52 ms P95 (MongoDB) or 22 ms median / 146 ms P95 (PostgreSQL).

  • Do not oversize the JVM heap. Allocate the baseline heap (2g at 2 vCPU, 8g at 4–8 vCPU) and leave the rest of the machine’s RAM to the OS page cache — larger heaps showed no measurable benefit.

  • Avoid 2 vCPU deployments under sustained concurrent load. P95 exceeds 4 s for both backends.