diff --git a/Cargo.lock b/Cargo.lock index e3fcc2d41c..aafa225b4b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13506,6 +13506,7 @@ dependencies = [ "aquamarine", "async-stream", "async-trait", + "bytes", "common-base", "common-error", "common-grpc", diff --git a/src/cmd/src/frontend.rs b/src/cmd/src/frontend.rs index b192bc7608..da2c111e7c 100644 --- a/src/cmd/src/frontend.rs +++ b/src/cmd/src/frontend.rs @@ -411,13 +411,17 @@ impl StartCommand { ); let fundamental_cache_registry = build_fundamental_cache_registry(readonly_meta_backend.clone()); - let layered_cache_registry = Arc::new( - with_default_composite_cache_registry( - layered_cache_builder.add_cache_registry(fundamental_cache_registry), - ) - .context(error::BuildCacheRegistrySnafu)? - .build(), - ); + let mut layered_cache_builder = with_default_composite_cache_registry( + layered_cache_builder.add_cache_registry(fundamental_cache_registry), + ) + .context(error::BuildCacheRegistrySnafu)?; + + if let Some(plugin_cache_builder) = plugins::frontend::configure_cache_registry(&plugins) { + layered_cache_builder = + layered_cache_builder.add_cache_registry(plugin_cache_builder.build()); + } + + let layered_cache_registry = Arc::new(layered_cache_builder.build()); // frontend to datanode need not timeout. // Some queries are expected to take long time. diff --git a/src/cmd/src/standalone.rs b/src/cmd/src/standalone.rs index b8a21a98c4..b0601088cf 100644 --- a/src/cmd/src/standalone.rs +++ b/src/cmd/src/standalone.rs @@ -411,13 +411,18 @@ impl StartCommand { // Builds cache registry let layered_cache_builder = LayeredCacheRegistryBuilder::default(); let fundamental_cache_registry = build_fundamental_cache_registry(kv_backend.clone()); - let layered_cache_registry = Arc::new( - with_default_composite_cache_registry( - layered_cache_builder.add_cache_registry(fundamental_cache_registry), - ) - .context(error::BuildCacheRegistrySnafu)? - .build(), - ); + let mut layered_cache_builder = with_default_composite_cache_registry( + layered_cache_builder.add_cache_registry(fundamental_cache_registry), + ) + .context(error::BuildCacheRegistrySnafu)?; + + if let Some(plugin_cache_builder) = plugins::standalone::configure_cache_registry(&plugins) + { + layered_cache_builder = + layered_cache_builder.add_cache_registry(plugin_cache_builder.build()); + } + + let layered_cache_registry = Arc::new(layered_cache_builder.build()); let mut builder = DatanodeBuilder::new(dn_opts, plugins.clone(), kv_backend.clone()); builder.with_cache_registry(layered_cache_registry.clone()); diff --git a/src/common/meta/src/cache_invalidator.rs b/src/common/meta/src/cache_invalidator.rs index 8a1ffc9d6f..ffc3dd1c9a 100644 --- a/src/common/meta/src/cache_invalidator.rs +++ b/src/common/meta/src/cache_invalidator.rs @@ -149,6 +149,10 @@ where let key = NodeAddressKey::with_flownode(*node_id); self.invalidate_key(&key.to_bytes()).await; } + CacheIdent::User(_) => { + // User cache invalidation is handled by external + // CacheInvalidator implementations. + } } } Ok(()) diff --git a/src/common/meta/src/instruction.rs b/src/common/meta/src/instruction.rs index fd8d17bdff..3fa6b1bad0 100644 --- a/src/common/meta/src/instruction.rs +++ b/src/common/meta/src/instruction.rs @@ -288,6 +288,14 @@ pub enum CacheIdent { SchemaName(SchemaName), CreateFlow(CreateFlow), DropFlow(DropFlow), + /// Indicate change of user metadata. + User(UserCacheIdent), +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +pub struct UserCacheIdent { + pub catalog: String, + pub username: String, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] diff --git a/src/meta-srv/src/bootstrap.rs b/src/meta-srv/src/bootstrap.rs index a9cc72ec53..ee9221381e 100644 --- a/src/meta-srv/src/bootstrap.rs +++ b/src/meta-srv/src/bootstrap.rs @@ -46,7 +46,8 @@ use crate::cluster::{MetaPeerClientBuilder, MetaPeerClientRef}; use crate::error::OtherSnafu; use crate::metasrv::builder::MetasrvBuilder; use crate::metasrv::{ - BackendImpl, ElectionRef, Metasrv, MetasrvOptions, SelectTarget, SelectorRef, + BackendImpl, ElectionRef, Metasrv, MetasrvOptions, SelectTarget, SelectorFactoryContext, + SelectorFactoryRef, SelectorRef, }; use crate::selector::lease_based::LeaseBasedSelector; use crate::selector::load_based::LoadBasedSelector; @@ -381,30 +382,37 @@ pub async fn metasrv_builder( let in_memory = Arc::new(MemoryKvBackend::new()) as ResettableKvBackendRef; let meta_peer_client = build_default_meta_peer_client(&election, &in_memory); - let selector = if let Some(selector) = plugins.get::() { - info!("Using selector from plugins"); - selector + let base_selector: Arc< + dyn Selector< + Context = crate::metasrv::SelectorContext, + Output = Vec, + >, + > = match opts.selector { + SelectorType::LoadBased => Arc::new(LoadBasedSelector::new( + RegionNumsBasedWeightCompute, + meta_peer_client.clone(), + )) as SelectorRef, + SelectorType::LeaseBased => Arc::new(LeaseBasedSelector) as SelectorRef, + SelectorType::RoundRobin => { + Arc::new(RoundRobinSelector::new(SelectTarget::Datanode)) as SelectorRef + } + }; + info!( + "Using selector from options, selector type: {}", + opts.selector.as_ref() + ); + + let selector = if let Some(factory) = plugins.get::() { + info!("Building selector from plugin factory"); + factory.build(SelectorFactoryContext { + metasrv_options: opts.clone(), + meta_peer_client: meta_peer_client.clone(), + in_memory: in_memory.clone(), + election: election.clone(), + base_selector, + }) } else { - let selector: Arc< - dyn Selector< - Context = crate::metasrv::SelectorContext, - Output = Vec, - >, - > = match opts.selector { - SelectorType::LoadBased => Arc::new(LoadBasedSelector::new( - RegionNumsBasedWeightCompute, - meta_peer_client.clone(), - )) as SelectorRef, - SelectorType::LeaseBased => Arc::new(LeaseBasedSelector) as SelectorRef, - SelectorType::RoundRobin => { - Arc::new(RoundRobinSelector::new(SelectTarget::Datanode)) as SelectorRef - } - }; - info!( - "Using selector from options, selector type: {}", - opts.selector.as_ref() - ); - selector + base_selector }; Ok(MetasrvBuilder::new() @@ -429,3 +437,47 @@ pub(crate) fn build_default_meta_peer_client( // Safety: all required fields set at initialization .unwrap() } + +#[cfg(test)] +mod tests { + use std::sync::atomic::{AtomicBool, Ordering}; + + use common_meta::kv_backend::memory::MemoryKvBackend; + + use super::*; + use crate::metasrv::{SelectorFactory, SelectorFactoryContext}; + + struct RecordingSelectorFactory { + called: Arc, + } + + impl SelectorFactory for RecordingSelectorFactory { + fn build(&self, ctx: SelectorFactoryContext) -> SelectorRef { + self.called.store(true, Ordering::Relaxed); + ctx.base_selector + } + } + + #[tokio::test] + async fn metasrv_builder_builds_load_based_selector_from_plugin_factory() { + let called = Arc::new(AtomicBool::new(false)); + let plugins = Plugins::new(); + plugins.insert(Arc::new(RecordingSelectorFactory { + called: called.clone(), + }) as SelectorFactoryRef); + let opts = MetasrvOptions { + selector: SelectorType::LoadBased, + ..Default::default() + }; + + metasrv_builder( + &opts, + plugins, + Some(Arc::new(MemoryKvBackend::new()) as KvBackendRef), + ) + .await + .unwrap(); + + assert!(called.load(Ordering::Relaxed)); + } +} diff --git a/src/meta-srv/src/metasrv.rs b/src/meta-srv/src/metasrv.rs index 6544578148..df2a3a35b8 100644 --- a/src/meta-srv/src/metasrv.rs +++ b/src/meta-srv/src/metasrv.rs @@ -482,6 +482,28 @@ pub struct SelectorContext { } pub type SelectorRef = Arc>>; + +/// Context passed to a selector factory during metasrv bootstrap. +/// +/// The factory runs after bootstrap has constructed the selector configured by +/// [`MetasrvOptions::selector`], so plugins can either decorate `base_selector` or +/// build a completely different selector using bootstrap-only dependencies like +/// [`MetaPeerClientRef`]. +pub struct SelectorFactoryContext { + pub metasrv_options: MetasrvOptions, + pub meta_peer_client: MetaPeerClientRef, + pub in_memory: ResettableKvBackendRef, + pub election: Option, + pub base_selector: SelectorRef, +} + +/// Builds the final datanode selector metasrv should use. +pub trait SelectorFactory: Send + Sync { + fn build(&self, ctx: SelectorFactoryContext) -> SelectorRef; +} + +/// Shared selector factory plugin registered through [`common_base::Plugins`]. +pub type SelectorFactoryRef = Arc; pub type RegionStatAwareSelectorRef = Arc>>; diff --git a/src/metric-engine/src/engine/flush.rs b/src/metric-engine/src/engine/flush.rs index 5953859cf0..62b2266249 100644 --- a/src/metric-engine/src/engine/flush.rs +++ b/src/metric-engine/src/engine/flush.rs @@ -133,12 +133,12 @@ mod tests { assert_eq!( debug_format, r#" -ManifestSstEntry { table_dir: "test_metric_region/", region_id: 47244640257(11, 1), table_id: 11, region_number: 1, region_group: 0, region_sequence: 1, file_id: "", index_version: 0, level: 0, file_path: "test_metric_region/11_0000000001/data/.parquet", file_size: 3000, index_file_path: Some("test_metric_region/11_0000000001/data/index/.puffin"), index_file_size: Some(0), num_rows: 10, num_row_groups: 1, num_series: Some(1), min_ts: 0::Millisecond, max_ts: 9::Millisecond, sequence: Some(20), origin_region_id: 47244640257(11, 1), node_id: None, visible: true } -ManifestSstEntry { table_dir: "test_metric_region/", region_id: 47244640258(11, 2), table_id: 11, region_number: 2, region_group: 0, region_sequence: 2, file_id: "", index_version: 0, level: 0, file_path: "test_metric_region/11_0000000002/data/.parquet", file_size: 3000, index_file_path: Some("test_metric_region/11_0000000002/data/index/.puffin"), index_file_size: Some(0), num_rows: 10, num_row_groups: 1, num_series: Some(1), min_ts: 0::Millisecond, max_ts: 9::Millisecond, sequence: Some(10), origin_region_id: 47244640258(11, 2), node_id: None, visible: true } -ManifestSstEntry { table_dir: "test_metric_region/", region_id: 47261417473(11, 16777217), table_id: 11, region_number: 16777217, region_group: 1, region_sequence: 1, file_id: "", index_version: 0, level: 0, file_path: "test_metric_region/11_0000000001/metadata/.parquet", file_size: 4000, index_file_path: None, index_file_size: None, num_rows: 8, num_row_groups: 1, num_series: Some(8), min_ts: 0::Millisecond, max_ts: 0::Millisecond, sequence: Some(8), origin_region_id: 47261417473(11, 16777217), node_id: None, visible: true } -ManifestSstEntry { table_dir: "test_metric_region/", region_id: 47261417474(11, 16777218), table_id: 11, region_number: 16777218, region_group: 1, region_sequence: 2, file_id: "", index_version: 0, level: 0, file_path: "test_metric_region/11_0000000002/metadata/.parquet", file_size: 4000, index_file_path: None, index_file_size: None, num_rows: 4, num_row_groups: 1, num_series: Some(4), min_ts: 0::Millisecond, max_ts: 0::Millisecond, sequence: Some(4), origin_region_id: 47261417474(11, 16777218), node_id: None, visible: true } -ManifestSstEntry { table_dir: "test_metric_region/", region_id: 94489280554(22, 42), table_id: 22, region_number: 42, region_group: 0, region_sequence: 42, file_id: "", index_version: 0, level: 0, file_path: "test_metric_region/22_0000000042/data/.parquet", file_size: 3000, index_file_path: Some("test_metric_region/22_0000000042/data/index/.puffin"), index_file_size: Some(0), num_rows: 10, num_row_groups: 1, num_series: Some(1), min_ts: 0::Millisecond, max_ts: 9::Millisecond, sequence: Some(10), origin_region_id: 94489280554(22, 42), node_id: None, visible: true } -ManifestSstEntry { table_dir: "test_metric_region/", region_id: 94506057770(22, 16777258), table_id: 22, region_number: 16777258, region_group: 1, region_sequence: 42, file_id: "", index_version: 0, level: 0, file_path: "test_metric_region/22_0000000042/metadata/.parquet", file_size: 4000, index_file_path: None, index_file_size: None, num_rows: 4, num_row_groups: 1, num_series: Some(4), min_ts: 0::Millisecond, max_ts: 0::Millisecond, sequence: Some(4), origin_region_id: 94506057770(22, 16777258), node_id: None, visible: true }"#, +ManifestSstEntry { table_dir: "test_metric_region/", region_id: 47244640257(11, 1), table_id: 11, region_number: 1, region_group: 0, region_sequence: 1, file_id: "", index_version: 0, level: 0, file_path: "test_metric_region/11_0000000001/data/.parquet", file_size: 3000, index_file_path: Some("test_metric_region/11_0000000001/data/index/.puffin"), index_file_size: Some(0), num_rows: 10, num_row_groups: 1, num_series: Some(1), min_ts: 0::Millisecond, max_ts: 9::Millisecond, sequence: Some(20), origin_region_id: 47244640257(11, 1), node_id: None, visible: true, primary_key_min: Some(b"\x80\0\0\x04\x01\0\0\x04W\x80\0\0\x03\x01)\x02K\x9f\xa5A\xb7\xb3\0\0\0\x02\x01\x01tag_0\0\0\0\x05"), primary_key_max: Some(b"\x80\0\0\x04\x01\0\0\x04W\x80\0\0\x03\x01)\x02K\x9f\xa5A\xb7\xb3\0\0\0\x02\x01\x01tag_0\0\0\0\x05") } +ManifestSstEntry { table_dir: "test_metric_region/", region_id: 47244640258(11, 2), table_id: 11, region_number: 2, region_group: 0, region_sequence: 2, file_id: "", index_version: 0, level: 0, file_path: "test_metric_region/11_0000000002/data/.parquet", file_size: 3000, index_file_path: Some("test_metric_region/11_0000000002/data/index/.puffin"), index_file_size: Some(0), num_rows: 10, num_row_groups: 1, num_series: Some(1), min_ts: 0::Millisecond, max_ts: 9::Millisecond, sequence: Some(10), origin_region_id: 47244640258(11, 2), node_id: None, visible: true, primary_key_min: Some(b"\x80\0\0\x04\x01\0\0\x04W\x80\0\0\x03\x01)\x02K\x9f\xa5A\xb7\xb3\0\0\0\x02\x01\x01tag_0\0\0\0\x05"), primary_key_max: Some(b"\x80\0\0\x04\x01\0\0\x04W\x80\0\0\x03\x01)\x02K\x9f\xa5A\xb7\xb3\0\0\0\x02\x01\x01tag_0\0\0\0\x05") } +ManifestSstEntry { table_dir: "test_metric_region/", region_id: 47261417473(11, 16777217), table_id: 11, region_number: 16777217, region_group: 1, region_sequence: 1, file_id: "", index_version: 0, level: 0, file_path: "test_metric_region/11_0000000001/metadata/.parquet", file_size: 4000, index_file_path: None, index_file_size: None, num_rows: 8, num_row_groups: 1, num_series: Some(8), min_ts: 0::Millisecond, max_ts: 0::Millisecond, sequence: Some(8), origin_region_id: 47261417473(11, 16777217), node_id: None, visible: true, primary_key_min: Some(b"\x01\x01__column\t_4771708\t665867_Z\t3JlcHRpb\tWVfdGltZ\tXN0YW1w\0\x07"), primary_key_max: Some(b"\x01\x01__region\t_4771708\t665868\0\0\x06") } +ManifestSstEntry { table_dir: "test_metric_region/", region_id: 47261417474(11, 16777218), table_id: 11, region_number: 16777218, region_group: 1, region_sequence: 2, file_id: "", index_version: 0, level: 0, file_path: "test_metric_region/11_0000000002/metadata/.parquet", file_size: 4000, index_file_path: None, index_file_size: None, num_rows: 4, num_row_groups: 1, num_series: Some(4), min_ts: 0::Millisecond, max_ts: 0::Millisecond, sequence: Some(4), origin_region_id: 47261417474(11, 16777218), node_id: None, visible: true, primary_key_min: Some(b"\x01\x01__column\t_4771708\t665858_Z\t3JlcHRpb\tWVfdGltZ\tXN0YW1w\0\x07"), primary_key_max: Some(b"\x01\x01__region\t_4771708\t665858\0\0\x06") } +ManifestSstEntry { table_dir: "test_metric_region/", region_id: 94489280554(22, 42), table_id: 22, region_number: 42, region_group: 0, region_sequence: 42, file_id: "", index_version: 0, level: 0, file_path: "test_metric_region/22_0000000042/data/.parquet", file_size: 3000, index_file_path: Some("test_metric_region/22_0000000042/data/index/.puffin"), index_file_size: Some(0), num_rows: 10, num_row_groups: 1, num_series: Some(1), min_ts: 0::Millisecond, max_ts: 9::Millisecond, sequence: Some(10), origin_region_id: 94489280554(22, 42), node_id: None, visible: true, primary_key_min: Some(b"\x80\0\0\x04\x01\0\0\x08\xae\x80\0\0\x03\x01)\x02K\x9f\xa5A\xb7\xb3\0\0\0\x02\x01\x01tag_0\0\0\0\x05"), primary_key_max: Some(b"\x80\0\0\x04\x01\0\0\x08\xae\x80\0\0\x03\x01)\x02K\x9f\xa5A\xb7\xb3\0\0\0\x02\x01\x01tag_0\0\0\0\x05") } +ManifestSstEntry { table_dir: "test_metric_region/", region_id: 94506057770(22, 16777258), table_id: 22, region_number: 16777258, region_group: 1, region_sequence: 42, file_id: "", index_version: 0, level: 0, file_path: "test_metric_region/22_0000000042/metadata/.parquet", file_size: 4000, index_file_path: None, index_file_size: None, num_rows: 4, num_row_groups: 1, num_series: Some(4), min_ts: 0::Millisecond, max_ts: 0::Millisecond, sequence: Some(4), origin_region_id: 94506057770(22, 16777258), node_id: None, visible: true, primary_key_min: Some(b"\x01\x01__column\t_9543417\t331754_Z\t3JlcHRpb\tWVfdGltZ\tXN0YW1w\0\x07"), primary_key_max: Some(b"\x01\x01__region\t_9543417\t331754\0\0\x06") }"#, ); // list from storage let storage_entries = mito diff --git a/src/mito2/src/engine/basic_test.rs b/src/mito2/src/engine/basic_test.rs index c85c540488..f256f88694 100644 --- a/src/mito2/src/engine/basic_test.rs +++ b/src/mito2/src/engine/basic_test.rs @@ -865,9 +865,9 @@ async fn test_cache_null_primary_key_with_format(flat_format: bool) { #[tokio::test] async fn test_list_ssts() { test_list_ssts_with_format(false, r#" -ManifestSstEntry { table_dir: "test/", region_id: 47244640257(11, 1), table_id: 11, region_number: 1, region_group: 0, region_sequence: 1, file_id: "", index_version: 0, level: 0, file_path: "test/11_0000000001/.parquet", file_size: 2701, index_file_path: Some("test/11_0000000001/index/.puffin"), index_file_size: Some(250), num_rows: 10, num_row_groups: 1, num_series: Some(1), min_ts: 0::Millisecond, max_ts: 9000::Millisecond, sequence: Some(10), origin_region_id: 47244640257(11, 1), node_id: None, visible: true } -ManifestSstEntry { table_dir: "test/", region_id: 47244640258(11, 2), table_id: 11, region_number: 2, region_group: 0, region_sequence: 2, file_id: "", index_version: 0, level: 0, file_path: "test/11_0000000002/.parquet", file_size: 2701, index_file_path: Some("test/11_0000000002/index/.puffin"), index_file_size: Some(250), num_rows: 10, num_row_groups: 1, num_series: Some(1), min_ts: 0::Millisecond, max_ts: 9000::Millisecond, sequence: Some(10), origin_region_id: 47244640258(11, 2), node_id: None, visible: true } -ManifestSstEntry { table_dir: "test/", region_id: 94489280554(22, 42), table_id: 22, region_number: 42, region_group: 0, region_sequence: 42, file_id: "", index_version: 0, level: 0, file_path: "test/22_0000000042/.parquet", file_size: 2701, index_file_path: Some("test/22_0000000042/index/.puffin"), index_file_size: Some(250), num_rows: 10, num_row_groups: 1, num_series: Some(1), min_ts: 0::Millisecond, max_ts: 9000::Millisecond, sequence: Some(10), origin_region_id: 94489280554(22, 42), node_id: None, visible: true }"# , +ManifestSstEntry { table_dir: "test/", region_id: 47244640257(11, 1), table_id: 11, region_number: 1, region_group: 0, region_sequence: 1, file_id: "", index_version: 0, level: 0, file_path: "test/11_0000000001/.parquet", file_size: 2701, index_file_path: Some("test/11_0000000001/index/.puffin"), index_file_size: Some(250), num_rows: 10, num_row_groups: 1, num_series: Some(1), min_ts: 0::Millisecond, max_ts: 9000::Millisecond, sequence: Some(10), origin_region_id: 47244640257(11, 1), node_id: None, visible: true, primary_key_min: Some(b"\x01\x01x\0\0\0\0\0\0\0\x01"), primary_key_max: Some(b"\x01\x01x\0\0\0\0\0\0\0\x01") } +ManifestSstEntry { table_dir: "test/", region_id: 47244640258(11, 2), table_id: 11, region_number: 2, region_group: 0, region_sequence: 2, file_id: "", index_version: 0, level: 0, file_path: "test/11_0000000002/.parquet", file_size: 2701, index_file_path: Some("test/11_0000000002/index/.puffin"), index_file_size: Some(250), num_rows: 10, num_row_groups: 1, num_series: Some(1), min_ts: 0::Millisecond, max_ts: 9000::Millisecond, sequence: Some(10), origin_region_id: 47244640258(11, 2), node_id: None, visible: true, primary_key_min: Some(b"\x01\x01x\0\0\0\0\0\0\0\x01"), primary_key_max: Some(b"\x01\x01x\0\0\0\0\0\0\0\x01") } +ManifestSstEntry { table_dir: "test/", region_id: 94489280554(22, 42), table_id: 22, region_number: 42, region_group: 0, region_sequence: 42, file_id: "", index_version: 0, level: 0, file_path: "test/22_0000000042/.parquet", file_size: 2701, index_file_path: Some("test/22_0000000042/index/.puffin"), index_file_size: Some(250), num_rows: 10, num_row_groups: 1, num_series: Some(1), min_ts: 0::Millisecond, max_ts: 9000::Millisecond, sequence: Some(10), origin_region_id: 94489280554(22, 42), node_id: None, visible: true, primary_key_min: Some(b"\x01\x01x\0\0\0\0\0\0\0\x01"), primary_key_max: Some(b"\x01\x01x\0\0\0\0\0\0\0\x01") }"# , r#" StorageSstEntry { file_path: "test/11_0000000001/.parquet", file_size: None, last_modified_ms: None, node_id: None } StorageSstEntry { file_path: "test/11_0000000001/index/.puffin", file_size: None, last_modified_ms: None, node_id: None } @@ -876,9 +876,9 @@ StorageSstEntry { file_path: "test/11_0000000002/index/.puffin", file_s StorageSstEntry { file_path: "test/22_0000000042/.parquet", file_size: None, last_modified_ms: None, node_id: None } StorageSstEntry { file_path: "test/22_0000000042/index/.puffin", file_size: None, last_modified_ms: None, node_id: None }"#).await; test_list_ssts_with_format(true, r#" -ManifestSstEntry { table_dir: "test/", region_id: 47244640257(11, 1), table_id: 11, region_number: 1, region_group: 0, region_sequence: 1, file_id: "", index_version: 0, level: 0, file_path: "test/11_0000000001/.parquet", file_size: 3099, index_file_path: Some("test/11_0000000001/index/.puffin"), index_file_size: Some(250), num_rows: 10, num_row_groups: 1, num_series: Some(1), min_ts: 0::Millisecond, max_ts: 9000::Millisecond, sequence: Some(10), origin_region_id: 47244640257(11, 1), node_id: None, visible: true } -ManifestSstEntry { table_dir: "test/", region_id: 47244640258(11, 2), table_id: 11, region_number: 2, region_group: 0, region_sequence: 2, file_id: "", index_version: 0, level: 0, file_path: "test/11_0000000002/.parquet", file_size: 3099, index_file_path: Some("test/11_0000000002/index/.puffin"), index_file_size: Some(250), num_rows: 10, num_row_groups: 1, num_series: Some(1), min_ts: 0::Millisecond, max_ts: 9000::Millisecond, sequence: Some(10), origin_region_id: 47244640258(11, 2), node_id: None, visible: true } -ManifestSstEntry { table_dir: "test/", region_id: 94489280554(22, 42), table_id: 22, region_number: 42, region_group: 0, region_sequence: 42, file_id: "", index_version: 0, level: 0, file_path: "test/22_0000000042/.parquet", file_size: 3099, index_file_path: Some("test/22_0000000042/index/.puffin"), index_file_size: Some(250), num_rows: 10, num_row_groups: 1, num_series: Some(1), min_ts: 0::Millisecond, max_ts: 9000::Millisecond, sequence: Some(10), origin_region_id: 94489280554(22, 42), node_id: None, visible: true }"#, +ManifestSstEntry { table_dir: "test/", region_id: 47244640257(11, 1), table_id: 11, region_number: 1, region_group: 0, region_sequence: 1, file_id: "", index_version: 0, level: 0, file_path: "test/11_0000000001/.parquet", file_size: 3099, index_file_path: Some("test/11_0000000001/index/.puffin"), index_file_size: Some(250), num_rows: 10, num_row_groups: 1, num_series: Some(1), min_ts: 0::Millisecond, max_ts: 9000::Millisecond, sequence: Some(10), origin_region_id: 47244640257(11, 1), node_id: None, visible: true, primary_key_min: Some(b"\x01\x01x\0\0\0\0\0\0\0\x01"), primary_key_max: Some(b"\x01\x01x\0\0\0\0\0\0\0\x01") } +ManifestSstEntry { table_dir: "test/", region_id: 47244640258(11, 2), table_id: 11, region_number: 2, region_group: 0, region_sequence: 2, file_id: "", index_version: 0, level: 0, file_path: "test/11_0000000002/.parquet", file_size: 3099, index_file_path: Some("test/11_0000000002/index/.puffin"), index_file_size: Some(250), num_rows: 10, num_row_groups: 1, num_series: Some(1), min_ts: 0::Millisecond, max_ts: 9000::Millisecond, sequence: Some(10), origin_region_id: 47244640258(11, 2), node_id: None, visible: true, primary_key_min: Some(b"\x01\x01x\0\0\0\0\0\0\0\x01"), primary_key_max: Some(b"\x01\x01x\0\0\0\0\0\0\0\x01") } +ManifestSstEntry { table_dir: "test/", region_id: 94489280554(22, 42), table_id: 22, region_number: 42, region_group: 0, region_sequence: 42, file_id: "", index_version: 0, level: 0, file_path: "test/22_0000000042/.parquet", file_size: 3099, index_file_path: Some("test/22_0000000042/index/.puffin"), index_file_size: Some(250), num_rows: 10, num_row_groups: 1, num_series: Some(1), min_ts: 0::Millisecond, max_ts: 9000::Millisecond, sequence: Some(10), origin_region_id: 94489280554(22, 42), node_id: None, visible: true, primary_key_min: Some(b"\x01\x01x\0\0\0\0\0\0\0\x01"), primary_key_max: Some(b"\x01\x01x\0\0\0\0\0\0\0\x01") }"#, r#" StorageSstEntry { file_path: "test/11_0000000001/.parquet", file_size: None, last_modified_ms: None, node_id: None } StorageSstEntry { file_path: "test/11_0000000001/index/.puffin", file_size: None, last_modified_ms: None, node_id: None } diff --git a/src/mito2/src/engine/skip_wal_test.rs b/src/mito2/src/engine/skip_wal_test.rs index c59be6ba2c..97f159b8ac 100644 --- a/src/mito2/src/engine/skip_wal_test.rs +++ b/src/mito2/src/engine/skip_wal_test.rs @@ -171,6 +171,48 @@ async fn test_close_follower_region_skip_wal() { assert_eq!(0, total_rows); } +#[tokio::test] +async fn test_close_follower_region_skip_wal_with_pending_data() { + common_telemetry::init_default_ut_logging(); + let mut env = TestEnv::with_prefix("close-follower-skip-wal-pending-data").await; + let engine = env.create_engine(MitoConfig::default()).await; + + let region_id = RegionId::new(1, 1); + let mut request = CreateRequestBuilder::new().build(); + + let wal_options = WalOptions::Noop; + request.options.insert( + WAL_OPTIONS_KEY.to_string(), + serde_json::to_string(&wal_options).unwrap(), + ); + + engine + .handle_request(region_id, RegionRequest::Create(request.clone())) + .await + .unwrap(); + + let rows = Rows { + schema: rows_schema(&request), + rows: build_rows(0, 3), + }; + put_rows(&engine, region_id, rows).await; + + let region = engine.get_region(region_id).unwrap(); + assert!(!region.version().memtables.is_empty()); + + engine + .set_region_role(region_id, RegionRole::Follower) + .unwrap(); + assert!(region.is_follower()); + + engine + .handle_request(region_id, RegionRequest::Close(RegionCloseRequest {})) + .await + .unwrap(); + + assert!(!engine.is_region_exists(region_id)); +} + #[tokio::test] async fn test_close_region_after_truncate_skip_wal() { common_telemetry::init_default_ut_logging(); diff --git a/src/mito2/src/error.rs b/src/mito2/src/error.rs index d61cf8470f..189354f1da 100644 --- a/src/mito2/src/error.rs +++ b/src/mito2/src/error.rs @@ -1250,6 +1250,18 @@ pub enum Error { #[snafu(implicit)] location: Location, }, + + #[snafu(display( + "Region {} is in {:?} state, expect: Writable, Staging or Downgrading", + region_id, + state + ))] + FlushableRegionState { + region_id: RegionId, + state: RegionRoleState, + #[snafu(implicit)] + location: Location, + }, } pub type Result = std::result::Result; @@ -1440,6 +1452,8 @@ impl ErrorExt for Error { TooManyFilesToRead { .. } | TooManyGcJobs { .. } => StatusCode::RateLimited, PruneFile { source, .. } => source.status_code(), + + FlushableRegionState { .. } => StatusCode::RegionNotReady, } } diff --git a/src/mito2/src/region.rs b/src/mito2/src/region.rs index ef88d6dd7c..c85599bf58 100644 --- a/src/mito2/src/region.rs +++ b/src/mito2/src/region.rs @@ -45,8 +45,8 @@ pub use utils::*; use crate::access_layer::AccessLayerRef; use crate::error::{ - InvalidPartitionExprSnafu, RegionNotFoundSnafu, RegionStateSnafu, RegionTruncatedSnafu, Result, - UnexpectedSnafu, UpdateManifestSnafu, + FlushableRegionStateSnafu, InvalidPartitionExprSnafu, RegionNotFoundSnafu, RegionStateSnafu, + RegionTruncatedSnafu, Result, UnexpectedSnafu, UpdateManifestSnafu, }; use crate::manifest::action::{ RegionChange, RegionManifest, RegionMetaAction, RegionMetaActionList, @@ -713,6 +713,8 @@ impl MitoRegion { origin_region_id, node_id: None, visible, + primary_key_min: meta.primary_key_min.clone(), + primary_key_max: meta.primary_key_max.clone(), } }) .collect() @@ -1433,35 +1435,19 @@ impl RegionMap { /// Gets flushable region by region id. /// - /// Returns error if the region does not exist. - /// Returns None if the region exists but not operatable. - fn flushable_region(&self, region_id: RegionId) -> Result> { + /// Returns error if the region does not exist or not flushable. + pub(crate) fn flushable_region(&self, region_id: RegionId) -> Result { let region = self .get_region(region_id) .context(RegionNotFoundSnafu { region_id })?; - if region.is_flushable() { - Ok(Some(region)) - } else { - Ok(None) - } - } - - /// Gets flushable region by region id. - /// - /// Calls the callback if the region does not exist. - /// Returns None if the region exists but not operatable. - pub(crate) fn flushable_region_or( - &self, - region_id: RegionId, - cb: &mut F, - ) -> Option { - match self.flushable_region(region_id) { - Ok(region) => region, - Err(e) => { - cb.on_failure(e); - None + ensure!( + region.is_flushable(), + FlushableRegionStateSnafu { + region_id, + state: region.state(), } - } + ); + Ok(region) } /// Remove region by id. diff --git a/src/mito2/src/worker/handle_close.rs b/src/mito2/src/worker/handle_close.rs index 1abd772f9f..3e0f5e52a6 100644 --- a/src/mito2/src/worker/handle_close.rs +++ b/src/mito2/src/worker/handle_close.rs @@ -37,8 +37,8 @@ impl RegionWorkerLoop { info!("Try to close region {}, worker: {}", region_id, self.id); - // If the region is using Noop WAL and has data in memtable, - // we should flush it before closing to ensure durability. + // If the region is using Noop WAL and has data in memtable and region is flushable (like, + // not in follower state), we should flush it before closing to ensure durability. if region.provider == Provider::Noop && !region .version_control @@ -46,6 +46,7 @@ impl RegionWorkerLoop { .version .memtables .is_empty() + && region.is_flushable() { info!("Region {} has pending data, waiting for flush", region_id); self.handle_flush_request( diff --git a/src/mito2/src/worker/handle_flush.rs b/src/mito2/src/worker/handle_flush.rs index fce995134d..cec922a6e1 100644 --- a/src/mito2/src/worker/handle_flush.rs +++ b/src/mito2/src/worker/handle_flush.rs @@ -182,11 +182,16 @@ impl RegionWorkerLoop { region_id: RegionId, request: RegionFlushRequest, reason: Option, - mut sender: OptionOutputTx, + sender: OptionOutputTx, ) { - let Some(region) = self.regions.flushable_region_or(region_id, &mut sender) else { - return; + let region = match self.regions.flushable_region(region_id) { + Ok(region) => region, + Err(e) => { + sender.send(Err(e)); + return; + } }; + // `update_topic_latest_entry_id` updates `topic_latest_entry_id` when memtables are empty. // But the flush is skipped if memtables are empty. Thus should update the `topic_latest_entry_id` // when handling flush request instead of in `schedule_flush` or `flush_finished`. diff --git a/src/plugins/src/frontend.rs b/src/plugins/src/frontend.rs index 4014460020..df7ec4fcb9 100644 --- a/src/plugins/src/frontend.rs +++ b/src/plugins/src/frontend.rs @@ -14,6 +14,7 @@ use auth::{DefaultPermissionChecker, PermissionCheckerRef, UserProviderRef}; use common_base::Plugins; +use common_meta::cache::CacheRegistryBuilder; use frontend::error::{IllegalAuthConfigSnafu, Result}; use frontend::frontend::FrontendOptions; use snafu::ResultExt; @@ -54,6 +55,11 @@ pub async fn start_frontend_plugins(_plugins: Plugins) -> Result<()> { Ok(()) } +/// Allows frontend plugins to add cache invalidators to the layered registry. +pub fn configure_cache_registry(_plugins: &Plugins) -> Option { + None +} + pub mod context { use std::sync::Arc; diff --git a/src/plugins/src/standalone.rs b/src/plugins/src/standalone.rs index 0cb7ee60e5..510b84106b 100644 --- a/src/plugins/src/standalone.rs +++ b/src/plugins/src/standalone.rs @@ -13,6 +13,7 @@ // limitations under the License. use common_base::Plugins; +use common_meta::cache::CacheRegistryBuilder; use common_meta::kv_backend::KvBackendRef; use standalone::error::Result; use standalone::options::StandaloneOptions; @@ -34,6 +35,11 @@ pub async fn start_standalone_plugins(_plugins: Plugins) -> Result<()> { Ok(()) } +/// Allows standalone plugins to add cache invalidators to the layered registry. +pub fn configure_cache_registry(_plugins: &Plugins) -> Option { + None +} + pub mod context { use std::sync::Arc; diff --git a/src/store-api/Cargo.toml b/src/store-api/Cargo.toml index 3eee78b2d1..61ce914fe1 100644 --- a/src/store-api/Cargo.toml +++ b/src/store-api/Cargo.toml @@ -11,6 +11,7 @@ workspace = true api.workspace = true aquamarine.workspace = true async-trait.workspace = true +bytes.workspace = true common-base.workspace = true common-error.workspace = true common-grpc.workspace = true diff --git a/src/store-api/src/sst_entry.rs b/src/store-api/src/sst_entry.rs index 840fef2268..bf5bcb0c2c 100644 --- a/src/store-api/src/sst_entry.rs +++ b/src/store-api/src/sst_entry.rs @@ -14,14 +14,15 @@ use std::sync::Arc; +use bytes::Bytes; use common_recordbatch::DfRecordBatch; use common_time::Timestamp; use common_time::timestamp::TimeUnit; use datafusion_common::DataFusionError; use datafusion_expr::{LogicalPlan, LogicalPlanBuilder, LogicalTableSource}; use datatypes::arrow::array::{ - ArrayRef, BooleanArray, TimestampMillisecondArray, TimestampNanosecondArray, UInt8Array, - UInt32Array, UInt64Array, + ArrayRef, BinaryArray, BooleanArray, TimestampMillisecondArray, TimestampNanosecondArray, + UInt8Array, UInt32Array, UInt64Array, }; use datatypes::arrow::error::ArrowError; use datatypes::arrow_array::StringArray; @@ -77,6 +78,10 @@ pub struct ManifestSstEntry { pub node_id: Option, /// Whether this file is visible in current version. pub visible: bool, + /// Minimum encoded primary key in the SST. + pub primary_key_min: Option, + /// Maximum encoded primary key in the SST. + pub primary_key_max: Option, } impl ManifestSstEntry { @@ -106,6 +111,8 @@ impl ManifestSstEntry { ColumnSchema::new("origin_region_id", Ty::uint64_datatype(), false), ColumnSchema::new("node_id", Ty::uint64_datatype(), true), ColumnSchema::new("visible", Ty::boolean_datatype(), false), + ColumnSchema::new("primary_key_min", Ty::binary_datatype(), true), + ColumnSchema::new("primary_key_max", Ty::binary_datatype(), true), ])) } @@ -142,6 +149,8 @@ impl ManifestSstEntry { let origin_region_ids = entries.iter().map(|e| e.origin_region_id.as_u64()); let node_ids = entries.iter().map(|e| e.node_id); let visible_flags = entries.iter().map(|e| Some(e.visible)); + let primary_key_min = entries.iter().map(|e| e.primary_key_min.as_deref()); + let primary_key_max = entries.iter().map(|e| e.primary_key_max.as_deref()); let columns: Vec = vec![ Arc::new(StringArray::from_iter_values(table_dirs)), @@ -166,6 +175,8 @@ impl ManifestSstEntry { Arc::new(UInt64Array::from_iter_values(origin_region_ids)), Arc::new(UInt64Array::from_iter(node_ids)), Arc::new(BooleanArray::from_iter(visible_flags)), + Arc::new(BinaryArray::from_iter(primary_key_min)), + Arc::new(BinaryArray::from_iter(primary_key_max)), ]; DfRecordBatch::try_new(schema.arrow_schema().clone(), columns) @@ -403,8 +414,8 @@ mod tests { use datafusion_common::TableReference; use datafusion_expr::{LogicalPlan, Operator, binary_expr, col, lit}; use datatypes::arrow::array::{ - Array, TimestampMillisecondArray, TimestampNanosecondArray, UInt8Array, UInt32Array, - UInt64Array, + Array, BinaryArray, TimestampMillisecondArray, TimestampNanosecondArray, UInt8Array, + UInt32Array, UInt64Array, }; use datatypes::arrow_array::StringArray; @@ -449,6 +460,8 @@ mod tests { origin_region_id: region_id1, node_id: Some(1), visible: false, + primary_key_min: Some(Bytes::from_static(b"aaa")), + primary_key_max: Some(Bytes::from_static(b"zzz")), }, ManifestSstEntry { table_dir: "tdir2".to_string(), @@ -473,6 +486,8 @@ mod tests { origin_region_id: region_id2, node_id: None, visible: true, + primary_key_min: None, + primary_key_max: None, }, ]; @@ -664,6 +679,22 @@ mod tests { .unwrap(); assert!(!visible.value(0)); assert!(visible.value(1)); + + let primary_key_min = batch + .column(22) + .as_any() + .downcast_ref::() + .unwrap(); + assert_eq!(b"aaa", primary_key_min.value(0)); + assert!(primary_key_min.is_null(1)); + + let primary_key_max = batch + .column(23) + .as_any() + .downcast_ref::() + .unwrap(); + assert_eq!(b"zzz", primary_key_max.value(0)); + assert!(primary_key_max.is_null(1)); } #[test] diff --git a/tests/cases/standalone/common/information_schema/ssts.result b/tests/cases/standalone/common/information_schema/ssts.result index a5b487ad26..caf6a23960 100644 --- a/tests/cases/standalone/common/information_schema/ssts.result +++ b/tests/cases/standalone/common/information_schema/ssts.result @@ -25,6 +25,8 @@ DESC TABLE information_schema.ssts_manifest; | origin_region_id | UInt64 | | NO | | FIELD | | node_id | UInt64 | | YES | | FIELD | | visible | Boolean | | NO | | FIELD | +| primary_key_min | Binary | | YES | | FIELD | +| primary_key_max | Binary | | YES | | FIELD | +------------------+---------------------+-----+------+---------+---------------+ DESC TABLE information_schema.ssts_storage; @@ -97,13 +99,13 @@ ADMIN FLUSH_TABLE('sst_case'); -- SQLNESS REPLACE (/public/\d+) /public/ SELECT * FROM information_schema.ssts_manifest order by file_path; -+----------------------------+---------------+----------+---------------+--------------+-----------------+--------------------------------------+---------------+-------+----------------------------------------------------------------------------------------+-----------+---------------------------------------------------------------------------------------------+-----------------+----------+----------------+------------+-------------------------+-------------------------+----------+------------------+---------+---------+ -| table_dir | region_id | table_id | region_number | region_group | region_sequence | file_id | index_version | level | file_path | file_size | index_file_path | index_file_size | num_rows | num_row_groups | num_series | min_ts | max_ts | sequence | origin_region_id | node_id | visible | -+----------------------------+---------------+----------+---------------+--------------+-----------------+--------------------------------------+---------------+-------+----------------------------------------------------------------------------------------+-----------+---------------------------------------------------------------------------------------------+-----------------+----------+----------------+------------+-------------------------+-------------------------+----------+------------------+---------+---------+ -| data/greptime/public// |||||| ||| data/greptime/public//_/.parquet || data/greptime/public//_/index/.puffin ||||| | |||| true | -| data/greptime/public// |||||| ||| data/greptime/public//_/.parquet || data/greptime/public//_/index/.puffin ||||| | |||| true | -| data/greptime/public// |||||| ||| data/greptime/public//_/.parquet || data/greptime/public//_/index/.puffin ||||| | |||| true | -+----------------------------+---------------+----------+---------------+--------------+-----------------+--------------------------------------+---------------+-------+----------------------------------------------------------------------------------------+-----------+---------------------------------------------------------------------------------------------+-----------------+----------+----------------+------------+-------------------------+-------------------------+----------+------------------+---------+---------+ ++----------------------------+---------------+----------+---------------+--------------+-----------------+--------------------------------------+---------------+-------+----------------------------------------------------------------------------------------+-----------+---------------------------------------------------------------------------------------------+-----------------+----------+----------------+------------+-------------------------+-------------------------+----------+------------------+---------+---------+-----------------+-----------------+ +| table_dir | region_id | table_id | region_number | region_group | region_sequence | file_id | index_version | level | file_path | file_size | index_file_path | index_file_size | num_rows | num_row_groups | num_series | min_ts | max_ts | sequence | origin_region_id | node_id | visible | primary_key_min | primary_key_max | ++----------------------------+---------------+----------+---------------+--------------+-----------------+--------------------------------------+---------------+-------+----------------------------------------------------------------------------------------+-----------+---------------------------------------------------------------------------------------------+-----------------+----------+----------------+------------+-------------------------+-------------------------+----------+------------------+---------+---------+-----------------+-----------------+ +| data/greptime/public// |||||| ||| data/greptime/public//_/.parquet || data/greptime/public//_/index/.puffin ||||| | |||| true | 01800001f4 | 01800001f4 | +| data/greptime/public// |||||| ||| data/greptime/public//_/.parquet || data/greptime/public//_/index/.puffin ||||| | |||| true | 01800005dc | 01800005dc | +| data/greptime/public// |||||| ||| data/greptime/public//_/.parquet || data/greptime/public//_/index/.puffin ||||| | |||| true | 01800009c4 | 01800009c4 | ++----------------------------+---------------+----------+---------------+--------------+-----------------+--------------------------------------+---------------+-------+----------------------------------------------------------------------------------------+-----------+---------------------------------------------------------------------------------------------+-----------------+----------+----------------+------------+-------------------------+-------------------------+----------+------------------+---------+---------+-----------------+-----------------+ -- SQLNESS REPLACE (\s+\d+\s+) -- SQLNESS REPLACE ([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}) @@ -164,17 +166,17 @@ ADMIN FLUSH_TABLE('sst_case'); -- SQLNESS REPLACE (\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{3,9})?[[:blank:]]*) -- SQLNESS REPLACE (/public/\d+/\d+_\d+) /public//_ -- SQLNESS REPLACE (/public/\d+) /public/ -SELECT * FROM information_schema.ssts_manifest order by file_path; +SELECT * FROM information_schema.ssts_manifest order by region_id, sequence; -+----------------------------+---------------+----------+---------------+--------------+-----------------+--------------------------------------+---------------+-------+----------------------------------------------------------------------------------------+-----------+---------------------------------------------------------------------------------------------+-----------------+----------+----------------+------------+-------------------------+-------------------------+----------+------------------+---------+---------+ -| table_dir | region_id | table_id | region_number | region_group | region_sequence | file_id | index_version | level | file_path | file_size | index_file_path | index_file_size | num_rows | num_row_groups | num_series | min_ts | max_ts | sequence | origin_region_id | node_id | visible | -+----------------------------+---------------+----------+---------------+--------------+-----------------+--------------------------------------+---------------+-------+----------------------------------------------------------------------------------------+-----------+---------------------------------------------------------------------------------------------+-----------------+----------+----------------+------------+-------------------------+-------------------------+----------+------------------+---------+---------+ -| data/greptime/public// |||||| ||| data/greptime/public//_/.parquet || data/greptime/public//_/index/.puffin ||||| | |||| true | -| data/greptime/public// |||||| ||| data/greptime/public//_/.parquet || data/greptime/public//_/index/.puffin ||||| | |||| true | -| data/greptime/public// |||||| ||| data/greptime/public//_/.parquet || data/greptime/public//_/index/.puffin ||||| | |||| true | -| data/greptime/public// |||||| ||| data/greptime/public//_/.parquet || data/greptime/public//_/index/.puffin ||||| | |||| true | -| data/greptime/public// |||||| ||| data/greptime/public//_/.parquet || data/greptime/public//_/index/.puffin ||||| | |||| true | -+----------------------------+---------------+----------+---------------+--------------+-----------------+--------------------------------------+---------------+-------+----------------------------------------------------------------------------------------+-----------+---------------------------------------------------------------------------------------------+-----------------+----------+----------------+------------+-------------------------+-------------------------+----------+------------------+---------+---------+ ++----------------------------+---------------+----------+---------------+--------------+-----------------+--------------------------------------+---------------+-------+----------------------------------------------------------------------------------------+-----------+---------------------------------------------------------------------------------------------+-----------------+----------+----------------+------------+-------------------------+-------------------------+----------+------------------+---------+---------+-----------------+-----------------+ +| table_dir | region_id | table_id | region_number | region_group | region_sequence | file_id | index_version | level | file_path | file_size | index_file_path | index_file_size | num_rows | num_row_groups | num_series | min_ts | max_ts | sequence | origin_region_id | node_id | visible | primary_key_min | primary_key_max | ++----------------------------+---------------+----------+---------------+--------------+-----------------+--------------------------------------+---------------+-------+----------------------------------------------------------------------------------------+-----------+---------------------------------------------------------------------------------------------+-----------------+----------+----------------+------------+-------------------------+-------------------------+----------+------------------+---------+---------+-----------------+-----------------+ +| data/greptime/public// |||||| ||| data/greptime/public//_/.parquet || data/greptime/public//_/index/.puffin ||||| | |||| true | 01800001f4 | 01800001f4 | +| data/greptime/public// |||||| ||| data/greptime/public//_/.parquet || data/greptime/public//_/index/.puffin ||||| | |||| true || 018000007c | +| data/greptime/public// |||||| ||| data/greptime/public//_/.parquet || data/greptime/public//_/index/.puffin ||||| | |||| true | 01800005dc | 01800005dc | +| data/greptime/public// |||||| ||| data/greptime/public//_/.parquet || data/greptime/public//_/index/.puffin ||||| | |||| true ||| +| data/greptime/public// |||||| ||| data/greptime/public//_/.parquet || data/greptime/public//_/index/.puffin ||||| | |||| true | 01800009c4 | 01800009c4 | ++----------------------------+---------------+----------+---------------+--------------+-----------------+--------------------------------------+---------------+-------+----------------------------------------------------------------------------------------+-----------+---------------------------------------------------------------------------------------------+-----------------+----------+----------------+------------+-------------------------+-------------------------+----------+------------------+---------+---------+-----------------+-----------------+ -- SQLNESS REPLACE (\s+\d+\s+) -- SQLNESS REPLACE ([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}) diff --git a/tests/cases/standalone/common/information_schema/ssts.sql b/tests/cases/standalone/common/information_schema/ssts.sql index 79a22ceb6b..43512de680 100644 --- a/tests/cases/standalone/common/information_schema/ssts.sql +++ b/tests/cases/standalone/common/information_schema/ssts.sql @@ -56,7 +56,7 @@ ADMIN FLUSH_TABLE('sst_case'); -- SQLNESS REPLACE (\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{3,9})?[[:blank:]]*) -- SQLNESS REPLACE (/public/\d+/\d+_\d+) /public//_ -- SQLNESS REPLACE (/public/\d+) /public/ -SELECT * FROM information_schema.ssts_manifest order by file_path; +SELECT * FROM information_schema.ssts_manifest order by region_id, sequence; -- SQLNESS REPLACE (\s+\d+\s+) -- SQLNESS REPLACE ([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}) diff --git a/tests/cases/standalone/common/system/information_schema.result b/tests/cases/standalone/common/system/information_schema.result index c0eb8e5319..38f3ea52a4 100644 --- a/tests/cases/standalone/common/system/information_schema.result +++ b/tests/cases/standalone/common/system/information_schema.result @@ -412,6 +412,8 @@ select * from information_schema.columns order by table_schema, table_name, colu | greptime | information_schema | ssts_manifest | num_rows | 14 | | | 20 | 0 | | | | | | select,insert | | UInt64 | bigint unsigned | FIELD | | No | bigint unsigned | | | | greptime | information_schema | ssts_manifest | num_series | 16 | | | 20 | 0 | | | | | | select,insert | | UInt64 | bigint unsigned | FIELD | | Yes | bigint unsigned | | | | greptime | information_schema | ssts_manifest | origin_region_id | 20 | | | 20 | 0 | | | | | | select,insert | | UInt64 | bigint unsigned | FIELD | | No | bigint unsigned | | | +| greptime | information_schema | ssts_manifest | primary_key_max | 24 | | | | | | | | | | select,insert | | Binary | varbinary | FIELD | | Yes | varbinary | | | +| greptime | information_schema | ssts_manifest | primary_key_min | 23 | | | | | | | | | | select,insert | | Binary | varbinary | FIELD | | Yes | varbinary | | | | greptime | information_schema | ssts_manifest | region_group | 5 | | | 3 | 0 | | | | | | select,insert | | UInt8 | tinyint unsigned | FIELD | | No | tinyint unsigned | | | | greptime | information_schema | ssts_manifest | region_id | 2 | | | 20 | 0 | | | | | | select,insert | | UInt64 | bigint unsigned | FIELD | | No | bigint unsigned | | | | greptime | information_schema | ssts_manifest | region_number | 4 | | | 10 | 0 | | | | | | select,insert | | UInt32 | int unsigned | FIELD | | No | int unsigned | | |