Skip to content

refactor: mpu redesign#54

Merged
destifo merged 11 commits into
mainfrom
refactor/mpu-redesign
Jul 4, 2025
Merged

refactor: mpu redesign#54
destifo merged 11 commits into
mainfrom
refactor/mpu-redesign

Conversation

@destifo
Copy link
Copy Markdown
Contributor

@destifo destifo commented Jul 2, 2025

Migration notes


  • The change comes with new or modified tests
  • Hard-to-understand functions have explanatory comments
  • End-user documentation is updated to reflect the change

Summary by CodeRabbit

  • Refactor

    • Updated authentication type configuration, replacing "none" with "default" as the allowed and default value.
    • Adjusted authentication and signature verification logic to align with the new "default" authentication type.
    • Removed warning message for "none" authentication type at startup.
    • Improved multipart upload handling by storing session metadata in individual files instead of a centralized index.
    • Modified retry logic to only retry failed tasks on server errors (5xx), not client errors (4xx).
  • Chores

    • Updated test workflow to use the "default" authentication type during automated tests.

@destifo destifo self-assigned this Jul 2, 2025
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jul 2, 2025

📝 Walkthrough
## Walkthrough

The changes update authentication type handling across configuration, main entry, workflow, and backend logic, switching from `"none"` to `"default"`. Multipart upload tracking in the Swift backend is refactored to use per-session files instead of a centralized index. Worker retry logic is adjusted to exclude client errors from retries.

## Changes

| File(s)                                | Change Summary                                                                                                    |
|---------------------------------------|------------------------------------------------------------------------------------------------------------------|
| .github/workflows/tests.yml            | Changed `AUTH_TYPE` in test job from `"none"` to `"default"`.                                                    |
| src/config/types.ts                    | Updated `auth_type` enum and default from `["service_account", "none"]` to `["service_account", "default"]`.     |
| src/main.ts                           | Removed startup warning for `auth_type` set to `"none"`.                                                         |
| src/backends/mod.ts                   | Refactored signature verification: now only runs for `"default"` auth type; adjusted access checks accordingly.   |
| src/backends/swift/objects.ts         | Refactored multipart upload tracking to use per-session files instead of a central index; updated related logic.  |
| src/backends/worker.ts                | Changed retry logic to only retry on server errors (status ≥ 500), not on client errors (status 4xx).             |
| tests/swift/basic/multipartupload_test.ts | Removed creation of empty multipart uploads index file in tests.                                                |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant Client
    participant Server
    participant AuthModule

    Client->>Server: Request (with possible signature)
    alt auth_type == "default"
        Server->>AuthModule: verifyV4Signature()
        AuthModule-->>Server: Verification result
        Server->>Server: Check service account access
    else auth_type != "default"
        Server->>Server: Check service account access only
    end
    Server-->>Client: Response
sequenceDiagram
    participant Client
    participant SwiftBackend

    Client->>SwiftBackend: Initiate Multipart Upload
    SwiftBackend->>SwiftBackend: Write session metadata to uploadId.json

    Client->>SwiftBackend: Upload Part
    SwiftBackend->>SwiftBackend: Update parts metadata in uploadId.json

    Client->>SwiftBackend: Complete Multipart Upload
    SwiftBackend->>SwiftBackend: Delete uploadId.json and finalize upload

    Client->>SwiftBackend: Abort Multipart Upload
    SwiftBackend->>SwiftBackend: Delete uploadId.json

    Client->>SwiftBackend: List Multipart Uploads
    SwiftBackend->>SwiftBackend: Read all uploadId.json files and aggregate
Loading

Possibly related PRs

Suggested labels

enhancement

Suggested reviewers

  • Kirubel-Fikru
  • Sabian-A
  • zifeo
  • hailatGH

</details>

<!-- walkthrough_end -->


---

<details>
<summary>📜 Recent review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**


<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between fb1397702c2b8537813cf18a3ac5402ec46b7060 and d9cbfbd196660d3c8f61660306ef6be241eb2369.

