diff --git a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/ClickHouseSchemaInitializer.java b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/ClickHouseSchemaInitializer.java new file mode 100644 index 00000000..a2a5f720 --- /dev/null +++ b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/ClickHouseSchemaInitializer.java @@ -0,0 +1,52 @@ +package com.cameleer3.server.app.config; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.event.EventListener; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Comparator; + +@Component +@ConditionalOnProperty(name = "clickhouse.enabled", havingValue = "true") +public class ClickHouseSchemaInitializer { + + private static final Logger log = LoggerFactory.getLogger(ClickHouseSchemaInitializer.class); + + private final JdbcTemplate clickHouseJdbc; + + public ClickHouseSchemaInitializer( + @Qualifier("clickHouseJdbcTemplate") JdbcTemplate clickHouseJdbc) { + this.clickHouseJdbc = clickHouseJdbc; + } + + @EventListener(ApplicationReadyEvent.class) + public void initializeSchema() throws IOException { + PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); + Resource[] scripts = resolver.getResources("classpath:clickhouse/*.sql"); + + Arrays.sort(scripts, Comparator.comparing(Resource::getFilename)); + + for (Resource script : scripts) { + String sql = script.getContentAsString(StandardCharsets.UTF_8); + log.info("Executing ClickHouse schema script: {}", script.getFilename()); + for (String statement : sql.split(";")) { + String trimmed = statement.trim(); + if (!trimmed.isEmpty()) { + clickHouseJdbc.execute(trimmed); + } + } + } + + log.info("ClickHouse schema initialization complete ({} scripts)", scripts.length); + } +} diff --git a/cameleer3-server-app/src/main/resources/clickhouse/V1__agent_metrics.sql b/cameleer3-server-app/src/main/resources/clickhouse/V1__agent_metrics.sql new file mode 100644 index 00000000..807e882c --- /dev/null +++ b/cameleer3-server-app/src/main/resources/clickhouse/V1__agent_metrics.sql @@ -0,0 +1,14 @@ +CREATE TABLE IF NOT EXISTS agent_metrics ( + tenant_id LowCardinality(String) DEFAULT 'default', + collected_at DateTime64(3), + agent_id LowCardinality(String), + metric_name LowCardinality(String), + metric_value Float64, + tags Map(String, String) DEFAULT map(), + server_received_at DateTime64(3) DEFAULT now64(3) +) +ENGINE = MergeTree() +PARTITION BY (tenant_id, toYYYYMM(collected_at)) +ORDER BY (tenant_id, agent_id, metric_name, collected_at) +TTL collected_at + INTERVAL 365 DAY DELETE +SETTINGS index_granularity = 8192;