Skip to content

OUT-3667: retry transient Dropbox 5xx and wrap getDropboxFileMetadata#98

Merged
SandipBajracharya merged 1 commit into
mainfrom
OUT-3667
Apr 30, 2026
Merged

OUT-3667: retry transient Dropbox 5xx and wrap getDropboxFileMetadata#98
SandipBajracharya merged 1 commit into
mainfrom
OUT-3667

Conversation

@SandipBajracharya
Copy link
Copy Markdown
Collaborator

@SandipBajracharya SandipBajracharya commented Apr 29, 2026

Changes

  • Broaden withRetry retryable status codes to include 502, 503, 504 alongside the existing 429 and 500. Centralized into a RETRYABLE_STATUS_CODES set so shouldRetry and onFailedAttempt share one source of truth.
  • Wrap DropboxWebhook.getDropboxFileMetadata's filesGetMetadata call in withRetry (3s/12s backoff, matching DropboxClient's wrapped methods).

Sentry DROPBOX-INTEGRATION-G — recurring DropboxResponseError: Response failed with a 504 code from the webhook hot path. The 504 is a transient Dropbox upstream blip; before this PR it surfaced because the call wasn't wrapped, and even if it were, our retry policy ignored gateway-level codes.

Testing Criteria

  • pnpm typecheck passes
  • pnpm lint passes
  • 409 (moved-root) recovery path in handleDbxRootPathMove still works — 409 is not in the retryable set, so pRetry throws on first attempt and the existing catch recovers via dbxRootId lookup. Worth a manual confirmation in staging by simulating a renamed root folder.
  • Verify in production that DROPBOX-INTEGRATION-G stops regressing.

Notes

  • Worst-case retry latency on a hard outage: ~21s (3+6+12s backoff, 3 retries). Webhook task budget is 1 hour, so well within bounds.
  • Sentry retry-noise suppression (the existing scope event processor that drops "An error occurred during retry") is unchanged — only terminal failures will surface.

Impact & Surface Area of Change

  • withRetry is shared by DropboxClient.{downloadFile, uploadFile, getAllFilesFolders} and now getDropboxFileMetadata. All four will now retry on 502/503/504. Verify no regression on the bidirectional sync path under partial Dropbox availability.
  • No DB / schema / API contract changes.

Follow-up (separate ticket)

  • Move getDropboxFileMetadata into DropboxClient so retry wiring lives next to the other wrapped SDK calls. A // Refactor below code... marker is in place at the call site.

…adata

Sentry DROPBOX-INTEGRATION-G surfaces a transient Dropbox 504 from
DropboxWebhook.getDropboxFileMetadata because the call wasn't wrapped in
retry, and withRetry's retryable set excluded 502/503/504. Broaden the
retryable codes and route filesGetMetadata through withRetry.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@linear-code
Copy link
Copy Markdown

linear-code Bot commented Apr 29, 2026

@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 29, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
dropbox-integration Ready Ready Preview, Comment Apr 29, 2026 11:55am

Request Review

@SandipBajracharya SandipBajracharya changed the title fix(OUT-3667): retry transient Dropbox 5xx and wrap getDropboxFileMetadata OUT-3667: retry transient Dropbox 5xx and wrap getDropboxFileMetadata Apr 29, 2026
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Apr 29, 2026

Greptile Summary

This PR broadens withRetry's retryable status codes from {429, 500} to {429, 500, 502, 503, 504} using a shared RETRYABLE_STATUS_CODES set, and wraps DropboxWebhook.getDropboxFileMetadata in withRetry to address recurring 504 errors in the webhook hot path (Sentry DROPBOX-INTEGRATION-G).

The refactoring of shouldRetry/onFailedAttempt to use the shared set is semantically equivalent to the old code, and the 409 recovery path in handleDbxRootPathMove is unaffected since 409 is deliberately excluded from the retryable set.

Confidence Score: 5/5

This PR is safe to merge — changes are well-scoped, semantically correct, and the 409 recovery path is unaffected.

No logic bugs found. The RETRYABLE_STATUS_CODES refactoring is semantically equivalent to the old per-status comparisons. The withRetry wrapping on getDropboxFileMetadata correctly propagates non-retryable errors (409) so the existing catch-based recovery still works. Backoff parameters match the existing DropboxClient pattern.

No files require special attention.

Important Files Changed

Filename Overview
src/lib/withRetry.ts Adds RETRYABLE_STATUS_CODES set with 429/500/502/503/504 and refactors shouldRetry/onFailedAttempt to use it — semantically equivalent to old logic, cleaner and DRY.
src/features/webhook/dropbox/lib/webhook.service.ts Wraps filesGetMetadata in withRetry (3s/12s backoff) to handle transient 5xx on the webhook path; 409 recovery path is unaffected as pRetry aborts on first 409 and the existing catch handler recovers.

Sequence Diagram

sequenceDiagram
    participant WS as DropboxWebhook
    participant WR as withRetry
    participant DBX as Dropbox API

    WS->>WR: getDropboxFileMetadata(filePath)
    loop up to 3 retries
        WR->>DBX: filesGetMetadata({ path })
        alt 5xx (500/502/503/504)
            DBX-->>WR: DropboxResponseError (retryable)
            WR->>WR: shouldRetry → true
            WR->>WR: exponential backoff (3s→6s→12s)
        else 429 + Retry-After header
            DBX-->>WR: DropboxResponseError (rate limited)
            WR->>WR: sleep(retryAfter * 1000)
            WR->>WR: shouldRetry → true
        else 409 (path moved / conflict)
            DBX-->>WR: DropboxResponseError 409
            WR->>WR: shouldRetry → false
            WR-->>WS: throw DropboxResponseError(409)
            WS->>WS: catch → dbxRootId lookup recovery
        else 2xx success
            DBX-->>WR: FileMetadata
            WR-->>WS: FileMetadata
        end
    end
    note over WR: All retries exhausted → throws terminal error
Loading

Reviews (1): Last reviewed commit: "fix(OUT-3667): retry transient Dropbox 5..." | Re-trigger Greptile

Copy link
Copy Markdown
Collaborator

@priosshrsth priosshrsth left a comment

Choose a reason for hiding this comment

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

lgtm

@SandipBajracharya SandipBajracharya merged commit ea09829 into main Apr 30, 2026
5 checks passed
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.

2 participants