1use futures_core::stream::{Stream, TryStream};
2use loadpace::{
3 DispatchReservation, DispatchState, EndpointConfig, EndpointController, InFlightRequest,
4 Outcome,
5};
6use std::future::Future;
7use std::marker::PhantomData;
8use std::pin::Pin;
9use std::sync::{Arc, Mutex};
10use std::task::{Context, Poll};
11use std::time::Instant;
12use tower::Service;
13use tower::discover::Change;
14use tower::load::Load;
15
16#[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd)]
21pub struct LoadMetric(pub f64);
22
23impl LoadMetric {
24 pub fn as_secs(self) -> f64 {
25 self.0
26 }
27}
28
29struct Shared<S> {
30 inner: tokio::sync::Mutex<S>,
31 controller: Mutex<EndpointController>,
32 ready: Arc<tokio::sync::Semaphore>,
33 dispatch: tokio::sync::Notify,
34}
35
36type ReadinessFuture = Pin<
37 Box<
38 dyn Future<Output = Result<tokio::sync::OwnedSemaphorePermit, tokio::sync::AcquireError>>
39 + Send
40 + 'static,
41 >,
42>;
43
44pub struct AdaptiveEndpoint<S> {
52 shared: Arc<Shared<S>>,
53 readiness_permit: Option<tokio::sync::OwnedSemaphorePermit>,
54 readiness: Option<ReadinessFuture>,
55}
56
57impl<S> AdaptiveEndpoint<S> {
58 pub fn new(inner: S, config: EndpointConfig) -> Self {
59 Self::new_at(inner, config, Instant::now())
60 }
61
62 pub fn new_at(inner: S, config: EndpointConfig, now: Instant) -> Self {
63 let queue_capacity = config.queue_capacity;
64 Self {
65 shared: Arc::new(Shared {
66 inner: tokio::sync::Mutex::new(inner),
67 controller: Mutex::new(EndpointController::new(config, now)),
68 ready: Arc::new(tokio::sync::Semaphore::new(queue_capacity)),
69 dispatch: tokio::sync::Notify::new(),
70 }),
71 readiness_permit: None,
72 readiness: None,
73 }
74 }
75
76 pub fn controller(&self) -> &Mutex<EndpointController> {
77 &self.shared.controller
78 }
79
80 pub fn snapshot(&self) -> loadpace::ControllerSnapshot {
81 self.shared
82 .controller
83 .lock()
84 .expect("controller mutex poisoned")
85 .snapshot(Instant::now())
86 }
87
88 pub fn load_metric(&self) -> LoadMetric {
89 let mut controller = self
90 .shared
91 .controller
92 .lock()
93 .expect("controller mutex poisoned");
94 let now = Instant::now();
95 controller.refresh(now);
96 LoadMetric(controller.load(now))
97 }
98}
99
100pub struct AdaptiveDiscovery<D, Request> {
107 inner: D,
108 config: EndpointConfig,
109 _request: PhantomData<fn() -> Request>,
110}
111
112impl<D, Request> AdaptiveDiscovery<D, Request> {
113 pub fn new(inner: D, config: EndpointConfig) -> Self {
114 Self {
115 inner,
116 config,
117 _request: PhantomData,
118 }
119 }
120
121 pub fn into_inner(self) -> D {
122 self.inner
123 }
124}
125
126impl<D, Request, K, S> Stream for AdaptiveDiscovery<D, Request>
127where
128 D: TryStream<Ok = Change<K, S>> + Unpin,
129 K: Eq,
130 S: Service<Request> + Send + 'static,
131 S::Future: Send + 'static,
132 S::Response: Send + 'static,
133 S::Error: Send + 'static,
134 Request: 'static,
135{
136 type Item = Result<Change<K, AdaptiveEndpoint<S>>, D::Error>;
137
138 fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
139 let this = self.get_mut();
140 Pin::new(&mut this.inner).try_poll_next(cx).map(|change| {
141 change.map(|result| {
142 result.map(|change| match change {
143 Change::Insert(key, service) => {
144 Change::Insert(key, AdaptiveEndpoint::new(service, this.config.clone()))
145 }
146 Change::Remove(key) => Change::Remove(key),
147 })
148 })
149 })
150 }
151}
152
153impl<S> Clone for AdaptiveEndpoint<S> {
154 fn clone(&self) -> Self {
155 Self {
156 shared: Arc::clone(&self.shared),
157 readiness_permit: None,
158 readiness: None,
159 }
160 }
161}
162
163impl<S> Load for AdaptiveEndpoint<S> {
164 type Metric = LoadMetric;
165
166 fn load(&self) -> Self::Metric {
167 self.load_metric()
168 }
169}
170
171impl<S, Request> Service<Request> for AdaptiveEndpoint<S>
172where
173 S: Service<Request> + Send + 'static,
174 S::Future: Send + 'static,
175 S::Response: Send + 'static,
176 S::Error: Send + 'static,
177 Request: Send + 'static,
178{
179 type Response = S::Response;
180 type Error = S::Error;
181 type Future = ResponseFuture<S::Response, S::Error>;
182
183 fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
184 if self.readiness_permit.is_some() {
185 return Poll::Ready(Ok(()));
186 }
187
188 if self.readiness.is_none() {
189 match Arc::clone(&self.shared.ready).try_acquire_owned() {
190 Ok(permit) => {
191 self.readiness_permit = Some(permit);
192 return Poll::Ready(Ok(()));
193 }
194 Err(tokio::sync::TryAcquireError::NoPermits) => {
195 self.readiness = Some(Box::pin(Arc::clone(&self.shared.ready).acquire_owned()));
196 }
197 Err(tokio::sync::TryAcquireError::Closed) => {
198 panic!("AdaptiveEndpoint readiness semaphore was closed")
199 }
200 }
201 }
202
203 match self
204 .readiness
205 .as_mut()
206 .expect("readiness future must exist")
207 .as_mut()
208 .poll(cx)
209 {
210 Poll::Ready(Ok(permit)) => {
211 self.readiness = None;
212 self.readiness_permit = Some(permit);
213 Poll::Ready(Ok(()))
214 }
215 Poll::Ready(Err(_)) => {
216 panic!("AdaptiveEndpoint readiness semaphore was closed")
217 }
218 Poll::Pending => Poll::Pending,
219 }
220 }
221
222 fn call(&mut self, request: Request) -> Self::Future {
223 assert!(
224 self.readiness_permit.is_some(),
225 "AdaptiveEndpoint::call invoked without available readiness"
226 );
227 let readiness_permit = self
228 .readiness_permit
229 .take()
230 .expect("readiness permit must exist after poll_ready");
231 let reservation = self
232 .shared
233 .controller
234 .lock()
235 .expect("controller mutex poisoned")
236 .reserve(Instant::now())
237 .expect("readiness reservation was not reflected in controller capacity");
238
239 let guard = RequestGuard::new(Arc::clone(&self.shared), reservation, readiness_permit);
240 let future = dispatch_request(Arc::clone(&self.shared), request, guard);
241 ResponseFuture {
242 inner: Box::pin(future),
243 }
244 }
245}
246
247pub struct ResponseFuture<T, E> {
249 inner: Pin<Box<dyn Future<Output = Result<T, E>> + Send + 'static>>,
250}
251
252impl<T, E> Future for ResponseFuture<T, E> {
253 type Output = Result<T, E>;
254
255 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
256 unsafe { self.get_unchecked_mut() }.inner.as_mut().poll(cx)
259 }
260}
261
262struct RequestGuard<S> {
263 shared: Arc<Shared<S>>,
264 reservation: Option<DispatchReservation>,
265 readiness_permit: Option<tokio::sync::OwnedSemaphorePermit>,
266 active: Option<InFlightRequest>,
267 finished: bool,
268}
269
270impl<S> RequestGuard<S> {
271 fn new(
272 shared: Arc<Shared<S>>,
273 reservation: DispatchReservation,
274 readiness_permit: tokio::sync::OwnedSemaphorePermit,
275 ) -> Self {
276 Self {
277 shared,
278 reservation: Some(reservation),
279 readiness_permit: Some(readiness_permit),
280 active: None,
281 finished: false,
282 }
283 }
284
285 fn mark_dispatched(&mut self, active: InFlightRequest) {
286 self.reservation = None;
287 self.active = Some(active);
288 self.readiness_permit = None;
291 }
292
293 fn finish(&mut self, outcome: Outcome, now: Instant) {
294 if self.finished {
295 return;
296 }
297 self.finished = true;
298 if let Some(active) = self.active.take() {
299 let latency = now.saturating_duration_since(active.dispatched_at());
300 self.shared
301 .controller
302 .lock()
303 .expect("controller mutex poisoned")
304 .on_complete(active, outcome, latency, now);
305 } else if let Some(reservation) = self.reservation.take() {
306 self.shared
307 .controller
308 .lock()
309 .expect("controller mutex poisoned")
310 .cancel(reservation, now);
311 }
312 self.readiness_permit = None;
315 self.shared.dispatch.notify_waiters();
316 }
317}
318
319impl<S> Drop for RequestGuard<S> {
320 fn drop(&mut self) {
321 if !self.finished {
322 self.finish(Outcome::Failure, Instant::now());
323 }
324 }
325}
326
327async fn dispatch_request<S, Request>(
328 shared: Arc<Shared<S>>,
329 request: Request,
330 mut guard: RequestGuard<S>,
331) -> Result<S::Response, S::Error>
332where
333 S: Service<Request> + Send + 'static,
334 S::Future: Send + 'static,
335 S::Response: Send + 'static,
336 S::Error: Send + 'static,
337 Request: Send + 'static,
338{
339 let reservation = guard
340 .reservation
341 .expect("request guard must begin with a reservation");
342
343 loop {
344 let notified = shared.dispatch.notified();
345 let decision = {
346 let mut controller = shared.controller.lock().expect("controller mutex poisoned");
347 let now = Instant::now();
348 controller.refresh(now);
349 controller.dispatch_state(reservation, now)
350 };
351
352 match decision {
353 DispatchState::Ready => {
354 break;
355 }
356 DispatchState::WaitUntil(deadline) => {
357 let delay = deadline.saturating_duration_since(Instant::now());
358 tokio::select! {
359 _ = tokio::time::sleep(delay) => {},
360 _ = notified => {},
361 }
362 }
363 DispatchState::WaitForPrevious | DispatchState::InflightLimit => {
364 notified.await;
365 }
366 DispatchState::Cancelled => {
367 panic!("an AdaptiveEndpoint request was cancelled while being polled");
368 }
369 }
370 }
371
372 let result = {
373 let mut inner = shared.inner.lock().await;
374 match std::future::poll_fn(|cx| inner.poll_ready(cx)).await {
375 Ok(()) => {
376 let now = Instant::now();
377 let active = shared
378 .controller
379 .lock()
380 .expect("controller mutex poisoned")
381 .on_dispatched(reservation, now)
382 .expect("dispatch state changed unexpectedly");
383 guard.mark_dispatched(active);
384 let future = inner.call(request);
385 drop(inner);
386 future.await
387 }
388 Err(error) => {
389 shared
390 .controller
391 .lock()
392 .expect("controller mutex poisoned")
393 .on_admission_failure(Instant::now());
394 Err(error)
395 }
396 }
397 };
398
399 let outcome = if result.is_ok() {
400 Outcome::Success
401 } else {
402 Outcome::Failure
403 };
404 let now = Instant::now();
405 guard.finish(outcome, now);
406 result
407}