Skip to content

Commit 9a5b36a

Browse files
teh-cmcemilk
andauthored
Consistent open/import/log_file behaviors in all common scenarios (#7966)
All the major data-loading paths now behave sanely and consistently on both native and web. You can try and look at the matrix below for the specific behavior of each path, but really my advice would be to just play with it to get a sense of things. One of these days we'll clearly need a bottom-up in-hindsight refactoring for all these things, it is _so_ complex. But today is not that day. * Fixes #7965 --------- Co-authored-by: Emil Ernerfeldt <[email protected]>
1 parent 44501bd commit 9a5b36a

File tree

13 files changed

+170
-50
lines changed

13 files changed

+170
-50
lines changed

Cargo.lock

+2
Original file line numberDiff line numberDiff line change
@@ -5523,6 +5523,7 @@ dependencies = [
55235523
"re_types",
55245524
"tempfile",
55255525
"thiserror",
5526+
"uuid",
55265527
"walkdir",
55275528
]
55285529

@@ -6562,6 +6563,7 @@ dependencies = [
65626563
"strum",
65636564
"strum_macros",
65646565
"thiserror",
6566+
"uuid",
65656567
"wasm-bindgen",
65666568
"wasm-bindgen-futures",
65676569
"web-sys",

crates/store/re_data_loader/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ once_cell.workspace = true
4343
parking_lot.workspace = true
4444
rayon.workspace = true
4545
thiserror.workspace = true
46+
uuid.workspace = true
4647
walkdir.workspace = true
4748

4849
[target.'cfg(not(any(target_arch = "wasm32")))'.dependencies]

crates/store/re_data_loader/src/lib.rs

+29-8
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,12 @@ pub struct DataLoaderSettings {
6464
/// The [`re_log_types::StoreId`] that is currently opened in the viewer, if any.
6565
pub opened_store_id: Option<re_log_types::StoreId>,
6666

67+
/// Whether `SetStoreInfo`s should be sent, regardless of the surrounding context.
68+
///
69+
/// Only useful when creating a recording just-in-time directly in the viewer (which is what
70+
/// happens when importing things into the welcome screen).
71+
pub force_store_info: bool,
72+
6773
/// What should the logged entity paths be prefixed with?
6874
pub entity_path_prefix: Option<EntityPath>,
6975

@@ -79,6 +85,7 @@ impl DataLoaderSettings {
7985
opened_application_id: Default::default(),
8086
store_id: store_id.into(),
8187
opened_store_id: Default::default(),
88+
force_store_info: false,
8289
entity_path_prefix: Default::default(),
8390
timepoint: Default::default(),
8491
}
@@ -91,6 +98,7 @@ impl DataLoaderSettings {
9198
opened_application_id,
9299
store_id,
93100
opened_store_id,
101+
force_store_info: _,
94102
entity_path_prefix,
95103
timepoint,
96104
} = self;
@@ -150,6 +158,8 @@ impl DataLoaderSettings {
150158
}
151159
}
152160

161+
pub type DataLoaderName = String;
162+
153163
/// A [`DataLoader`] loads data from a file path and/or a file's contents.
154164
///
155165
/// Files can be loaded in 3 different ways:
@@ -205,8 +215,8 @@ impl DataLoaderSettings {
205215
pub trait DataLoader: Send + Sync {
206216
/// Name of the [`DataLoader`].
207217
///
208-
/// Doesn't need to be unique.
209-
fn name(&self) -> String;
218+
/// Should be globally unique.
219+
fn name(&self) -> DataLoaderName;
210220

211221
/// Loads data from a file on the local filesystem and sends it to `tx`.
212222
///
@@ -314,20 +324,31 @@ impl DataLoaderError {
314324
/// most convenient for them, whether it is raw components, arrow chunks or even
315325
/// full-on [`LogMsg`]s.
316326
pub enum LoadedData {
317-
Chunk(re_log_types::StoreId, Chunk),
318-
ArrowMsg(re_log_types::StoreId, ArrowMsg),
319-
LogMsg(LogMsg),
327+
Chunk(DataLoaderName, re_log_types::StoreId, Chunk),
328+
ArrowMsg(DataLoaderName, re_log_types::StoreId, ArrowMsg),
329+
LogMsg(DataLoaderName, LogMsg),
320330
}
321331

322332
impl LoadedData {
333+
/// Returns the name of the [`DataLoader`] that generated this data.
334+
#[inline]
335+
pub fn data_loader_name(&self) -> &DataLoaderName {
336+
match self {
337+
Self::Chunk(name, ..) | Self::ArrowMsg(name, ..) | Self::LogMsg(name, ..) => name,
338+
}
339+
}
340+
323341
/// Pack the data into a [`LogMsg`].
342+
#[inline]
324343
pub fn into_log_msg(self) -> ChunkResult<LogMsg> {
325344
match self {
326-
Self::Chunk(store_id, chunk) => Ok(LogMsg::ArrowMsg(store_id, chunk.to_arrow_msg()?)),
345+
Self::Chunk(_name, store_id, chunk) => {
346+
Ok(LogMsg::ArrowMsg(store_id, chunk.to_arrow_msg()?))
347+
}
327348

328-
Self::ArrowMsg(store_id, msg) => Ok(LogMsg::ArrowMsg(store_id, msg)),
349+
Self::ArrowMsg(_name, store_id, msg) => Ok(LogMsg::ArrowMsg(store_id, msg)),
329350

330-
Self::LogMsg(msg) => Ok(msg),
351+
Self::LogMsg(_name, msg) => Ok(msg),
331352
}
332353
}
333354
}

crates/store/re_data_loader/src/load_file.rs

+33-16
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use ahash::{HashMap, HashMapExt};
44
use re_log_types::{FileSource, LogMsg};
55
use re_smart_channel::Sender;
66

7-
use crate::{DataLoaderError, LoadedData};
7+
use crate::{DataLoader, DataLoaderError, LoadedData, RrdLoader};
88

99
// ---
1010

@@ -37,7 +37,7 @@ pub fn load_from_path(
3737

3838
let rx = load(settings, path, None)?;
3939

40-
send(settings.clone(), file_source, path.to_owned(), rx, tx);
40+
send(settings.clone(), file_source, rx, tx);
4141

4242
Ok(())
4343
}
@@ -64,7 +64,7 @@ pub fn load_from_file_contents(
6464

6565
let data = load(settings, filepath, Some(contents))?;
6666

67-
send(settings.clone(), file_source, filepath.to_owned(), data, tx);
67+
send(settings.clone(), file_source, data, tx);
6868

6969
Ok(())
7070
}
@@ -73,21 +73,20 @@ pub fn load_from_file_contents(
7373

7474
/// Prepares an adequate [`re_log_types::StoreInfo`] [`LogMsg`] given the input.
7575
pub(crate) fn prepare_store_info(
76+
application_id: re_log_types::ApplicationId,
7677
store_id: &re_log_types::StoreId,
7778
file_source: FileSource,
78-
path: &std::path::Path,
7979
) -> LogMsg {
80-
re_tracing::profile_function!(path.display().to_string());
80+
re_tracing::profile_function!();
8181

8282
use re_log_types::SetStoreInfo;
8383

84-
let app_id = re_log_types::ApplicationId(path.display().to_string());
8584
let store_source = re_log_types::StoreSource::File { file_source };
8685

8786
LogMsg::SetStoreInfo(SetStoreInfo {
8887
row_id: *re_chunk::RowId::new(),
8988
info: re_log_types::StoreInfo {
90-
application_id: app_id.clone(),
89+
application_id,
9190
store_id: store_id.clone(),
9291
cloned_from: None,
9392
is_official_example: false,
@@ -263,14 +262,19 @@ pub(crate) fn load(
263262
pub(crate) fn send(
264263
settings: crate::DataLoaderSettings,
265264
file_source: FileSource,
266-
path: std::path::PathBuf,
267265
rx_loader: std::sync::mpsc::Receiver<LoadedData>,
268266
tx: &Sender<LogMsg>,
269267
) {
270268
spawn({
271269
re_tracing::profile_function!();
272270

273-
let mut store_info_tracker: HashMap<re_log_types::StoreId, bool> = HashMap::new();
271+
#[derive(Default, Debug)]
272+
struct Tracked {
273+
is_rrd_or_rbl: bool,
274+
already_has_store_info: bool,
275+
}
276+
277+
let mut store_info_tracker: HashMap<re_log_types::StoreId, Tracked> = HashMap::new();
274278

275279
let tx = tx.clone();
276280
move || {
@@ -280,6 +284,7 @@ pub(crate) fn send(
280284
// poll the channel in any case so as to make sure that the data producer
281285
// doesn't get stuck.
282286
for data in rx_loader {
287+
let data_loader_name = data.data_loader_name().clone();
283288
let msg = match data.into_log_msg() {
284289
Ok(msg) => {
285290
let store_info = match &msg {
@@ -293,7 +298,10 @@ pub(crate) fn send(
293298
};
294299

295300
if let Some((store_id, store_info_created)) = store_info {
296-
*store_info_tracker.entry(store_id).or_default() |= store_info_created;
301+
let tracked = store_info_tracker.entry(store_id).or_default();
302+
tracked.is_rrd_or_rbl =
303+
*data_loader_name == RrdLoader::name(&RrdLoader);
304+
tracked.already_has_store_info |= store_info_created;
297305
}
298306

299307
msg
@@ -306,16 +314,25 @@ pub(crate) fn send(
306314
tx.send(msg).ok();
307315
}
308316

309-
for (store_id, store_info_already_created) in store_info_tracker {
317+
for (store_id, tracked) in store_info_tracker {
310318
let is_a_preexisting_recording =
311319
Some(&store_id) == settings.opened_store_id.as_ref();
312320

313-
if store_info_already_created || is_a_preexisting_recording {
314-
continue;
315-
}
321+
// Never try to send custom store info for RRDs and RBLs, they always have their own, and
322+
// it's always right.
323+
let should_force_store_info = settings.force_store_info && !tracked.is_rrd_or_rbl;
324+
325+
let should_send_new_store_info = should_force_store_info
326+
|| (!tracked.already_has_store_info && !is_a_preexisting_recording);
316327

317-
let store_info = prepare_store_info(&store_id, file_source.clone(), &path);
318-
tx.send(store_info).ok();
328+
if should_send_new_store_info {
329+
let app_id = settings
330+
.opened_application_id
331+
.clone()
332+
.unwrap_or_else(|| uuid::Uuid::new_v4().to_string().into());
333+
let store_info = prepare_store_info(app_id, &store_id, file_source.clone());
334+
tx.send(store_info).ok();
335+
}
319336
}
320337

321338
tx.quit(None).ok();

crates/store/re_data_loader/src/loader_archetype.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ impl DataLoader for ArchetypeLoader {
138138
.clone()
139139
.unwrap_or_else(|| settings.store_id.clone());
140140
for row in rows {
141-
let data = LoadedData::Chunk(store_id.clone(), row);
141+
let data = LoadedData::Chunk(Self::name(&Self), store_id.clone(), row);
142142
if tx.send(data).is_err() {
143143
break; // The other end has decided to hang up, not our problem.
144144
}

crates/store/re_data_loader/src/loader_external.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use std::{
66
use ahash::HashMap;
77
use once_cell::sync::Lazy;
88

9-
use crate::LoadedData;
9+
use crate::{DataLoader, LoadedData};
1010

1111
// ---
1212

@@ -321,7 +321,7 @@ fn decode_and_stream<R: std::io::Read>(
321321
}
322322
};
323323

324-
let data = LoadedData::LogMsg(msg);
324+
let data = LoadedData::LogMsg(ExternalLoader::name(&ExternalLoader), msg);
325325
if tx.send(data).is_err() {
326326
break; // The other end has decided to hang up, not our problem.
327327
}

crates/store/re_data_loader/src/loader_rrd.rs

+13-4
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use re_log_encoding::decoder::Decoder;
44
use crossbeam::channel::Receiver;
55
use re_log_types::{ApplicationId, StoreId};
66

7-
use crate::LoadedData;
7+
use crate::{DataLoader as _, LoadedData};
88

99
// ---
1010

@@ -130,12 +130,21 @@ impl crate::DataLoader for RrdLoader {
130130
},
131131
};
132132

133+
// * We never want to patch blueprints' store IDs, only their app IDs.
134+
// * We neer use import semantics at all for .rrd files.
135+
let forced_application_id = if extension == "rbl" {
136+
settings.opened_application_id.as_ref()
137+
} else {
138+
None
139+
};
140+
let forced_recording_id = None;
141+
133142
decode_and_stream(
134143
&filepath,
135144
&tx,
136145
decoder,
137-
settings.opened_application_id.as_ref(),
138-
settings.opened_store_id.as_ref(),
146+
forced_application_id,
147+
forced_recording_id,
139148
);
140149

141150
Ok(())
@@ -192,7 +201,7 @@ fn decode_and_stream<R: std::io::Read>(
192201
msg
193202
};
194203

195-
let data = LoadedData::LogMsg(msg);
204+
let data = LoadedData::LogMsg(RrdLoader::name(&RrdLoader), msg);
196205
if tx.send(data).is_err() {
197206
break; // The other end has decided to hang up, not our problem.
198207
}

crates/store/re_data_source/src/data_source.rs

+3
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ impl DataSource {
178178
let settings = re_data_loader::DataLoaderSettings {
179179
opened_application_id: file_source.recommended_application_id().cloned(),
180180
opened_store_id: file_source.recommended_recording_id().cloned(),
181+
force_store_info: file_source.force_store_info(),
181182
..re_data_loader::DataLoaderSettings::recommended(shared_store_id)
182183
};
183184
re_data_loader::load_from_path(&settings, file_source, &path, &tx)
@@ -206,6 +207,7 @@ impl DataSource {
206207
let settings = re_data_loader::DataLoaderSettings {
207208
opened_application_id: file_source.recommended_application_id().cloned(),
208209
opened_store_id: file_source.recommended_recording_id().cloned(),
210+
force_store_info: file_source.force_store_info(),
209211
..re_data_loader::DataLoaderSettings::recommended(shared_store_id)
210212
};
211213
re_data_loader::load_from_file_contents(
@@ -275,6 +277,7 @@ fn test_data_source_from_uri() {
275277
let file_source = FileSource::DragAndDrop {
276278
recommended_application_id: None,
277279
recommended_recording_id: None,
280+
force_store_info: false,
278281
};
279282

280283
for uri in file {

crates/store/re_log_types/src/lib.rs

+31
Original file line numberDiff line numberDiff line change
@@ -413,21 +413,39 @@ pub enum FileSource {
413413
DragAndDrop {
414414
/// The [`ApplicationId`] that the viewer heuristically recommends should be used when loading
415415
/// this data source, based on the surrounding context.
416+
#[cfg_attr(feature = "serde", serde(skip))]
416417
recommended_application_id: Option<ApplicationId>,
417418

418419
/// The [`StoreId`] that the viewer heuristically recommends should be used when loading
419420
/// this data source, based on the surrounding context.
421+
#[cfg_attr(feature = "serde", serde(skip))]
420422
recommended_recording_id: Option<StoreId>,
423+
424+
/// Whether `SetStoreInfo`s should be sent, regardless of the surrounding context.
425+
///
426+
/// Only useful when creating a recording just-in-time directly in the viewer (which is what
427+
/// happens when importing things into the welcome screen).
428+
#[cfg_attr(feature = "serde", serde(skip))]
429+
force_store_info: bool,
421430
},
422431

423432
FileDialog {
424433
/// The [`ApplicationId`] that the viewer heuristically recommends should be used when loading
425434
/// this data source, based on the surrounding context.
435+
#[cfg_attr(feature = "serde", serde(skip))]
426436
recommended_application_id: Option<ApplicationId>,
427437

428438
/// The [`StoreId`] that the viewer heuristically recommends should be used when loading
429439
/// this data source, based on the surrounding context.
440+
#[cfg_attr(feature = "serde", serde(skip))]
430441
recommended_recording_id: Option<StoreId>,
442+
443+
/// Whether `SetStoreInfo`s should be sent, regardless of the surrounding context.
444+
///
445+
/// Only useful when creating a recording just-in-time directly in the viewer (which is what
446+
/// happens when importing things into the welcome screen).
447+
#[cfg_attr(feature = "serde", serde(skip))]
448+
force_store_info: bool,
431449
},
432450

433451
Sdk,
@@ -463,6 +481,19 @@ impl FileSource {
463481
Self::Cli | Self::Sdk => None,
464482
}
465483
}
484+
485+
#[inline]
486+
pub fn force_store_info(&self) -> bool {
487+
match self {
488+
Self::FileDialog {
489+
force_store_info, ..
490+
}
491+
| Self::DragAndDrop {
492+
force_store_info, ..
493+
} => *force_store_info,
494+
Self::Cli | Self::Sdk => false,
495+
}
496+
}
466497
}
467498

468499
/// The source of a recording or blueprint.

crates/top/re_sdk/src/recording_stream.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1259,6 +1259,7 @@ impl RecordingStream {
12591259
opened_application_id: None,
12601260
store_id: store_info.store_id.clone(),
12611261
opened_store_id: None,
1262+
force_store_info: false,
12621263
entity_path_prefix,
12631264
timepoint: (!static_).then(|| {
12641265
self.with(|inner| {

0 commit comments

Comments
 (0)