Files
greptimedb/tests-integration/tests/reconciliation_event.rs
T
Dhruv Vaishnav 9198462869 feat(meta): record physical table reconciliation events (#8935)
* feat(meta): record physical table reconciliation events

Signed-off-by: dhruvxvaishnav <dhruvvaishnav687@gmail.com>

* docs(config): add reconciliation table event

Signed-off-by: dhruvxvaishnav <dhruvvaishnav687@gmail.com>

* fix(meta): address reconciliation event review feedback

Signed-off-by: dhruvxvaishnav <dhruvvaishnav687@gmail.com>

* fix(meta): keep reconciliation event summary volatile

Signed-off-by: dhruvxvaishnav <dhruvvaishnav687@gmail.com>

* refactor(meta): remove unused table state downcasting

Signed-off-by: dhruvxvaishnav <dhruvvaishnav687@gmail.com>

---------

Signed-off-by: dhruvxvaishnav <dhruvvaishnav687@gmail.com>
2026-08-28 06:17:55 +00:00

145 lines
5.0 KiB
Rust

// Copyright 2023 Greptime Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use client::OutputData;
use common_meta::reconciliation::ResolveStrategy;
use common_procedure::watcher;
use frontend::instance::Instance;
use servers::query_handler::sql::SqlQueryHandler;
use session::context::QueryContext;
use table::table_reference::TableReference;
use tests_integration::cluster::GreptimeDbClusterBuilder;
use crate::event_recorder_test_util::{assert_eventually_eq, find_eventually_string};
const EVENTS_TABLE: &str = "greptime_private.events";
const CATALOG: &str = "greptime";
const DATABASE: &str = "reconciliation_event_database";
#[tokio::test(flavor = "multi_thread")]
async fn test_table_reconciliation_events() {
common_telemetry::init_default_ut_logging();
let cluster = GreptimeDbClusterBuilder::new("table_reconciliation_events")
.await
.with_datanodes(1)
.build(true)
.await;
let frontend = cluster.fe_instance().clone();
run_sql(frontend.as_ref(), &format!("CREATE DATABASE {DATABASE}")).await;
run_sql(
frontend.as_ref(),
&format!("CREATE TABLE {DATABASE}.metrics (ts TIMESTAMP TIME INDEX)"),
)
.await;
let table_procedure_id = cluster
.metasrv
.reconciliation_manager()
.reconcile_table(
TableReference {
catalog: CATALOG,
schema: DATABASE,
table: "metrics",
},
ResolveStrategy::UseLatest,
)
.await
.unwrap();
let mut procedure_watcher = cluster
.metasrv
.procedure_manager()
.procedure_watcher(table_procedure_id)
.unwrap();
watcher::wait(&mut procedure_watcher).await.unwrap();
let table_procedure_id = table_procedure_id.to_string();
assert_eventually_eq(
&frontend,
&format!(
"SELECT count(*) = 1 AS matches FROM {EVENTS_TABLE} \
WHERE type = 'reconcile_table' AND procedure_id = '{table_procedure_id}' \
AND json_get_string(procedure_trigger, 'type') = 'Submitted' \
AND catalog_name = '{CATALOG}' AND schema_name = '{DATABASE}' \
AND table_name = 'metrics' AND table_id IS NOT NULL \
AND physical_table_id IS NULL \
AND json_get_int(payload, 'version') = 1 \
AND json_get_string(payload, 'resolve_strategy') = 'use_latest' \
AND json_get_bool(payload, 'is_subprocedure') = false"
),
"\
+---------+
| matches |
+---------+
| true |
+---------+",
)
.await;
let table_result_payload = find_eventually_string(
&frontend,
&format!(
"SELECT json_to_string(payload) AS payload FROM {EVENTS_TABLE} \
WHERE type = 'reconcile_table' AND procedure_id = '{table_procedure_id}' \
AND json_get_string(procedure_trigger, 'type') = 'Succeeded' \
AND catalog_name = '{CATALOG}' AND schema_name = '{DATABASE}' \
AND table_name = 'metrics' AND table_id IS NOT NULL \
AND physical_table_id IS NULL LIMIT 1"
),
"payload",
)
.await;
assert_eq!(
serde_json::from_str::<serde_json::Value>(&table_result_payload).unwrap(),
serde_json::json!({
"version": 1,
"complete": true,
"metadata_state": "consistent",
"resolution_strategy_applied": null,
"resolved_column_count": 1,
"scanned_region_count": 1,
"updated_region_count": 0,
"table_info_updated": true,
"last_completed_phase": "update_table_info",
})
);
assert_eventually_eq(
&frontend,
&format!(
"SELECT count(*) AS terminal_event_count FROM {EVENTS_TABLE} \
WHERE type = 'reconcile_table' AND procedure_id = '{table_procedure_id}' \
AND json_get_string(procedure_trigger, 'type') = 'Succeeded' \
AND catalog_name = '{CATALOG}' AND schema_name = '{DATABASE}' \
AND table_name = 'metrics' AND table_id IS NOT NULL \
AND physical_table_id IS NULL"
),
"\
+----------------------+
| terminal_event_count |
+----------------------+
| 1 |
+----------------------+",
)
.await;
}
async fn run_sql(instance: &Instance, sql: &str) {
let output = SqlQueryHandler::do_query(instance, sql, QueryContext::arc())
.await
.remove(0)
.unwrap();
assert!(matches!(output.data, OutputData::AffectedRows(_)), "{sql}");
}