Skip to main content

RetentionPolicy

Struct RetentionPolicy 

Source
#[non_exhaustive]
pub struct RetentionPolicy { pub terminal_max_age: Duration, pub batch_size: u32, pub idempotency_key_max_age: Option<Duration>, pub max_batches: Option<u32>, }
Expand description

How long terminal tasks are kept, and how aggressively they are removed.

#[non_exhaustive]: build it with new and the with_* setters, which cover every field. STABILITY.md §4 lists the configuration structs that carry this marking so a new option on any of them is additive; this one was missed by the 0.12.0 conversion that introduced the rule, and adding idempotency_key_max_age is what found it.

Fields (Non-exhaustive)§

This struct is marked as non-exhaustive
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.
§terminal_max_age: Duration

Terminal tasks whose last update is older than this are eligible.

Measured against updated_at, which the store maintains, and evaluated by the database rather than the caller — an application clock that runs fast would otherwise delete work that is younger than it looks.

§batch_size: u32

Rows per DELETE. Default 1,000.

The point of batching is the lock, not the throughput. One statement deleting a million rows holds locks and grows a transaction for as long as it takes; a thousand statements deleting a thousand rows each let every other query through in between.

§idempotency_key_max_age: Option<Duration>

Idempotency keys older than this are deleted. None keeps them for ever, which is what every release before this one did.

§Why a key has to expire at all

A key is released when the send it guards fails, and otherwise it stays — deliberately, because it has to outlive the task it names or a late retry would execute a second time. Nothing else ever removed one. A busy deployment therefore accumulated a row per keyed send for the life of the database, and the index that makes the claim atomic grew with it.

§What expiring one costs

Exactly what the key was preventing: a retry arriving after the key expires re-executes the send. That is the trade every idempotency key has, and the reason the default is a full day rather than something tidier — it has to exceed the longest window in which a client might still retry, and clients retry in seconds.

§It is never allowed below terminal_max_age

A key expiring while the task it names is still retained is the bad case: the retry does not replay, it creates a second task alongside the first, and the caller ends up with two ids for one logical send. So the sweep uses effective_idempotency_key_max_age, which never returns less than terminal_max_age — the invariant holds by construction rather than by the operator having read this paragraph.

§max_batches: Option<u32>

Stop after this many batches, leaving the rest for the next call. None runs until nothing is left.

Set it to bound how long one sweep can run when the first sweep after switching retention on has years of backlog to work through.

Implementations§

Source§

impl RetentionPolicy

Source

pub const fn new(terminal_max_age: Duration) -> Self

A policy keeping terminal tasks for terminal_max_age, with default batching.

Source

pub const fn with_batch_size(self, batch_size: u32) -> Self

Sets the rows-per-DELETE batch size. Zero is treated as one.

Source

pub const fn with_idempotency_key_max_age( self, max_age: Option<Duration>, ) -> Self

Sets how long an idempotency key is kept; None keeps them for ever.

See idempotency_key_max_age for what expiring one costs and why the sweep will not honour a value below terminal_max_age.

Source

pub fn effective_idempotency_key_max_age(&self) -> Option<Duration>

The key age the sweep actually uses: never below terminal_max_age.

A key must outlive the task it names. Deleting one while its task is still retained turns the next retry into a second task rather than a replay, so a policy that asks for that is clamped rather than obeyed — the same shape as effective_batch_size — which is pub(crate) and feature-gated, so it cannot be linked from a public doc comment — where a nonsensical value is floored at the nearest sensible one instead of being honoured into a defect.

Source

pub const fn with_max_batches(self, max_batches: u32) -> Self

Bounds how many batches a single sweep runs.

Trait Implementations§

Source§

impl Clone for RetentionPolicy

Source§

fn clone(&self) -> Self

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 Debug for RetentionPolicy

Source§

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

Formats the value using the given formatter. Read more

Auto Trait Implementations§

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<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

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> FromRef<T> for T
where T: Clone,

Source§

fn from_ref(input: &T) -> T

Converts to this type from a reference to the input type.
Source§

impl<T> FutureExt for T

Source§

fn with_context(self, otel_cx: Context) -> WithContext<Self> ⓘ

Attaches the provided Context to this type, returning a WithContext wrapper. Read more
Source§

fn with_current_context(self) -> WithContext<Self> ⓘ

Attaches the current Context to this type, returning a WithContext wrapper. Read more
Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
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> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> IntoRequest<T> for T

Source§

fn into_request(self) -> Request<T>

Wrap the input message T in a tonic::Request
Source§

impl<L> LayerExt<L> for L

Source§

fn named_layer<S>(&self, service: S) -> Layered<<L as Layer<S>>::Service, S>
where L: Layer<S>,

Applies the layer to a service and wraps it in Layered.
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
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.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more