1use std::{marker::PhantomData, rc::Rc, time::Duration};
2
3use super::{
4 CancellationToken, DiagnosticAdmission, DiagnosticEvent, DiagnosticOutcome, DiagnosticSource,
5 ErasedDomainResult, InvocationContext, NativeAppRuntime, NativeEndpointBinding,
6 RequestCapability, RequestId, RuntimeFailure, await_with_generation_context,
7 diagnostics::diagnostic_operation, ensure_context_active,
8 schedule_module_supervision_after_failure,
9};
10
11#[derive(Debug)]
13pub struct NativeRequestHandle<C: RequestCapability> {
14 pub(super) endpoints: Vec<NativeEndpointBinding>,
15 pub(super) runtime: Rc<NativeAppRuntime>,
16 pub(super) caller_instance: String,
17 pub(super) caller_is_planned: bool,
18 pub(super) allow_before_ready: bool,
19 pub(super) capability: PhantomData<fn() -> C>,
20}
21
22impl<C: RequestCapability> NativeRequestHandle<C> {
23 pub(super) fn from_endpoints(
24 endpoints: &[NativeEndpointBinding],
25 runtime: Rc<NativeAppRuntime>,
26 caller_instance: &str,
27 allow_before_ready: bool,
28 ) -> Self {
29 let caller_is_planned = runtime.plan.module_instance(caller_instance).is_some();
30 Self {
31 endpoints: endpoints.to_vec(),
32 runtime,
33 caller_instance: caller_instance.to_owned(),
34 caller_is_planned,
35 allow_before_ready,
36 capability: PhantomData,
37 }
38 }
39
40 pub fn binding_count(&self) -> usize {
42 self.endpoints.len()
43 }
44
45 fn diagnostic_caller_instance(&self) -> Option<String> {
46 self.caller_is_planned.then(|| self.caller_instance.clone())
47 }
48
49 pub async fn invoke(
51 &self,
52 operation: &str,
53 request: C::Request,
54 ) -> Result<Result<C::Response, C::DomainError>, RuntimeFailure> {
55 let context = self.next_context();
56 self.invoke_with_context(operation, context, request).await
57 }
58
59 pub async fn invoke_with_context(
61 &self,
62 operation: &str,
63 context: InvocationContext,
64 request: C::Request,
65 ) -> Result<Result<C::Response, C::DomainError>, RuntimeFailure> {
66 let context = context
67 .with_caller_instance(self.caller_instance.clone())
68 .for_target(C::ID, operation);
69 let started_at = (self.runtime.driver.now)();
70 let operation_name = self
71 .endpoints
72 .first()
73 .and_then(|endpoint| diagnostic_operation(endpoint.state.operations, operation));
74 self.runtime
75 .diagnostics
76 .emit(DiagnosticSource::Invocation, started_at, |_| {
77 DiagnosticEvent::InvocationStarted {
78 request_id: context.request_id(),
79 caller_instance: self.diagnostic_caller_instance(),
80 provider_instance: self
81 .endpoints
82 .first()
83 .map(|endpoint| endpoint.module_instance.clone()),
84 capability: C::ID,
85 operation: operation_name,
86 }
87 });
88 let request_id = context.request_id();
89 let result = self
90 .invoke_with_context_inner(operation, context, request)
91 .await;
92 let outcome = request_diagnostic_outcome(&result);
93 self.runtime.diagnostics.emit(
94 DiagnosticSource::Invocation,
95 (self.runtime.driver.now)(),
96 |_| DiagnosticEvent::InvocationCompleted {
97 request_id,
98 caller_instance: self.diagnostic_caller_instance(),
99 provider_instance: self
100 .endpoints
101 .first()
102 .map(|endpoint| endpoint.module_instance.clone()),
103 capability: C::ID,
104 operation: operation_name,
105 outcome,
106 elapsed: (self.runtime.driver.now)().saturating_sub(started_at),
107 },
108 );
109 if let Err(error) = &result {
110 self.runtime.diagnostics.emit_runtime_failure(
111 (self.runtime.driver.now)(),
112 self.endpoints
113 .first()
114 .map(|endpoint| endpoint.module_instance.as_str()),
115 error,
116 );
117 if let Some(admission) = diagnostic_admission(error) {
118 self.runtime.diagnostics.emit(
119 DiagnosticSource::Admission,
120 (self.runtime.driver.now)(),
121 |_| DiagnosticEvent::AdmissionRejected {
122 request_id,
123 caller_instance: self.diagnostic_caller_instance(),
124 provider_instance: self
125 .endpoints
126 .first()
127 .map(|endpoint| endpoint.module_instance.clone()),
128 capability: C::ID,
129 operation: operation_name,
130 outcome: admission,
131 },
132 );
133 }
134 }
135 result
136 }
137
138 async fn invoke_with_context_inner(
139 &self,
140 operation: &str,
141 context: InvocationContext,
142 request: C::Request,
143 ) -> Result<Result<C::Response, C::DomainError>, RuntimeFailure> {
144 if self.runtime.shutdown_started.get()
145 || (!self.allow_before_ready && self.runtime.admission.is_closed())
146 {
147 return Err(RuntimeFailure::AdmissionClosed);
148 }
149 let endpoint = match self.endpoints.as_slice() {
150 [] => return Err(RuntimeFailure::Unavailable { capability: C::ID }),
151 [endpoint] => endpoint,
152 endpoints => {
153 return Err(RuntimeFailure::AmbiguousBinding {
154 capability: C::ID,
155 providers: endpoints.len(),
156 });
157 }
158 };
159 let snapshot = endpoint
160 .state
161 .snapshot()
162 .ok_or(RuntimeFailure::Unavailable { capability: C::ID })?;
163 let admission =
164 endpoint
165 .admission(operation)
166 .ok_or_else(|| RuntimeFailure::UnknownOperation {
167 capability: C::ID,
168 operation: operation.to_owned(),
169 })?;
170 let _permit = admission
171 .acquire(
172 C::ID,
173 operation,
174 context.clone(),
175 self.runtime.driver.clone(),
176 )
177 .await?;
178 if !endpoint.state.is_current(snapshot.generation) {
179 return Err(RuntimeFailure::Unavailable { capability: C::ID });
180 }
181 ensure_context_active(&self.runtime.driver, &context)?;
182 let outcome = await_with_generation_context(
183 &self.runtime.driver,
184 &context,
185 snapshot.cancellation,
186 C::ID,
187 snapshot
188 .endpoint
189 .invoke(operation, Box::new(request), context.clone()),
190 )
191 .await
192 .map_err(|error| {
193 schedule_module_supervision_after_failure(
194 &self.runtime,
195 &endpoint.module_instance,
196 error,
197 )
198 })?
199 .map_err(|error| {
200 schedule_module_supervision_after_failure(
201 &self.runtime,
202 &endpoint.module_instance,
203 error,
204 )
205 })?;
206 decode_outcome::<C>(outcome)
207 }
208
209 pub async fn invoke_many(
211 &self,
212 operation: &str,
213 request: C::Request,
214 ) -> Result<Vec<Result<C::Response, C::DomainError>>, RuntimeFailure>
215 where
216 C::Request: Clone,
217 {
218 let context = self.next_context();
219 self.invoke_many_with_context(operation, context, request)
220 .await
221 }
222
223 pub async fn invoke_many_with_context(
225 &self,
226 operation: &str,
227 context: InvocationContext,
228 request: C::Request,
229 ) -> Result<Vec<Result<C::Response, C::DomainError>>, RuntimeFailure>
230 where
231 C::Request: Clone,
232 {
233 let context = context.with_caller_instance(self.caller_instance.clone());
234 let started_at = (self.runtime.driver.now)();
235 let operation_name = self
236 .endpoints
237 .first()
238 .and_then(|endpoint| diagnostic_operation(endpoint.state.operations, operation));
239 let request_id = context.request_id();
240 self.runtime
241 .diagnostics
242 .emit(DiagnosticSource::Invocation, started_at, |_| {
243 DiagnosticEvent::InvocationStarted {
244 request_id,
245 caller_instance: self.diagnostic_caller_instance(),
246 provider_instance: None,
247 capability: C::ID,
248 operation: operation_name,
249 }
250 });
251 let result = self
252 .invoke_many_with_context_inner(operation, context, request)
253 .await;
254 let outcome = many_request_diagnostic_outcome(&result);
255 self.runtime.diagnostics.emit(
256 DiagnosticSource::Invocation,
257 (self.runtime.driver.now)(),
258 |_| DiagnosticEvent::InvocationCompleted {
259 request_id,
260 caller_instance: self.diagnostic_caller_instance(),
261 provider_instance: None,
262 capability: C::ID,
263 operation: operation_name,
264 outcome,
265 elapsed: (self.runtime.driver.now)().saturating_sub(started_at),
266 },
267 );
268 if let Err(error) = &result {
269 self.runtime
270 .diagnostics
271 .emit_runtime_failure((self.runtime.driver.now)(), None, error);
272 if let Some(admission) = diagnostic_admission(error) {
273 self.runtime.diagnostics.emit(
274 DiagnosticSource::Admission,
275 (self.runtime.driver.now)(),
276 |_| DiagnosticEvent::AdmissionRejected {
277 request_id,
278 caller_instance: self.diagnostic_caller_instance(),
279 provider_instance: None,
280 capability: C::ID,
281 operation: operation_name,
282 outcome: admission,
283 },
284 );
285 }
286 }
287 result
288 }
289
290 async fn invoke_many_with_context_inner(
291 &self,
292 operation: &str,
293 context: InvocationContext,
294 request: C::Request,
295 ) -> Result<Vec<Result<C::Response, C::DomainError>>, RuntimeFailure>
296 where
297 C::Request: Clone,
298 {
299 if self.runtime.shutdown_started.get()
300 || (!self.allow_before_ready && self.runtime.admission.is_closed())
301 {
302 return Err(RuntimeFailure::AdmissionClosed);
303 }
304 if self.endpoints.is_empty() {
305 return Ok(Vec::new());
306 }
307 let mut outcomes = Vec::with_capacity(self.endpoints.len());
308 for endpoint in &self.endpoints {
309 let snapshot = endpoint
310 .state
311 .snapshot()
312 .ok_or(RuntimeFailure::Unavailable { capability: C::ID })?;
313 let admission =
314 endpoint
315 .admission(operation)
316 .ok_or_else(|| RuntimeFailure::UnknownOperation {
317 capability: C::ID,
318 operation: operation.to_owned(),
319 })?;
320 let _permit = admission
321 .acquire(
322 C::ID,
323 operation,
324 context.clone(),
325 self.runtime.driver.clone(),
326 )
327 .await?;
328 if !endpoint.state.is_current(snapshot.generation) {
329 return Err(RuntimeFailure::Unavailable { capability: C::ID });
330 }
331 ensure_context_active(&self.runtime.driver, &context)?;
332 let outcome = await_with_generation_context(
333 &self.runtime.driver,
334 &context,
335 snapshot.cancellation,
336 C::ID,
337 snapshot
338 .endpoint
339 .invoke(operation, Box::new(request.clone()), context.clone()),
340 )
341 .await
342 .map_err(|error| {
343 schedule_module_supervision_after_failure(
344 &self.runtime,
345 &endpoint.module_instance,
346 error,
347 )
348 })?
349 .map_err(|error| {
350 schedule_module_supervision_after_failure(
351 &self.runtime,
352 &endpoint.module_instance,
353 error,
354 )
355 })?;
356 outcomes.push(decode_outcome::<C>(outcome)?);
357 }
358 Ok(outcomes)
359 }
360
361 pub fn invocation_context(
363 &self,
364 deadline: Option<Duration>,
365 cancellation: CancellationToken,
366 ) -> InvocationContext {
367 InvocationContext::new(self.next_request_id(), deadline, cancellation)
368 }
369
370 pub(super) fn next_context(&self) -> InvocationContext {
371 self.invocation_context(None, CancellationToken::new())
372 .with_caller_instance(self.caller_instance.clone())
373 }
374
375 pub(super) fn next_request_id(&self) -> RequestId {
376 let request_id = self.runtime.request_ids.get();
377 self.runtime.request_ids.set(request_id.saturating_add(1));
378 request_id
379 }
380}
381
382fn decode_outcome<C: RequestCapability>(
383 outcome: ErasedDomainResult,
384) -> Result<Result<C::Response, C::DomainError>, RuntimeFailure> {
385 match outcome {
386 Ok(value) => value
387 .downcast::<C::Response>()
388 .map(|value| Ok(*value))
389 .map_err(|_| RuntimeFailure::ProtocolViolation { capability: C::ID }),
390 Err(value) => value
391 .downcast::<C::DomainError>()
392 .map(|value| Err(*value))
393 .map_err(|_| RuntimeFailure::ProtocolViolation { capability: C::ID }),
394 }
395}
396
397fn request_diagnostic_outcome<Response, DomainError>(
398 result: &Result<Result<Response, DomainError>, RuntimeFailure>,
399) -> DiagnosticOutcome {
400 match result {
401 Ok(Ok(_)) => DiagnosticOutcome::Succeeded,
402 Ok(Err(_)) => DiagnosticOutcome::DomainError,
403 Err(error) => DiagnosticOutcome::RuntimeFailure(error.into()),
404 }
405}
406
407fn many_request_diagnostic_outcome<Response, DomainError>(
408 result: &Result<Vec<Result<Response, DomainError>>, RuntimeFailure>,
409) -> DiagnosticOutcome {
410 match result {
411 Ok(outcomes) if outcomes.iter().any(Result::is_err) => DiagnosticOutcome::DomainError,
412 Ok(_) => DiagnosticOutcome::Succeeded,
413 Err(error) => DiagnosticOutcome::RuntimeFailure(error.into()),
414 }
415}
416
417fn diagnostic_admission(error: &RuntimeFailure) -> Option<DiagnosticAdmission> {
418 match error {
419 RuntimeFailure::AdmissionClosed => Some(DiagnosticAdmission::Closed),
420 RuntimeFailure::ResourceExhausted { .. } => Some(DiagnosticAdmission::Exhausted),
421 RuntimeFailure::Unavailable { .. } => Some(DiagnosticAdmission::Unavailable),
422 _ => None,
423 }
424}