pub struct Service {
store: Store,
broker: Arc<Broker>,
config: Arc<ConfigState>,
permits: Arc<Semaphore>,
memory: Arc<Semaphore>,
batches: Mutex<HashMap<String, Arc<AtomicBool>>>,
inflight: Arc<Mutex<HashMap<String, usize>>>,
session_budget: Mutex<()>,
}Fields§
§store: Store§broker: Arc<Broker>§config: Arc<ConfigState>§permits: Arc<Semaphore>Bounds concurrent jobs (CPU).
memory: Arc<Semaphore>Bounds the decode memory of concurrent jobs, in MiB.
batches: Mutex<HashMap<String, Arc<AtomicBool>>>Cancellation flags of batches still running.
inflight: Arc<Mutex<HashMap<String, usize>>>Jobs currently processing each id. Identical images processed at once share one stored original, so a failing job may only discard it when no other job is still using it. Placing an original and the discard check both happen under this lock.
session_budget: Mutex<()>Serializes the per-session inline-budget read, spend and write, so two overlapping turns of one Claude session can’t both spend the same remaining budget or overwrite each other’s count.
Implementations§
Source§impl Service
impl Service
pub fn new(store: Store, broker: Arc<Broker>, config: Arc<ConfigState>) -> Self
pub fn store(&self) -> &Store
pub fn limits(&self) -> Limits
fn start_sweeper(self: &Arc<Self>)
Sourcepub fn ingest(
self: &Arc<Self>,
req: CommandAttachmentsIngestData,
) -> AttachmentsIngestResult
pub fn ingest( self: &Arc<Self>, req: CommandAttachmentsIngestData, ) -> AttachmentsIngestResult
Classify paths, apply the limits, and (unless dry_run) start
processing the accepted images in the background. Returns at once.
pub fn cancel(&self, batch_id: &str) -> bool
Sourcepub async fn info(self: &Arc<Self>, ids: &[String]) -> Vec<AttachmentInfo>
pub async fn info(self: &Arc<Self>, ids: &[String]) -> Vec<AttachmentInfo>
Info for each id still in the store, in request order. May decode (after a send-edge change), so call it off the async workers.
Sourcepub async fn ensure_derived(self: &Arc<Self>, id: &str) -> Option<StoredMeta>
pub async fn ensure_derived(self: &Arc<Self>, id: &str) -> Option<StoredMeta>
Metadata under the current fingerprint. When the send edge or the pipeline changed since the attachment was processed, re-derive it from the stored original under the same CPU and memory limits as ingest, so a transcript full of old thumbnails can’t start an unbounded number of decodes at once.
Sourceasync fn derive_kind(
self: &Arc<Self>,
id: &str,
blob: PathBuf,
kind: FileKind,
name: &str,
edge: u32,
) -> Result<StoredMeta, ProcessError>
async fn derive_kind( self: &Arc<Self>, id: &str, blob: PathBuf, kind: FileKind, name: &str, edge: u32, ) -> Result<StoredMeta, ProcessError>
Derive a placed original. Images reserve decode memory from their
header first; other files are bounded by the extraction limits
(extract.rs) and PDFs run out of process. The caller holds a CPU slot.
Sourcepub fn with_session_inline_budget<T>(
&self,
key: &str,
carry_from: Option<&str>,
max: u64,
f: impl FnOnce(u64) -> (T, u64),
) -> T
pub fn with_session_inline_budget<T>( &self, key: &str, carry_from: Option<&str>, max: u64, f: impl FnOnce(u64) -> (T, u64), ) -> T
Run f with the inline budget left in Claude session key (out of
max), and record what it reports spending, as one step under a
lock. carry_from is a per-block key counted before the session had
an id; its count moves into key first. Blocking.
Sourcepub fn send_target(&self, id: &str, name: &str) -> Option<SendTarget>
pub fn send_target(&self, id: &str, name: &str) -> Option<SendTarget>
What the agent gets for attachment id sent under the user’s name,
marking it used: an image’s send-copy, or for any other file a
freshly made copy named name, with a note for the agent’s list.
Doesn’t decode: call Self::ensure_derived first so a stale
fingerprint is re-derived under the limits.
Sourceasync fn reserve_memory(&self, w: u32, h: u32) -> Option<OwnedSemaphorePermit>
async fn reserve_memory(&self, w: u32, h: u32) -> Option<OwnedSemaphorePermit>
Reserve decode memory for a w×h image. Held until dropped.
Sourcepub async fn commit_upload(
self: &Arc<Self>,
tmp: PathBuf,
id: String,
name: String,
) -> Result<AttachmentInfo, ProcessError>
pub async fn commit_upload( self: &Arc<Self>, tmp: PathBuf, id: String, name: String, ) -> Result<AttachmentInfo, ProcessError>
Store a file already sitting in incoming/ (an upload): place it,
reserve decode memory, derive. Returns its info.
Sourcefn place_tracked(
&self,
tmp: &Path,
id: &str,
name: &str,
) -> Result<(PathBuf, FileKind, InflightGuard), ProcessError>
fn place_tracked( &self, tmp: &Path, id: &str, name: &str, ) -> Result<(PathBuf, FileKind, InflightGuard), ProcessError>
Store::place, registering this job as a user of the id first,
under the same lock the discard check takes. Blocking.
Sourceasync fn discard_unprocessed(&self, guard: InflightGuard)
async fn discard_unprocessed(&self, guard: InflightGuard)
After a failed job: discard the original unless another job for the
same bytes is still processing it (Store::discard_if_unprocessed
also keeps it when any fingerprint has metadata). Consumes the guard.
async fn run_batch( self: Arc<Self>, block_id: String, batch_id: String, items: Vec<AttachmentPending>, limits: Limits, cancel: Arc<AtomicBool>, )
Sourceasync fn run_item(
self: &Arc<Self>,
block_id: &str,
batch_id: &str,
item: AttachmentPending,
limits: Limits,
cancel: &Arc<AtomicBool>,
) -> Result<AttachmentInfo, ProcessError>
async fn run_item( self: &Arc<Self>, block_id: &str, batch_id: &str, item: AttachmentPending, limits: Limits, cancel: &Arc<AtomicBool>, ) -> Result<AttachmentInfo, ProcessError>
One item: wait for a CPU slot, copy + hash + place, reserve decode memory from the header, derive. The cancel flag is checked before each step and between copy chunks; a decode already running finishes but is reported as cancelled.
Sourceasync fn derive_placed(
self: &Arc<Self>,
block_id: &str,
batch_id: &str,
item: &AttachmentPending,
id: &str,
blob: PathBuf,
kind: FileKind,
limits: Limits,
cancel: &Arc<AtomicBool>,
) -> Result<AttachmentInfo, ProcessError>
async fn derive_placed( self: &Arc<Self>, block_id: &str, batch_id: &str, item: &AttachmentPending, id: &str, blob: PathBuf, kind: FileKind, limits: Limits, cancel: &Arc<AtomicBool>, ) -> Result<AttachmentInfo, ProcessError>
The part of Self::run_item after the original is in blobs/:
reserve decode memory from the header and derive.
Sourcefn copy_and_place(
&self,
block_id: &str,
batch_id: &str,
item: &AttachmentPending,
limits: Limits,
cancel: &AtomicBool,
) -> Result<(String, PathBuf, FileKind, InflightGuard), ProcessError>
fn copy_and_place( &self, block_id: &str, batch_id: &str, item: &AttachmentPending, limits: Limits, cancel: &AtomicBool, ) -> Result<(String, PathBuf, FileKind, InflightGuard), ProcessError>
Copy the source into incoming/ with progress and cancellation, then
move it into blobs/. Runs on a blocking thread.
fn publish<E: Event>(&self, block_id: &str, data: &E::Payload)
Auto Trait Implementations§
impl !Freeze for Service
impl !RefUnwindSafe for Service
impl !UnwindSafe for Service
impl Send for Service
impl Sync for Service
impl Unpin for Service
impl UnsafeUnpin for Service
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<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.§impl<T> DowncastSync for T
impl<T> DowncastSync for T
impl<A, B, T> HttpServerConnExec<A, B> for Twhere
B: Body,
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> 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