diff --git a/docs/inkless/configs.rst b/docs/inkless/configs.rst index 5c5e9b487c8..7845fe4e0c1 100644 --- a/docs/inkless/configs.rst +++ b/docs/inkless/configs.rst @@ -295,6 +295,94 @@ Under ``inkless.control.plane.`` * Valid Values: [1,...] * Importance: medium +``jdbc.binary.transfer`` + Enable binary transfer for better serialization performance + + * Type: boolean + * Default: true + * Importance: low + +``jdbc.default.row.fetch.size`` + Number of rows to fetch at once from the database + + * Type: int + * Default: 100 + * Valid Values: [0,...] + * Importance: low + +``jdbc.prepare.threshold`` + Number of executions before a statement is server-prepared + + * Type: int + * Default: 5 + * Valid Values: [0,...] + * Importance: low + +``jdbc.prepared.statement.cache.queries`` + Maximum number of queries to cache per connection + + * Type: int + * Default: 256 + * Valid Values: [0,...] + * Importance: low + +``jdbc.prepared.statement.cache.size.mib`` + Maximum size of the prepared statement cache in MiB + + * Type: int + * Default: 5 + * Valid Values: [0,...] + * Importance: low + +``jdbc.tcp.keep.alive`` + Enable TCP keepalive for long-lived connections + + * Type: boolean + * Default: true + * Importance: low + +``jooq.cache.record.mappers`` + Cache record mappers for faster result mapping + + * Type: boolean + * Default: true + * Importance: low + +``jooq.execute.logging`` + Enable JOOQ query logging + + * Type: boolean + * Default: false + * Importance: low + +``jooq.in.list.padding`` + Pad IN lists to powers of 2 for better query plan cache reuse + + * Type: boolean + * Default: true + * Importance: low + +``jooq.reflection.caching`` + Cache reflection operations for faster record mapping + + * Type: boolean + * Default: true + * Importance: low + +``jooq.render.catalog`` + Render catalog prefix in SQL + + * Type: boolean + * Default: false + * Importance: low + +``jooq.render.schema`` + Render schema prefix in SQL + + * Type: boolean + * Default: false + * Importance: low + ----------------- @@ -347,6 +435,94 @@ Under ``inkless.control.plane.read.`` * Valid Values: [1,...] * Importance: medium +``jdbc.binary.transfer`` + Enable binary transfer for better serialization performance + + * Type: boolean + * Default: true + * Importance: low + +``jdbc.default.row.fetch.size`` + Number of rows to fetch at once from the database + + * Type: int + * Default: 100 + * Valid Values: [0,...] + * Importance: low + +``jdbc.prepare.threshold`` + Number of executions before a statement is server-prepared + + * Type: int + * Default: 5 + * Valid Values: [0,...] + * Importance: low + +``jdbc.prepared.statement.cache.queries`` + Maximum number of queries to cache per connection + + * Type: int + * Default: 256 + * Valid Values: [0,...] + * Importance: low + +``jdbc.prepared.statement.cache.size.mib`` + Maximum size of the prepared statement cache in MiB + + * Type: int + * Default: 5 + * Valid Values: [0,...] + * Importance: low + +``jdbc.tcp.keep.alive`` + Enable TCP keepalive for long-lived connections + + * Type: boolean + * Default: true + * Importance: low + +``jooq.cache.record.mappers`` + Cache record mappers for faster result mapping + + * Type: boolean + * Default: true + * Importance: low + +``jooq.execute.logging`` + Enable JOOQ query logging + + * Type: boolean + * Default: false + * Importance: low + +``jooq.in.list.padding`` + Pad IN lists to powers of 2 for better query plan cache reuse + + * Type: boolean + * Default: true + * Importance: low + +``jooq.reflection.caching`` + Cache reflection operations for faster record mapping + + * Type: boolean + * Default: true + * Importance: low + +``jooq.render.catalog`` + Render catalog prefix in SQL + + * Type: boolean + * Default: false + * Importance: low + +``jooq.render.schema`` + Render schema prefix in SQL + + * Type: boolean + * Default: false + * Importance: low + ----------------- @@ -399,6 +575,94 @@ Under ``inkless.control.plane.write.`` * Valid Values: [1,...] * Importance: medium +``jdbc.binary.transfer`` + Enable binary transfer for better serialization performance + + * Type: boolean + * Default: true + * Importance: low + +``jdbc.default.row.fetch.size`` + Number of rows to fetch at once from the database + + * Type: int + * Default: 100 + * Valid Values: [0,...] + * Importance: low + +``jdbc.prepare.threshold`` + Number of executions before a statement is server-prepared + + * Type: int + * Default: 5 + * Valid Values: [0,...] + * Importance: low + +``jdbc.prepared.statement.cache.queries`` + Maximum number of queries to cache per connection + + * Type: int + * Default: 256 + * Valid Values: [0,...] + * Importance: low + +``jdbc.prepared.statement.cache.size.mib`` + Maximum size of the prepared statement cache in MiB + + * Type: int + * Default: 5 + * Valid Values: [0,...] + * Importance: low + +``jdbc.tcp.keep.alive`` + Enable TCP keepalive for long-lived connections + + * Type: boolean + * Default: true + * Importance: low + +``jooq.cache.record.mappers`` + Cache record mappers for faster result mapping + + * Type: boolean + * Default: true + * Importance: low + +``jooq.execute.logging`` + Enable JOOQ query logging + + * Type: boolean + * Default: false + * Importance: low + +``jooq.in.list.padding`` + Pad IN lists to powers of 2 for better query plan cache reuse + + * Type: boolean + * Default: true + * Importance: low + +``jooq.reflection.caching`` + Cache reflection operations for faster record mapping + + * Type: boolean + * Default: true + * Importance: low + +``jooq.render.catalog`` + Render catalog prefix in SQL + + * Type: boolean + * Default: false + * Importance: low + +``jooq.render.schema`` + Render schema prefix in SQL + + * Type: boolean + * Default: false + * Importance: low + ----------------- diff --git a/storage/inkless/src/main/java/io/aiven/inkless/control_plane/postgres/PostgresConnectionConfig.java b/storage/inkless/src/main/java/io/aiven/inkless/control_plane/postgres/PostgresConnectionConfig.java index 42a7fee6c95..5533ae52e58 100644 --- a/storage/inkless/src/main/java/io/aiven/inkless/control_plane/postgres/PostgresConnectionConfig.java +++ b/storage/inkless/src/main/java/io/aiven/inkless/control_plane/postgres/PostgresConnectionConfig.java @@ -37,6 +37,56 @@ public class PostgresConnectionConfig extends AbstractControlPlaneConfig { public static final String MAX_CONNECTIONS_CONFIG = "max.connections"; private static final String MAX_CONNECTIONS_DOC = "Maximum number of connections to the database"; + // JDBC driver tuning + public static final String JDBC_PREPARE_THRESHOLD_CONFIG = "jdbc.prepare.threshold"; + private static final String JDBC_PREPARE_THRESHOLD_DOC = + "Number of executions before a statement is server-prepared"; + + public static final String JDBC_PREPARED_STATEMENT_CACHE_QUERIES_CONFIG = "jdbc.prepared.statement.cache.queries"; + private static final String JDBC_PREPARED_STATEMENT_CACHE_QUERIES_DOC = + "Maximum number of queries to cache per connection"; + + public static final String JDBC_PREPARED_STATEMENT_CACHE_SIZE_MIB_CONFIG = "jdbc.prepared.statement.cache.size.mib"; + private static final String JDBC_PREPARED_STATEMENT_CACHE_SIZE_MIB_DOC = + "Maximum size of the prepared statement cache in MiB"; + + public static final String JDBC_DEFAULT_ROW_FETCH_SIZE_CONFIG = "jdbc.default.row.fetch.size"; + private static final String JDBC_DEFAULT_ROW_FETCH_SIZE_DOC = + "Number of rows to fetch at once from the database"; + + public static final String JDBC_TCP_KEEP_ALIVE_CONFIG = "jdbc.tcp.keep.alive"; + private static final String JDBC_TCP_KEEP_ALIVE_DOC = + "Enable TCP keepalive for long-lived connections"; + + public static final String JDBC_BINARY_TRANSFER_CONFIG = "jdbc.binary.transfer"; + private static final String JDBC_BINARY_TRANSFER_DOC = + "Enable binary transfer for better serialization performance"; + + // JOOQ settings + public static final String JOOQ_EXECUTE_LOGGING_CONFIG = "jooq.execute.logging"; + private static final String JOOQ_EXECUTE_LOGGING_DOC = + "Enable JOOQ query logging"; + + public static final String JOOQ_RENDER_CATALOG_CONFIG = "jooq.render.catalog"; + private static final String JOOQ_RENDER_CATALOG_DOC = + "Render catalog prefix in SQL"; + + public static final String JOOQ_RENDER_SCHEMA_CONFIG = "jooq.render.schema"; + private static final String JOOQ_RENDER_SCHEMA_DOC = + "Render schema prefix in SQL"; + + public static final String JOOQ_REFLECTION_CACHING_CONFIG = "jooq.reflection.caching"; + private static final String JOOQ_REFLECTION_CACHING_DOC = + "Cache reflection operations for faster record mapping"; + + public static final String JOOQ_CACHE_RECORD_MAPPERS_CONFIG = "jooq.cache.record.mappers"; + private static final String JOOQ_CACHE_RECORD_MAPPERS_DOC = + "Cache record mappers for faster result mapping"; + + public static final String JOOQ_IN_LIST_PADDING_CONFIG = "jooq.in.list.padding"; + private static final String JOOQ_IN_LIST_PADDING_DOC = + "Pad IN lists to powers of 2 for better query plan cache reuse"; + public static ConfigDef configDef() { return baseConfigDef() .define( @@ -70,6 +120,96 @@ public static ConfigDef configDef() { ConfigDef.Range.atLeast(1), ConfigDef.Importance.MEDIUM, MAX_CONNECTIONS_DOC + ) + // JDBC driver tuning + .define( + JDBC_PREPARE_THRESHOLD_CONFIG, + ConfigDef.Type.INT, + 5, + ConfigDef.Range.atLeast(0), + ConfigDef.Importance.LOW, + JDBC_PREPARE_THRESHOLD_DOC + ) + .define( + JDBC_PREPARED_STATEMENT_CACHE_QUERIES_CONFIG, + ConfigDef.Type.INT, + 256, + ConfigDef.Range.atLeast(0), + ConfigDef.Importance.LOW, + JDBC_PREPARED_STATEMENT_CACHE_QUERIES_DOC + ) + .define( + JDBC_PREPARED_STATEMENT_CACHE_SIZE_MIB_CONFIG, + ConfigDef.Type.INT, + 5, + ConfigDef.Range.atLeast(0), + ConfigDef.Importance.LOW, + JDBC_PREPARED_STATEMENT_CACHE_SIZE_MIB_DOC + ) + .define( + JDBC_DEFAULT_ROW_FETCH_SIZE_CONFIG, + ConfigDef.Type.INT, + 100, + ConfigDef.Range.atLeast(0), + ConfigDef.Importance.LOW, + JDBC_DEFAULT_ROW_FETCH_SIZE_DOC + ) + .define( + JDBC_TCP_KEEP_ALIVE_CONFIG, + ConfigDef.Type.BOOLEAN, + true, + ConfigDef.Importance.LOW, + JDBC_TCP_KEEP_ALIVE_DOC + ) + .define( + JDBC_BINARY_TRANSFER_CONFIG, + ConfigDef.Type.BOOLEAN, + true, + ConfigDef.Importance.LOW, + JDBC_BINARY_TRANSFER_DOC + ) + // JOOQ settings + .define( + JOOQ_EXECUTE_LOGGING_CONFIG, + ConfigDef.Type.BOOLEAN, + false, + ConfigDef.Importance.LOW, + JOOQ_EXECUTE_LOGGING_DOC + ) + .define( + JOOQ_RENDER_CATALOG_CONFIG, + ConfigDef.Type.BOOLEAN, + false, + ConfigDef.Importance.LOW, + JOOQ_RENDER_CATALOG_DOC + ) + .define( + JOOQ_RENDER_SCHEMA_CONFIG, + ConfigDef.Type.BOOLEAN, + false, + ConfigDef.Importance.LOW, + JOOQ_RENDER_SCHEMA_DOC + ) + .define( + JOOQ_REFLECTION_CACHING_CONFIG, + ConfigDef.Type.BOOLEAN, + true, + ConfigDef.Importance.LOW, + JOOQ_REFLECTION_CACHING_DOC + ) + .define( + JOOQ_CACHE_RECORD_MAPPERS_CONFIG, + ConfigDef.Type.BOOLEAN, + true, + ConfigDef.Importance.LOW, + JOOQ_CACHE_RECORD_MAPPERS_DOC + ) + .define( + JOOQ_IN_LIST_PADDING_CONFIG, + ConfigDef.Type.BOOLEAN, + true, + ConfigDef.Importance.LOW, + JOOQ_IN_LIST_PADDING_DOC ); } @@ -93,4 +233,54 @@ public String password() { public int maxConnections() { return getInt(MAX_CONNECTIONS_CONFIG); } + + // JDBC driver tuning getters + public int jdbcPrepareThreshold() { + return getInt(JDBC_PREPARE_THRESHOLD_CONFIG); + } + + public int jdbcPreparedStatementCacheQueries() { + return getInt(JDBC_PREPARED_STATEMENT_CACHE_QUERIES_CONFIG); + } + + public int jdbcPreparedStatementCacheSizeMib() { + return getInt(JDBC_PREPARED_STATEMENT_CACHE_SIZE_MIB_CONFIG); + } + + public int jdbcDefaultRowFetchSize() { + return getInt(JDBC_DEFAULT_ROW_FETCH_SIZE_CONFIG); + } + + public boolean jdbcTcpKeepAlive() { + return getBoolean(JDBC_TCP_KEEP_ALIVE_CONFIG); + } + + public boolean jdbcBinaryTransfer() { + return getBoolean(JDBC_BINARY_TRANSFER_CONFIG); + } + + // JOOQ settings getters + public boolean jooqExecuteLogging() { + return getBoolean(JOOQ_EXECUTE_LOGGING_CONFIG); + } + + public boolean jooqRenderCatalog() { + return getBoolean(JOOQ_RENDER_CATALOG_CONFIG); + } + + public boolean jooqRenderSchema() { + return getBoolean(JOOQ_RENDER_SCHEMA_CONFIG); + } + + public boolean jooqReflectionCaching() { + return getBoolean(JOOQ_REFLECTION_CACHING_CONFIG); + } + + public boolean jooqCacheRecordMappers() { + return getBoolean(JOOQ_CACHE_RECORD_MAPPERS_CONFIG); + } + + public boolean jooqInListPadding() { + return getBoolean(JOOQ_IN_LIST_PADDING_CONFIG); + } } diff --git a/storage/inkless/src/main/java/io/aiven/inkless/control_plane/postgres/PostgresControlPlane.java b/storage/inkless/src/main/java/io/aiven/inkless/control_plane/postgres/PostgresControlPlane.java index 0e7d3ad99b0..19f9b261810 100644 --- a/storage/inkless/src/main/java/io/aiven/inkless/control_plane/postgres/PostgresControlPlane.java +++ b/storage/inkless/src/main/java/io/aiven/inkless/control_plane/postgres/PostgresControlPlane.java @@ -28,6 +28,7 @@ import org.jooq.DSLContext; import org.jooq.SQLDialect; +import org.jooq.conf.Settings; import org.jooq.generated.udt.records.CommitFileMergeWorkItemBatchV1Record; import org.jooq.generated.udt.records.CommitFileMergeWorkItemResponseV1Record; import org.jooq.impl.DSL; @@ -107,13 +108,15 @@ public void configure(final Map configs) { throw new RuntimeException(e); } - jobsJooqCtx = DSL.using(jobsDataSource, SQLDialect.POSTGRES); + final Settings jobsJooqSettings = jooqSettings(controlPlaneConfig); + jobsJooqCtx = DSL.using(jobsDataSource, SQLDialect.POSTGRES, jobsJooqSettings); // Set up read and write contexts if configured + // Each context uses its own jooqSettings to respect per-context overrides if (controlPlaneConfig.writeConfig() != null) { LOGGER.info("Using separate write configuration"); writeDataSource = new HikariDataSource(dataSourceConfig(metrics, POOL_NAME + "-write", controlPlaneConfig.writeConfig())); - writeJooqCtx = DSL.using(writeDataSource, SQLDialect.POSTGRES); + writeJooqCtx = DSL.using(writeDataSource, SQLDialect.POSTGRES, jooqSettings(controlPlaneConfig.writeConfig())); } else { LOGGER.info("No separate write configuration found, using jobs context for writes"); writeJooqCtx = jobsJooqCtx; @@ -121,13 +124,26 @@ public void configure(final Map configs) { if (controlPlaneConfig.readConfig() != null) { LOGGER.info("Using separate read configuration"); readDataSource = new HikariDataSource(dataSourceConfig(metrics, POOL_NAME + "-read", controlPlaneConfig.readConfig())); - readJooqCtx = DSL.using(readDataSource, SQLDialect.POSTGRES); + readJooqCtx = DSL.using(readDataSource, SQLDialect.POSTGRES, jooqSettings(controlPlaneConfig.readConfig())); } else { - LOGGER.info("No separate write configuration found, using jobs context for reads"); + LOGGER.info("No separate read configuration found, using jobs context for reads"); readJooqCtx = jobsJooqCtx; } } + /** + * Creates JOOQ Settings from configuration. + */ + private static Settings jooqSettings(final PostgresConnectionConfig config) { + return new Settings() + .withExecuteLogging(config.jooqExecuteLogging()) + .withRenderCatalog(config.jooqRenderCatalog()) + .withRenderSchema(config.jooqRenderSchema()) + .withReflectionCaching(config.jooqReflectionCaching()) + .withCacheRecordMappers(config.jooqCacheRecordMappers()) + .withInListPadding(config.jooqInListPadding()); + } + private static HikariConfig dataSourceConfig(final KafkaMetricsGroup metrics, final String name, final PostgresConnectionConfig connectionConfig) { final HikariConfig config = new HikariConfig(); config.setPoolName(name); @@ -136,11 +152,17 @@ private static HikariConfig dataSourceConfig(final KafkaMetricsGroup metrics, fi config.setPassword(connectionConfig.password()); config.setMetricsTrackerFactory((poolName, poolStats) -> new PostgresConnectionPoolMetrics(metrics, poolName, poolStats)); config.setTransactionIsolation(IsolationLevel.TRANSACTION_READ_COMMITTED.name()); - config.setMaximumPoolSize(connectionConfig.maxConnections()); - - // We're doing interactive transactions. config.setAutoCommit(false); + + // PostgreSQL JDBC driver tuning + config.addDataSourceProperty("prepareThreshold", connectionConfig.jdbcPrepareThreshold()); + config.addDataSourceProperty("preparedStatementCacheQueries", connectionConfig.jdbcPreparedStatementCacheQueries()); + config.addDataSourceProperty("preparedStatementCacheSizeMiB", connectionConfig.jdbcPreparedStatementCacheSizeMib()); + config.addDataSourceProperty("defaultRowFetchSize", connectionConfig.jdbcDefaultRowFetchSize()); + config.addDataSourceProperty("tcpKeepAlive", connectionConfig.jdbcTcpKeepAlive()); + config.addDataSourceProperty("binaryTransfer", connectionConfig.jdbcBinaryTransfer()); + return config; } diff --git a/storage/inkless/src/test/java/io/aiven/inkless/control_plane/postgres/PostgresControlPlaneConfigTest.java b/storage/inkless/src/test/java/io/aiven/inkless/control_plane/postgres/PostgresControlPlaneConfigTest.java index c21e194e706..5a4bfc99dc5 100644 --- a/storage/inkless/src/test/java/io/aiven/inkless/control_plane/postgres/PostgresControlPlaneConfigTest.java +++ b/storage/inkless/src/test/java/io/aiven/inkless/control_plane/postgres/PostgresControlPlaneConfigTest.java @@ -176,4 +176,132 @@ void writeReadConfigs() { assertThat(config.writeConfig().username()).isEqualTo("username-w"); assertThat(config.writeConfig().password()).isEqualTo("password-w"); } + + @Test + void jdbcTuningDefaults() { + // Verify JDBC tuning config defaults + final var config = new PostgresControlPlaneConfig( + Map.of( + "connection.string", "jdbc:postgresql://127.0.0.1:5432/inkless", + "username", "username", + "password", "password" + ) + ); + + // JDBC driver tuning defaults + assertThat(config.jdbcPrepareThreshold()).isEqualTo(5); + assertThat(config.jdbcPreparedStatementCacheQueries()).isEqualTo(256); + assertThat(config.jdbcPreparedStatementCacheSizeMib()).isEqualTo(5); + assertThat(config.jdbcDefaultRowFetchSize()).isEqualTo(100); + assertThat(config.jdbcTcpKeepAlive()).isTrue(); + assertThat(config.jdbcBinaryTransfer()).isTrue(); + } + + @Test + void jooqSettingsDefaults() { + // Verify JOOQ settings defaults + final var config = new PostgresControlPlaneConfig( + Map.of( + "connection.string", "jdbc:postgresql://127.0.0.1:5432/inkless", + "username", "username", + "password", "password" + ) + ); + + // JOOQ settings defaults + assertThat(config.jooqExecuteLogging()).isFalse(); + assertThat(config.jooqRenderCatalog()).isFalse(); + assertThat(config.jooqRenderSchema()).isFalse(); + assertThat(config.jooqReflectionCaching()).isTrue(); + assertThat(config.jooqCacheRecordMappers()).isTrue(); + assertThat(config.jooqInListPadding()).isTrue(); + } + + @Test + void jdbcTuningOverrides() { + // Verify JDBC tuning configs can be overridden + final var config = new PostgresControlPlaneConfig( + Map.of( + "connection.string", "jdbc:postgresql://127.0.0.1:5432/inkless", + "username", "username", + "password", "password", + "jdbc.prepare.threshold", "10", + "jdbc.prepared.statement.cache.queries", "512", + "jdbc.prepared.statement.cache.size.mib", "10", + "jdbc.default.row.fetch.size", "500", + "jdbc.tcp.keep.alive", "false", + "jdbc.binary.transfer", "false" + ) + ); + + assertThat(config.jdbcPrepareThreshold()).isEqualTo(10); + assertThat(config.jdbcPreparedStatementCacheQueries()).isEqualTo(512); + assertThat(config.jdbcPreparedStatementCacheSizeMib()).isEqualTo(10); + assertThat(config.jdbcDefaultRowFetchSize()).isEqualTo(500); + assertThat(config.jdbcTcpKeepAlive()).isFalse(); + assertThat(config.jdbcBinaryTransfer()).isFalse(); + } + + @Test + void jooqSettingsOverrides() { + // Verify JOOQ settings can be overridden + final var config = new PostgresControlPlaneConfig( + Map.of( + "connection.string", "jdbc:postgresql://127.0.0.1:5432/inkless", + "username", "username", + "password", "password", + "jooq.execute.logging", "true", + "jooq.render.catalog", "true", + "jooq.render.schema", "true", + "jooq.reflection.caching", "false", + "jooq.cache.record.mappers", "false", + "jooq.in.list.padding", "false" + ) + ); + + assertThat(config.jooqExecuteLogging()).isTrue(); + assertThat(config.jooqRenderCatalog()).isTrue(); + assertThat(config.jooqRenderSchema()).isTrue(); + assertThat(config.jooqReflectionCaching()).isFalse(); + assertThat(config.jooqCacheRecordMappers()).isFalse(); + assertThat(config.jooqInListPadding()).isFalse(); + } + + @Test + void readWritePrefixedJdbcJooqOverrides() { + // Verify read/write prefixed JDBC/JOOQ configs are properly parsed + final Map configs = new HashMap<>(); + configs.put("connection.string", "jdbc:postgresql://127.0.0.1:5432/inkless"); + configs.put("username", "username"); + configs.put("password", "password"); + // Read-prefixed overrides + configs.put("read.connection.string", "jdbc:postgresql://127.0.0.1:5432/inkless-read"); + configs.put("read.username", "username-r"); + configs.put("read.password", "password-r"); + configs.put("read.jdbc.default.row.fetch.size", "1000"); // Higher for read replicas + configs.put("read.jooq.execute.logging", "true"); + // Write-prefixed overrides + configs.put("write.connection.string", "jdbc:postgresql://127.0.0.1:5432/inkless-write"); + configs.put("write.username", "username-w"); + configs.put("write.password", "password-w"); + configs.put("write.jdbc.default.row.fetch.size", "50"); // Lower for write primary + configs.put("write.jooq.execute.logging", "false"); + + final var config = new PostgresControlPlaneConfig(configs); + config.initializeReadWriteConfigs(); + + // Base config has defaults + assertThat(config.jdbcDefaultRowFetchSize()).isEqualTo(100); + assertThat(config.jooqExecuteLogging()).isFalse(); + + // Read config has overrides + assertThat(config.readConfig()).isNotNull(); + assertThat(config.readConfig().jdbcDefaultRowFetchSize()).isEqualTo(1000); + assertThat(config.readConfig().jooqExecuteLogging()).isTrue(); + + // Write config has overrides + assertThat(config.writeConfig()).isNotNull(); + assertThat(config.writeConfig().jdbcDefaultRowFetchSize()).isEqualTo(50); + assertThat(config.writeConfig().jooqExecuteLogging()).isFalse(); + } }