Skip to main content

ProjectionCheckpoint

Struct ProjectionCheckpoint 

Source
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>

Source

pub fn new(cursor: C) -> ProjectionCheckpoint<C>

English:
Wraps cursor without validating its position or comparing previous progress.

日本語:
位置の検証や以前の進捗との比較をせずに cursor を包みます。

Examples found in repository?
examples/reliable_delivery.rs (line 226)
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}
Source

pub fn cursor(&self) -> &C

English:
Borrows the stored cursor without advancing it.

日本語:
進めることなく、保存されたカーソルを借用します。

Examples found in repository?
examples/reliable_delivery.rs (line 179)
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}
Source

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,

Source§

fn clone(&self) -> ProjectionCheckpoint<C>

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl<C> Debug for ProjectionCheckpoint<C>
where C: Debug,

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl<C> Eq for ProjectionCheckpoint<C>
where C: Eq,

Source§

impl<C> Hash for ProjectionCheckpoint<C>
where C: Hash,

Source§

fn hash<__H>(&self, state: &mut __H)
where __H: Hasher,

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where H: Hasher, Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl<C> PartialEq for ProjectionCheckpoint<C>
where C: PartialEq,

Source§

fn eq(&self, other: &ProjectionCheckpoint<C>) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl<C> StructuralPartialEq for ProjectionCheckpoint<C>
where C: PartialEq,

Auto Trait Implementations§

§

impl<C> Freeze for ProjectionCheckpoint<C>
where C: Freeze,

§

impl<C> RefUnwindSafe for ProjectionCheckpoint<C>
where C: RefUnwindSafe,

§

impl<C> Send for ProjectionCheckpoint<C>
where C: Send,

§

impl<C> Sync for ProjectionCheckpoint<C>
where C: Sync,

§

impl<C> Unpin for ProjectionCheckpoint<C>
where C: Unpin,

§

impl<C> UnsafeUnpin for ProjectionCheckpoint<C>
where C: UnsafeUnpin,

§

impl<C> UnwindSafe for ProjectionCheckpoint<C>
where C: UnwindSafe,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.