This is an automated email from the ASF dual-hosted git repository. zqr10159 pushed a commit to branch 2.0.0 in repository https://gitbox.apache.org/repos/asf/hertzbeat.git
commit e1d8eb0627424a25abc52eb27601997c58ddbe5b Author: Logic <[email protected]> AuthorDate: Tue Aug 25 20:01:18 2026 +0800 fix: restore managed database startup --- .../manager/config/FlywayConfiguration.java | 32 ++-------------------- .../config/ApplicationConfigDocumentCodec.java | 21 ++++++++++++++ .../manager/config/FlywayConfigurationTest.java | 26 ++++++++++++++++++ ...anagedOptionalConfigurationPersistenceTest.java | 25 +++++++++++++++++ 4 files changed, 75 insertions(+), 29 deletions(-) diff --git a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/config/FlywayConfiguration.java b/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/config/FlywayConfiguration.java index 394767407e..1f47692214 100644 --- a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/config/FlywayConfiguration.java +++ b/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/config/FlywayConfiguration.java @@ -20,43 +20,17 @@ package org.apache.hertzbeat.manager.config; import org.flywaydb.core.Flyway; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.flyway.autoconfigure.FlywayMigrationInitializer; -import org.springframework.boot.flyway.autoconfigure.FlywayProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.DependsOn; -/** - * Flyway database migration config. - * Delays Flyway execution until after Hibernate has created/updated the schema. - */ +/** Runs Flyway before JPA so empty databases use the current baseline migration. */ @Configuration @ConditionalOnProperty(prefix = "spring.flyway", name = "enabled", havingValue = "true") public class FlywayConfiguration { - /** - * Disable the default FlywayMigrationInitializer by providing an empty callback. - */ + /** Retains an explicit initializer while preserving Spring Boot's migrate-before-JPA contract. */ @Bean public FlywayMigrationInitializer flywayInitializer(Flyway flyway) { - return new FlywayMigrationInitializer(flyway, (f) -> { - // Empty callback - we'll run migrations manually after Hibernate - }); - } - - /** - * Delayed Flyway migration that runs after EntityManagerFactory is initialized. - * This ensures Hibernate's ddl-auto runs first to create/update tables, - * then Flyway can perform additional migrations if needed. - */ - @Bean - @DependsOn("entityManagerFactory") - Dummy delayedFlywayInitializer(Flyway flyway, FlywayProperties flywayProperties) { - if (flywayProperties.isEnabled()) { - flyway.migrate(); - } - return new Dummy(); - } - - static class Dummy { + return new FlywayMigrationInitializer(flyway); } } diff --git a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/setup/config/ApplicationConfigDocumentCodec.java b/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/setup/config/ApplicationConfigDocumentCodec.java index f7e2284fd7..ea950ca8fd 100644 --- a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/setup/config/ApplicationConfigDocumentCodec.java +++ b/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/setup/config/ApplicationConfigDocumentCodec.java @@ -51,6 +51,8 @@ final class ApplicationConfigDocumentCodec implements ManagedDocumentCodec<Manag // This exact flat-key allowlist is the boundary that prevents setup from becoming an arbitrary YAML editor. private static final String DUCKDB_ENABLED = "warehouse.store.duckdb.enabled"; + private static final String DATASOURCE_DRIVER = "spring.datasource.driver-class-name"; + private static final String HIBERNATE_DIALECT = "spring.jpa.properties.hibernate.dialect"; private static final String MAIL_PORT = "spring.mail.port"; private static final String MAIL_USERNAME = "spring.mail.username"; private static final String MANAGED_SERVER_PROFILE_ID = "server-direct"; @@ -79,9 +81,28 @@ final class ApplicationConfigDocumentCodec implements ManagedDocumentCodec<Manag Map<String, Object> properties = new LinkedHashMap<>(); plainProperties(value).forEach( (key, item) -> properties.put(key, Integrity.literalForSpring(item))); + MetadataDatabaseKind kind = value.metadataDatabase().kind(); + properties.put(DATASOURCE_DRIVER, driverFor(kind)); + properties.put(HIBERNATE_DIALECT, dialectFor(kind)); return Map.copyOf(properties); } + private static String driverFor(MetadataDatabaseKind kind) { + return switch (kind) { + case H2 -> "org.h2.Driver"; + case MYSQL -> "com.mysql.cj.jdbc.Driver"; + case POSTGRESQL -> "org.postgresql.Driver"; + }; + } + + private static String dialectFor(MetadataDatabaseKind kind) { + return switch (kind) { + case H2 -> "org.hibernate.dialect.H2Dialect"; + case MYSQL -> "org.hibernate.dialect.MySQLDialect"; + case POSTGRESQL -> "org.hibernate.dialect.PostgreSQLDialect"; + }; + } + private static Map<String, String> plainProperties(ManagedApplicationConfig value) { Map<String, String> values = new LinkedHashMap<>(); values.put(DATASOURCE_URL, value.metadataDatabase().jdbcUrl()); diff --git a/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/config/FlywayConfigurationTest.java b/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/config/FlywayConfigurationTest.java new file mode 100644 index 0000000000..0582d931ba --- /dev/null +++ b/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/config/FlywayConfigurationTest.java @@ -0,0 +1,26 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0. + */ + +package org.apache.hertzbeat.manager.config; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import org.flywaydb.core.Flyway; +import org.junit.jupiter.api.Test; + +class FlywayConfigurationTest { + + @Test + void migratesBeforeJpaInsteadOfWaitingForHibernateSchemaMutation() throws Exception { + Flyway flyway = mock(Flyway.class); + + new FlywayConfiguration().flywayInitializer(flyway).afterPropertiesSet(); + + verify(flyway).migrate(); + } +} diff --git a/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/setup/config/ManagedOptionalConfigurationPersistenceTest.java b/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/setup/config/ManagedOptionalConfigurationPersistenceTest.java index 84bb49ae96..db51dc9d64 100644 --- a/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/setup/config/ManagedOptionalConfigurationPersistenceTest.java +++ b/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/setup/config/ManagedOptionalConfigurationPersistenceTest.java @@ -30,6 +30,23 @@ class ManagedOptionalConfigurationPersistenceTest { @TempDir private java.nio.file.Path root; + @Test + void materializesTheDriverAndHibernateDialectForEachManagedDatabaseKind() { + assertThat(databaseProperties(MetadataDatabaseKind.H2, "jdbc:h2:./data/hertzbeat")) + .containsEntry("spring.datasource.driver-class-name", "org.h2.Driver") + .containsEntry("spring.jpa.database", "H2") + .containsEntry("spring.jpa.properties.hibernate.dialect", "org.hibernate.dialect.H2Dialect"); + assertThat(databaseProperties(MetadataDatabaseKind.MYSQL, "jdbc:mysql://db/hertzbeat")) + .containsEntry("spring.datasource.driver-class-name", "com.mysql.cj.jdbc.Driver") + .containsEntry("spring.jpa.database", "MYSQL") + .containsEntry("spring.jpa.properties.hibernate.dialect", "org.hibernate.dialect.MySQLDialect"); + assertThat(databaseProperties(MetadataDatabaseKind.POSTGRESQL, "jdbc:postgresql://db/hertzbeat")) + .containsEntry("spring.datasource.driver-class-name", "org.postgresql.Driver") + .containsEntry("spring.jpa.database", "POSTGRESQL") + .containsEntry("spring.jpa.properties.hibernate.dialect", + "org.hibernate.dialect.PostgreSQLDialect"); + } + @Test void optionsUpdatePreservesRequiredSettingsAndStoresMailPasswordOnlyInSecrets() throws Exception { ManagedConfigurationTransaction transaction = new ManagedConfigurationTransaction(root); @@ -175,4 +192,12 @@ class ManagedOptionalConfigurationPersistenceTest { return new ManagedConfigurationBundle(application, ManagedSecrets.withoutTelemetryPassword(SecretValue.of("database-secret"))); } + + private static java.util.Map<String, Object> databaseProperties( + MetadataDatabaseKind kind, String jdbcUrl) { + return ApplicationConfigDocumentCodec.springProperties(new ManagedApplicationConfig( + new MetadataDatabaseSettings(kind, jdbcUrl, "hertzbeat"), + GreptimeSettings.anonymous( + new GreptimeEndpoints("localhost:4001", "http://localhost:4000"), "public"))); + } } --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
