Skip to content

Commit 0b300fb

Browse files
authored
New data APIs 4: cached latest-at mono helpers everywhere (#5606)
Now that we have a component-based latest-at cache, we can start replacing legacy uncached helpers with new ones. Commit-by-commit review should be trivial. Because the new APIs are designed with promises in mind, this already highlights a whole bunch of places where we need to think about what to do in case the data is not ready yet. As indicated in #5607, these places have been labeled `TODO(#5607)` in the code. For now, we simply treat a pending promise the same as missing data. This PR also adds the new `Caches` and `PromiseResolver` to the `EntityDb`. To run a cached query, you now need a `DataStore`, a `Caches` and a `PromiseResolver`, i.e. you need an `EntityDb`. --- Part of a PR series to completely revamp the data APIs in preparation for the removal of instance keys and the introduction of promises: - #5573 - #5574 - #5581 - #5605 - #5606 - #5633 - #5673 - #5679 - #5687 - #5755 - TODO - TODO Builds on top of the static data PR series: - #5534
1 parent efca999 commit 0b300fb

File tree

38 files changed

+620
-527
lines changed

38 files changed

+620
-527
lines changed

Cargo.lock

+3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/re_data_store/src/lib.rs

-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ pub use self::arrow_util::ArrayExt;
3535
pub use self::store::{DataStore, DataStoreConfig, StoreGeneration};
3636
pub use self::store_event::{StoreDiff, StoreDiffKind, StoreEvent};
3737
pub use self::store_gc::{GarbageCollectionOptions, GarbageCollectionTarget};
38-
pub use self::store_helpers::VersionedComponent;
3938
pub use self::store_read::{LatestAtQuery, RangeQuery};
4039
pub use self::store_stats::{DataStoreRowStats, DataStoreStats, EntityStats};
4140
pub use self::store_subscriber::{StoreSubscriber, StoreSubscriberHandle};

crates/re_data_store/src/store_helpers.rs

+2-204
Original file line numberDiff line numberDiff line change
@@ -1,209 +1,7 @@
1-
use re_log_types::{DataCell, DataRow, EntityPath, RowId, TimeInt, TimePoint, Timeline};
2-
1+
use re_log_types::{DataCell, DataRow, EntityPath, RowId, TimePoint};
32
use re_types_core::{Component, ComponentName};
43

5-
use crate::{DataStore, LatestAtQuery};
6-
7-
// --- Read ---
8-
9-
// TODO(cmc): these helpers have got to go once the new APIs land.
10-
11-
/// A [`Component`] at a specific _data_ time, versioned with a specific [`RowId`].
12-
///
13-
/// This is not enough to globally, uniquely identify an instance of a component.
14-
/// For that you will need to combine the `InstancePath` that was used to query
15-
/// the versioned component with the returned [`RowId`], therefore creating a
16-
/// `VersionedInstancePath`.
17-
#[derive(Debug, Clone)]
18-
pub struct VersionedComponent<C: Component> {
19-
pub data_time: TimeInt,
20-
pub row_id: RowId,
21-
pub value: C,
22-
}
23-
24-
impl<C: Component> VersionedComponent<C> {
25-
#[inline]
26-
pub fn new(data_time: TimeInt, row_id: RowId, value: C) -> Self {
27-
Self {
28-
data_time,
29-
row_id,
30-
value,
31-
}
32-
}
33-
}
34-
35-
impl<C: Component> std::ops::Deref for VersionedComponent<C> {
36-
type Target = C;
37-
38-
#[inline]
39-
fn deref(&self) -> &Self::Target {
40-
&self.value
41-
}
42-
}
43-
44-
impl DataStore {
45-
/// Get the latest value for a given [`re_types_core::Component`], as well as the associated
46-
/// _data_ time and [`RowId`].
47-
///
48-
/// This assumes that the row we get from the store only contains a single instance for this
49-
/// component; it will generate a log message of `level` otherwise.
50-
///
51-
/// This should only be used for "mono-components" such as `Transform` and `Tensor`.
52-
///
53-
/// This is a best-effort helper, it will merely log messages on failure.
54-
pub fn query_latest_component_with_log_level<C: Component>(
55-
&self,
56-
entity_path: &EntityPath,
57-
query: &LatestAtQuery,
58-
level: re_log::Level,
59-
) -> Option<VersionedComponent<C>> {
60-
re_tracing::profile_function!();
61-
62-
let (data_time, row_id, cells) =
63-
self.latest_at(query, entity_path, C::name(), &[C::name()])?;
64-
let cell = cells.first()?.as_ref()?;
65-
66-
cell.try_to_native_mono::<C>()
67-
.map_err(|err| {
68-
if let re_log_types::DataCellError::LoggableDeserialize(err) = err {
69-
let bt = err.backtrace().map(|mut bt| {
70-
bt.resolve();
71-
bt
72-
});
73-
74-
let err = Box::new(err) as Box<dyn std::error::Error>;
75-
if let Some(bt) = bt {
76-
re_log::log_once!(
77-
level,
78-
"Couldn't deserialize component at {entity_path}#{}: {}\n{:#?}",
79-
C::name(),
80-
re_error::format(&err),
81-
bt,
82-
);
83-
} else {
84-
re_log::log_once!(
85-
level,
86-
"Couldn't deserialize component at {entity_path}#{}: {}",
87-
C::name(),
88-
re_error::format(&err)
89-
);
90-
}
91-
return err;
92-
}
93-
94-
let err = Box::new(err) as Box<dyn std::error::Error>;
95-
re_log::log_once!(
96-
level,
97-
"Couldn't deserialize component at {entity_path}#{}: {}",
98-
C::name(),
99-
re_error::format(&err)
100-
);
101-
102-
err
103-
})
104-
.ok()?
105-
.map(|c| VersionedComponent::new(data_time, row_id, c))
106-
}
107-
108-
/// Get the latest value for a given [`re_types_core::Component`], as well as the associated
109-
/// _data_ time and [`RowId`].
110-
///
111-
/// This assumes that the row we get from the store only contains a single instance for this
112-
/// component; it will log a warning otherwise.
113-
///
114-
/// This should only be used for "mono-components" such as `Transform` and `Tensor`.
115-
///
116-
/// This is a best-effort helper, it will merely log errors on failure.
117-
#[inline]
118-
pub fn query_latest_component<C: Component>(
119-
&self,
120-
entity_path: &EntityPath,
121-
query: &LatestAtQuery,
122-
) -> Option<VersionedComponent<C>> {
123-
self.query_latest_component_with_log_level(entity_path, query, re_log::Level::Warn)
124-
}
125-
126-
/// Get the latest value for a given [`re_types_core::Component`], as well as the associated
127-
/// _data_ time and [`RowId`].
128-
///
129-
/// This assumes that the row we get from the store only contains a single instance for this
130-
/// component; it will return None and log a debug message otherwise.
131-
///
132-
/// This should only be used for "mono-components" such as `Transform` and `Tensor`.
133-
///
134-
/// This is a best-effort helper, it will merely logs debug messages on failure.
135-
#[inline]
136-
pub fn query_latest_component_quiet<C: Component>(
137-
&self,
138-
entity_path: &EntityPath,
139-
query: &LatestAtQuery,
140-
) -> Option<VersionedComponent<C>> {
141-
self.query_latest_component_with_log_level(entity_path, query, re_log::Level::Debug)
142-
}
143-
144-
/// Call [`Self::query_latest_component`] at the given path, walking up the hierarchy until an instance is found.
145-
pub fn query_latest_component_at_closest_ancestor<C: Component>(
146-
&self,
147-
entity_path: &EntityPath,
148-
query: &LatestAtQuery,
149-
) -> Option<(EntityPath, VersionedComponent<C>)> {
150-
re_tracing::profile_function!();
151-
152-
let mut cur_path = Some(entity_path.clone());
153-
while let Some(path) = cur_path {
154-
if let Some(vc) = self.query_latest_component::<C>(&path, query) {
155-
return Some((path, vc));
156-
}
157-
cur_path = path.parent();
158-
}
159-
None
160-
}
161-
162-
/// Get the latest value for a given [`re_types_core::Component`] and the associated [`RowId`],
163-
/// assuming it is static.
164-
///
165-
/// This assumes that the row we get from the store only contains a single instance for this
166-
/// component; it will log a warning otherwise.
167-
///
168-
/// This should only be used for "mono-components" such as `Transform` and `Tensor`.
169-
///
170-
/// This is a best-effort helper, it will merely log errors on failure.
171-
pub fn query_static_component<C: Component>(
172-
&self,
173-
entity_path: &EntityPath,
174-
) -> Option<VersionedComponent<C>> {
175-
re_tracing::profile_function!();
176-
177-
let query = LatestAtQuery::latest(Timeline::default());
178-
self.query_latest_component(entity_path, &query).map(|vc| {
179-
debug_assert!(vc.data_time.is_static());
180-
vc
181-
})
182-
}
183-
184-
/// Get the latest value for a given [`re_types_core::Component`] and the associated [`RowId`],
185-
/// assuming it is static.
186-
///
187-
/// This assumes that the row we get from the store only contains a single instance for this
188-
/// component; it will return None and log a debug message otherwise.
189-
///
190-
/// This should only be used for "mono-components" such as `Transform` and `Tensor`.
191-
///
192-
/// This is a best-effort helper, it will merely log debug on failure.
193-
pub fn query_static_component_quiet<C: Component>(
194-
&self,
195-
entity_path: &EntityPath,
196-
) -> Option<VersionedComponent<C>> {
197-
re_tracing::profile_function!();
198-
199-
let query = LatestAtQuery::latest(Timeline::default());
200-
self.query_latest_component_quiet(entity_path, &query)
201-
.map(|vc| {
202-
debug_assert!(vc.data_time.is_static());
203-
vc
204-
})
205-
}
206-
}
4+
use crate::DataStore;
2075

2086
// --- Write ---
2097

crates/re_data_store/tests/correctness.rs

+25-12
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,24 @@ use re_types_core::Loggable as _;
1919

2020
// ---
2121

22+
fn query_latest_component<C: re_types_core::Component>(
23+
store: &DataStore,
24+
entity_path: &EntityPath,
25+
query: &LatestAtQuery,
26+
) -> Option<(TimeInt, RowId, C)> {
27+
re_tracing::profile_function!();
28+
29+
let (data_time, row_id, cells) =
30+
store.latest_at(query, entity_path, C::name(), &[C::name()])?;
31+
let cell = cells.first()?.as_ref()?;
32+
33+
cell.try_to_native_mono::<C>()
34+
.ok()?
35+
.map(|c| (data_time, row_id, c))
36+
}
37+
38+
// ---
39+
2240
#[test]
2341
fn row_id_ordering_semantics() -> anyhow::Result<()> {
2442
let entity_path: EntityPath = "some_entity".into();
@@ -60,10 +78,8 @@ fn row_id_ordering_semantics() -> anyhow::Result<()> {
6078

6179
{
6280
let query = LatestAtQuery::new(timeline_frame, 11);
63-
let got_point = store
64-
.query_latest_component::<MyPoint>(&entity_path, &query)
65-
.unwrap()
66-
.value;
81+
let (_, _, got_point) =
82+
query_latest_component::<MyPoint>(&store, &entity_path, &query).unwrap();
6783
similar_asserts::assert_eq!(point2, got_point);
6884
}
6985
}
@@ -129,10 +145,8 @@ fn row_id_ordering_semantics() -> anyhow::Result<()> {
129145

130146
{
131147
let query = LatestAtQuery::new(timeline_frame, 11);
132-
let got_point = store
133-
.query_latest_component::<MyPoint>(&entity_path, &query)
134-
.unwrap()
135-
.value;
148+
let (_, _, got_point) =
149+
query_latest_component::<MyPoint>(&store, &entity_path, &query).unwrap();
136150
similar_asserts::assert_eq!(point1, got_point);
137151
}
138152
}
@@ -170,10 +184,9 @@ fn row_id_ordering_semantics() -> anyhow::Result<()> {
170184
store.insert_row(&row)?;
171185

172186
{
173-
let got_point = store
174-
.query_static_component::<MyPoint>(&entity_path)
175-
.unwrap()
176-
.value;
187+
let query = LatestAtQuery::new(Timeline::new_temporal("doesnt_matter"), TimeInt::MAX);
188+
let (_, _, got_point) =
189+
query_latest_component::<MyPoint>(&store, &entity_path, &query).unwrap();
177190
similar_asserts::assert_eq!(point1, got_point);
178191
}
179192
}

crates/re_data_ui/src/annotation_context.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,12 @@ fn annotation_info(
8686
query: &re_data_store::LatestAtQuery,
8787
keypoint_id: KeypointId,
8888
) -> Option<AnnotationInfo> {
89+
// TODO(#5607): what should happen if the promise is still pending?
8990
let class_id = ctx
90-
.recording_store()
91-
.query_latest_component::<re_types::components::ClassId>(entity_path, query)?;
91+
.recording()
92+
.latest_at_component::<re_types::components::ClassId>(entity_path, query)?;
9293
let annotations = crate::annotations(ctx, query, entity_path);
93-
let class = annotations.resolved_class_description(Some(*class_id));
94+
let class = annotations.resolved_class_description(Some(class_id.value));
9495
class.keypoint_map?.get(&keypoint_id).cloned()
9596
}
9697

crates/re_data_ui/src/image.rs

+7-5
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,11 @@ impl EntityDataUi for re_types::components::TensorData {
3838
) {
3939
re_tracing::profile_function!();
4040

41+
// TODO(#5607): what should happen if the promise is still pending?
4142
let tensor_data_row_id = ctx
42-
.recording_store()
43-
.query_latest_component::<re_types::components::TensorData>(entity_path, query)
44-
.map_or(RowId::ZERO, |tensor| tensor.row_id);
43+
.recording()
44+
.latest_at_component::<re_types::components::TensorData>(entity_path, query)
45+
.map_or(RowId::ZERO, |tensor| tensor.index.1);
4546

4647
let decoded = ctx
4748
.cache
@@ -92,8 +93,9 @@ pub fn tensor_ui(
9293
let meaning = image_meaning_for_entity(entity_path, query, store);
9394

9495
let meter = if meaning == TensorDataMeaning::Depth {
95-
ctx.recording_store()
96-
.query_latest_component::<DepthMeter>(entity_path, query)
96+
// TODO(#5607): what should happen if the promise is still pending?
97+
ctx.recording()
98+
.latest_at_component::<DepthMeter>(entity_path, query)
9799
.map(|meter| meter.value.0)
98100
} else {
99101
None

crates/re_data_ui/src/item_ui.rs

+7-7
Original file line numberDiff line numberDiff line change
@@ -162,31 +162,31 @@ pub fn instance_path_icon(
162162
/// timeline, then use the blueprint. Otherwise, use the recording.
163163
// TODO(jleibs): Ideally this wouldn't be necessary and we could make the assessment
164164
// directly from the entity_path.
165-
pub fn guess_query_and_store_for_selected_entity<'a>(
165+
pub fn guess_query_and_db_for_selected_entity<'a>(
166166
ctx: &'a ViewerContext<'_>,
167167
entity_path: &EntityPath,
168-
) -> (re_data_store::LatestAtQuery, &'a re_data_store::DataStore) {
168+
) -> (re_data_store::LatestAtQuery, &'a re_entity_db::EntityDb) {
169169
if ctx.app_options.inspect_blueprint_timeline
170170
&& ctx.store_context.blueprint.is_logged_entity(entity_path)
171171
{
172172
(
173173
ctx.blueprint_cfg.time_ctrl.read().current_query(),
174-
ctx.store_context.blueprint.store(),
174+
ctx.store_context.blueprint,
175175
)
176176
} else {
177177
(
178178
ctx.rec_cfg.time_ctrl.read().current_query(),
179-
ctx.recording_store(),
179+
ctx.recording(),
180180
)
181181
}
182182
}
183183

184184
pub fn guess_instance_path_icon(
185185
ctx: &ViewerContext<'_>,
186186
instance_path: &InstancePath,
187-
) -> &'static icons::Icon {
188-
let (query, store) = guess_query_and_store_for_selected_entity(ctx, &instance_path.entity_path);
189-
instance_path_icon(&query.timeline(), store, instance_path)
187+
) -> &'static re_ui::icons::Icon {
188+
let (query, db) = guess_query_and_db_for_selected_entity(ctx, &instance_path.entity_path);
189+
instance_path_icon(&query.timeline(), db.store(), instance_path)
190190
}
191191

192192
/// Show an instance id and make it selectable.

crates/re_entity_db/Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ re_log.workspace = true
3131
re_log_encoding = { workspace = true, features = ["decoder"] }
3232
re_log_types.workspace = true
3333
re_query.workspace = true
34+
re_query2.workspace = true
3435
re_query_cache.workspace = true
36+
re_query_cache2.workspace = true
3537
re_smart_channel.workspace = true
3638
re_tracing.workspace = true
3739
re_types_core.workspace = true

0 commit comments

Comments
 (0)