</details>

<details>
<summary>📒 Files selected for processing (2)</summary>

* `src/backends/swift/objects.ts` (12 hunks)
* `tests/swift/basic/multipartupload_test.ts` (0 hunks)

</details>

<details>
<summary>💤 Files with no reviewable changes (1)</summary>

* tests/swift/basic/multipartupload_test.ts

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧠 Learnings (2)</summary>

<details>
<summary>📓 Common learnings</summary>

Learnt from: destifo
PR: #10
File: src/backends/s3/buckets.ts:0-0
Timestamp: 2025-01-28T17:36:22.149Z
Learning: The routeQueryParamedRequest and headBucket functions in src/backends/s3/buckets.ts already implement retry logic for replica failover when the primary request fails, supporting both S3 and Swift replicas.


</details>
<details>
<summary>src/backends/swift/objects.ts (5)</summary>

Learnt from: destifo
PR: #10
File: src/backends/s3/buckets.ts:0-0
Timestamp: 2025-01-28T17:36:22.149Z
Learning: The routeQueryParamedRequest and headBucket functions in src/backends/s3/buckets.ts already implement retry logic for replica failover when the primary request fails, supporting both S3 and Swift replicas.


Learnt from: destifo
PR: #49
File: src/backends/task_store.ts:170-228
Timestamp: 2025-06-26T12:48:25.255Z
Learning: In src/backends/task_store.ts, the TaskStore class creates Request objects with localhost URLs that are not actually used for HTTP requests. Instead, these Request objects serve as structured data containers that get passed to backend storage implementations (s3PutObject/swiftPutObject) which extract the necessary information (bucket, key, body, headers) and make the actual requests to the configured storage backends.


Learnt from: destifo
PR: #10
File: src/backends/mirror.ts:170-172
Timestamp: 2025-01-28T17:41:43.238Z
Learning: In the mirror operations (src/backends/mirror.ts), bucket existence is guaranteed by design as mirror tasks are only created for buckets that exist in the bucket store, which is initialized during startup. Therefore, non-null assertions when finding buckets in the store are safe.


Learnt from: destifo
PR: #41
File: src/backends/swift/mod.ts:318-323
Timestamp: 2025-06-16T14:40:12.564Z
Learning: In conversion functions that return Result<Response, Error>, the Result represents whether the conversion process succeeded, not the HTTP semantics. A function like convertSwiftUploadPartToS3Response should return createOk(errorResponse) when it successfully converts to a proper S3 error response, and createErr(conversionError) only when the conversion process itself fails.


Learnt from: destifo
PR: #35
File: src/backends/swift/mod.ts:0-0
Timestamp: 2025-06-11T20:21:26.693Z
Learning: For the Swift backend's PUT Object path (convertSwiftPutObjectToS3Response), Swift can return only 404 (container missing), 411 (length required), and 422 (checksum mismatch). It will not surface a 408 timeout here, so mapping all handled error cases to HTTP 400 in the S3 response is correct.


</details>

</details><details>
<summary>🪛 Biome (1.9.4)</summary>

<details>
<summary>src/backends/swift/objects.ts</summary>

[error] 1268-1268: Change to an optional chain.

Unsafe fix: Change to an optional chain.


(lint/complexity/useOptionalChain)

</details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (1)</summary>

* GitHub Check: test-full

</details>

<details>
<summary>🔇 Additional comments (5)</summary><blockquote>

<details>
<summary>src/backends/swift/objects.ts (5)</summary>

`590-674`: **LGTM! Clean implementation of per-session file approach.**

The refactor to use individual session files instead of a centralized index is well-implemented. The error handling is comprehensive with proper XML error responses, and the session metadata structure is maintained correctly.

---

`756-793`: **Good error handling strategy for session cleanup.**

The approach of continuing with the main upload completion even if session file deletion fails is appropriate. This ensures that successful uploads aren't blocked by metadata cleanup issues while still logging relevant errors.

---

`870-880`: **Good addition of uploadId validation.**

