diff --git a/crates/store/re_dataframe/src/engine.rs b/crates/store/re_dataframe/src/engine.rs index e88e7ebdfab8..adead8572079 100644 --- a/crates/store/re_dataframe/src/engine.rs +++ b/crates/store/re_dataframe/src/engine.rs @@ -1,7 +1,7 @@ use re_chunk::{EntityPath, TransportChunk}; use re_chunk_store::{ChunkStoreHandle, ColumnDescriptor, QueryExpression}; use re_log_types::EntityPathFilter; -use re_query::QueryCache; +use re_query::QueryCacheHandle; use crate::QueryHandle; @@ -15,26 +15,24 @@ use re_chunk_store::ComponentColumnDescriptor; // `TransportChunk` type until we migrate to `arrow-rs`. // `TransportChunk` maps 1:1 to `RecordBatch` so the switch (and the compatibility layer in the meantime) // will be trivial. -// TODO(cmc): add an `arrow` feature to transportchunk in a follow-up pr and call it a day. pub type RecordBatch = TransportChunk; // --- Queries --- /// A handle to our user-facing query engine. /// +/// Cheap to clone. +/// /// See the following methods: /// * [`QueryEngine::schema`]: get the complete schema of the recording. /// * [`QueryEngine::query`]: execute a [`QueryExpression`] on the recording. -// -// TODO(cmc): This needs to be a refcounted type that can be easily be passed around: the ref has -// got to go. But for that we need to generally introduce `ChunkStoreHandle` and `QueryCacheHandle` -// first, and this is not as straightforward as it seems. -pub struct QueryEngine<'a> { +#[derive(Clone)] +pub struct QueryEngine { pub store: ChunkStoreHandle, - pub cache: &'a QueryCache, + pub cache: QueryCacheHandle, } -impl QueryEngine<'_> { +impl QueryEngine { /// Returns the full schema of the store. /// /// This will include a column descriptor for every timeline and every component on every @@ -60,8 +58,8 @@ impl QueryEngine<'_> { /// Starts a new query by instantiating a [`QueryHandle`]. #[inline] - pub fn query(&self, query: QueryExpression) -> QueryHandle<'_> { - QueryHandle::new(self, query) + pub fn query(&self, query: QueryExpression) -> QueryHandle { + QueryHandle::new(self.clone(), query) } /// Returns an iterator over all the [`EntityPath`]s present in the database. diff --git a/crates/store/re_dataframe/src/lib.rs b/crates/store/re_dataframe/src/lib.rs index 6615c300bc66..58bdbcd8e6b5 100644 --- a/crates/store/re_dataframe/src/lib.rs +++ b/crates/store/re_dataframe/src/lib.rs @@ -12,15 +12,15 @@ pub use self::external::arrow2::chunk::Chunk as ArrowChunk; pub use self::external::re_chunk::{util::concatenate_record_batches, TransportChunk}; #[doc(no_inline)] pub use self::external::re_chunk_store::{ - ColumnSelector, ComponentColumnSelector, Index, IndexRange, IndexValue, QueryExpression, - SparseFillStrategy, TimeColumnSelector, ViewContentsSelector, + ChunkStoreHandle, ColumnSelector, ComponentColumnSelector, Index, IndexRange, IndexValue, + QueryExpression, SparseFillStrategy, TimeColumnSelector, ViewContentsSelector, }; #[doc(no_inline)] pub use self::external::re_log_types::{ EntityPath, EntityPathFilter, ResolvedTimeRange, TimeInt, Timeline, }; #[doc(no_inline)] -pub use self::external::re_query::QueryCache; +pub use self::external::re_query::{QueryCache, QueryCacheHandle}; pub mod external { pub use re_chunk; diff --git a/crates/store/re_dataframe/src/query.rs b/crates/store/re_dataframe/src/query.rs index 8ac8c491e307..a170eb0bedd9 100644 --- a/crates/store/re_dataframe/src/query.rs +++ b/crates/store/re_dataframe/src/query.rs @@ -54,9 +54,9 @@ use crate::{QueryEngine, RecordBatch}; /// Cheaply created via [`QueryEngine::query`]. /// /// See [`QueryHandle::next_row`] or [`QueryHandle::into_iter`]. -pub struct QueryHandle<'a> { +pub struct QueryHandle { /// Handle to the [`QueryEngine`]. - pub(crate) engine: &'a QueryEngine<'a>, + pub(crate) engine: QueryEngine, /// The original query expression used to instantiate this handle. pub(crate) query: QueryExpression, @@ -140,8 +140,8 @@ struct QueryHandleState { unique_index_values: Vec, } -impl<'a> QueryHandle<'a> { - pub(crate) fn new(engine: &'a QueryEngine<'a>, query: QueryExpression) -> Self { +impl QueryHandle { + pub(crate) fn new(engine: QueryEngine, query: QueryExpression) -> Self { Self { engine, query, @@ -150,7 +150,7 @@ impl<'a> QueryHandle<'a> { } } -impl QueryHandle<'_> { +impl QueryHandle { /// Lazily initialize internal private state. /// /// It is important that query handles stay cheap to create. @@ -163,6 +163,7 @@ impl QueryHandle<'_> { re_tracing::profile_scope!("init"); let store = self.engine.store.read(); + let query_cache = self.engine.cache.read(); // The timeline doesn't matter if we're running in static-only mode. let filtered_index = self.query.filtered_index.unwrap_or_default(); @@ -328,7 +329,7 @@ impl QueryHandle<'_> { let query = re_chunk::LatestAtQuery::new(Timeline::default(), TimeInt::STATIC); - let results = self.engine.cache.latest_at( + let results = query_cache.latest_at( &query, &descr.entity_path, [descr.component_name], @@ -587,7 +588,11 @@ impl QueryHandle<'_> { // // TODO(cmc): Going through the cache is very useful in a Viewer context, but // not so much in an SDK context. Make it configurable. - let results = self.engine.cache.range(query, entity_path, component_names); + let results = self + .engine + .cache + .read() + .range(query, entity_path, component_names); debug_assert!( results.components.len() <= 1, @@ -793,6 +798,8 @@ impl QueryHandle<'_> { pub fn next_row(&self) -> Option>> { re_tracing::profile_function!(); + let query_cache = self.engine.cache.read(); + /// Temporary state used to resolve the streaming join for the current iteration. #[derive(Debug)] struct StreamingJoinStateEntry<'a> { @@ -994,11 +1001,8 @@ impl QueryHandle<'_> { let query = re_chunk::LatestAtQuery::new(state.filtered_index, *cur_index_value); - let results = self.engine.cache.latest_at( - &query, - &descr.entity_path, - [descr.component_name], - ); + let results = + query_cache.latest_at(&query, &descr.entity_path, [descr.component_name]); *streaming_state = results .components @@ -1169,28 +1173,28 @@ impl QueryHandle<'_> { } } -impl<'a> QueryHandle<'a> { +impl QueryHandle { /// Returns an iterator backed by [`Self::next_row`]. #[allow(clippy::should_implement_trait)] // we need an anonymous closure, this won't work - pub fn iter(&'a self) -> impl Iterator>> + 'a { + pub fn iter(&self) -> impl Iterator>> + '_ { std::iter::from_fn(move || self.next_row()) } /// Returns an iterator backed by [`Self::next_row`]. #[allow(clippy::should_implement_trait)] // we need an anonymous closure, this won't work - pub fn into_iter(self) -> impl Iterator>> + 'a { + pub fn into_iter(self) -> impl Iterator>> { std::iter::from_fn(move || self.next_row()) } /// Returns an iterator backed by [`Self::next_row_batch`]. #[allow(clippy::should_implement_trait)] // we need an anonymous closure, this won't work - pub fn batch_iter(&'a self) -> impl Iterator + 'a { + pub fn batch_iter(&self) -> impl Iterator + '_ { std::iter::from_fn(move || self.next_row_batch()) } /// Returns an iterator backed by [`Self::next_row_batch`]. #[allow(clippy::should_implement_trait)] // we need an anonymous closure, this won't work - pub fn into_batch_iter(self) -> impl Iterator + 'a { + pub fn into_batch_iter(self) -> impl Iterator { std::iter::from_fn(move || self.next_row_batch()) } } diff --git a/crates/store/re_entity_db/src/entity_db.rs b/crates/store/re_entity_db/src/entity_db.rs index f7d80fc0d302..033e75848c35 100644 --- a/crates/store/re_entity_db/src/entity_db.rs +++ b/crates/store/re_entity_db/src/entity_db.rs @@ -12,6 +12,7 @@ use re_log_types::{ ApplicationId, EntityPath, EntityPathHash, LogMsg, ResolvedTimeRange, ResolvedTimeRangeF, SetStoreInfo, StoreId, StoreInfo, StoreKind, Timeline, }; +use re_query::{QueryCache, QueryCacheHandle}; use crate::{Error, TimesPerTimeline}; @@ -65,7 +66,7 @@ pub struct EntityDb { data_store: ChunkStoreHandle, /// Query caches for the data in [`Self::data_store`]. - query_caches: re_query::QueryCache, + query_caches: QueryCacheHandle, stats: IngestionStatistics, } @@ -77,7 +78,7 @@ impl EntityDb { pub fn with_store_config(store_id: StoreId, store_config: ChunkStoreConfig) -> Self { let data_store = ChunkStoreHandle::new(ChunkStore::new(store_id.clone(), store_config)); - let query_caches = re_query::QueryCache::new(data_store.clone()); + let query_caches = QueryCacheHandle::new(QueryCache::new(data_store.clone())); Self { data_source: None, @@ -112,14 +113,15 @@ impl EntityDb { } #[inline] - pub fn query_caches(&self) -> &re_query::QueryCache { + pub fn query_caches(&self) -> &QueryCacheHandle { &self.query_caches } - pub fn query_engine(&self) -> re_dataframe::QueryEngine<'_> { + #[inline] + pub fn query_engine(&self) -> re_dataframe::QueryEngine { re_dataframe::QueryEngine { store: self.store().clone(), - cache: self.query_caches(), + cache: self.query_caches().clone(), } } @@ -136,6 +138,7 @@ impl EntityDb { component_names: impl IntoIterator, ) -> re_query::LatestAtResults { self.query_caches() + .read() .latest_at(query, entity_path, component_names) } @@ -155,6 +158,7 @@ impl EntityDb { ) -> Option<((TimeInt, RowId), C)> { let results = self .query_caches() + .read() .latest_at(query, entity_path, [C::name()]); results .component_mono() @@ -177,6 +181,7 @@ impl EntityDb { ) -> Option<((TimeInt, RowId), C)> { let results = self .query_caches() + .read() .latest_at(query, entity_path, [C::name()]); results .component_mono_quiet() @@ -360,7 +365,7 @@ impl EntityDb { // Update our internal views by notifying them of resulting [`ChunkStoreEvent`]s. self.times_per_timeline.on_events(&store_events); self.time_histogram_per_timeline.on_events(&store_events); - self.query_caches.on_events(&store_events); + self.query_caches.write().on_events(&store_events); self.tree.on_store_additions(&store_events); // It is possible for writes to trigger deletions: specifically in the case of @@ -421,7 +426,9 @@ impl EntityDb { // to regain some space. // See for the // complete rationale. - self.query_caches.purge_fraction_of_ram(fraction_to_purge); + self.query_caches + .write() + .purge_fraction_of_ram(fraction_to_purge); } store_events @@ -499,7 +506,7 @@ impl EntityDb { re_tracing::profile_function!(); self.times_per_timeline.on_events(store_events); - self.query_caches.on_events(store_events); + self.query_caches.write().on_events(store_events); self.time_histogram_per_timeline.on_events(store_events); self.tree.on_store_deletions(store, store_events); } diff --git a/crates/top/rerun/src/lib.rs b/crates/top/rerun/src/lib.rs index 4f9cda601b35..fb1b3953db98 100644 --- a/crates/top/rerun/src/lib.rs +++ b/crates/top/rerun/src/lib.rs @@ -145,7 +145,7 @@ pub mod dataframe { /// Everything needed to build custom `ChunkStoreSubscriber`s. pub use re_entity_db::external::re_chunk_store::{ ChunkStore, ChunkStoreConfig, ChunkStoreDiff, ChunkStoreDiffKind, ChunkStoreEvent, - ChunkStoreGeneration, ChunkStoreSubscriber, VersionPolicy, + ChunkStoreGeneration, ChunkStoreHandle, ChunkStoreSubscriber, VersionPolicy, }; pub use re_log_types::StoreKind; diff --git a/crates/viewer/re_data_ui/src/component_path.rs b/crates/viewer/re_data_ui/src/component_path.rs index 2035aecb8b45..dc8804b5d460 100644 --- a/crates/viewer/re_data_ui/src/component_path.rs +++ b/crates/viewer/re_data_ui/src/component_path.rs @@ -25,6 +25,7 @@ impl DataUi for ComponentPath { } else { let results = db .query_caches() + .read() .latest_at(query, entity_path, [*component_name]); if let Some(unit) = results.components.get(component_name) { crate::ComponentPathLatestAtResults { diff --git a/crates/viewer/re_data_ui/src/instance_path.rs b/crates/viewer/re_data_ui/src/instance_path.rs index f0bbb90d9c74..04d713cb9f34 100644 --- a/crates/viewer/re_data_ui/src/instance_path.rs +++ b/crates/viewer/re_data_ui/src/instance_path.rs @@ -122,9 +122,10 @@ fn latest_at( let components: Vec<(ComponentName, UnitChunkShared)> = components .iter() .filter_map(|&component_name| { - let mut results = db - .query_caches() - .latest_at(query, entity_path, [component_name]); + let mut results = + db.query_caches() + .read() + .latest_at(query, entity_path, [component_name]); // We ignore components that are unset at this point in time results diff --git a/crates/viewer/re_selection_panel/src/defaults_ui.rs b/crates/viewer/re_selection_panel/src/defaults_ui.rs index 26aeb0ff7427..d5c88abbe788 100644 --- a/crates/viewer/re_selection_panel/src/defaults_ui.rs +++ b/crates/viewer/re_selection_panel/src/defaults_ui.rs @@ -199,6 +199,7 @@ fn active_defaults( .into_iter() .filter(|c| { db.query_caches() + .read() .latest_at(query, &view.defaults_path, [*c]) .component_batch_raw(c) .map_or(false, |data| !data.is_empty()) diff --git a/crates/viewer/re_space_view/src/query.rs b/crates/viewer/re_space_view/src/query.rs index a8b58f6dd33c..ee98d23c3053 100644 --- a/crates/viewer/re_space_view/src/query.rs +++ b/crates/viewer/re_space_view/src/query.rs @@ -41,7 +41,7 @@ pub fn range_with_blueprint_resolved_data( // No need to query for components that have overrides. component_set.retain(|component| !overrides.components.contains_key(component)); - let results = ctx.recording().query_caches().range( + let results = ctx.recording().query_caches().read().range( range_query, &data_result.entity_path, component_set.iter().copied(), @@ -51,11 +51,16 @@ pub fn range_with_blueprint_resolved_data( // This means we over-query for defaults that will never be used. // component_set.retain(|component| !results.components.contains_key(component)); - let defaults = ctx.viewer_ctx.blueprint_db().query_caches().latest_at( - ctx.viewer_ctx.blueprint_query, - ctx.defaults_path, - component_set.iter().copied(), - ); + let defaults = ctx + .viewer_ctx + .blueprint_db() + .query_caches() + .read() + .latest_at( + ctx.viewer_ctx.blueprint_query, + ctx.defaults_path, + component_set.iter().copied(), + ); HybridRangeResults { overrides, @@ -96,7 +101,7 @@ pub fn latest_at_with_blueprint_resolved_data<'a>( component_set.retain(|component| !overrides.components.contains_key(component)); } - let results = ctx.viewer_ctx.recording().query_caches().latest_at( + let results = ctx.viewer_ctx.recording().query_caches().read().latest_at( latest_at_query, &data_result.entity_path, component_set.iter().copied(), @@ -106,11 +111,16 @@ pub fn latest_at_with_blueprint_resolved_data<'a>( // This means we over-query for defaults that will never be used. // component_set.retain(|component| !results.components.contains_key(component)); - let defaults = ctx.viewer_ctx.blueprint_db().query_caches().latest_at( - ctx.viewer_ctx.blueprint_query, - ctx.defaults_path, - component_set.iter().copied(), - ); + let defaults = ctx + .viewer_ctx + .blueprint_db() + .query_caches() + .read() + .latest_at( + ctx.viewer_ctx.blueprint_query, + ctx.defaults_path, + component_set.iter().copied(), + ); HybridLatestAtResults { overrides, @@ -190,7 +200,7 @@ fn query_overrides<'a>( // TODO(jleibs): This probably is not right, but this code path is not used // currently. This may want to use range_query instead depending on how // component override data-references are resolved. - ctx.store_context.blueprint.query_caches().latest_at( + ctx.store_context.blueprint.query_caches().read().latest_at( ¤t_query, &override_value.path, [*component_name], @@ -200,6 +210,7 @@ fn query_overrides<'a>( .store_context .blueprint .query_caches() + .read() .latest_at(¤t_query, &override_value.path, [*component_name]), }; diff --git a/crates/viewer/re_space_view_dataframe/src/dataframe_ui.rs b/crates/viewer/re_space_view_dataframe/src/dataframe_ui.rs index b170881c9dec..17c48a7aa1d5 100644 --- a/crates/viewer/re_space_view_dataframe/src/dataframe_ui.rs +++ b/crates/viewer/re_space_view_dataframe/src/dataframe_ui.rs @@ -32,7 +32,7 @@ pub(crate) enum HideColumnAction { pub(crate) fn dataframe_ui( ctx: &ViewerContext<'_>, ui: &mut egui::Ui, - query_handle: &re_dataframe::QueryHandle<'_>, + query_handle: &re_dataframe::QueryHandle, expanded_rows_cache: &mut ExpandedRowsCache, space_view_id: &SpaceViewId, ) -> Vec { @@ -182,7 +182,7 @@ impl RowsDisplayData { /// [`egui_table::TableDelegate`] implementation for displaying a [`QueryHandle`] in a table. struct DataframeTableDelegate<'a> { ctx: &'a ViewerContext<'a>, - query_handle: &'a QueryHandle<'a>, + query_handle: &'a QueryHandle, selected_columns: &'a [ColumnDescriptor], header_entity_paths: Vec>, display_data: anyhow::Result, diff --git a/crates/viewer/re_space_view_dataframe/src/space_view_class.rs b/crates/viewer/re_space_view_dataframe/src/space_view_class.rs index 10ec07774a32..4caea49a2597 100644 --- a/crates/viewer/re_space_view_dataframe/src/space_view_class.rs +++ b/crates/viewer/re_space_view_dataframe/src/space_view_class.rs @@ -129,7 +129,7 @@ mode sets the default time range to _everything_. You can override this in the s let query_engine = re_dataframe::QueryEngine { store: ctx.recording().store().clone(), - cache: ctx.recording().query_caches(), + cache: ctx.recording().query_caches().clone(), }; let view_contents = query diff --git a/crates/viewer/re_space_view_spatial/src/visualizers/videos.rs b/crates/viewer/re_space_view_spatial/src/visualizers/videos.rs index 9613ea0a87af..a30594b5ac33 100644 --- a/crates/viewer/re_space_view_spatial/src/visualizers/videos.rs +++ b/crates/viewer/re_space_view_spatial/src/visualizers/videos.rs @@ -404,7 +404,7 @@ fn latest_at_query_video_from_datastore( ) -> Option<(Arc>, Blob)> { let query = ctx.current_query(); - let results = ctx.recording().query_caches().latest_at( + let results = ctx.recording().query_caches().read().latest_at( &query, entity_path, AssetVideo::all_components().iter().copied(), diff --git a/crates/viewer/re_space_view_time_series/src/line_visualizer_system.rs b/crates/viewer/re_space_view_time_series/src/line_visualizer_system.rs index 1c15fa1a6bd5..a599e94473ee 100644 --- a/crates/viewer/re_space_view_time_series/src/line_visualizer_system.rs +++ b/crates/viewer/re_space_view_time_series/src/line_visualizer_system.rs @@ -460,7 +460,7 @@ fn collect_recursive_clears( let mut clear_entity_path = entity_path.clone(); loop { - let results = ctx.recording().query_caches().range( + let results = ctx.recording().query_caches().read().range( query, &clear_entity_path, [ClearIsRecursive::name()], diff --git a/crates/viewer/re_viewer/src/app.rs b/crates/viewer/re_viewer/src/app.rs index b534050b3926..e71f6ca5e8fb 100644 --- a/crates/viewer/re_viewer/src/app.rs +++ b/crates/viewer/re_viewer/src/app.rs @@ -818,13 +818,13 @@ impl App { #[cfg(not(target_arch = "wasm32"))] UICommand::ClearPrimaryCache => { if let Some(ctx) = store_context { - ctx.recording.query_caches().clear(); + ctx.recording.query_caches().write().clear(); } } #[cfg(not(target_arch = "wasm32"))] UICommand::PrintPrimaryCache => { if let Some(ctx) = store_context { - let text = format!("{:?}", ctx.recording.query_caches()); + let text = format!("{:?}", ctx.recording.query_caches().read()); egui_ctx.output_mut(|o| o.copied_text = text.clone()); println!("{text}"); } diff --git a/crates/viewer/re_viewer/src/blueprint/validation.rs b/crates/viewer/re_viewer/src/blueprint/validation.rs index 4593f08ca152..f8d973d289b6 100644 --- a/crates/viewer/re_viewer/src/blueprint/validation.rs +++ b/crates/viewer/re_viewer/src/blueprint/validation.rs @@ -22,6 +22,7 @@ pub(crate) fn validate_component(blueprint: &EntityDb) -> bool { for path in blueprint.entity_paths() { if let Some(array) = blueprint .query_caches() + .read() .latest_at(&query, path, [C::name()]) .component_batch_raw(&C::name()) { diff --git a/crates/viewer/re_viewer_context/src/item.rs b/crates/viewer/re_viewer_context/src/item.rs index 8e1eecac71b2..86b33a813b11 100644 --- a/crates/viewer/re_viewer_context/src/item.rs +++ b/crates/viewer/re_viewer_context/src/item.rs @@ -200,6 +200,7 @@ pub fn resolve_mono_instance_path( for component_name in component_names { if let Some(array) = entity_db .query_caches() + .read() .latest_at(query, &instance.entity_path, [component_name]) .component_batch_raw(&component_name) { diff --git a/crates/viewer/re_viewer_context/src/store_hub.rs b/crates/viewer/re_viewer_context/src/store_hub.rs index 53ff6bf67ba9..c3ce4fc734e8 100644 --- a/crates/viewer/re_viewer_context/src/store_hub.rs +++ b/crates/viewer/re_viewer_context/src/store_hub.rs @@ -793,8 +793,7 @@ impl StoreHub { /// Populate a [`StoreHubStats`] based on the active app. // - // TODO(jleibs): We probably want stats for all recordings, not just - // the active recording. + // TODO(jleibs): We probably want stats for all recordings, not just the active recording. pub fn stats(&self) -> StoreHubStats { re_tracing::profile_function!(); @@ -810,7 +809,7 @@ impl StoreHub { .unwrap_or_default(); let blueprint_cached_stats = blueprint - .map(|entity_db| entity_db.query_caches().stats()) + .map(|entity_db| entity_db.query_caches().read().stats()) .unwrap_or_default(); let blueprint_config = blueprint @@ -827,7 +826,7 @@ impl StoreHub { .unwrap_or_default(); let recording_cached_stats = recording - .map(|entity_db| entity_db.query_caches().stats()) + .map(|entity_db| entity_db.query_caches().read().stats()) .unwrap_or_default(); let recording_config2 = recording diff --git a/crates/viewer/re_viewport_blueprint/src/container.rs b/crates/viewer/re_viewport_blueprint/src/container.rs index 50407f62b42f..e54fac58e1ce 100644 --- a/crates/viewer/re_viewport_blueprint/src/container.rs +++ b/crates/viewer/re_viewport_blueprint/src/container.rs @@ -46,7 +46,7 @@ impl ContainerBlueprint { // ---- - let results = blueprint_db.query_caches().latest_at( + let results = blueprint_db.query_caches().read().latest_at( query, &id.as_entity_path(), blueprint_archetypes::ContainerBlueprint::all_components() diff --git a/crates/viewer/re_viewport_blueprint/src/space_view.rs b/crates/viewer/re_viewport_blueprint/src/space_view.rs index 76e9b4a0bad3..d9b8033c9e30 100644 --- a/crates/viewer/re_viewport_blueprint/src/space_view.rs +++ b/crates/viewer/re_viewport_blueprint/src/space_view.rs @@ -131,7 +131,7 @@ impl SpaceViewBlueprint { ) -> Option { re_tracing::profile_function!(); - let results = blueprint_db.query_caches().latest_at( + let results = blueprint_db.query_caches().read().latest_at( query, &id.as_entity_path(), blueprint_archetypes::SpaceViewBlueprint::all_components() @@ -275,6 +275,7 @@ impl SpaceViewBlueprint { .filter_map(|component_name| { let array = blueprint .query_caches() + .read() .latest_at(query, path, [component_name]) .component_batch_raw(&component_name); array.map(|array| (component_name, array)) diff --git a/crates/viewer/re_viewport_blueprint/src/space_view_contents.rs b/crates/viewer/re_viewport_blueprint/src/space_view_contents.rs index 466d8eda0157..69ea1577b977 100644 --- a/crates/viewer/re_viewport_blueprint/src/space_view_contents.rs +++ b/crates/viewer/re_viewport_blueprint/src/space_view_contents.rs @@ -433,6 +433,7 @@ impl DataQueryPropertyResolver<'_> { { if let Some(component_data) = blueprint .query_caches() + .read() .latest_at(blueprint_query, &recursive_override_path, [component_name]) .component_batch_raw(&component_name) { @@ -462,6 +463,7 @@ impl DataQueryPropertyResolver<'_> { { if let Some(component_data) = blueprint .query_caches() + .read() .latest_at(blueprint_query, &individual_override_path, [component_name]) .component_batch_raw(&component_name) { diff --git a/crates/viewer/re_viewport_blueprint/src/viewport_blueprint.rs b/crates/viewer/re_viewport_blueprint/src/viewport_blueprint.rs index f4ae9f8873f2..a3a5aecf8b51 100644 --- a/crates/viewer/re_viewport_blueprint/src/viewport_blueprint.rs +++ b/crates/viewer/re_viewport_blueprint/src/viewport_blueprint.rs @@ -68,7 +68,7 @@ impl ViewportBlueprint { ) -> Self { re_tracing::profile_function!(); - let results = blueprint_db.query_caches().latest_at( + let results = blueprint_db.query_caches().read().latest_at( query, &VIEWPORT_PATH.into(), blueprint_archetypes::ViewportBlueprint::all_components() diff --git a/docs/snippets/all/reference/dataframe_query.rs b/docs/snippets/all/reference/dataframe_query.rs index 92f3ef90df7c..35bca50e35b3 100644 --- a/docs/snippets/all/reference/dataframe_query.rs +++ b/docs/snippets/all/reference/dataframe_query.rs @@ -1,8 +1,10 @@ //! Query and display the first 10 rows of a recording. use rerun::{ - dataframe::{QueryCache, QueryEngine, QueryExpression, SparseFillStrategy, Timeline}, - ChunkStore, ChunkStoreConfig, VersionPolicy, + dataframe::{ + QueryCache, QueryCacheHandle, QueryEngine, QueryExpression, SparseFillStrategy, Timeline, + }, + ChunkStore, ChunkStoreConfig, ChunkStoreHandle, VersionPolicy, }; fn main() -> Result<(), Box> { @@ -11,19 +13,22 @@ fn main() -> Result<(), Box> { let path_to_rrd = &args[1]; let timeline = Timeline::log_time(); - let stores = ChunkStore::from_rrd_filepath( + let mut stores = ChunkStore::from_rrd_filepath( &ChunkStoreConfig::DEFAULT, path_to_rrd, VersionPolicy::Warn, - )?; - let Some((_, store)) = stores.first_key_value() else { + )? + .into_values() + .map(ChunkStoreHandle::new); + + let Some(store) = stores.next() else { return Ok(()); }; - let query_cache = QueryCache::new(store); + let query_cache = QueryCacheHandle::new(QueryCache::new(store.clone())); let query_engine = QueryEngine { store, - cache: &query_cache, + cache: query_cache, }; let query = QueryExpression { diff --git a/examples/rust/custom_space_view/src/color_coordinates_visualizer_system.rs b/examples/rust/custom_space_view/src/color_coordinates_visualizer_system.rs index d34774e86485..0d499e0494fc 100644 --- a/examples/rust/custom_space_view/src/color_coordinates_visualizer_system.rs +++ b/examples/rust/custom_space_view/src/color_coordinates_visualizer_system.rs @@ -61,7 +61,7 @@ impl VisualizerSystem for InstanceColorSystem { for data_result in query.iter_visible_data_results(ctx, Self::identifier()) { // …gather all colors and their instance ids. - let results = ctx.recording().query_caches().latest_at( + let results = ctx.recording().query_caches().read().latest_at( &ctx.current_query(), &data_result.entity_path, [Color::name()], diff --git a/examples/rust/dataframe_query/src/main.rs b/examples/rust/dataframe_query/src/main.rs index 12a480c259da..daf0e4257573 100644 --- a/examples/rust/dataframe_query/src/main.rs +++ b/examples/rust/dataframe_query/src/main.rs @@ -4,10 +4,9 @@ use itertools::Itertools; use rerun::{ dataframe::{ - concatenate_record_batches, EntityPathFilter, QueryCache, QueryEngine, QueryExpression, - SparseFillStrategy, Timeline, + concatenate_record_batches, ChunkStoreHandle, EntityPathFilter, QueryCache, + QueryCacheHandle, QueryEngine, QueryExpression, SparseFillStrategy, Timeline, }, - external::re_chunk_store::ChunkStoreHandle, ChunkStore, ChunkStoreConfig, StoreKind, VersionPolicy, }; @@ -56,10 +55,10 @@ fn main() -> Result<(), Box> { continue; } - let query_cache = QueryCache::new(store.clone()); + let query_cache = QueryCacheHandle::new(QueryCache::new(store.clone())); let query_engine = QueryEngine { store: store.clone(), - cache: &query_cache, + cache: query_cache.clone(), }; let query = QueryExpression { diff --git a/examples/rust/extend_viewer_ui/src/main.rs b/examples/rust/extend_viewer_ui/src/main.rs index 8a4b073e0f43..00b64c2aace7 100644 --- a/examples/rust/extend_viewer_ui/src/main.rs +++ b/examples/rust/extend_viewer_ui/src/main.rs @@ -156,6 +156,7 @@ fn component_ui( let results = entity_db .query_caches() + .read() .latest_at(&query, entity_path, [component_name]); if let Some(data) = results.component_batch_raw(&component_name) { diff --git a/rerun_py/src/dataframe.rs b/rerun_py/src/dataframe.rs index 91959377e00c..65e2b61601c8 100644 --- a/rerun_py/src/dataframe.rs +++ b/rerun_py/src/dataframe.rs @@ -567,7 +567,7 @@ impl PySchema { #[pyclass(name = "Recording")] pub struct PyRecording { store: ChunkStoreHandle, - cache: re_dataframe::QueryCache, + cache: re_dataframe::QueryCacheHandle, } /// A view of a recording restricted to a given index, containing a specific set of entities and components. @@ -1140,10 +1140,10 @@ impl PyRecordingView { } impl PyRecording { - fn engine(&self) -> QueryEngine<'_> { + fn engine(&self) -> QueryEngine { QueryEngine { store: self.store.clone(), - cache: &self.cache, + cache: self.cache.clone(), } } @@ -1403,7 +1403,9 @@ impl PyRRDArchive { .iter() .filter(|(id, _)| matches!(id.kind, StoreKind::Recording)) .map(|(_, store)| { - let cache = re_dataframe::QueryCache::new(store.clone()); + let cache = re_dataframe::QueryCacheHandle::new(re_dataframe::QueryCache::new( + store.clone(), + )); PyRecording { store: store.clone(), cache,