query_runtime/runtime/context/
lakehouse.rs1use std::{
4 future::Future,
5 sync,
6 sync::Arc,
7 time::{Duration, Instant},
8};
9
10use ::object_store::ObjectStoreExt;
11#[cfg(any(test, feature = "test-hooks"))]
12use bytes::Bytes;
13use object_store::{aws::AwsCredential, path::Path as ObjectStorePath};
14use query_planning::{
15 error::{QueryError, QueryErrorCode, Result},
16 source::redacted_object_store_path_diagnostic,
17};
18use tokio::{runtime::Runtime as TokioRuntime, task::JoinHandle};
19
20use super::{
21 super::{
22 CacheFillByteBudget, CacheFillBytePermit, SourceStorageProfile,
23 cache_fill::CacheFillOperationPermit,
24 object_store::{ObjectStoreCustomFetchResult, ObjectStoreFetchResult, S3ObjectStoreKey},
25 },
26 ExecutionContext, QueryServiceStreamPool,
27};
28
29impl ExecutionContext {
30 pub fn service_stream_pool(&self) -> QueryServiceStreamPool {
36 QueryServiceStreamPool {
37 pool: self.device.service_stream_pool.clone(),
38 service_epoch: self.admitted().map(|attempt| attempt.service_epoch.clone()),
39 query_anchor: self.plan.query_anchor.clone(),
40 }
41 }
42
43 pub fn lakehouse_cache_fill_acquire_timeout(&self) -> Duration {
45 self.device.lakehouse_cache_fill_config.acquire_timeout
46 }
47
48 pub fn lakehouse_cache_fill_budget(&self) -> CacheFillByteBudget {
50 self.device.lakehouse_cache_fill_budget.clone()
51 }
52
53 #[cfg(test)]
55 pub(crate) fn lakehouse_cache_fill_max_concurrent_ops(&self) -> usize {
56 self.device.lakehouse_cache_fill_concurrency.max_concurrent_ops()
57 }
58
59 #[doc(hidden)]
60 pub fn acquire_lakehouse_cache_fill_operation_with_timeout(
61 &self,
62 timeout: Duration,
63 operation: &'static str,
64 ) -> Result<Option<CacheFillOperationPermit>> {
65 self.device.lakehouse_cache_fill_concurrency.acquire_with_timeout(self.cancellation(), timeout, operation)
66 }
67
68 #[doc(hidden)]
69 pub fn acquire_lakehouse_cache_fill_budget_with_timeout(
70 &self,
71 bytes: u64,
72 timeout: Duration,
73 operation: &'static str,
74 ) -> Result<Option<CacheFillBytePermit>> {
75 self.device.lakehouse_cache_fill_budget.acquire_with_timeout(self.cancellation(), bytes, timeout, operation)
76 }
77
78 #[doc(hidden)]
79 pub fn s3_object_store(&self, key: S3ObjectStoreKey) -> Result<Arc<dyn ::object_store::ObjectStore>> {
80 self.device.source_object_stores.s3_store(key)
81 }
82
83 #[doc(hidden)]
84 pub fn source_storage_profile(&self, id: &str) -> Result<&SourceStorageProfile> {
85 self.device.source_object_stores.profile(id)
86 }
87
88 #[doc(hidden)]
95 pub fn resolve_s3_default_chain_credential(
96 &self,
97 key: S3ObjectStoreKey,
98 operation: &'static str,
99 ) -> Result<Arc<AwsCredential>> {
100 if let Some(credential) = self.device.source_object_stores.recently_resolved_s3_credential(&key) {
101 return Ok(credential);
102 }
103 let storage_profile_id = key.storage_profile_id.clone();
104 let provider = self.device.source_object_stores.s3_credential_provider(key.clone())?;
105 let (sender, receiver) = sync::mpsc::channel();
110 self.device.object_store_control_runtime.spawn(async move {
111 let credential = provider.get_credential().await;
112 let _ = sender.send(credential);
113 });
114 let credential_unavailable = |query_error: QueryError| {
115 let query_error = query_error
116 .with_fact("resource", "remote_parquet_source")
117 .with_fact("credential_source", "default_chain")
118 .with_fact("reason", "default_chain_credentials_unavailable");
119 match storage_profile_id.as_deref() {
120 Some(profile_id) => query_error.with_fact("storage_profile_id", profile_id),
121 None => query_error,
122 }
123 };
124 let credential = receiver.recv().map_err(|_error| {
125 credential_unavailable(QueryError::new(
126 QueryErrorCode::Internal,
127 operation,
128 "source credential resolution worker stopped before returning a result",
129 ))
130 })?;
131 let credential = credential.map_err(|error| {
132 credential_unavailable(
133 QueryError::new(
134 QueryErrorCode::CudfDependencyFailure,
135 operation,
136 "failed to resolve AWS default-chain credentials for native remote-direct read",
137 )
138 .with_source(error),
139 )
140 })?;
141 self.device.source_object_stores.record_resolved_s3_credential(key, Arc::clone(&credential));
142 Ok(credential)
143 }
144
145 #[doc(hidden)]
146 pub fn spawn_cache_maintenance_fetch(
147 &self,
148 store: Arc<dyn ::object_store::ObjectStore>,
149 location: ObjectStorePath,
150 on_result: impl FnOnce(ObjectStoreFetchResult) + Send + 'static,
151 ) {
152 Self::spawn_cache_maintenance_fetch_on_runtime(
153 self.device.object_store_control_runtime.as_ref(),
154 store,
155 location,
156 on_result,
157 )
158 }
159
160 #[doc(hidden)]
166 pub fn spawn_object_store_control_task<F, Fut, T>(
167 &self,
168 future_factory: F,
169 on_result: impl FnOnce(ObjectStoreCustomFetchResult<T>) + Send + 'static,
170 ) -> JoinHandle<()>
171 where
172 F: FnOnce() -> Fut + Send + 'static,
173 Fut: Future<Output = Result<T>> + Send + 'static,
174 T: Send + 'static,
175 {
176 self.device.object_store_control_runtime.spawn(async move {
177 let timer = Instant::now();
178 let value = future_factory().await;
179 on_result(ObjectStoreCustomFetchResult { value, elapsed: timer.elapsed() });
180 })
181 }
182
183 #[doc(hidden)]
185 pub fn spawn_lakehouse_cache_maintenance<F, Fut, T>(
186 &self,
187 future_factory: F,
188 on_result: impl FnOnce(ObjectStoreCustomFetchResult<T>) + Send + 'static,
189 ) -> JoinHandle<()>
190 where
191 F: FnOnce() -> Fut + Send + 'static,
192 Fut: Future<Output = Result<T>> + Send + 'static,
193 T: Send + 'static,
194 {
195 self.device.object_store_control_runtime.spawn(async move {
196 let timer = Instant::now();
197 let value = future_factory().await;
198 on_result(ObjectStoreCustomFetchResult { value, elapsed: timer.elapsed() });
199 })
200 }
201
202 #[cfg(any(test, feature = "test-hooks"))]
203 #[cfg_attr(not(test), expect(dead_code, reason = "used by in-crate runtime source tests"))]
204 pub(in crate::runtime) fn fetch_object_store_bytes_on_runtime(
205 object_store_control_runtime: &TokioRuntime,
206 store: Arc<dyn ::object_store::ObjectStore>,
207 location: ObjectStorePath,
208 ) -> Result<Bytes> {
209 let (sender, receiver) = sync::mpsc::channel();
210 Self::spawn_cache_maintenance_fetch_on_runtime(object_store_control_runtime, store, location, move |result| {
211 let _ = sender.send(result.bytes);
212 });
213 receiver.recv().map_err(|error| {
214 QueryError::new(
215 QueryErrorCode::Internal,
216 "query_engine.lakehouse_cache.maintenance.receive",
217 "object-store cache maintenance worker stopped before returning a result",
218 )
219 .with_source(error)
220 })?
221 }
222
223 fn spawn_cache_maintenance_fetch_on_runtime(
224 object_store_control_runtime: &TokioRuntime,
225 store: Arc<dyn ::object_store::ObjectStore>,
226 location: ObjectStorePath,
227 on_result: impl FnOnce(ObjectStoreFetchResult) + Send + 'static,
228 ) {
229 object_store_control_runtime.spawn(async move {
230 let path_fact = redacted_object_store_path_diagnostic(location.as_ref());
231 let bytes = async move {
232 let result = store.get(&location).await?;
233 result.bytes().await
234 }
235 .await
236 .map_err(|_error| {
237 QueryError::new(
238 QueryErrorCode::CudfDependencyFailure,
239 "query_engine.lakehouse_cache.maintenance.object_store",
240 "failed to fetch lakehouse cache object through object_store",
241 )
242 .with_fact("path", path_fact)
243 });
244 on_result(ObjectStoreFetchResult { bytes });
245 });
246 }
247}