The explicit validation of uploadId parameter improves error handling and prevents potential issues downstream.

---

`1123-1158`: **Consistent implementation with good error handling.**

The session file deletion logic is consistent with `completeMultipartUpload` and appropriately continues execution even if cleanup fails, ensuring the main operation isn't blocked.

---

`1205-1214`: **Well-defined interface for session structure.**

The `MultipartUploadSession` interface properly defines the structure of session files and improves type safety.

</details>

</blockquote></details>

</details>
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details open="true">
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=expnt/herald&utm_content=54):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Explain this complex logic.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai explain this code block.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and explain its main purpose.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

### Support

Need help? Create a ticket on our [support page](https://www.coderabbit.ai/contact-us/support) for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->

@coderabbitai coderabbitai Bot added the enhancement New feature or request label Jul 2, 2025
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🔭 Outside diff range comments (3)
src/backends/swift/objects.ts (3)

756-781: Inconsistent implementation in completeMultipartUpload.

The function attempts to fetch and update data at reqUrl (the object URL) instead of the session file created during createMultipartUpload. This doesn't align with the new per-session file approach.

The code should be updated to:

  1. Read from the session file at ${MULTIPART_UPLOADS_PATH}/${uploadId}.json
  2. Delete the session file after successful completion
  3. Not attempt to filter uploads array as each session has its own file
-// Fetch the existing index file to remove the upload metadata
+// Fetch and delete the session file after completion
 try {
-  const fetchIndexFunc = async () => {
-    return await fetch(reqUrl, {
+  const sessionPath = `${MULTIPART_UPLOADS_PATH}/${uploadId}.json`;
+  const sessionUrl = `${swiftUrl}/${bucket}/${sessionPath}`;
+  
+  // Delete the session file
+  const deleteSessionFunc = async () => {
+    return await fetch(sessionUrl, {
-      method: "GET",
+      method: "DELETE",
       headers: headers,
     });
   };
-  const indexResponse = await retryWithExponentialBackoff(fetchIndexFunc);
+  const deleteResponse = await retryWithExponentialBackoff(deleteSessionFunc);
 
-  if (isOk(indexResponse) && unwrapOk(indexResponse).ok) {
-    const successResponse = unwrapOk(indexResponse);
-    const indexData = await successResponse.json();
-    // Filter out the completed upload
-    const updatedUploads = indexData.uploads.filter((
-      upload: { uploadId: string },
-    ) => upload.uploadId !== uploadId);
-
-    // Update the index file
-    const updateIndexFunc = async () => {
-      return await fetch(reqUrl, {
-        method: "PUT",
-        headers: headers,
-        body: JSON.stringify(updatedUploads),
-      });
-    };
-    await retryWithExponentialBackoff(updateIndexFunc);
-    logger.info(`Removed upload ${uploadId} from multipart uploads index`);
+  if (isOk(deleteResponse) && unwrapOk(deleteResponse).status === 204) {
+    logger.info(`Deleted multipart upload session file for ${uploadId}`);
   } else {
     logger.warn(
-      `Failed to fetch multipart uploads index: ${
-        !isOk(indexResponse)
-          ? unwrapErr(indexResponse).message
-          : unwrapOk(indexResponse).statusText
+      `Failed to delete multipart upload session file: ${
+        !isOk(deleteResponse)
+          ? unwrapErr(deleteResponse).message
+          : unwrapOk(deleteResponse).statusText
       }`,
     );
   }

1174-1375: listMultipartUploads still uses centralized index approach.

The listMultipartUploads function still expects a centralized index file at ${MULTIPART_UPLOADS_PATH}/index.json, which is inconsistent with the new per-session file approach implemented in createMultipartUpload.

This function needs to be updated to:

  1. List all files in the ${MULTIPART_UPLOADS_PATH}/ directory
  2. Read individual session files to build the uploads list
  3. Apply filtering and pagination on the aggregated data

Would you like me to help implement a solution that lists and aggregates individual session files for the multipart uploads listing?


1031-1172: abortMultipartUpload still uses centralized index approach.

Similar to listMultipartUploads, this function attempts to update a centralized index file which no longer exists with the new per-session approach.

Update to simply delete the session file:

-  // First, update the multipart uploads index to remove this upload
-  const multipartIndexPath = `${MULTIPART_UPLOADS_PATH}/index.json`;
-  const multipartIndexUrl = `${swiftUrl}/${bucket}/${multipartIndexPath}`;
+  // Delete the session file for this upload
+  const sessionPath = `${MULTIPART_UPLOADS_PATH}/${uploadId}.json`;
+  const sessionUrl = `${swiftUrl}/${bucket}/${sessionPath}`;

-  // Try to fetch the existing index file
-  let existingUploads = [];
   try {
-    const fetchIndexFunc = async () => {
-      return await fetch(multipartIndexUrl, {
-        method: "GET",
-        headers: headers,
-      });
-    };
-    // ... (remove all the index update logic)
+    const deleteSessionFunc = async () => {
+      return await fetch(sessionUrl, {
+        method: "DELETE",
+        headers: headers,
+      });
+    };
+    
+    const deleteResponse = await retryWithExponentialBackoff(deleteSessionFunc);
+    
+    if (isOk(deleteResponse) && unwrapOk(deleteResponse).status === 204) {
+      logger.info(`Deleted multipart upload session file for ${uploadId}`);
+    } else {
+      logger.warn(`Failed to delete session file for upload ${uploadId}`);
+    }
🧹 Nitpick comments (1)
src/backends/worker.ts (1)

157-158: Good change to exclude client errors from retries, but consider handling rate limits.

Excluding 4xx errors from retries is correct as they typically indicate client-side issues. However, some client errors like 429 (Too Many Requests) might benefit from retries with appropriate backoff.

Consider implementing more sophisticated error categorization:

-// FIXME: filter out which errors can be ignored and which should be retried
-while (!isOk(res) || unwrapOk(res).status >= 500) {
+// Determine if error is retryable
+const shouldRetry = (result: Result<Response, Error>): boolean => {
+  if (!isOk(result)) return true; // Network/connection errors
+  
+  const status = unwrapOk(result).status;
+  // Retry server errors and rate limits
+  return status >= 500 || status === 429 || status === 503;
+};
+
+while (shouldRetry(res)) {

Would you like me to create an issue to track implementing a comprehensive retry strategy that handles rate limiting, circuit breaking, and jitter?

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0e8942e and bb318b5.

📒 Files selected for processing (6)
  • .github/workflows/tests.yml (1 hunks)
  • src/backends/mod.ts (1 hunks)
  • src/backends/swift/objects.ts (6 hunks)
  • src/backends/worker.ts (1 hunks)
  • src/config/types.ts (1 hunks)
  • src/main.ts (0 hunks)
💤 Files with no reviewable changes (1)
  • src/main.ts
🧰 Additional context used
🧠 Learnings (4)
.github/workflows/tests.yml (2)
Learnt from: destifo
PR: expnt/herald#41
File: .github/workflows/tests.yml:118-118
Timestamp: 2025-06-16T14:41:56.939Z
Learning: In GitHub Actions CI with start-server-and-test, deno serve command works fine without explicit --host=0.0.0.0 binding when the test URL targets http://0.0.0.0:8000/. The CI environment allows the test runner to reach the server even with default deno serve binding behavior.
Learnt from: destifo
PR: expnt/herald#41
File: .github/workflows/tests.yml:118-118
Timestamp: 2025-06-16T14:41:42.937Z
Learning: In GitHub Actions CI environment, `deno serve` without explicit `--host=0.0.0.0` flag works fine for `start-server-and-test` targeting `http://0.0.0.0:8000/` in the Herald project.
src/backends/worker.ts (3)
Learnt from: destifo
PR: expnt/herald#11
File: src/workers/mod.ts:0-0
Timestamp: 2025-01-24T12:45:29.893Z
Learning: In Deno applications, web worker initialization can fail due to module loading issues (missing files, syntax errors, broken imports), resource constraints (memory limits, process limits), or permission issues (Deno security restrictions, file system access). Consider adding retry mechanisms in production environments.
Learnt from: destifo
PR: expnt/herald#10
File: src/backends/s3/buckets.ts:0-0
Timestamp: 2025-01-28T17:36:22.149Z
Learning: The `routeQueryParamedRequest` and `headBucket` functions in `src/backends/s3/buckets.ts` already implement retry logic for replica failover when the primary request fails, supporting both S3 and Swift replicas.
Learnt from: destifo
PR: expnt/herald#5
File: src/backends/tasks.ts:0-0
Timestamp: 2025-01-08T06:46:12.523Z
Learning: In the Herald project, when managing worker availability for task processing, workers are removed from the workers map while processing a task and added back when the task is completed or fails. This prevents other tasks from using a busy worker.
src/backends/mod.ts (4)
Learnt from: destifo
PR: expnt/herald#3
File: src/main.ts:68-71
Timestamp: 2025-01-14T23:52:40.127Z
Learning: In the herald project, token verification errors in the auth module are handled by throwing HTTPException, which is then caught by the global error handler in main.ts. Additional try-catch blocks around the verification calls are not needed.
Learnt from: destifo
PR: expnt/herald#41
File: src/backends/mirror.ts:166-174
Timestamp: 2025-06-16T14:42:52.174Z
Learning: In src/backends/mirror.ts, when creating replica requests that reuse originalRequest.headers, the authentication headers (Authorization, x-amz-date, etc.) are automatically flushed/replaced down the request pipeline by functions like s3.putObject, so there's no signature mismatch issue with forwarding the original headers.
Learnt from: destifo
PR: expnt/herald#10
File: src/backends/mirror.ts:170-172
Timestamp: 2025-01-28T17:41:43.238Z
Learning: In the mirror operations (src/backends/mirror.ts), bucket existence is guaranteed by design as mirror tasks are only created for buckets that exist in the bucket store, which is initialized during startup. Therefore, non-null assertions when finding buckets in the store are safe.
Learnt from: destifo
PR: expnt/herald#10
File: src/backends/s3/buckets.ts:0-0
Timestamp: 2025-01-28T17:36:22.149Z
Learning: The `routeQueryParamedRequest` and `headBucket` functions in `src/backends/s3/buckets.ts` already implement retry logic for replica failover when the primary request fails, supporting both S3 and Swift replicas.
src/backends/swift/objects.ts (4)
Learnt from: destifo
PR: expnt/herald#49
File: src/backends/task_store.ts:170-228
Timestamp: 2025-06-26T12:48:25.255Z
Learning: In src/backends/task_store.ts, the TaskStore class creates Request objects with localhost URLs that are not actually used for HTTP requests. Instead, these Request objects serve as structured data containers that get passed to backend storage implementations (s3PutObject/swiftPutObject) which extract the necessary information (bucket, key, body, headers) and make the actual requests to the configured storage backends.
Learnt from: destifo
PR: expnt/herald#10
File: src/backends/s3/buckets.ts:0-0
Timestamp: 2025-01-28T17:36:22.149Z
Learning: The `routeQueryParamedRequest` and `headBucket` functions in `src/backends/s3/buckets.ts` already implement retry logic for replica failover when the primary request fails, supporting both S3 and Swift replicas.
Learnt from: destifo
PR: expnt/herald#41
File: src/backends/swift/mod.ts:318-323
Timestamp: 2025-06-16T14:40:12.564Z
Learning: In conversion functions that return `Result<Response, Error>`, the Result represents whether the conversion process succeeded, not the HTTP semantics. A function like `convertSwiftUploadPartToS3Response` should return `createOk(errorResponse)` when it successfully converts to a proper S3 error response, and `createErr(conversionError)` only when the conversion process itself fails.
Learnt from: destifo
PR: expnt/herald#35
File: src/backends/swift/mod.ts:0-0
Timestamp: 2025-06-11T20:21:26.693Z
Learning: For the Swift backend's PUT Object path (`convertSwiftPutObjectToS3Response`), Swift can return only 404 (container missing), 411 (length required), and 422 (checksum mismatch). It will not surface a 408 timeout here, so mapping all handled error cases to HTTP 400 in the S3 response is correct.
🧬 Code Graph Analysis (1)
src/backends/mod.ts (3)
src/utils/signer.ts (1)
  • verifyV4Signature (335-402)
src/config/mod.ts (1)
  • globalConfig (23-23)
src/auth/mod.ts (1)
  • hasBucketAccess (225-238)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: test-full
🔇 Additional comments (2)
.github/workflows/tests.yml (1)

100-100: LGTM!

The AUTH_TYPE environment variable update correctly aligns with the authentication type changes in src/config/types.ts.

src/backends/swift/objects.ts (1)

590-619: Good refactoring to per-session multipart upload files.

The change from a centralized index to individual session files improves scalability and reduces contention. Each upload session now has its own metadata file at ${MULTIPART_UPLOADS_PATH}/${uploadId}.json.

Comment thread src/config/types.ts
Comment thread src/backends/mod.ts
Comment thread src/backends/mod.ts
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (4)
src/backends/swift/objects.ts (4)

590-643: Consider extracting XML error response generation to reduce duplication.

The XML error response is constructed twice with identical content. Consider extracting this to a helper function for better maintainability.

+function createInternalServerErrorXmlResponse(): Response {
+  const xmlError = `<?xml version="1.0" encoding="UTF-8"?>\n<Error>\n  <Code>InternalError</Code>\n  <Message>Failed to save multipart upload metadata</Message>\n  <RequestId>dummy-request-id</RequestId>\n  <HostId>dummy-host-id</HostId>\n</Error>`;
+  return new Response(xmlError, {
+    status: 500,
+    headers: {
+      "Content-Type": "application/xml",
+      "x-amz-request-id": "dummy-request-id",
+      "x-amz-id-2": "dummy-host-id",
+    },
+  });
+}

 if (!isOk(putResponse)) {
   const errRes = unwrapErr(putResponse);
   const errMessage = `Failed to save multipart upload session: ${errRes.message}`;
   logger.warn(errMessage);
   reportToSentry(errMessage);
-  const xmlError = `<?xml version="1.0" encoding="UTF-8"?>\n<Error>\n  <Code>InternalError</Code>\n  <Message>Failed to save multipart upload metadata</Message>\n  <RequestId>dummy-request-id</RequestId>\n  <HostId>dummy-host-id</HostId>\n</Error>`;
-  return createOk(
-    new Response(xmlError, {
-      status: 500,
-      headers: {
-        "Content-Type": "application/xml",
-        "x-amz-request-id": "dummy-request-id",
-        "x-amz-id-2": "dummy-host-id",
-      },
-    }),
-  );
+  return createOk(createInternalServerErrorXmlResponse());
 }

1205-1214: Improve type safety for the parts array.

The parts property uses Array<unknown> which provides minimal type safety. Based on the part metadata structure used in uploadPart, this can be more specific.

+interface PartMetadata {
+  partNumber: string;
+  eTag: string;
+  size: number;
+  lastModified: string;
+}

 interface MultipartUploadSession {
   uploadId: string;
   bucket: string;
   objectKey: string;
   initiated: string;
   initiator: { ID: string; DisplayName: string };
   owner: { ID: string; DisplayName: string };
   storageClass: string;
-  parts?: Array<unknown>;
+  parts?: PartMetadata[];
 }

1267-1270: Apply optional chaining for cleaner code.

The static analysis tool correctly identified an opportunity to use optional chaining.

 uploadJsonFiles = (listData || [])
   .filter((item: { name: string }) =>
-    item.name && item.name.startsWith(multipartIndexPrefix) &&
-    item.name.endsWith(".json")
+    item.name?.startsWith(multipartIndexPrefix) &&
+    item.name?.endsWith(".json")
   )
   .map((item: { name: string }) => item.name);

590-1434: Consider the architectural trade-offs of this multipart upload redesign.

This refactor introduces significant architectural changes with important trade-offs:

Benefits:

  • Better isolation of upload sessions
  • No single point of contention for the index file
  • Easier cleanup of individual upload sessions

Concerns:

  • Performance: listMultipartUploads now requires N+1 HTTP requests vs. 1
  • Race Conditions: Concurrent uploadPart calls for the same upload may conflict when updating session files
  • Complexity: Increased code complexity and error handling surface area

Recommendations:

  1. Consider implementing optimistic locking or compare-and-swap semantics for session file updates
  2. Evaluate adding a caching layer for frequently accessed session metadata
  3. Monitor performance impact in production, especially for high-concurrency scenarios
  4. Consider batching session updates or using atomic operations where possible
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bb318b5 and fb13977.

📒 Files selected for processing (1)
  • src/backends/swift/objects.ts (12 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: destifo
PR: expnt/herald#10
File: src/backends/s3/buckets.ts:0-0
Timestamp: 2025-01-28T17:36:22.149Z
Learning: The `routeQueryParamedRequest` and `headBucket` functions in `src/backends/s3/buckets.ts` already implement retry logic for replica failover when the primary request fails, supporting both S3 and Swift replicas.
src/backends/swift/objects.ts (4)
Learnt from: destifo
PR: expnt/herald#10
File: src/backends/s3/buckets.ts:0-0
Timestamp: 2025-01-28T17:36:22.149Z
Learning: The `routeQueryParamedRequest` and `headBucket` functions in `src/backends/s3/buckets.ts` already implement retry logic for replica failover when the primary request fails, supporting both S3 and Swift replicas.
Learnt from: destifo
PR: expnt/herald#41
File: src/backends/swift/mod.ts:318-323
Timestamp: 2025-06-16T14:40:12.564Z
Learning: In conversion functions that return `Result<Response, Error>`, the Result represents whether the conversion process succeeded, not the HTTP semantics. A function like `convertSwiftUploadPartToS3Response` should return `createOk(errorResponse)` when it successfully converts to a proper S3 error response, and `createErr(conversionError)` only when the conversion process itself fails.
Learnt from: destifo
PR: expnt/herald#35
File: src/backends/swift/mod.ts:0-0
Timestamp: 2025-06-11T20:21:26.693Z
Learning: For the Swift backend's PUT Object path (`convertSwiftPutObjectToS3Response`), Swift can return only 404 (container missing), 411 (length required), and 422 (checksum mismatch). It will not surface a 408 timeout here, so mapping all handled error cases to HTTP 400 in the S3 response is correct.
Learnt from: destifo
PR: expnt/herald#49
File: src/backends/task_store.ts:170-228
Timestamp: 2025-06-26T12:48:25.255Z
Learning: In src/backends/task_store.ts, the TaskStore class creates Request objects with localhost URLs that are not actually used for HTTP requests. Instead, these Request objects serve as structured data containers that get passed to backend storage implementations (s3PutObject/swiftPutObject) which extract the necessary information (bucket, key, body, headers) and make the actual requests to the configured storage backends.
🪛 Biome (1.9.4)
src/backends/swift/objects.ts

[error] 1268-1268: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: test-full
🔇 Additional comments (1)
src/backends/swift/objects.ts (1)

876-880: Good addition of upload ID validation.

The validation ensures uploadId is present, improving robustness and providing clear error messages for malformed requests.

Comment thread src/backends/swift/objects.ts
Comment thread src/backends/swift/objects.ts
@destifo destifo requested a review from zifeo July 4, 2025 07:23
@destifo destifo merged commit 403a87d into main Jul 4, 2025
5 checks passed
@destifo destifo deleted the refactor/mpu-redesign branch July 4, 2025 08:06
@coderabbitai coderabbitai Bot mentioned this pull request Jul 29, 2025
3 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants