use anyhow::{Context, Result};
use chrono::{DurationRound, TimeDelta, Utc};
use micromegas::analytics::lakehouse::lakehouse_context::LakehouseContext;
use micromegas::analytics::lakehouse::runtime::make_runtime_env;
use micromegas::analytics::lakehouse::session_configurator::NoOpSessionConfigurator;
use micromegas::analytics::lakehouse::sql_batch_view::SqlBatchView;
use micromegas::analytics::lakehouse::view::View;
use micromegas::analytics::lakehouse::view_factory::{ViewFactory, default_view_factory};
use micromegas::analytics::time::TimeRange;
use micromegas::datafusion::execution::runtime_env::RuntimeEnv;
use micromegas::ingestion::data_lake_connection::{DataLakeConnection, connect_to_data_lake};
use micromegas::servers::maintenance::materialize_all_views;
use micromegas_telemetry_sink::TelemetryGuardBuilder;
use micromegas_tracing::prelude::*;
use std::sync::Arc;
async fn make_failing_view(
runtime: Arc<RuntimeEnv>,
lake: Arc<DataLakeConnection>,
view_factory: Arc<ViewFactory>,
view_set_name: &str,
nonexistent_table: &str,
update_group: i32,
) -> Result<SqlBatchView> {
let count_src_query = Arc::new(format!(
"SELECT count(*) as count FROM {nonexistent_table}
WHERE insert_time >= '{{begin}}' AND insert_time < '{{end}}';",
));
let extract_query = Arc::new(String::from(
"SELECT time, process_id FROM log_entries
WHERE insert_time >= '{begin}' AND insert_time < '{end}';",
));
let merge_partitions_query = Arc::new(String::from(
"SELECT time, process_id FROM {source} ORDER BY time;",
));
let time_column = Arc::new(String::from("time"));
SqlBatchView::new(
runtime,
Arc::new(view_set_name.to_owned()),
time_column.clone(),
time_column,
count_src_query,
extract_query,
merge_partitions_query,
lake,
view_factory,
Arc::new(NoOpSessionConfigurator),
Some(update_group),
TimeDelta::days(1),
TimeDelta::days(1),
)
.await
}
#[ignore]
#[tokio::test]
async fn materialize_all_views_isolates_same_group_failures() -> Result<()> {
let _telemetry_guard = TelemetryGuardBuilder::default()
.with_ctrlc_handling()
.with_local_sink_max_level(LevelFilter::Info)
.build();
let connection_string = std::env::var("MICROMEGAS_SQL_CONNECTION_STRING")
.with_context(|| "reading MICROMEGAS_SQL_CONNECTION_STRING")?;
let object_store_uri = std::env::var("MICROMEGAS_OBJECT_STORE_URI")
.with_context(|| "reading MICROMEGAS_OBJECT_STORE_URI")?;
let runtime = Arc::new(make_runtime_env()?);
let lake = Arc::new(connect_to_data_lake(&connection_string, &object_store_uri).await?);
let lakehouse = Arc::new(LakehouseContext::new(lake.clone(), runtime.clone())?);
let update_group = 4242;
let view_factory = Arc::new(
default_view_factory(runtime.clone(), lake.clone(), lakehouse.default_audience()).await?,
);
let first_view: Arc<dyn View> = Arc::new(
make_failing_view(
runtime.clone(),
lake.clone(),
view_factory.clone(),
"materialize_fail_isolation_first_view",
"this_table_does_not_exist_1393_a",
update_group,
)
.await?,
);
let second_view: Arc<dyn View> = Arc::new(
make_failing_view(
runtime.clone(),
lake.clone(),
view_factory.clone(),
"materialize_fail_isolation_second_view",
"this_table_does_not_exist_1393_b",
update_group,
)
.await?,
);
let end_range = Utc::now().duration_trunc(TimeDelta::minutes(1))?;
let begin_range = end_range - TimeDelta::minutes(3);
let insert_range = TimeRange::new(begin_range, end_range);
let views = Arc::new(vec![first_view, second_view]);
let err = materialize_all_views(lakehouse, views, insert_range, TimeDelta::minutes(1))
.await
.expect_err("both views should fail to materialize");
let message = format!("{err:?}");
assert!(
message.contains("materialize_fail_isolation_first_view"),
"expected the first view's failure to be reported, got: {message}"
);
assert!(
message.contains("materialize_fail_isolation_second_view"),
"expected the second view's failure to be reported too, not just the first one \
encountered -- a fail-fast regression would abort before reaching it, got: {message}"
);
Ok(())
}