apalis_core/backend/persistence/
mod.rs1use std::{
2 collections::VecDeque,
3 fmt::Debug,
4 ops::{Deref, DerefMut},
5 pin::Pin,
6 task::{Context, Poll},
7 time::Duration,
8};
9
10use futures_channel::mpsc::{self, Receiver};
11use futures_timer::Delay;
12use futures_util::FutureExt;
13
14pub use crate::backend::persistence::{event::TaskEvent, service::TaskPersistLayer, state::State};
15use crate::{backend::future::BoxSyncFuture, task::Task, worker::context::WorkerContext};
16
17mod event;
18mod service;
19mod sink;
20mod state;
21
22pub trait Persistence: Clone + Send + 'static {
25 type Compact;
27
28 type Response;
30
31 type Error: std::error::Error;
33
34 fn register(
36 &mut self,
37 worker: &WorkerContext,
38 ) -> impl Future<Output = Result<(), Self::Error>> + Send;
39
40 fn heartbeat(
42 &mut self,
43 worker: &WorkerContext,
44 ) -> impl Future<Output = Result<(), Self::Error>> + Send;
45
46 fn fetch_next(
48 &mut self,
49 worker: &WorkerContext,
50 ) -> impl Future<Output = Result<Vec<Task<Self::Compact>>, Self::Error>> + Send;
51
52 fn reenqueue_abandoned(
54 &mut self,
55 tasks: Vec<Task<Self::Compact>>,
56 worker: &WorkerContext,
57 ) -> impl Future<Output = Result<u64, Self::Error>> + Send;
58
59 fn handle_events(
61 &mut self,
62 events: Vec<TaskEvent<Self::Response>>,
63 worker: &WorkerContext,
64 ) -> impl Future<Output = Result<(), Self::Error>> + Send;
65
66 fn push_tasks(
68 &mut self,
69 tasks: Vec<Task<Self::Compact>>,
70 ) -> impl Future<Output = Result<(), Self::Error>> + Send;
71}
72
73#[derive(Debug)]
77pub struct Persisted<P>
78where
79 P: Persistence,
80{
81 inner: P,
82 state: State<Task<P::Compact>, P::Error>,
83 heartbeat_timer: Option<Delay>,
84 receiver: Option<Receiver<TaskEvent<P::Response>>>,
85 sink_buffer: Vec<Task<P::Compact>>,
86 sink_future: Option<BoxSyncFuture<Result<(), P::Error>>>,
87}
88
89impl<P: Clone> Clone for Persisted<P>
90where
91 P: Persistence,
92{
93 fn clone(&self) -> Self {
94 Self {
95 inner: self.inner.clone(),
96 state: State::NotBootstrapped,
97 heartbeat_timer: None,
98 receiver: None,
99 sink_buffer: Vec::new(),
100 sink_future: None,
101 }
102 }
103}
104
105impl<P> Deref for Persisted<P>
106where
107 P: Persistence,
108{
109 type Target = P;
110
111 fn deref(&self) -> &Self::Target {
112 &self.inner
113 }
114}
115
116impl<P> DerefMut for Persisted<P>
117where
118 P: Persistence,
119{
120 fn deref_mut(&mut self) -> &mut Self::Target {
121 &mut self.inner
122 }
123}
124
125macro_rules! poll_fut_or_return {
126 ($fut:expr, $cx:expr) => {
127 match $fut.poll_unpin($cx) {
128 Poll::Ready(Ok(v)) => v,
129 Poll::Ready(Err(e)) => return Poll::Ready(Err(e)),
130 Poll::Pending => return Poll::Pending,
131 }
132 };
133}
134
135impl<P> Persisted<P>
136where
137 P: Persistence + Clone,
138 P::Compact: Send + 'static,
139 P::Response: Send,
140{
141 pub fn new(inner: P) -> Self {
146 Self {
147 inner,
148 heartbeat_timer: None,
149 receiver: None,
150 state: State::NotBootstrapped,
151 sink_buffer: Vec::new(),
152 sink_future: None,
153 }
154 }
155
156 pub fn state(&self) -> &State<Task<P::Compact>, P::Error> {
158 &self.state
159 }
160
161 pub fn state_mut(&mut self) -> &mut State<Task<P::Compact>, P::Error> {
163 &mut self.state
164 }
165
166 fn try_drain_events(&mut self, _: &mut Context<'_>) -> Option<Vec<TaskEvent<P::Response>>> {
170 let receiver = self.receiver.as_mut()?;
171 let mut events = Vec::new();
172 let mut polls = 0u32;
173
174 while let Ok(event) = receiver.try_recv() {
175 polls += 1;
176 events.push(event);
177 }
178
179 trace!(
180 drained = events.len(),
181 polls, "try_drain_events: receiver drain complete"
182 );
183
184 if events.is_empty() {
185 None
186 } else {
187 debug!(
188 count = events.len(),
189 "try_drain_events: buffered events ready"
190 );
191 Some(events)
192 }
193 }
194
195 pub fn layer<C>(&mut self, codec: C, batch_size: usize) -> TaskPersistLayer<C, P::Response> {
197 let buffer = batch_size * 4;
199 let (tx, rx) = mpsc::channel(buffer);
200 self.receiver = Some(rx);
201 TaskPersistLayer::new(tx, codec)
202 }
203
204 pub fn poll_ready(
208 &mut self,
209 cx: &mut Context<'_>,
210 worker: &WorkerContext,
211 heartbeat_interval: Duration,
212 ) -> Poll<Result<(), P::Error>> {
213 trace!("poll_ready: {:?}", self.state);
214 loop {
215 match &mut self.state {
216 State::NotBootstrapped => {
217 debug!("poll_ready: NotBootstrapped -> registering worker");
218 let mut provider = self.inner.clone();
219 let worker = worker.clone();
220 let fut = async move {
221 provider.register(&worker).await?;
222 Ok(())
223 };
224 self.state = State::RegisterWorker(fut.boxed().into());
225 }
226
227 State::RegisterWorker(fut) | State::HeartBeat(fut) => match fut.poll_unpin(cx) {
228 Poll::Pending => {
229 return Poll::Pending;
230 }
231 Poll::Ready(Err(e)) => {
232 warn!(error = ?e, "poll_ready: HeartBeat failed -> Dead");
233 self.state = State::Dead;
234 return Poll::Ready(Err(e));
235 }
236 Poll::Ready(Ok(())) => {
237 debug!(
238 ?heartbeat_interval,
239 "poll_ready: HeartBeat complete, arming heartbeat timer"
240 );
241 worker.heartbeat(cx);
242 self.heartbeat_timer = Some(Delay::new(heartbeat_interval));
243 self.state = State::Ready;
244 }
245 },
246
247 State::ProcessEvents(fut) => match fut.poll_unpin(cx) {
248 Poll::Pending => {
249 return Poll::Pending;
250 }
251 Poll::Ready(Err(e)) => {
252 warn!(error = ?e, "poll_ready: ProcessEvents failed -> Dead");
253 self.state = State::Dead;
254 return Poll::Ready(Err(e));
255 }
256 Poll::Ready(Ok(())) => {
257 trace!("poll_ready: ProcessEvents complete -> Ready");
258 self.state = State::Ready;
259 }
260 },
261
262 State::Ready => {
263 let heartbeat_due = Pin::new(self.heartbeat_timer.as_mut().unwrap())
264 .poll(cx)
265 .is_ready();
266
267 trace!(
268 heartbeat_due,
269 ?heartbeat_interval,
270 "poll_ready: State::Ready evaluating heartbeat timer"
271 );
272
273 if heartbeat_due {
274 debug!("poll_ready: heartbeat due -> dispatching HeartBeat future");
275 let mut provider = self.inner.clone();
276 let worker = worker.clone();
277 let fut = async move {
278 provider.heartbeat(&worker).await?;
279 Ok(())
280 };
281 self.state = State::HeartBeat(fut.boxed().into());
282 continue;
283 }
284
285 if let Some(events) = self.try_drain_events(cx) {
286 debug!(
287 count = events.len(),
288 "poll_ready: draining events -> dispatching ProcessEvents future"
289 );
290 let mut provider = self.inner.clone();
291 let worker = worker.clone();
292 let fut = async move { provider.handle_events(events, &worker).await };
293 self.state = State::ProcessEvents(fut.boxed().into());
294 continue;
295 }
296
297 trace!("poll_ready: no heartbeat/events pending -> Ready(Ok)");
298 return Poll::Ready(Ok(()));
299 }
300
301 _ => {
302 trace!(state = ?self.state, "poll_ready: state handled elsewhere -> Ready(Ok)");
303 return Poll::Ready(Ok(()));
304 }
305 }
306 }
307 }
308
309 #[allow(clippy::type_complexity)]
316 pub fn poll_next(
317 &mut self,
318 cx: &mut Context<'_>,
319 worker: &WorkerContext,
320 ) -> Poll<Option<Result<Task<P::Compact>, P::Error>>> {
321 trace!("poll_next: poll: {state:?}", state = self.state);
322 loop {
323 match &mut self.state {
324 State::Ready => {
325 trace!("poll_next: State::Ready -> dispatching Fetch future");
326 let mut provider = self.inner.clone();
327 let worker = worker.clone();
328 let fut = async move { provider.fetch_next(&worker).await };
329 self.state = State::Fetch(fut.boxed().into());
330 }
331
332 State::Fetch(fut) => match fut.poll_unpin(cx) {
333 Poll::Pending => {
334 return Poll::Pending;
335 }
336 Poll::Ready(Ok(tasks)) if tasks.is_empty() => {
337 trace!(
338 "poll_next: fetch_next returned empty batch -> Ready, parking waker"
339 );
340 self.state = State::Ready;
341 worker.register_waker(cx);
342 return Poll::Pending;
343 }
344 Poll::Ready(Ok(tasks)) => {
345 debug!(count = tasks.len(), "poll_next: fetch_next returned batch");
346 self.state = State::Buffered(VecDeque::from(tasks));
347 }
348 Poll::Ready(Err(e)) => {
349 warn!(error = ?e, "poll_next: fetch_next failed -> Dead");
350 self.state = State::Dead;
351 return Poll::Ready(Some(Err(e)));
352 }
353 },
354
355 State::Buffered(buffer) => {
356 if let Some(task) = buffer.pop_front() {
357 if buffer.is_empty() {
358 trace!("poll_next: buffer exhausted after pop -> Ready");
359 self.state = State::Ready;
360 }
361 trace!(task_id = ?task.task_id(), "poll_next: yielding task from buffer");
362 return Poll::Ready(Some(Ok(task)));
363 }
364 trace!("poll_next: buffer already empty -> Ready");
365 self.state = State::Ready;
366 }
367
368 State::Dead => {
369 debug!("poll_next: State::Dead -> Ready(None)");
370 return Poll::Ready(None);
371 }
372
373 _ => {
377 error!(state = ?self.state, "poll_next: state not owned by poll_next -> Panic");
378 unreachable!("poll_ready should have been called first");
379 }
380 }
381 }
382 }
383
384 pub fn poll_close(
388 &mut self,
389 cx: &mut Context<'_>,
390 worker: &WorkerContext,
391 ) -> Poll<Result<(), P::Error>> {
392 trace!("poll_close: Start: {:?}", self.state);
393 loop {
394 match &mut self.state {
395 State::RegisterWorker(fut) | State::HeartBeat(fut) => {
397 poll_fut_or_return!(fut, cx);
398 debug!("poll_close: in-flight RegisterWorker/HeartBeat completed");
399 worker.heartbeat(cx);
400 self.state = State::Ready;
401 }
402
403 State::ProcessEvents(fut) => {
404 poll_fut_or_return!(fut, cx);
405 trace!("poll_close: ProcessEvents complete -> Ready");
406 self.state = State::Ready;
407 }
408
409 State::ReenqueueOrphaned(fut) => {
410 poll_fut_or_return!(fut, cx);
411 debug!("poll_close: ReenqueueOrphaned complete -> Ready");
412 self.state = State::Ready;
413 }
414
415 State::Buffered(remaining) => {
416 let mut provider = self.inner.clone();
417 let worker = worker.clone();
418
419 if !remaining.is_empty() {
420 debug!(
421 count = remaining.len(),
422 "poll_close: re-enqueuing abandoned buffered tasks"
423 );
424 let tasks = Vec::from(std::mem::take(remaining));
425 let fut = async move { provider.reenqueue_abandoned(tasks, &worker).await };
426 self.state = State::ReenqueueOrphaned(fut.boxed().into());
427 } else {
428 trace!("poll_close: buffer already empty -> Ready");
429 self.state = State::Ready;
430 }
431 }
432
433 State::Ready => {
434 let mut provider = self.inner.clone();
435 let worker = worker.clone();
436
437 if let Some(events) = self.try_drain_events(cx) {
438 debug!(
439 count = events.len(),
440 "poll_close: flushing remaining events before shutdown"
441 );
442 let fut = async move { provider.handle_events(events, &worker).await };
443 self.state = State::ProcessEvents(fut.boxed().into());
444 continue;
445 }
446 debug!("poll_close: no remaining work -> Ready(Ok)");
447 return Poll::Ready(Ok(()));
448 }
449
450 _ => {
451 trace!("poll_close: unexpected state -> forcing Ready");
452 self.state = State::Ready;
453 }
454 }
455 }
456 }
457}