apalis_core/backend/persistence/
service.rs1use std::task::{Context, Poll};
2
3use futures_channel::mpsc::Sender;
4use futures_core::future::BoxFuture;
5use futures_util::{FutureExt, SinkExt};
6use tower_service::Service;
7
8use crate::{
9 backend::{TaskResult, codec::Codec, persistence::event::TaskEvent},
10 error::BoxDynError,
11 task::Task,
12};
13
14#[derive(Clone, Debug)]
25pub struct TaskPersistLayer<C, Compact> {
26 sender: Sender<TaskEvent<Compact>>,
27 codec: C,
28 persist_results: bool,
29 lock_tasks: bool,
30}
31
32impl<Compact, C> TaskPersistLayer<C, Compact> {
33 #[must_use]
37 pub(super) fn new(sender: Sender<TaskEvent<Compact>>, codec: C) -> Self {
38 Self {
39 sender,
40 codec,
41 persist_results: false,
42 lock_tasks: false,
43 }
44 }
45
46 #[must_use]
50 pub fn persist_results(self, persist_results: bool) -> Self {
51 Self {
52 sender: self.sender,
53 codec: self.codec,
54 persist_results,
55 lock_tasks: self.lock_tasks,
56 }
57 }
58
59 #[must_use]
63 pub fn lock_tasks(self, lock_tasks: bool) -> Self {
64 Self {
65 sender: self.sender,
66 codec: self.codec,
67 persist_results: self.persist_results,
68 lock_tasks,
69 }
70 }
71}
72
73impl<S, Compact, C> tower_layer::Layer<S> for TaskPersistLayer<C, Compact>
74where
75 C: Clone,
76{
77 type Service = TaskPersistService<S, Compact, C>;
78
79 fn layer(&self, inner: S) -> Self::Service {
80 TaskPersistService {
81 inner,
82 sender: self.sender.clone(),
83 codec: self.codec.clone(),
84 should_ack: self.persist_results,
85 should_lock: self.lock_tasks,
86 }
87 }
88}
89
90#[derive(Clone, Debug)]
91pub struct TaskPersistService<S, Compact, C> {
92 inner: S,
93 sender: Sender<TaskEvent<Compact>>,
94 codec: C,
95 should_ack: bool,
96 should_lock: bool,
97}
98
99impl<S, Args, C> Service<Task<Args>> for TaskPersistService<S, C::Compact, C>
100where
101 S: Service<Task<Args>> + Send + 'static,
102 S::Future: Send + 'static,
103 S::Error: Into<BoxDynError>,
104 Args: Send + 'static,
105 C: Codec<S::Response> + Clone + Send + 'static,
106 C::Error: std::error::Error,
107 S::Response: Send,
108 C::Compact: Send,
109{
110 type Response = S::Response;
111 type Error = BoxDynError;
112 type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;
113
114 fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
115 match self.sender.poll_ready(cx) {
116 Poll::Ready(Ok(_)) => self.inner.poll_ready(cx).map_err(|e| e.into()),
117 Poll::Ready(Err(e)) => Poll::Ready(Err(e.into())),
118 Poll::Pending => Poll::Pending,
119 }
120 }
121
122 fn call(&mut self, req: Task<Args>) -> Self::Future {
123 let mut sender = self.sender.clone();
124 let ctx = req.ctx().clone();
125 let fut = self.inner.call(req);
126 let codec = self.codec.clone();
127 let should_ack = self.should_ack;
128 let should_lock = self.should_lock;
129 async move {
130 let task_id = ctx.task_id().unwrap().clone();
131 if should_lock {
132 sender
133 .send(TaskEvent::Lock {
134 task_id: task_id.clone(),
135 })
136 .await?;
137 }
138
139 let res = fut.await.map_err(|e| e.into());
140 if should_ack {
141 let status = ctx.status();
142 let result = res
143 .as_ref()
144 .map_err(|e| e.to_string())
145 .and_then(|res| codec.encode(res).map_err(|e| e.to_string()));
146 let attempt = ctx.attempt();
147 sender
148 .send(TaskEvent::Complete(TaskResult {
149 task_id,
150 attempt,
151 result,
152 status,
153 }))
154 .await?;
155 }
156 res
157 }
158 .boxed()
159 }
160}