first commit
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
# `complete-chunked.ts`
|
||||
|
||||
## Purpose
|
||||
|
||||
Internal back-end implementing the `complete` operation of `FileUploadService`. Extracted from the main service file so each module stays under the suaveplan 300-line budget. The pipeline is: assemble every chunk in `[0, totalChunks)` order → verify SHA-256 against the declared expected hash (or an explicit override) → write the assembled bytes to the canonical record key → save the `UploadRecord` → garbage-collect per-chunk storage entries → clear the in-flight session.
|
||||
|
||||
## Features
|
||||
|
||||
- Single-pass assembly via the `assembler` helper so chunk ordering and missing-chunk detection live in one place.
|
||||
- Override-on-complete lets clients adjust the declared hash if they recomputed mid-flight without rebuilding the entire session.
|
||||
- Emits both the outcome counter (`upload.completes_total{outcome}`) and the duration histogram (`upload.complete_duration_ms{outcome}`) on every code path so dashboards can correlate failure rate with latency.
|
||||
- Cleanup runs even on success (chunk-side storage entries are deleted) so the storage adapter never accumulates orphaned chunk keys.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { runComplete } from "@suaveplan/api-plugin-file-upload";
|
||||
|
||||
const record = await runComplete(session, undefined, {
|
||||
storage,
|
||||
sessionStore,
|
||||
clock: () => Date.now(),
|
||||
});
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import { runComplete, FileUploadError } from "@suaveplan/api-plugin-file-upload";
|
||||
|
||||
try {
|
||||
await runComplete(session, clientRecomputedHash, opts);
|
||||
} catch (err) {
|
||||
if (err instanceof FileUploadError) {
|
||||
switch (err.uploadCode) {
|
||||
case "UPLOAD.INCOMPLETE":
|
||||
return c.json({ retry: true, reason: "missing-chunks" }, 409);
|
||||
case "UPLOAD.HASH_MISMATCH":
|
||||
return c.json({ retry: true, reason: "hash-mismatch" }, 422);
|
||||
}
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `RunCompleteOptions` — `{ storage, sessionStore, clock }`.
|
||||
- `runComplete(session, overrideSha256?, opts): Promise<UploadRecord>` — see Purpose for pipeline order; throws `UPLOAD.INCOMPLETE`, `UPLOAD.HASH_MISMATCH`, or `UPLOAD.STORAGE_FAILURE`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The chunk deletion loop runs sequentially. A parallel `Promise.all` is tempting but most storage adapters (`FileSystemUploadStorage` in particular) prefer FIFO syscalls to avoid file-descriptor exhaustion on very large uploads.
|
||||
- The histogram is recorded INSIDE the `try` so even early failures (e.g. an assemble that throws on the second chunk) still contribute to the observed-failure-latency picture.
|
||||
Reference in New Issue
Block a user