From 96bcb022a31d105106d7dcebfd5eb9055dc7895d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Harri=20Sm=C3=A5tt?= Date: Sun, 15 Oct 2023 11:14:41 +0300 Subject: [PATCH] [#3562] Migrate to Quarkus JDBC implementation --- services/base-jdbc/pom.xml | 6 +- .../service/base/jdbc/config/JdbcOptions.java | 26 +++++- .../base/jdbc/config/JdbcProperties.java | 90 +++++++++++++++++-- .../store/device/AbstractDeviceStore.java | 4 +- .../store/device/TableManagementStore.java | 12 +-- .../jdbc/store/tenant/ManagementStore.java | 6 +- services/device-registry-jdbc/pom.xml | 14 ++- .../src/main/resources/application.properties | 3 + .../jdbc/impl/AbstractJdbcRegistryTest.java | 4 +- .../jdbc-device-registry-config.md | 12 +++ 10 files changed, 155 insertions(+), 22 deletions(-) diff --git a/services/base-jdbc/pom.xml b/services/base-jdbc/pom.xml index 7f5b001982..faee15f781 100644 --- a/services/base-jdbc/pom.xml +++ b/services/base-jdbc/pom.xml @@ -1,6 +1,6 @@ diff --git a/services/base-jdbc/src/main/java/org/eclipse/hono/service/base/jdbc/config/JdbcOptions.java b/services/base-jdbc/src/main/java/org/eclipse/hono/service/base/jdbc/config/JdbcOptions.java index 9a99158780..903e34abbf 100644 --- a/services/base-jdbc/src/main/java/org/eclipse/hono/service/base/jdbc/config/JdbcOptions.java +++ b/services/base-jdbc/src/main/java/org/eclipse/hono/service/base/jdbc/config/JdbcOptions.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2022 Contributors to the Eclipse Foundation + * Copyright (c) 2022, 2023 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -85,6 +85,30 @@ public interface JdbcOptions { @WithDefault("3600") int maximumIdleTime(); + /** + * Gets the maximum connection time for acquiring a connection from the DB connection pool. + * + * @return The maximum connection time for acquiring a connection from the pool. + */ + @WithDefault("30") + int maximumConnectionTime(); + + /** + * Gets the connection validation time interval in the DB connection pool. + * + * @return The connection validation time interval in the pool. + */ + @WithDefault("30") + int validationTime(); + + /** + * Gets the connection leak time limit from the DB connection pool. + * + * @return The connection leak time limit from the pool. + */ + @WithDefault("60") + int leakTime(); + /** * Gets the name of the table that contains the data. * diff --git a/services/base-jdbc/src/main/java/org/eclipse/hono/service/base/jdbc/config/JdbcProperties.java b/services/base-jdbc/src/main/java/org/eclipse/hono/service/base/jdbc/config/JdbcProperties.java index 2c8798f4d6..621e71fcff 100644 --- a/services/base-jdbc/src/main/java/org/eclipse/hono/service/base/jdbc/config/JdbcProperties.java +++ b/services/base-jdbc/src/main/java/org/eclipse/hono/service/base/jdbc/config/JdbcProperties.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2020, 2022 Contributors to the Eclipse Foundation + * Copyright (c) 2020, 2023 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -13,7 +13,9 @@ package org.eclipse.hono.service.base.jdbc.config; +import java.time.Duration; import java.util.Objects; +import java.util.Optional; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -21,6 +23,12 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; +import io.agroal.api.configuration.AgroalConnectionPoolConfiguration.ConnectionValidator; +import io.agroal.api.configuration.AgroalDataSourceConfiguration.DataSourceImplementation; +import io.agroal.api.configuration.supplier.AgroalDataSourceConfigurationSupplier; +import io.agroal.api.security.NamePrincipal; +import io.agroal.api.security.SimplePassword; +import io.agroal.pool.DataSource; import io.vertx.core.Vertx; import io.vertx.core.json.JsonObject; import io.vertx.ext.jdbc.JDBCClient; @@ -35,6 +43,9 @@ public class JdbcProperties { public static final int DEFAULT_MINIMUM_POOL_SIZE = 3; public static final int DEFAULT_INITIAL_POOL_SIZE = 3; public static final int DEFAULT_MAXIMUM_IDLE_TIME = 3600; + public static final int DEFAULT_MAXIMUM_CONNECTION_TIME = 30; + public static final int DEFAULT_VALIDATION_TIME = 30; + public static final int DEFAULT_LEAK_TIME = 60; private static final Logger log = LoggerFactory.getLogger(JdbcProperties.class); private String url; @@ -45,6 +56,9 @@ public class JdbcProperties { private int minimumPoolSize = DEFAULT_MINIMUM_POOL_SIZE; private int initialPoolSize = DEFAULT_INITIAL_POOL_SIZE; private int maximumIdleTime = DEFAULT_MAXIMUM_IDLE_TIME; + private int maximumConnectionTime = DEFAULT_MAXIMUM_CONNECTION_TIME; + private int validationTime = DEFAULT_VALIDATION_TIME; + private int leakTime = DEFAULT_LEAK_TIME; private String tableName; /** @@ -67,6 +81,9 @@ public JdbcProperties(final JdbcOptions options) { setMinimumPoolSize(options.minimumPoolSize()); setInitialPoolSize(options.initialPoolSize()); setMaximumIdleTime(options.maximumIdleTime()); + setMaximumConnectionTime(options.maximumConnectionTime()); + setValidationTime(options.validationTime()); + setLeakTime(options.leakTime()); options.password().ifPresent(this::setPassword); options.tableName().ifPresent(this::setTableName); setUrl(options.url()); @@ -129,6 +146,27 @@ public int getMaximumIdleTime() { return maximumIdleTime; } + public void setMaximumConnectionTime(final int maximumConnectionTime) { + this.maximumConnectionTime = maximumConnectionTime; + } + public int getMaximumConnectionTime() { + return maximumConnectionTime; + } + + public void setValidationTime(final int validationTime) { + this.validationTime = validationTime; + } + public int getValidationTime() { + return validationTime; + } + + public void setLeakTime(final int leakTime) { + this.leakTime = leakTime; + } + public int getLeakTime() { + return leakTime; + } + public String getTableName() { return tableName; } @@ -155,11 +193,18 @@ public static JDBCClient dataSource(final Vertx vertx, final JdbcProperties data config.put("driver_class", dataSourceProperties.getDriverClass()); } + final String maxIdleLabel = "max_idle_time"; + final String maxConnectionLabel = "max_connection_time"; + final String validationLabel = "validation_time"; + final String leakLabel = "leak_time"; final String minSizeLabel = "min_pool_size"; final String maxSizeLabel = "max_pool_size"; final String initSizeLabel = "initial_pool_size"; - putValidValueIntoConfig(config, "max_idle_time", dataSourceProperties.getMaximumIdleTime(), 0, true); + putValidValueIntoConfig(config, maxIdleLabel, dataSourceProperties.getMaximumIdleTime(), 0, true); + putValidValueIntoConfig(config, maxConnectionLabel, dataSourceProperties.getMaximumConnectionTime(), 0, true); + putValidValueIntoConfig(config, validationLabel, dataSourceProperties.getValidationTime(), 0, true); + putValidValueIntoConfig(config, leakLabel, dataSourceProperties.getLeakTime(), 0, true); putValidValueIntoConfig(config, minSizeLabel, dataSourceProperties.getMinimumPoolSize(), 0, true); putValidValueIntoConfig(config, maxSizeLabel, dataSourceProperties.getMaximumPoolSize(), Math.max(1, config.getInteger(minSizeLabel)), true); // check that initial pool size is between min and max pool size @@ -168,14 +213,43 @@ public static JDBCClient dataSource(final Vertx vertx, final JdbcProperties data log.info("Creating new SQL client: {} - table: {}", config, dataSourceProperties.getTableName()); - // put password after logging - - config - .put("password", dataSourceProperties.getPassword()); - // create new client - return JDBCClient.create(vertx, config); + final int minSize = config.getInteger(minSizeLabel); + final int maxSize = config.getInteger(maxSizeLabel); + final int initSize = config.getInteger(initSizeLabel); + final Duration idleTime = Duration.ofSeconds(config.getInteger(maxIdleLabel)); + final Duration connectionTime = Duration.ofSeconds(config.getInteger(maxConnectionLabel)); + final Duration validationTime = Duration.ofSeconds(config.getInteger(validationLabel)); + final Duration leakTime = Duration.ofSeconds(config.getInteger(leakLabel)); + final NamePrincipal username = Optional + .ofNullable(dataSourceProperties.getUsername()) + .map(NamePrincipal::new) + .orElse(null); + final SimplePassword password = Optional + .ofNullable(dataSourceProperties.getPassword()) + .map(SimplePassword::new) + .orElse(null); + + final AgroalDataSourceConfigurationSupplier configuration = new AgroalDataSourceConfigurationSupplier() + .metricsEnabled(false) + .dataSourceImplementation(DataSourceImplementation.AGROAL) + .connectionPoolConfiguration(poolConfig -> poolConfig + .minSize(minSize) + .maxSize(maxSize) + .initialSize(initSize) + .acquisitionTimeout(connectionTime) + .validationTimeout(validationTime) + .leakTimeout(leakTime) + .reapTimeout(idleTime) + .connectionValidator(ConnectionValidator.defaultValidator()) + .connectionFactoryConfiguration(connConfig -> connConfig + .jdbcUrl(dataSourceProperties.getUrl()) + .connectionProviderClassName(dataSourceProperties.getDriverClass()) + .principal(username) + .credential(password))); + + return JDBCClient.create(vertx, new DataSource(configuration.get())); } diff --git a/services/base-jdbc/src/main/java/org/eclipse/hono/service/base/jdbc/store/device/AbstractDeviceStore.java b/services/base-jdbc/src/main/java/org/eclipse/hono/service/base/jdbc/store/device/AbstractDeviceStore.java index 91363f447b..7db6a3c195 100644 --- a/services/base-jdbc/src/main/java/org/eclipse/hono/service/base/jdbc/store/device/AbstractDeviceStore.java +++ b/services/base-jdbc/src/main/java/org/eclipse/hono/service/base/jdbc/store/device/AbstractDeviceStore.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2020, 2022 Contributors to the Eclipse Foundation + * Copyright (c) 2020, 2023 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -124,7 +124,7 @@ protected Future read(final SQLOperations operations, final DeviceKey return expanded .trace(this.tracer, spanContext) - .query(this.client); + .query(operations); } diff --git a/services/base-jdbc/src/main/java/org/eclipse/hono/service/base/jdbc/store/device/TableManagementStore.java b/services/base-jdbc/src/main/java/org/eclipse/hono/service/base/jdbc/store/device/TableManagementStore.java index 595d846a1a..66ad853c4b 100644 --- a/services/base-jdbc/src/main/java/org/eclipse/hono/service/base/jdbc/store/device/TableManagementStore.java +++ b/services/base-jdbc/src/main/java/org/eclipse/hono/service/base/jdbc/store/device/TableManagementStore.java @@ -59,6 +59,7 @@ import io.vertx.ext.jdbc.JDBCClient; import io.vertx.ext.sql.ResultSet; import io.vertx.ext.sql.SQLConnection; +import io.vertx.ext.sql.SQLOperations; import io.vertx.ext.sql.UpdateResult; /** @@ -328,14 +329,14 @@ public Future> createDevice( log.debug("createDevice - statement: {}", expanded); - return getDeviceCount(key.getTenantId(), span.context(), this.countDevicesOfTenantStatement, null, null) + return getDeviceCount(connection, key.getTenantId(), span.context(), this.countDevicesOfTenantStatement, null, null) .compose(currentDeviceCount -> tenant.checkDeviceLimitReached( key.getTenantId(), currentDeviceCount, globalDevicesPerTenantLimit)) .compose(ok -> expanded .trace(this.tracer, context) - .update(this.client) + .update(connection) .recover(SQL::translateException)) .compose(x -> createGroups(connection, key, new HashSet<>(device.getMemberOf()), context)); @@ -649,6 +650,7 @@ public Future dropTenant(final String tenantId, final SpanContext /** * Gets the number of devices that are registered for a tenant. * + * @param operations The SQL operations instance to use. * @param tenantId The tenant to count devices for. * @param spanContext The span to contribute to. * @param countStatement The count statement to use. @@ -657,7 +659,7 @@ public Future dropTenant(final String tenantId, final SpanContext * @return A future tracking the outcome of the operation. * @throws NullPointerException if tenant is {@code null}. */ - public Future getDeviceCount(final String tenantId, final SpanContext spanContext, final Statement countStatement, final String field, final String value) { + public Future getDeviceCount(final SQLOperations operations, final String tenantId, final SpanContext spanContext, final Statement countStatement, final String field, final String value) { Objects.requireNonNull(tenantId); @@ -675,7 +677,7 @@ public Future getDeviceCount(final String tenantId, final SpanContext s return expanded .trace(this.tracer, span.context()) - .query(this.client) + .query(operations) .map(r -> { final var entries = r.getRows(true); switch (entries.size()) { @@ -1007,7 +1009,7 @@ public Future> findDevices(final String tenantId, fin .withTag(TracingHelper.TAG_TENANT_ID, tenantId) .start(); - final Future deviceCountFuture = getDeviceCount(tenantId, span.context(), countStatement, field, value); + final Future deviceCountFuture = getDeviceCount(this.client, tenantId, span.context(), countStatement, field, value); return deviceCountFuture .compose(count -> expanded.trace(this.tracer, span.context()).query(this.client)) diff --git a/services/base-jdbc/src/main/java/org/eclipse/hono/service/base/jdbc/store/tenant/ManagementStore.java b/services/base-jdbc/src/main/java/org/eclipse/hono/service/base/jdbc/store/tenant/ManagementStore.java index 20e843fbe7..5627b885b9 100644 --- a/services/base-jdbc/src/main/java/org/eclipse/hono/service/base/jdbc/store/tenant/ManagementStore.java +++ b/services/base-jdbc/src/main/java/org/eclipse/hono/service/base/jdbc/store/tenant/ManagementStore.java @@ -220,7 +220,7 @@ public Future> create(final String tenantId, final Tenant tenant log.debug("create - statement: {}", expanded); return expanded .trace(this.tracer, span.context()) - .update(this.client) + .update(connection) .recover(SQL::translateException) // insert all trust anchors @@ -443,13 +443,13 @@ protected Future updateJsonField( // execute update final var result = expanded .trace(this.tracer, span.context()) - .update(this.client); + .update(operations); // process result, check optimistic lock return checkOptimisticLock( result, span, resourceVersion, - checkSpan -> readTenantEntryById(this.client, tenantId, checkSpan.context())); + checkSpan -> readTenantEntryById(operations, tenantId, checkSpan.context())); } /** diff --git a/services/device-registry-jdbc/pom.xml b/services/device-registry-jdbc/pom.xml index 990c2d1c62..6a8d90a6e5 100644 --- a/services/device-registry-jdbc/pom.xml +++ b/services/device-registry-jdbc/pom.xml @@ -1,6 +1,6 @@ + + io.quarkus + quarkus-jdbc-postgresql + + + io.quarkus + quarkus-jdbc-h2 + com.h2database h2 diff --git a/services/device-registry-jdbc/src/main/resources/application.properties b/services/device-registry-jdbc/src/main/resources/application.properties index 35eb4801a1..7be05aa117 100644 --- a/services/device-registry-jdbc/src/main/resources/application.properties +++ b/services/device-registry-jdbc/src/main/resources/application.properties @@ -3,3 +3,6 @@ quarkus.jackson.accept-case-insensitive-enums=true # fail deserialization of JSON objects sent by clients if they contain unexpected content quarkus.jackson.fail-on-unknown-properties=true +# enable h2 and postgres extensions +quarkus.datasource.h2.db-kind=h2 +quarkus.datasource.pg.db-kind=pg diff --git a/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/AbstractJdbcRegistryTest.java b/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/AbstractJdbcRegistryTest.java index 53d6a500ed..40b5d855b9 100644 --- a/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/AbstractJdbcRegistryTest.java +++ b/services/device-registry-jdbc/src/test/java/org/eclipse/hono/deviceregistry/jdbc/impl/AbstractJdbcRegistryTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2020, 2022 Contributors to the Eclipse Foundation + * Copyright (c) 2020, 2023 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. @@ -153,6 +153,8 @@ void startDevices(final Vertx vertx) throws IOException, SQLException { private JdbcProperties resolveJdbcProperties() { final var jdbc = new JdbcProperties(); + jdbc.setInitialPoolSize(0); + jdbc.setMinimumPoolSize(0); if (DATABASE_TYPE != DatabaseType.H2) { final JdbcDatabaseContainer databaseContainer = getDatabaseContainer(); jdbc.setDriverClass(databaseContainer.getDriverClassName()); diff --git a/site/documentation/content/admin-guide/jdbc-device-registry-config.md b/site/documentation/content/admin-guide/jdbc-device-registry-config.md index 7ac4dcbb6a..29d5467e8b 100644 --- a/site/documentation/content/admin-guide/jdbc-device-registry-config.md +++ b/site/documentation/content/admin-guide/jdbc-device-registry-config.md @@ -69,6 +69,9 @@ and availability. | `HONO_REGISTRY_JDBC_ADAPTER_MINIMUMPOOLSIZE`
`hono.registry.jdbc.adapter.minimumPoolSize` | no | `3` | The minimum size of the connection pool. | | `HONO_REGISTRY_JDBC_ADAPTER_INITIALPOOLSIZE`
`hono.registry.jdbc.adapter.initialPoolSize` | no | `3` | Number of connections a pool will try to acquire upon startup. Should be between minPoolSize and maxPoolSize. | | `HONO_REGISTRY_JDBC_ADAPTER_MAXIMUMIDLETIME`
`hono.registry.jdbc.adapter.maximumIdleTime` | no | `3600` | Seconds a connection can remain pooled but unused before being discarded. Zero means idle connections never expire. | +| `HONO_REGISTRY_JDBC_ADAPTER_MAXIMUMCONNECTIONTIME`
`hono.registry.jdbc.adapter.maximumConnectionTime` | no | `30` | Maximum wait time in seconds to obtain a connection from the pool before the request fails. A value of zero disables the timeout. | +| `HONO_REGISTRY_JDBC_ADAPTER_VALIDATIONTIME`
`hono.registry.jdbc.adapter.validationTime` | no | `30` | Interval, in seconds, in which connections in the pool are validated. A value of zero means connection validation is disabled. | +| `HONO_REGISTRY_JDBC_ADAPTER_LEAKTIME`
`hono.registry.jdbc.adapter.leakTime` | no | `60` | Seconds after which a connection is considered to be leaking if it has not returned to the pool. A value of zero means connection leakage detection is disabled. | | `HONO_REGISTRY_JDBC_ADAPTER_TABLENAME`
`hono.registry.jdbc.adapter.tableName` | no | - | The name of the table the datastore uses. If the datastore requires multiple tables, this is the prefix. | | `HONO_REGISTRY_JDBC_MANAGEMENT_URL`
`hono.registry.jdbc.management.url` | yes | - | The JDBC URL to the database. | | `HONO_REGISTRY_JDBC_MANAGEMENT_DRIVERCLASS`
`hono.registry.jdbc.management.driverClass` | no | The default driver registered for the JDBC URL. | The class name of the JDBC driver. | @@ -78,6 +81,9 @@ and availability. | `HONO_REGISTRY_JDBC_MANAGEMENT_MINIMUMPOOLSIZE`
`hono.registry.jdbc.management.minimumPoolSize` | no | `3` | The minimum size of the connection pool. | | `HONO_REGISTRY_JDBC_MANAGEMENT_INITIALPOOLSIZE`
`hono.registry.jdbc.management.initialPoolSize` | no | `3` | Number of connections a pool will try to acquire upon startup. Should be between minPoolSize and maxPoolSize. | | `HONO_REGISTRY_JDBC_MANAGEMENT_MAXIMUMIDLETIME`
`hono.registry.jdbc.management.maximumIdleTime` | no | `3600` | Seconds a connection can remain pooled but unused before being discarded. Zero means idle connections never expire. | +| `HONO_REGISTRY_JDBC_MANAGEMENT_MAXIMUMCONNECTIONTIME`
`hono.registry.jdbc.management.maximumConnectionTime` | no | `30` | Maximum wait time in seconds to obtain a connection from the pool before the request fails. A value of zero disables the timeout. | +| `HONO_REGISTRY_JDBC_MANAGEMENT_VALIDATIONTIME`
`hono.registry.jdbc.management.validationTime` | no | `30` | Interval, in seconds, in which connections in the pool are validated. A value of zero means connection validation is disabled. | +| `HONO_REGISTRY_JDBC_MANAGEMENT_LEAKTIME`
`hono.registry.jdbc.management.leakTime` | no | `60` | Seconds after which a connection is considered to be leaking if it has not returned to the pool. A value of zero means connection leakage detection is disabled. | | `HONO_REGISTRY_JDBC_MANAGEMENT_TABLENAME`
`hono.registry.jdbc.management.tableName` | no | - | The name of the table the datastore uses. If the datastore requires multiple tables, this is the prefix. | | `HONO_REGISTRY_SVC_CREDENTIALSTTL`
`hono.registry.svc.credentialsTtl` | no | `1m` | The TTL for credentials responses. | | `HONO_REGISTRY_SVC_HASHALGORITHMSWHITELIST`
`hono.registry.svc.hashAlgorithmsWhitelist` | no | `empty` | An array of supported hashing algorithms to be used with the `hashed-password` type of credentials. When not set, all values will be accepted. | @@ -93,6 +99,9 @@ and availability. | `HONO_TENANT_JDBC_ADAPTER_MINIMUMPOOLSIZE`
`hono.tenant.jdbc.adapter.minimumPoolSize` | no | `3` | The minimum size of the connection pool. | | `HONO_TENANT_JDBC_ADAPTER_INITIALPOOLSIZE`
`hono.tenant.jdbc.adapter.initialPoolSize` | no | `3` | Number of connections a pool will try to acquire upon startup. Should be between minPoolSize and maxPoolSize. | | `HONO_TENANT_JDBC_ADAPTER_MAXIMUMIDLETIME`
`hono.tenant.jdbc.adapter.maximumIdleTime` | no | `3600` | Seconds a connection can remain pooled but unused before being discarded. Zero means idle connections never expire. | +| `HONO_TENANT_JDBC_ADAPTER_MAXIMUMCONNECTIONTIME`
`hono.tenant.jdbc.adapter.maximumConnectionTime` | no | `30` | Maximum wait time in seconds to obtain a connection from the pool before the request fails. A value of zero disables the timeout. | +| `HONO_TENANT_JDBC_ADAPTER_VALIDATIONTIME`
`hono.tenant.jdbc.adapter.validationTime` | no | `30` | Interval, in seconds, in which connections in the pool are validated. A value of zero means connection validation is disabled. | +| `HONO_TENANT_JDBC_ADAPTER_LEAKTIME`
`hono.tenant.jdbc.adapter.leakTime` | no | `60` | Seconds after which a connection is considered to be leaking if it has not returned to the pool. A value of zero means connection leakage detection is disabled. | | `HONO_TENANT_JDBC_ADAPTER_TABLENAME`
`hono.tenant.jdbc.adapter.tableName` | no | - | The name of the table the datastore uses. If the datastore requires multiple tables, this is the prefix. | | `HONO_TENANT_JDBC_MANAGEMENT_URL`
`hono.tenant.jdbc.management.url` | yes | - | The JDBC URL to the database. | | `HONO_TENANT_JDBC_MANAGEMENT_DRIVERCLASS`
`hono.tenant.jdbc.management.driverClass` | no | The default driver registered for the JDBC URL. | The class name of the JDBC driver. | @@ -102,6 +111,9 @@ and availability. | `HONO_TENANT_JDBC_MANAGEMENT_MINIMUMPOOLSIZE`
`hono.tenant.jdbc.management.minimumPoolSize` | no | `3` | The minimum size of the connection pool. | | `HONO_TENANT_JDBC_MANAGEMENT_INITIALPOOLSIZE`
`hono.tenant.jdbc.management.initialPoolSize` | no | `3` | Number of connections a pool will try to acquire upon startup. Should be between minPoolSize and maxPoolSize. | | `HONO_TENANT_JDBC_MANAGEMENT_MAXIMUMIDLETIME`
`hono.tenant.jdbc.management.maximumIdleTime` | no | `3600` | Seconds a connection can remain pooled but unused before being discarded. Zero means idle connections never expire. | +| `HONO_TENANT_JDBC_MANAGEMENT_MAXIMUMCONNECTIONTIME`
`hono.tenant.jdbc.management.maximumConnectionTime` | no | `30` | Maximum wait time in seconds to obtain a connection from the pool before the request fails. A value of zero disables the timeout. | +| `HONO_TENANT_JDBC_MANAGEMENT_VALIDATIONTIME`
`hono.tenant.jdbc.management.validationTime` | no | `30` | Interval, in seconds, in which connections in the pool are validated. A value of zero means connection validation is disabled. | +| `HONO_TENANT_JDBC_MANAGEMENT_LEAKTIME`
`hono.tenant.jdbc.management.leakTime` | no | `60` | Seconds after which a connection is considered to be leaking if it has not returned to the pool. A value of zero means connection leakage detection is disabled. | | `HONO_TENANT_JDBC_MANAGEMENT_TABLENAME`
`hono.tenant.jdbc.management.tableName` | no | - | The name of the table the datastore uses. If the datastore requires multiple tables, this is the prefix. | | `HONO_TENANT_SVC_TENANTTTL`
`hono.tenant.service.tenantTtl` | no | `1m` | The TTL for tenant responses. |