#[non_exhaustive]pub struct TaskStoreConfig {
pub max_capacity: Option<usize>,
pub task_ttl: Option<Duration>,
pub eviction_interval: u64,
pub max_page_size: u32,
pub idempotency_key_ttl: Option<Duration>,
pub max_events_per_task: Option<usize>,
}Expand description
Configuration for InMemoryTaskStore.
#[non_exhaustive]: build it with Default and the with_* setters,
which cover every field; a struct literal is not available outside this
crate, so a field added later does not break callers.
Fields (Non-exhaustive)§
This struct is marked as non-exhaustive
Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.max_capacity: Option<usize>Maximum number of tasks to keep in the store. Once exceeded, the oldest
terminal (completed/failed/canceled/rejected) tasks are evicted first.
None means no limit.
Overload behavior: if the overflow cannot be covered by terminal
tasks alone, the oldest non-terminal tasks are evicted as a last
resort — bounded memory is prioritized over retaining in-flight rows.
An evicted in-flight task answers GetTask with task-not-found until
its next event is persisted (the background processor re-saves it),
so under sustained over-capacity write pressure the cap is a strong
bound on steady-state size, not an absolute invariant. Size
max_capacity above the realistic concurrent in-flight task count.
task_ttl: Option<Duration>Time-to-live for completed or failed tasks. Tasks in terminal states
older than this duration are evicted on the next write operation.
None means no TTL-based eviction.
eviction_interval: u64Number of writes between automatic eviction sweeps. Default: 64.
Amortizes the O(n) eviction cost so it doesn’t run on every single save().
max_page_size: u32Maximum allowed page size for list queries. Default: 1000.
Larger requested page sizes are clamped to this limit.
idempotency_key_ttl: Option<Duration>How long an idempotency key is kept after it is claimed. None keeps
them for the life of the process, which is what every release before
this one did. Default: Some(24h).
§Why this has to be bounded at all
max_capacity and task_ttl
bound the number of tasks, and a key is deliberately not evicted with
the task it names — dropping it there would let a late retry execute
the send a second time. So nothing removed a key except the send that
failed while holding it, and the index grew by one entry per keyed send
for as long as the process ran.
§What expiring one costs
Exactly what the key was preventing: a retry arriving after the key
expires re-executes the send rather than replaying. That trade is
inherent to any expiring idempotency key, and it is why 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 this SDK’s own
RetryPolicy is bounded in the low tens of seconds.
A day is also comfortably above the one-hour default
task_ttl, which keeps the ordering the design rests
on: the key outlives the task it names, so a retry inside the window
gets a replay or a clear “that task is gone”, never a second task
alongside the first.
max_events_per_task: Option<usize>How many events one task’s log may hold before the oldest are dropped.
None means no limit. Default: Some(512).
§Why this has to be bounded at all
max_capacity and task_ttl
bound the number of tasks; until 0.13 nothing bounded what one task
held. The log keeps a full event.clone() per append, artifact
payloads included, so a streaming agent that emits 10,000 chunks leaves
the store holding the folded task and a second copy of every chunk.
Memory per task went from O(final task size) to O(events × event size),
and the only thing that freed it was the whole task being evicted.
§Why 512
The log’s job is to let a subscriber that dropped its connection resume
where it left off, so what it has to cover is a reconnect, not a run.
512 positions is roughly a 500-chunk stream — the length this
repository’s own backpressure/append_volume benchmark uses as its
large case — so a reconnect inside one typical response replays in
full. It is deliberately not “whatever the longest run produces”: that
number is unbounded and set by the agent, not by the operator.
Raise it where subscribers reconnect after long gaps and the events are small; lower it where artifact chunks are large. Truncation is safe either way — see the next paragraph — so the cost of too low a value is a resubscribe that is told it cannot be served, not one served wrongly.
§Dropping the oldest is safe, and this is what makes it safe
A reader that asks to resume from a position the log no longer holds
must not be handed the surviving tail as though nothing were missing.
TaskStore::earliest_event_seq
reports the oldest position still held, and
TaskStore::event_log_covers
turns that into the question a resubscribe actually asks. Truncation
never empties a log — at least one event is always kept — so the
earliest position stays answerable.
Implementations§
Source§impl TaskStoreConfig
impl TaskStoreConfig
Sourcepub const fn with_idempotency_key_ttl(
self,
ttl: Option<Duration>,
) -> TaskStoreConfig
pub const fn with_idempotency_key_ttl( self, ttl: Option<Duration>, ) -> TaskStoreConfig
Sets how long an idempotency key is kept; None keeps it for the life
of the process.
See idempotency_key_ttl for what
expiring one costs, and why it should stay above
task_ttl.
Sourcepub const fn with_max_capacity(self, max: Option<usize>) -> TaskStoreConfig
pub const fn with_max_capacity(self, max: Option<usize>) -> TaskStoreConfig
Sets the maximum number of tasks kept; None is no limit. See
max_capacity for what happens past it.
Sourcepub const fn with_task_ttl(self, ttl: Option<Duration>) -> TaskStoreConfig
pub const fn with_task_ttl(self, ttl: Option<Duration>) -> TaskStoreConfig
Sets the time-to-live for terminal tasks; None disables TTL eviction.
Sourcepub const fn with_eviction_interval(self, writes: u64) -> TaskStoreConfig
pub const fn with_eviction_interval(self, writes: u64) -> TaskStoreConfig
Sets the number of writes between eviction sweeps.
Sourcepub const fn with_max_page_size(self, max: u32) -> TaskStoreConfig
pub const fn with_max_page_size(self, max: u32) -> TaskStoreConfig
Sets the maximum page size for list queries.
Sourcepub const fn with_max_events_per_task(
self,
max: Option<usize>,
) -> TaskStoreConfig
pub const fn with_max_events_per_task( self, max: Option<usize>, ) -> TaskStoreConfig
Sets how many events one task’s log keeps; None is no limit.
Some(0) is treated as Some(1): a log that keeps nothing could not
report an earliest position, and a resuming subscriber would be told
the log is complete when it holds nothing at all. See
max_events_per_task for the default and
for why dropping the oldest entries does not produce a gapped replay.
Trait Implementations§
Source§impl Clone for TaskStoreConfig
impl Clone for TaskStoreConfig
Source§fn clone(&self) -> TaskStoreConfig
fn clone(&self) -> TaskStoreConfig
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for TaskStoreConfig
impl Debug for TaskStoreConfig
Source§impl Default for TaskStoreConfig
impl Default for TaskStoreConfig
Source§fn default() -> TaskStoreConfig
fn default() -> TaskStoreConfig
Auto Trait Implementations§
impl Freeze for TaskStoreConfig
impl RefUnwindSafe for TaskStoreConfig
impl Send for TaskStoreConfig
impl Sync for TaskStoreConfig
impl Unpin for TaskStoreConfig
impl UnsafeUnpin for TaskStoreConfig
impl UnwindSafe for TaskStoreConfig
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> FutureExt for T
impl<T> FutureExt for T
Source§fn with_context(self, otel_cx: Context) -> WithContext<Self> ⓘ
fn with_context(self, otel_cx: Context) -> WithContext<Self> ⓘ
Source§fn with_current_context(self) -> WithContext<Self> ⓘ
fn with_current_context(self) -> WithContext<Self> ⓘ
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
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 moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
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 moreSource§impl<T> IntoRequest<T> for T
impl<T> IntoRequest<T> for T
Source§fn into_request(self) -> Request<T>
fn into_request(self) -> Request<T>
T in a tonic::Request