Skip to content

Commit ca5d678

Browse files
authored
fix: hook up repodata fetch reporter to gateway queries (#6112)
1 parent 39bf540 commit ca5d678

10 files changed

Lines changed: 140 additions & 21 deletions

File tree

crates/pixi_command_dispatcher/src/command_dispatcher/builder.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use crate::injected_config::{
1313
BackendOverrideKey, ChannelConfigKey, EnabledProtocolsKey, ToolBuildEnvironmentKey,
1414
};
1515
use crate::reporter::{
16-
BackendSourceBuildReporter, BuildBackendMetadataReporter, CondaSolveReporter,
16+
BackendSourceBuildReporter, BuildBackendMetadataReporter, CondaSolveReporter, GatewayReporter,
1717
InstantiateBackendReporter, PixiInstallReporter, PixiSolveReporter, SourceMetadataReporter,
1818
SourceRecordReporter,
1919
};
@@ -78,6 +78,7 @@ pub struct CommandDispatcherBuilder {
7878
source_metadata_reporter: Option<Arc<dyn SourceMetadataReporter>>,
7979
source_record_reporter: Option<Arc<dyn SourceRecordReporter>>,
8080
backend_source_build_reporter: Option<Arc<dyn BackendSourceBuildReporter>>,
81+
gateway_reporter: Option<Arc<dyn GatewayReporter>>,
8182
}
8283

8384
impl CommandDispatcherBuilder {
@@ -209,6 +210,16 @@ impl CommandDispatcherBuilder {
209210
}
210211
}
211212

213+
/// Register the [`GatewayReporter`](crate::GatewayReporter) used by
214+
/// every site that performs a repodata fetch through the
215+
/// [`Gateway`].
216+
pub fn with_gateway_reporter(self, reporter: Arc<dyn GatewayReporter>) -> Self {
217+
Self {
218+
gateway_reporter: Some(reporter),
219+
..self
220+
}
221+
}
222+
212223
/// Sets the reqwest client to use for network fetches.
213224
pub fn with_download_client(self, client: LazyClient) -> Self {
214225
Self {
@@ -484,6 +495,9 @@ impl CommandDispatcherBuilder {
484495
if let Some(r) = self.backend_source_build_reporter.clone() {
485496
engine_builder = engine_builder.with_data(r);
486497
}
498+
if let Some(r) = self.gateway_reporter.clone() {
499+
engine_builder = engine_builder.with_data(r);
500+
}
487501
if let Some(sem) = data.git_checkout_semaphore.clone() {
488502
engine_builder = engine_builder.with_data(GitCheckoutSemaphore(sem));
489503
}

crates/pixi_command_dispatcher/src/compute_data.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use tokio::sync::Semaphore;
1717

1818
use crate::cache::BuildBackendMetadataCache;
1919
use crate::reporter::{
20-
BackendSourceBuildReporter, BuildBackendMetadataReporter, CondaSolveReporter,
20+
BackendSourceBuildReporter, BuildBackendMetadataReporter, CondaSolveReporter, GatewayReporter,
2121
InstantiateBackendReporter, PixiInstallReporter, PixiSolveReporter, SourceMetadataReporter,
2222
SourceRecordReporter,
2323
};
@@ -55,6 +55,18 @@ impl HasCondaSolveReporter for DataStore {
5555
}
5656
}
5757

58+
/// Access the gateway-fetch reporter shared by every repodata-query
59+
/// site.
60+
pub trait HasGatewayReporter {
61+
fn gateway_reporter(&self) -> Option<&Arc<dyn GatewayReporter>>;
62+
}
63+
64+
impl HasGatewayReporter for DataStore {
65+
fn gateway_reporter(&self) -> Option<&Arc<dyn GatewayReporter>> {
66+
self.try_get::<Arc<dyn GatewayReporter>>()
67+
}
68+
}
69+
5870
/// Access the per-key pixi-solve reporter.
5971
pub trait HasPixiSolveReporter {
6072
fn pixi_solve_reporter(&self) -> Option<&Arc<dyn PixiSolveReporter>>;

crates/pixi_command_dispatcher/src/ephemeral_env/mod.rs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,14 @@ use xxhash_rust::xxh3::Xxh3;
3737

3838
use crate::SolveCondaEnvironmentSpec;
3939
use crate::cache::markers::BuildBackendsDir;
40-
use crate::compute_data::{HasGateway, HasInstantiateBackendReporter};
40+
use crate::compute_data::{HasGateway, HasGatewayReporter, HasInstantiateBackendReporter};
4141
use crate::injected_config::{ChannelConfigKey, ToolBuildEnvironmentKey};
4242
use crate::install_binary::install_binary_records;
43-
use crate::reporter::InstantiateBackendReporter;
43+
use crate::reporter::{InstantiateBackendReporter, WrappingGatewayReporter};
4444
use crate::solve_binary::SolveCondaExt;
4545
use crate::solve_conda::SolveCondaEnvironmentError;
4646
use pixi_compute_cache_dirs::CacheDirsExt;
47+
use pixi_compute_reporters::OperationId;
4748

4849
/// Specification for an ephemeral, binary-only conda environment.
4950
///
@@ -414,6 +415,13 @@ async fn fetch_binary_repodata(
414415

415416
let channel_config = ctx.compute(&ChannelConfigKey).await;
416417
let gateway = ctx.global_data().gateway().clone();
418+
// `fetch_binary_repodata` is invoked from `EphemeralEnvKey::compute`,
419+
// which runs inside the backend-instantiate op's `scope_active`.
420+
let gateway_reporter = OperationId::current().and_then(|op_id| {
421+
ctx.global_data()
422+
.gateway_reporter()
423+
.and_then(|r| r.create_gateway_reporter(op_id))
424+
});
417425

418426
let match_specs = binary_specs
419427
.clone()
@@ -425,13 +433,17 @@ async fn fetch_binary_repodata(
425433
.into_match_specs(&channel_config)
426434
.map_err(|e| EphemeralEnvError::SpecConversion(Arc::new(e)))?;
427435

428-
gateway
436+
let mut query = gateway
429437
.query(
430438
spec.channels.iter().cloned().map(Channel::from_url),
431439
[build_env.host_platform, Platform::NoArch],
432440
match_specs.into_iter().chain(constraint_specs),
433441
)
434-
.recursive(true)
442+
.recursive(true);
443+
if let Some(reporter) = gateway_reporter {
444+
query = query.with_reporter(WrappingGatewayReporter(reporter));
445+
}
446+
query
435447
.await
436448
.map_err(|e| EphemeralEnvError::Gateway(Arc::new(e)))
437449
}

crates/pixi_command_dispatcher/src/keys/solve_conda.rs

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,13 @@ use thiserror::Error;
2929
use tracing::instrument;
3030

3131
use crate::{
32-
SolveCondaEnvironmentSpec, SourceMetadata, compute_data::HasGateway,
33-
solve_binary::SolveCondaExt, solve_conda::SolveCondaEnvironmentError,
32+
SolveCondaEnvironmentSpec, SourceMetadata,
33+
compute_data::{HasGateway, HasGatewayReporter},
34+
reporter::WrappingGatewayReporter,
35+
solve_binary::SolveCondaExt,
36+
solve_conda::SolveCondaEnvironmentError,
3437
};
38+
use pixi_compute_reporters::OperationId;
3539

3640
/// Input to [`SolveCondaKey`]. All fields participate in the Key's
3741
/// identity so two callers with equal specs share one compute.
@@ -234,7 +238,16 @@ impl Key for SolveCondaKey {
234238
// Clone the gateway handle so we don't hold an immutable
235239
// borrow on `ctx` across the subsequent mutable-borrow solve.
236240
let gateway = ctx.global_data().gateway().clone();
237-
let binary_repodata = gateway
241+
// `solve-conda` runs inside the parent operation's
242+
// `scope_active` (e.g. the pixi-solve or backend-instantiate
243+
// op that triggered it), so `OperationId::current()` gives
244+
// the already-started op the gateway query belongs to.
245+
let gateway_reporter = OperationId::current().and_then(|op_id| {
246+
ctx.global_data()
247+
.gateway_reporter()
248+
.and_then(|r| r.create_gateway_reporter(op_id))
249+
});
250+
let mut query = gateway
238251
.query(
239252
spec.channels.iter().cloned().map(Channel::from_url),
240253
[spec.platform, Platform::NoArch],
@@ -244,8 +257,11 @@ impl Key for SolveCondaKey {
244257
.chain(source_repodata_fetch_specs)
245258
.chain(dev_source_fetch_specs),
246259
)
247-
.recursive(true)
248-
.await?;
260+
.recursive(true);
261+
if let Some(reporter) = gateway_reporter {
262+
query = query.with_reporter(WrappingGatewayReporter(reporter));
263+
}
264+
let binary_repodata = query.await?;
249265

250266
// Build the full solve spec and hand off to ctx.solve_conda
251267
// (semaphore + reporter lifecycle).

crates/pixi_command_dispatcher/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ pub use pixi_compute_sources::{
129129
SourceCheckoutExt, UrlCheckoutReporter, UrlDir,
130130
};
131131
pub use reporter::{
132-
BackendSourceBuildReporter, BuildBackendMetadataReporter, CondaSolveReporter,
132+
BackendSourceBuildReporter, BuildBackendMetadataReporter, CondaSolveReporter, GatewayReporter,
133133
InstantiateBackendReporter, PixiInstallReporter, PixiSolveEnvironmentSpec, PixiSolveReporter,
134134
SourceMetadataReporter, SourceMetadataReporterSpec, SourceRecordReporter,
135135
SourceRecordReporterSpec,

crates/pixi_command_dispatcher/src/reporter.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,37 @@ pub trait CondaSolveReporter: Send + Sync {
7979
fn on_finished(&self, solve_id: OperationId);
8080
}
8181

82+
/// Reporter for repodata fetches performed by the
83+
/// [`Gateway`](rattler_repodata_gateway::Gateway). Stored independently
84+
/// on the engine [`DataStore`](pixi_compute_engine::DataStore) and
85+
/// looked up at every gateway-query site.
86+
///
87+
/// `op_id` identifies the started operation under which the query
88+
/// runs (e.g. the surrounding pixi-solve or backend-instantiate
89+
/// op). Implementations can use it for parent lookups (e.g. to nest
90+
/// the repodata bar under the right solve), or ignore it.
91+
pub trait GatewayReporter: Send + Sync {
92+
/// Build a per-call rattler gateway reporter for the gateway
93+
/// query about to run inside the started operation `op_id`.
94+
/// `None` skips repodata-fetch progress reporting.
95+
fn create_gateway_reporter(
96+
&self,
97+
op_id: OperationId,
98+
) -> Option<Box<dyn rattler_repodata_gateway::Reporter>>;
99+
}
100+
101+
/// Adapts a `Box<dyn rattler_repodata_gateway::Reporter>` returned by
102+
/// [`GatewayReporter::create_gateway_reporter`] into the
103+
/// `impl Reporter + 'static` value expected by
104+
/// `rattler_repodata_gateway::Query::with_reporter`.
105+
pub struct WrappingGatewayReporter(pub Box<dyn rattler_repodata_gateway::Reporter>);
106+
107+
impl rattler_repodata_gateway::Reporter for WrappingGatewayReporter {
108+
fn download_reporter(&self) -> Option<&dyn rattler_repodata_gateway::DownloadReporter> {
109+
self.0.download_reporter()
110+
}
111+
}
112+
82113
/// Reporter for the compute-engine [`InstantiateBackendKey`](crate::InstantiateBackendKey).
83114
pub trait InstantiateBackendReporter: Send + Sync {
84115
fn on_queued(&self, spec: &JsonRpcBackendSpec) -> OperationId;

crates/pixi_command_dispatcher/tests/integration/event_reporter.rs

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ use std::{
66
use futures::{Stream, StreamExt};
77
use pixi_build_discovery::JsonRpcBackendSpec;
88
use pixi_command_dispatcher::{
9-
BackendSourceBuildSpec, BuildBackendMetadataInner, CondaSolveReporter, GitCheckoutReporter,
10-
InstallPixiEnvironmentSpec, PixiInstallReporter, PixiSolveEnvironmentSpec, PixiSolveReporter,
11-
SolveCondaEnvironmentSpec, SourceMetadataReporterSpec, SourceRecordReporterSpec,
12-
UrlCheckoutReporter,
9+
BackendSourceBuildSpec, BuildBackendMetadataInner, CondaSolveReporter, GatewayReporter,
10+
GitCheckoutReporter, InstallPixiEnvironmentSpec, PixiInstallReporter, PixiSolveEnvironmentSpec,
11+
PixiSolveReporter, SolveCondaEnvironmentSpec, SourceMetadataReporterSpec,
12+
SourceRecordReporterSpec, UrlCheckoutReporter,
1313
reporter::{
1414
BackendSourceBuildReporter, BuildBackendMetadataReporter, InstantiateBackendReporter,
1515
SourceMetadataReporter, SourceRecordReporter,
@@ -141,6 +141,15 @@ pub enum Event {
141141
InstantiateBackendFinished {
142142
id: OperationId,
143143
},
144+
145+
/// Recorded each time a gateway-query site requests a per-call
146+
/// repodata reporter. `id` is a fresh op id for the query itself;
147+
/// its parent in the registry is the started operation that
148+
/// triggered the fetch (carried in `op_id`).
149+
GatewayQuery {
150+
id: OperationId,
151+
op_id: OperationId,
152+
},
144153
}
145154

146155
pub struct EventReporter {
@@ -413,6 +422,17 @@ impl InstantiateBackendReporter for EventReporter {
413422
}
414423
}
415424

425+
impl GatewayReporter for EventReporter {
426+
fn create_gateway_reporter(
427+
&self,
428+
op_id: OperationId,
429+
) -> Option<Box<dyn rattler_repodata_gateway::Reporter>> {
430+
let id = self.alloc();
431+
self.record(Event::GatewayQuery { id, op_id });
432+
None
433+
}
434+
}
435+
416436
impl BackendSourceBuildReporter for EventReporter {
417437
fn on_queued(&self, spec: &BackendSourceBuildSpec) -> OperationId {
418438
let id = self.alloc();
@@ -462,6 +482,7 @@ impl EventReporter {
462482
.with_source_metadata_reporter(self.clone())
463483
.with_source_record_reporter(self.clone())
464484
.with_backend_source_build_reporter(self.clone())
485+
.with_gateway_reporter(self.clone())
465486
}
466487
}
467488

crates/pixi_command_dispatcher/tests/integration/event_tree.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ impl EventTree {
113113
}
114114
Event::InstantiateBackendStarted { id } => builder.alloc_pending(*id),
115115
Event::InstantiateBackendFinished { .. } => {}
116+
Event::GatewayQuery { id, .. } => {
117+
builder.alloc_node(*id, "Repodata fetch".to_owned());
118+
}
116119
Event::UrlCheckoutQueued { .. }
117120
| Event::UrlCheckoutStarted { .. }
118121
| Event::UrlCheckoutFinished { .. } => {
Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
11
---
22
source: crates/pixi_command_dispatcher/tests/integration/main.rs
33
expression: output
4+
snapshot_kind: text
45
---
56
Pixi solve (test@[PLATFORM])
67
├── Git Checkout (file://[LOCAL_GIT_REPO]#[GIT_HASH])
78
├── Source metadata (foobar-desktop @ git+file://[LOCAL_GIT_REPO]?subdirectory=recipe&rev=[GIT_REF]#[GIT_HASH])
89
│ ├── Build backend metadata (git+file://[LOCAL_GIT_REPO]?subdirectory=recipe&rev=[GIT_REF]#[GIT_HASH])
910
│ │ ├── Git Checkout (file://[LOCAL_GIT_REPO]#[GIT_HASH])
1011
│ │ └── Instantiate backend (pixi-build-rattler-build)
11-
│ │ └── Conda solve #6
12+
│ │ ├── Repodata fetch
13+
│ │ └── Conda solve #7
1214
│ └── Source record (foobar-desktop @ git+file://[LOCAL_GIT_REPO]?subdirectory=recipe&rev=[GIT_REF]#[GIT_HASH])
1315
├── Source metadata (foobar @ git+file://[LOCAL_GIT_REPO]?subdirectory=recipe&rev=[GIT_REF]#[GIT_HASH])
1416
│ └── Source record (foobar @ git+file://[LOCAL_GIT_REPO]?subdirectory=recipe&rev=[GIT_REF]#[GIT_HASH])
15-
└── Conda solve #10
17+
├── Repodata fetch
18+
└── Conda solve #12
1619
Pixi install (test-env)
1720
├── Backend source build (foobar-desktop)
1821
└── Backend source build (foobar)

crates/pixi_reporters/src/lib.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,6 @@ pub struct TopLevelProgress {
3434
/// `OperationId` → bar slot in `conda_solve_reporter`. Lets
3535
/// `on_started` / `on_finished` find the bar created at `on_queued`.
3636
solve_bars: Mutex<HashMap<OperationId, usize>>,
37-
// Held so on_clear can wipe it; the gateway integration that would
38-
// populate it currently goes through a different code path, so the
39-
// bar is effectively a placeholder for future wiring.
4037
repodata_reporter: RepodataReporter,
4138
sync_reporter: SyncReporter,
4239
}
@@ -114,6 +111,7 @@ impl TopLevelProgress {
114111
.with_instantiate_backend_reporter(self.clone())
115112
.with_git_checkout_reporter(self.source_checkout_reporter.clone())
116113
.with_backend_source_build_reporter(backend_source_build_reporter)
114+
.with_gateway_reporter(self.clone())
117115
}
118116

119117
/// Clear the current progress bars without tearing down the reporter.
@@ -216,3 +214,12 @@ impl pixi_command_dispatcher::CondaSolveReporter for TopLevelProgress {
216214
}
217215
}
218216
}
217+
218+
impl pixi_command_dispatcher::GatewayReporter for TopLevelProgress {
219+
fn create_gateway_reporter(
220+
&self,
221+
_op_id: OperationId,
222+
) -> Option<Box<dyn rattler_repodata_gateway::Reporter>> {
223+
Some(Box::new(self.repodata_reporter.clone()))
224+
}
225+
}

0 commit comments

Comments
 (0)