From 4fbf572f32c7082e50f2cb459303a168c8d07f12 Mon Sep 17 00:00:00 2001 From: lobo Date: Tue, 20 Oct 2026 11:41:31 +0200 Subject: [PATCH] perf(datasource): reuse query plans on core connections (#49) Planning was 94% of Dashboard DB time, and prod never reused a plan. Force generic plans and prepare on first use. Burst: 443 -> 229 ms. --- readme.md | 8 ++++++ src/main/resources/application.yml | 6 ++++ .../configuration/DataSourceSettingsTest.java | 28 +++++++++++++++++++ 3 files changed, 42 insertions(+) create mode 100644 src/test/java/core_service/configuration/DataSourceSettingsTest.java diff --git a/readme.md b/readme.md index 70dbf22..96c65d0 100644 --- a/readme.md +++ b/readme.md @@ -909,6 +909,14 @@ duration, e.g. `GET /api/library/games 200 28 ms`. The pattern is `-` when no ha routing). The line never holds ids, query strings, user data or tokens. `/actuator/**` is skipped. An exception that leaves the filter chain is logged as `500`. +### Database connection settings + +Every Core connection runs with `plan_cache_mode = force_generic_plan` and `prepareThreshold=1` +(`spring.datasource.hikari` in `application.yml`). Planning was 94% of the Dashboard DB time and prod never reused a +plan (few users, a new connection every 30 min). With a cached generic plan the Dashboard burst dropped from 443 to +229 ms on 2 CPUs (#49). Cost: the first burst on fresh connections costs about 100 ms more (cold start: 1101 -> 1205 +ms), and a generic plan ignores parameter values. + ## Tests ```bash diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 6f0914e..ca7f3dd 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -8,6 +8,12 @@ spring: accept-float-as-int: false jpa: open-in-view: false + datasource: + hikari: + # Plan once, not per request: Dashboard burst 443 -> 229 ms, 94% of the DB time was planning (#49). + data-source-properties: + prepareThreshold: 1 + connection-init-sql: set plan_cache_mode = force_generic_plan flyway: enabled: true baseline-on-migrate: true diff --git a/src/test/java/core_service/configuration/DataSourceSettingsTest.java b/src/test/java/core_service/configuration/DataSourceSettingsTest.java new file mode 100644 index 0000000..f465e33 --- /dev/null +++ b/src/test/java/core_service/configuration/DataSourceSettingsTest.java @@ -0,0 +1,28 @@ +package core_service.configuration; + +import core_service.shared.test.AbstractPersistenceTest; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; + +import static org.assertj.core.api.Assertions.assertThat; + +class DataSourceSettingsTest extends AbstractPersistenceTest { + + @Autowired + private JdbcTemplate jdbc; + + @Test + void coreConnection_ForcesGenericPlans() { + assertThat(jdbc.queryForObject("show plan_cache_mode", String.class)).isEqualTo("force_generic_plan"); + } + + @Test + void coreConnection_PreparesOnTheFirstUse() { + jdbc.queryForObject("select 1 where ? = 1", Integer.class, 1); + + assertThat(jdbc.queryForObject( + "select count(*) from pg_prepared_statements where statement like 'select 1 where%'", Integer.class)) + .isEqualTo(1); + } +}