Skip to content

feat(tiered): Support Resumable Uploads - #642

Open
lcian wants to merge 8 commits into
mainfrom
feat/tiered-resumable-uploads
Open

lcian wants to merge 8 commits into
mainfrom
feat/tiered-resumable-uploads

Conversation

@lcian

@lcian lcian commented Sep 21, 2026 •

Copy link
Copy Markdown
Member

This implements Resumable Uploads in TieredStorage.

Notes:

  • We only accept uploads bigger that exceed the BACKEND_SIZE_THRESHOLD, smaller ones are rejected at creation time. There was an idea to still accept the upload creation request and then force the client to upload the whole payload in a single shot by always reporting offset 0, but that cannot be achieved unless we always keep some state about which uploads exist/are active at a given moment. So, we might as well reject the creation request and then the user is still practically forced to use a regular put and upload in a single shot.
    If we change the way we do routing, this can trivially be adapted.
  • We intentionally release this acknowledging that it has consistency gaps. I didn't see any immediate way of making this consistent, so we defer solving this problem to the Consistency project, as a proper solution will likely require some level of fundamental redesign. I've documented the two most obvious problematic scenarios for the approach I've chosen, but the list is definitely not exhaustive.

Refs FS-508

Route resumable sessions to long-term storage and publish completed revisions through high-volume tombstones.
@codecov

codecov Bot commented Sep 21, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91.31%. Comparing base (bf02738) to head (242c847).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #642      +/-   ##
==========================================
+ Coverage   91.27%   91.31%   +0.04%     
==========================================
  Files         116      116              
  Lines       23496    23625     +129     
==========================================
+ Hits        21445    21574     +129     
  Misses       2051     2051              
Components Coverage Δ
Rust Backend 94.84% <100.00%> (+0.03%) ⬆️
Rust Client 81.95% <ø> (ø)
Python Client 93.75% <ø> (ø)

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@lcian lcian changed the title feat(tiered): Support resumable uploads feat(tiered): Support Resumable Uploads Sep 21, 2026
@lcian

This comment has been minimized.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 09aadd1. Configure here.

Comment thread objectstore-service/src/backend/tiered.rs
Comment thread objectstore-service/src/backend/tiered.rs
@lcian
lcian marked this pull request as ready for review September 21, 2026 14:56
@lcian
lcian requested a review from a team as a code owner September 21, 2026 14:56
@linear-code

linear-code Bot commented Sep 21, 2026

Copy link
Copy Markdown

FS-508

Route resumable sessions only to long-term storage, where uploads can be resumed. Update the existing tiered resumable tests to use long-term sizes and document the cutoff.
Comment thread objectstore-service/src/backend/tiered.rs
Comment thread objectstore-service/src/backend/tiered.rs Outdated
Comment thread objectstore-service/src/backend/tiered.rs
Comment thread objectstore-service/src/backend/tiered.rs
Comment thread objectstore-service/src/backend/tiered.rs
Store the long-term revision and backend token directly in the tiered token. Decode its authenticated payload without redundant key-shape validation.
@lcian
lcian requested a review from matt-codecov September 22, 2026 11:46
Comment thread objectstore-service/src/backend/tiered.rs
Comment thread objectstore-service/src/backend/tiered.rs
Comment thread objectstore-service/docs/architecture.md Outdated
struct TieredResumableToken {
inner: LongTermBackendToken,
revision: String,
total_length: u64,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We now have the total length at this layer and inside the wrapped long term backend token, at least in the GCS case. In order to fulfill the contract, we will always need the length. Do you see a way to hoist the length up or move it out of the encoded string so that it's readable by every layer?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either

  • move this one layer up and pass it as argument
  • make each token an object and Box it and have some traits to access the length from the inner token

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's easy to do it with moving it one layer up.
I'll do that in a follow-up as that likely needs to touch many files/call sites.

Comment thread objectstore-service/src/backend/tiered.rs Outdated
Comment thread objectstore-service/src/backend/tiered.rs
Comment thread objectstore-service/src/backend/tiered.rs
Comment thread objectstore-service/src/backend/tiered.rs
Comment on lines +479 to +483
return match progress {
UploadProgress::Incomplete { .. } => Ok(progress),
UploadProgress::Complete => Ok(UploadProgress::Complete),
};
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: When a non-final chunk upload returns UploadProgress::Complete, the code propagates this status without publishing a tombstone, making the object inaccessible.
Severity: LOW

Suggested Fix

When handling a non-final chunk, if the underlying put_chunk call returns UploadProgress::Complete, do not simply propagate the Complete status. Instead, ensure a tombstone is published for the object to make it accessible, or handle this state as a distinct case rather than a successful completion of the chunk.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location: objectstore-service/src/backend/tiered.rs#L479-L483

Potential issue: In the tiered storage backend, when a non-final chunk of a multi-part
upload is processed, the code forwards it to the long-term storage. If the long-term
storage reports that the upload is already `Complete` (e.g., due to a race condition or
a client retry after completion), the `put_chunk` function in `tiered.rs` incorrectly
returns `UploadProgress::Complete`. However, it fails to publish a 'tombstone' record,
which is required to finalize the object's state in the tiered system. This oversight
leads to the object being successfully stored but remaining inaccessible to clients. The
bug is an edge case that requires unusual client behavior or specific network timing.

Also affects:

  • objectstore-service/src/backend/tiered.rs:535~543

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants