pub struct ProjectionCheckpoint<C> { /* private fields */ }Expand description
English:
Owned cursor identifying progress in a projection’s input stream.
The cursor type and meaning belong to the application. This container performs no ordering, monotonicity, or validity checks and does not store progress by itself.
Borrow a cursor for inspection, then recover its ownership without cloning.
日本語:
プロジェクションの入力ストリームにおける進捗を示す、所有されたカーソルです。
カーソルの型と意味はアプリケーションが定めます。このコンテナーは順序、単調性、妥当性を 検証せず、単体では進捗を保存しません。
カーソルを借用して確認した後、複製せずに所有権を取り出せます。
§Example / 使用例
use ddd_toolkit_core::ProjectionCheckpoint;
let checkpoint = ProjectionCheckpoint::new(String::from("partition-2:17"));
assert_eq!(checkpoint.cursor(), "partition-2:17");
let cursor = checkpoint.into_cursor();
assert_eq!(cursor, "partition-2:17");Implementations§
Source§impl<C> ProjectionCheckpoint<C>
impl<C> ProjectionCheckpoint<C>
Sourcepub fn new(cursor: C) -> ProjectionCheckpoint<C>
pub fn new(cursor: C) -> ProjectionCheckpoint<C>
English:
Wraps cursor without validating its position or comparing previous progress.
日本語:
位置の検証や以前の進捗との比較をせずに cursor を包みます。
Examples found in repository?
215async fn deliver(
216 outbox: &mut Outbox,
217 broker: &mut Broker,
218 model: &mut ReadModel,
219 checkpoints: &mut Checkpoints,
220) -> Result<ProjectionApplication, &'static str> {
221 let claim = outbox.claim().await?.ok_or("queue empty")?;
222 let (id, event) = claim.into_parts();
223 broker.publish(&event).await?;
224 // This demo directly feeds a consumer after publishing; no real broker is used.
225 // Message IDs are stable across retries; these cursors are not sortable offsets.
226 let checkpoint = ProjectionCheckpoint::new(id.as_str().to_owned());
227 let result = model.apply_once(&event, &checkpoint).await?;
228 checkpoints.save(checkpoint).await?;
229 outbox.acknowledge(&id).await?;
230 Ok(result)
231}Sourcepub fn cursor(&self) -> &C
pub fn cursor(&self) -> &C
English:
Borrows the stored cursor without advancing it.
日本語:
進めることなく、保存されたカーソルを借用します。
Examples found in repository?
173 fn apply_once<'a>(
174 &'a mut self,
175 event: &'a Credited,
176 checkpoint: &'a ProjectionCheckpoint<String>,
177 ) -> PortFuture<'a, ProjectionApplication> {
178 Box::pin(async move {
179 if self.applied.contains(checkpoint.cursor()) {
180 return Ok(ProjectionApplication::AlreadyApplied);
181 }
182 let balance = self
183 .balance
184 .checked_add(event.0)
185 .ok_or("balance overflow")?;
186 // Deduplication marker + read model update must be atomic in production too.
187 self.applied.insert(checkpoint.cursor().clone());
188 self.balance = balance;
189 Ok(ProjectionApplication::Applied)
190 })
191 }
192}
193#[derive(Default)]
194struct Checkpoints {
195 last: Option<ProjectionCheckpoint<String>>,
196 fail_save: bool,
197}
198impl AsyncProjectionCheckpointStore for Checkpoints {
199 type Cursor = String;
200 type Error = &'static str;
201 fn load<'a>(&'a self) -> PortFuture<'a, Option<ProjectionCheckpoint<String>>> {
202 Box::pin(async move { Ok(self.last.clone()) })
203 }
204 fn save<'a>(&'a mut self, checkpoint: ProjectionCheckpoint<String>) -> PortFuture<'a, ()> {
205 Box::pin(async move {
206 if self.fail_save {
207 return Err("checkpoint unavailable");
208 }
209 self.last = Some(checkpoint);
210 Ok(())
211 })
212 }
213}
214
215async fn deliver(
216 outbox: &mut Outbox,
217 broker: &mut Broker,
218 model: &mut ReadModel,
219 checkpoints: &mut Checkpoints,
220) -> Result<ProjectionApplication, &'static str> {
221 let claim = outbox.claim().await?.ok_or("queue empty")?;
222 let (id, event) = claim.into_parts();
223 broker.publish(&event).await?;
224 // This demo directly feeds a consumer after publishing; no real broker is used.
225 // Message IDs are stable across retries; these cursors are not sortable offsets.
226 let checkpoint = ProjectionCheckpoint::new(id.as_str().to_owned());
227 let result = model.apply_once(&event, &checkpoint).await?;
228 checkpoints.save(checkpoint).await?;
229 outbox.acknowledge(&id).await?;
230 Ok(result)
231}
232fn main() {
233 let mut account = Account {
234 id: 1,
235 balance: 10,
236 events: vec![Credited(10)],
237 };
238 let mut outbox = Outbox::default();
239 let mut context = AsyncTransactionalOutboxLifecycleContext::new(
240 &mut account,
241 &mut outbox,
242 ExpectedRevision::NoStream(NoStream),
243 );
244 ready(context.save_and_enqueue()).unwrap();
245 context.complete().unwrap();
246 assert!(account.pull_events().is_empty());
247 assert_eq!(outbox.saved.as_ref().unwrap().balance, 10);
248 let mut broker = Broker {
249 fail: true,
250 ..Broker::default()
251 };
252 let mut model = ReadModel::default();
253 let mut checkpoints = Checkpoints::default();
254 assert_eq!(
255 ready(deliver(
256 &mut outbox,
257 &mut broker,
258 &mut model,
259 &mut checkpoints
260 )),
261 Err("broker down")
262 );
263 assert_eq!(outbox.queue.len(), 1);
264 assert_eq!(model.balance, 0);
265
266 broker.fail = false;
267 checkpoints.fail_save = true;
268 assert_eq!(
269 ready(deliver(
270 &mut outbox,
271 &mut broker,
272 &mut model,
273 &mut checkpoints
274 )),
275 Err("checkpoint unavailable")
276 );
277 assert_eq!(model.balance, 10);
278 assert!(ready(checkpoints.load()).unwrap().is_none());
279 checkpoints.fail_save = false;
280 outbox.fail_ack = true;
281 assert_eq!(
282 ready(deliver(
283 &mut outbox,
284 &mut broker,
285 &mut model,
286 &mut checkpoints
287 )),
288 Err("acknowledgement lost")
289 );
290 assert_eq!(model.balance, 10);
291 assert_eq!(outbox.queue.len(), 1);
292 outbox.fail_ack = false;
293 assert_eq!(
294 ready(deliver(
295 &mut outbox,
296 &mut broker,
297 &mut model,
298 &mut checkpoints
299 )),
300 Ok(ProjectionApplication::AlreadyApplied)
301 );
302 assert_eq!(model.balance, 10);
303 assert_eq!(broker.accepted.len(), 3); // At-least-once delivery, not exactly-once.
304 assert_eq!(ready(checkpoints.load()).unwrap().unwrap().cursor(), "1:0");
305 assert!(ready(outbox.claim()).unwrap().is_none());
306 println!("reliable_delivery: retried broker/checkpoint/ack failures; 3 deliveries, balance=10");
307}Sourcepub fn into_cursor(self) -> C
pub fn into_cursor(self) -> C
English:
Consumes the checkpoint and returns its cursor by value.
日本語:
チェックポイントを消費し、カーソルを値として返します。
Trait Implementations§
Source§impl<C> Clone for ProjectionCheckpoint<C>where
C: Clone,
impl<C> Clone for ProjectionCheckpoint<C>where
C: Clone,
Source§fn clone(&self) -> ProjectionCheckpoint<C>
fn clone(&self) -> ProjectionCheckpoint<C>
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more