Prompt and context
A client needs to upload a multi-gigabyte file over an unreliable network. A mobile client may retry or concurrently submit the same part. The service must expose progress, allow missing parts to be retried, and prove content integrity before and after finalization. The interview focuses on protocol state, idempotency, checksums, lifecycle, and object visibility.
What the interviewer is testing
The interviewer wants to see a large request decomposed into a recoverable session. Google Cloud defines resumable uploads as multiple requests that can continue after a communication failure; Amazon S3 multipart upload requires parts first and an explicit complete call before assembling an object, and recommends lifecycle cleanup for incomplete uploads. A strong answer also covers authorization, rate limits, and duplicate completion semantics.
Clarifying questions to ask first
Clarify object size, part size, and concurrency limits; whether the client can retain a session URL; whether objects may be overwritten or versioned; whether the client supplies a whole-file digest or the service computes one; and the session retention, cancellation, abuse, and tenant-quota policies. A presigned URL does not automatically carry business authorization.
A 30-second answer framework
Say: “I would expose create-session, upload-part, status, complete, and cancel operations. The session binds tenant, object key, size, part rules, expiry, and checksum policy. Each part is idempotent by (uploadId, partNumber, checksum). Completion verifies consecutive parts and the whole-object digest, then atomically publishes an object version. A worker cleans expired sessions; authorization, quota, and rate limits are checked at creation and on every part request.”
Step-by-step deep analysis
1. Create and authorize the upload session
POST /uploads checks tenant quota, object size, content type, and destination permission, then returns a random uploadId, part size, expiry, and scoped upload credentials. The session records expected size, object key, version policy, and checksum algorithm; the client cannot choose an arbitrary storage path.
2. Design idempotent parts and status
PUT /uploads/{id}/parts/{n} carries part length and checksum. The service stores the latest valid metadata for uploadId + partNumber; a duplicate with the same digest returns success, while a different digest returns conflict and asks the client to refresh status. GET /uploads/{id} returns confirmed parts, size, and the next action without exposing another tenant’s data.
3. Verify data integrity
Validate length and a part checksum on each upload, then validate consecutive part numbers, total length, and the whole-object digest at completion. Amazon S3 documents part or composite checksums; a mismatch must prevent publication. Store the algorithm and encoding in the session so client and service do not interpret the digest differently.
4. Complete and control visibility
POST /uploads/{id}/complete carries an ordered part list and optional whole-file digest. Completion is idempotent: the same list returns the same object version, while a conflicting list is rejected. Only after storage assembles and verifies the object does the database record move from UPLOADING to READY; reads never expose a partial object.
5. Cancel, expire, and clean orphaned parts
The client can cancel explicitly. A worker scans expired sessions, calls the storage abort operation, and removes uploaded parts. Cleanup is itself idempotent and retryable, with last error and cost metrics recorded. S3 notes that incomplete parts incur storage charges, so a lifecycle rule is a final safety net, not a replacement for application state.
6. Add security, quotas, and observability
Every operation checks tenant, object permission, session state, and part range. Credentials are scoped to the current session and expire quickly. Limit active sessions, total bytes, and part size per tenant. Monitor session success, retries, checksum failures, cleanup delay, and orphaned bytes, with tenant-isolated alerts.
High-quality sample answer
“I would create a session with POST /uploads, validate tenant quota and object size, and issue a random uploadId, fixed part size, checksum algorithm, and 24-hour expiry. Every part carries its number, length, and digest; (uploadId, partNumber) is the idempotency key. A duplicate with the same digest returns the existing result, while a different digest is rejected. The client uses the status endpoint to find missing parts. Completion supplies an ordered list and whole-file digest; the service verifies numbering, total length, and part checksums, then calls the object store’s complete operation. Only after assembly succeeds does it mark the object READY. Repeated completion returns the same version; a conflicting list changes nothing. Cancellation and expiry workers abort incomplete uploads with retries, avoiding orphaned storage charges. Credentials are tenant- and session-scoped, and creation, part, and completion paths all enforce quota, authorization, rate limits, and audit.”
Common mistakes and improvements
- Making upload one long request: Use a session and parts so a network failure affects only missing data.
- Tracking only uploaded bytes: Track part number, digest, and version to prevent duplicate or out-of-order replacement.
- Marking success before completion: Assemble and verify at storage first, then publish the
READYobject. - Ignoring expired-part cost: Provide cancellation, background abort, and storage lifecycle cleanup together.
Follow-up questions and responses
What if the client uploads the same part concurrently?
Serialize metadata updates by session and part number or use a conditional write. Same-digest duplicates return idempotent success; different digests return conflict, and the client refreshes status before retrying.
What if completion times out after storage actually finished?
Persist a completion idempotency key and target version. On retry, query storage and local state first. If the list matches a completed version, return that version; if uncertain, keep COMPLETING and let a worker reconcile rather than assembling blindly.
How do you stop an abusive client from filling storage?
Reserve quota at session creation and limit active sessions, total part bytes, concurrency, and expiry per tenant. Bind part credentials to the session and range, abort expired sessions promptly, and alert on abnormal retry rates.
Should same-key objects be overwritable?
Default to a new version or conditional write. If overwrite is required, accept a target version or If-Match condition and update the reference atomically after completion, so a slow upload cannot overwrite a newer object.