pub struct MultipartStateStore { /* private fields */ }Expand description
In-memory side-table mapping upload_id → context. One of these
hangs off S4Service (always-on, no flag — the per-upload state is
gateway-internal).
v0.8.2 #62 (H-6 audit fix): each entry carries the DateTime<Utc>
of its put insertion so sweep_stale(now, max_age) can drop
abandoned upload contexts (client called CreateMultipartUpload,
uploaded some parts, then crashed without invoking
CompleteMultipartUpload / AbortMultipartUpload). Without the
sweep, an SSE-C upload’s raw 32-byte customer key would linger in
MultipartSseMode::SseC indefinitely. The sweep + the new
Zeroizing wrapper together bound the key’s in-memory lifetime to
max_age (default 24h via --multipart-abandoned-ttl-hours).
Implementations§
Source§impl MultipartStateStore
impl MultipartStateStore
Sourcepub fn new() -> Self
pub fn new() -> Self
Empty store. Use Arc<MultipartStateStore> so S4Service’s
async handlers can borrow it across &self calls without
requiring Clone.
Sourcepub fn put(&self, upload_id: &str, ctx: MultipartUploadContext)
pub fn put(&self, upload_id: &str, ctx: MultipartUploadContext)
Register a new upload under upload_id. If upload_id is
already present (extremely unlikely — backend issues fresh ids)
the previous entry is overwritten silently to mirror
HashMap::insert’s replace-on-collision semantics.
v0.8.2 #62: the insertion timestamp (Utc::now()) is stored
alongside the context so sweep_stale can prune abandoned
uploads. The timestamp is set at insert-time only — re-puts on
the same upload_id (overwrite) reset the clock, which is the
behaviour we want (treat a re-Create as the abandonment-clock
restart).
Sourcepub fn get(&self, upload_id: &str) -> Option<MultipartUploadContext>
pub fn get(&self, upload_id: &str) -> Option<MultipartUploadContext>
Snapshot the context for upload_id. None when no entry was
registered (e.g. Complete arrived for an upload that the gateway
has no record of — passes through to the backend untouched, which
in turn surfaces NoSuchUpload).
Sourcepub fn remove(&self, upload_id: &str)
pub fn remove(&self, upload_id: &str)
Drop the entry. Called by Complete / Abort to release the SSE-C
key bytes and the tag-set memory promptly. The Zeroizing<[u8; 32]> wrapper inside the dropped MultipartSseMode::SseC
variant zeros the key bytes during its Drop.
Sourcepub fn sweep_stale(&self, now: DateTime<Utc>, max_age: Duration) -> usize
pub fn sweep_stale(&self, now: DateTime<Utc>, max_age: Duration) -> usize
v0.8.2 #62 (H-6 audit fix): drop every entry whose insertion
timestamp is older than now - max_age. Returns the number of
entries swept. Called from a hourly background tick spawned in
main.rs (default TTL = 24 h, configurable via
--multipart-abandoned-ttl-hours).
Each dropped MultipartUploadContext runs the inner
MultipartSseMode::SseC { key: Zeroizing<[u8; 32]>, .. }’s
Drop, wiping the customer-supplied AES key bytes from
process memory. SSE-S4 / SSE-KMS / None variants drop their
(smaller) state too; only SSE-C carries raw key material.
The cutoff is computed as now - max_age rather than
Utc::now() - max_age so callers can drive the clock
deterministically in tests (the unit tests below pass an
explicit now from a fixed timestamp).
Sourcepub fn completion_lock(&self, bucket: &str, key: &str) -> Arc<Mutex<()>> ⓘ
pub fn completion_lock(&self, bucket: &str, key: &str) -> Arc<Mutex<()>> ⓘ
v0.8.1 #59: get-or-create the per-(bucket, key) Mutex used to
serialise complete_multipart_upload invocations on the same
logical key. Caller does lock.lock().await and holds the
guard for the duration of its critical section (GET assembled
body → encrypt → PUT encrypted body → version-id mint → object-
lock apply → tagging persist → replication enqueue).
Returns an Arc<Mutex<()>> so the caller can drop the
DashMap shard’s read lock immediately and only retain the
mutex itself across the await point — DashMap’s shard guard
is !Send, so we must not hold it through an await.
Sourcepub fn prune_completion_locks(&self)
pub fn prune_completion_locks(&self)
v0.8.1 #59: best-effort cleanup of stale completion-lock
entries. A (bucket, key) entry is “stale” once no concurrent
Complete is referencing its Arc<Mutex<()>> — we detect that
by Arc::strong_count == 1 (only the DashMap itself holds a
reference). Called from complete_multipart_upload after the
guarded section returns, so a steady-state workload of unique
keys never accumulates locks.
The retain predicate is > 1 (keep entries with outstanding
borrowers), so prune is safe to invoke concurrently with other
completion_lock callers — at worst the prune sees the entry
during a brief window where the borrower has cloned but not yet
taken lock(), and the entry survives until the next sweep.
Trait Implementations§
Auto Trait Implementations§
impl !Freeze for MultipartStateStore
impl !RefUnwindSafe for MultipartStateStore
impl !UnwindSafe for MultipartStateStore
impl Send for MultipartStateStore
impl Sync for MultipartStateStore
impl Unpin for MultipartStateStore
impl UnsafeUnpin for MultipartStateStore
Blanket Implementations§
Source§impl<'a, T, E> AsTaggedExplicit<'a, E> for Twhere
T: 'a,
impl<'a, T, E> AsTaggedExplicit<'a, E> for Twhere
T: 'a,
Source§impl<'a, T, E> AsTaggedImplicit<'a, E> for Twhere
T: 'a,
impl<'a, T, E> AsTaggedImplicit<'a, E> for Twhere
T: 'a,
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> 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 more