From 8bf45d545604272f2b05cd9dec73726ee87c669f Mon Sep 17 00:00:00 2001 From: hsiegeln <37154749+hsiegeln@users.noreply.github.com> Date: Mon, 20 Apr 2026 07:36:55 +0200 Subject: [PATCH] fix(alerting): use ALTER TABLE MODIFY SETTING to enable projections on executions ReplacingMergeTree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Investigated three approaches for CH 24.12: - Inline SETTINGS on ADD PROJECTION: rejected (UNKNOWN_SETTING — not a query-level setting). - ALTER TABLE MODIFY SETTING deduplicate_merge_projection_mode='rebuild': works; persists in table metadata across connection restarts; runs before ADD PROJECTION in the SQL script. - Session-level JDBC URL param: not pursued (MODIFY SETTING is strictly better). alerting_projections.sql now runs MODIFY SETTING before the two executions ADD PROJECTIONs. AlertingProjectionsIT strengthened to assert all four projections (including alerting_app_status and alerting_route_status on executions) exist after schema init. Co-Authored-By: Claude Sonnet 4.6 --- .../clickhouse/alerting_projections.sql | 14 ++++++++------ .../app/search/AlertingProjectionsIT.java | 18 ++++++++++-------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/cameleer-server-app/src/main/resources/clickhouse/alerting_projections.sql b/cameleer-server-app/src/main/resources/clickhouse/alerting_projections.sql index 6a388c42..3413d12e 100644 --- a/cameleer-server-app/src/main/resources/clickhouse/alerting_projections.sql +++ b/cameleer-server-app/src/main/resources/clickhouse/alerting_projections.sql @@ -1,12 +1,12 @@ -- Alerting projections — additive and idempotent (IF NOT EXISTS). -- Safe to run on every startup alongside init.sql. -- --- NOTE: executions uses ReplacingMergeTree which requires deduplicate_merge_projection_mode='rebuild' --- to support projections (ClickHouse 24.x). The ADD PROJECTION and MATERIALIZE statements for --- executions are treated as best-effort by the schema initializer (non-fatal on failure). --- logs and agent_metrics use plain MergeTree and always succeed. +-- executions uses ReplacingMergeTree. ClickHouse 24.x requires deduplicate_merge_projection_mode='rebuild' +-- for projections to work on ReplacingMergeTree. ALTER TABLE MODIFY SETTING persists the setting in +-- table metadata (survives restarts) and runs before the ADD PROJECTION statements. +-- logs and agent_metrics use plain MergeTree and do not need this setting. -- --- MATERIALIZE statements are also wrapped as non-fatal to handle empty tables in fresh deployments. +-- MATERIALIZE statements are wrapped as non-fatal to handle empty tables in fresh deployments. -- Plain MergeTree tables: always succeed ALTER TABLE logs @@ -17,7 +17,9 @@ ALTER TABLE agent_metrics ADD PROJECTION IF NOT EXISTS alerting_instance_metric (SELECT * ORDER BY (tenant_id, environment, instance_id, metric_name, collected_at)); --- ReplacingMergeTree tables: best-effort (requires deduplicate_merge_projection_mode='rebuild') +-- ReplacingMergeTree: set table-level setting so ADD PROJECTION succeeds on any connection +ALTER TABLE executions MODIFY SETTING deduplicate_merge_projection_mode = 'rebuild'; + ALTER TABLE executions ADD PROJECTION IF NOT EXISTS alerting_app_status (SELECT * ORDER BY (tenant_id, environment, application_id, status, start_time)); diff --git a/cameleer-server-app/src/test/java/com/cameleer/server/app/search/AlertingProjectionsIT.java b/cameleer-server-app/src/test/java/com/cameleer/server/app/search/AlertingProjectionsIT.java index 15400f09..5c612390 100644 --- a/cameleer-server-app/src/test/java/com/cameleer/server/app/search/AlertingProjectionsIT.java +++ b/cameleer-server-app/src/test/java/com/cameleer/server/app/search/AlertingProjectionsIT.java @@ -34,17 +34,19 @@ class AlertingProjectionsIT { } @Test - void mergeTreeProjectionsExistAfterInit() { - // logs and agent_metrics are plain MergeTree — projections always succeed. - // executions is ReplacingMergeTree; its projections require the session setting - // deduplicate_merge_projection_mode='rebuild' which is unavailable via JDBC pool, - // so they are best-effort and not asserted here. + void allFourProjectionsExistAfterInit() { + // logs and agent_metrics are plain MergeTree — always succeed. + // executions is ReplacingMergeTree; its projections now succeed because + // alerting_projections.sql runs ALTER TABLE executions MODIFY SETTING + // deduplicate_merge_projection_mode='rebuild' before the ADD PROJECTION statements. List names = jdbc.queryForList( - "SELECT name FROM system.projections WHERE table IN ('logs', 'agent_metrics')", + "SELECT name FROM system.projections WHERE table IN ('logs', 'agent_metrics', 'executions')", String.class); - assertThat(names).contains( + assertThat(names).containsExactlyInAnyOrder( "alerting_app_level", - "alerting_instance_metric"); + "alerting_instance_metric", + "alerting_app_status", + "alerting_route_status"); } }