feat(thumbnails): stop writing sidecars (step 10d2)
The write paths now persist only to the blob tiers. Until this, dual-write
meant any render or upload recreated .thumbnails/ seconds after the import
job removed it, so step 10e's gate — "the directory no longer exists" —
could never hold.
Rendered thumbnails: the fs::write in persist_rendered is gone. Safe
because the read flip landed first, so nothing depended on that write to
be found, and a failed derived store now costs a re-render rather than
data — regenerable by definition. Existing sidecars are untouched and stay
readable through the fallback until the import drains them.
Uploaded previews needed a change first, and the order was not optional.
upload_thumbnail_impl logged and still returned 201 when store_attached_blob
failed — safe only while ext-{file_id}.jpg was a second copy. These bytes
have NO server-side render path, so removing the sidecar while the store
stayed best-effort would lose a user's upload behind a success response.
The PUT is now fatal, and drops the RAM entry too, or the cache would keep
serving a preview that was never persisted and vanishes on eviction,
contradicting the error the client just received. Only then does the ext-
write go.
thumb_import_check.sh had to change with it: its premise was "upload, then
delete the row, and what remains on disk is legacy state", which no longer
holds now that nothing writes sidecars. It lays them down itself, with the
bytes the API just served, at the exact paths the pre-10d2 code used. The
reconstruction stays faithful — same bytes, same paths — it just no longer
depends on current code to produce a shape current code has stopped
producing. Both sidecars get identical bytes, which is realistic rather
than a shortcut: they dedup to one blob while keeping separate mappings,
which is the property the keying split exists to preserve.
This commit is contained in:
@@ -800,16 +800,26 @@ impl FileHandler {
|
||||
)
|
||||
.await
|
||||
{
|
||||
// ERROR, not WARN: the sidecar keeps the feature looking healthy
|
||||
// on this box, so nothing else signals that copies are silently
|
||||
// losing the preview. A syntax error in the upsert hid behind a
|
||||
// warning for an entire test cycle exactly this way.
|
||||
// FATAL as of step 10d2, where it used to warn and return 201.
|
||||
//
|
||||
// That was safe only while `ext-{file_id}.jpg` existed as a
|
||||
// second copy. With the sidecar gone this is the ONLY durable
|
||||
// home for bytes that have no server-side render path — a
|
||||
// client-generated PDF preview cannot be recreated — so
|
||||
// succeeding here would lose a user's upload behind a success
|
||||
// response. Silent, and unrecoverable.
|
||||
//
|
||||
// The RAM entry is dropped too, or the cache would keep serving a
|
||||
// preview that was never persisted and vanishes on eviction,
|
||||
// contradicting the error the client just received.
|
||||
let _ = thumbnail_service.delete_thumbnails(&id).await;
|
||||
tracing::error!(
|
||||
target: "oxicloud::dedup",
|
||||
error = %e,
|
||||
file_id = %id,
|
||||
"failed to record attached thumbnail; sidecar written, copies will NOT inherit it"
|
||||
"failed to record attached thumbnail; upload rejected"
|
||||
);
|
||||
return AppError::internal_error("Failed to store thumbnail").into_response();
|
||||
}
|
||||
|
||||
StatusCode::CREATED.into_response()
|
||||
|
||||
Reference in New Issue
Block a user