Skip to content

Conversation

WcaleNieWolny
Copy link
Contributor

@WcaleNieWolny WcaleNieWolny commented Oct 10, 2025

Summary by CodeRabbit

  • Bug Fixes

    • Fewer errors during app version updates by safely handling missing or empty manifest entries and avoiding failed cleanups.
    • More reliable file-existence checks and deletion attempts using presigned requests to prevent incorrect or failed removals.
  • Refactor

    • Coordinated background cleanup for manifest removals with improved error handling and guaranteed resource closure.
  • Chores

    • Increased reliability and consistency of the version update cleanup process.

Copy link
Contributor

coderabbitai bot commented Oct 10, 2025

Walkthrough

Replaced Supabase manifest fetch with a Drizzle/Postgres query in the version-update trigger; added try/catch/finally to wrap retrieval and cleanup (ensuring client closure); prepare per-entry DB deletes and optional S3 deletes and run them via backgroundTask; S3 existence check now uses presigned HEAD with content-length guard.

Changes

Cohort / File(s) Summary
Trigger: version update cleanup
supabase/functions/_backend/triggers/on_version_update.ts
Replaced direct Supabase fetch with a Drizzle/Postgres query to load manifest entries; added try/catch with logging and a finally block to close the Drizzle/Postgres client; prepare per-entry deletions (DB row delete and optional S3 object delete) and execute them via backgroundTask; guard against empty/non-existent manifest entries.
S3 utility: existence check & delete
supabase/functions/_backend/utils/s3.ts
deleteObject now obtains a presigned DELETE URL and issues an HTTP DELETE, returning true for 2xx; checkIfExist now obtains a presigned HEAD URL and issues an HTTP HEAD, returning true only for HTTP 200 with content-length > 0 and false on error or other responses.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant T as on_version_update Trigger
  participant DC as Drizzle Client
  participant DB as Postgres (manifest)
  participant BG as backgroundTask
  participant S3 as Object Storage
  participant L as Logger

  T->>DC: init()
  T->>DB: SELECT manifest WHERE app_version_id=...
  DB-->>T: manifestEntries[]

  alt entries found
    loop per entry
      T->>DB: DELETE manifest row
      opt entry has S3 key
        T->>BG: prepare/enqueue S3 delete task (key)
      end
    end
    T->>BG: run queued deletions async
    BG->>S3: DELETE via presigned DELETE URL
    S3-->>BG: response (2xx / other)
  else no entries
    Note over T: skip deletions
  end

  T-->>T: try/catch errors
  T->>L: log error (if any)
  T->>DC: close() (finally)
Loading
sequenceDiagram
  autonumber
  participant Util as s3.checkIfExist
  participant Presigner as Presign Service
  participant HTTP as HTTP (HEAD)
  participant S3 as Object Storage

  Util->>Presigner: getPresignedHEAD(key)
  Presigner-->>Util: presigned HEAD URL
  Util->>HTTP: HEAD presigned URL
  HTTP-->>Util: 200 + headers or error
  alt 200 with content-length > 0
    Note right of Util: return true
  else any other response / error
    Note right of Util: return false
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

I nibble through queries, whiskers tuned to logs,
Drizzle pours rows while background hops,
HEAD peeks at crumbs, DELETE knocks soft and brisk,
Try, catch, finally — I tidy up the drops. 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description Check ⚠️ Warning No description was provided, so the PR fails to include the required sections from the project template such as a summary, test plan, screenshots, and checklist, making it unclear what problem is addressed or how to verify the changes. Please complete the PR description using the repository’s template by adding a concise summary of the changes, detailed steps for manual or automated testing, any relevant screenshots or logs, and a filled checklist for style, documentation, and testing coverage.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Title Check ✅ Passed The title succinctly and accurately summarizes the two primary fixes in this PR—migrating to drizzle to bypass row limits on manifest deletion and correcting the R2 file existence check—using a conventional “fix:” prefix and clear references to the affected functionality.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch r2_fixes

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@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: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 180620a and 4ebb112.

📒 Files selected for processing (1)
  • supabase/functions/_backend/triggers/on_version_update.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
{src,supabase/functions/_backend}/**/*.{ts,tsx,js,jsx,vue}

📄 CodeRabbit inference engine (CLAUDE.md)

Enforce code style per @antfu/eslint-config: single quotes and no semicolons

Files:

  • supabase/functions/_backend/triggers/on_version_update.ts
supabase/functions/_backend/triggers/**/*.{ts,tsx,js,sql}

📄 CodeRabbit inference engine (CLAUDE.md)

Place database triggers and CRON-related functions under supabase/functions/_backend/triggers/

Files:

  • supabase/functions/_backend/triggers/on_version_update.ts
supabase/functions/_backend/**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (CLAUDE.md)

Write backend code as Deno-compatible Supabase Edge Functions

Files:

  • supabase/functions/_backend/triggers/on_version_update.ts
🧬 Code graph analysis (1)
supabase/functions/_backend/triggers/on_version_update.ts (5)
supabase/functions/_backend/utils/pg.ts (3)
  • getPgClient (55-59)
  • getDrizzleClient (61-63)
  • closeClient (65-68)
supabase/functions/_backend/utils/postgress_schema.ts (1)
  • manifest (68-77)
supabase/functions/_backend/utils/supabase.ts (1)
  • supabaseAdmin (74-83)
supabase/functions/_backend/utils/loggin.ts (1)
  • cloudlog (3-15)
supabase/functions/_backend/utils/utils.ts (1)
  • backgroundTask (97-110)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Run tests

Comment on lines +139 to +154
const count = v.count ?? 0
if (!count) {
return supabaseAdmin(c)
.from('manifest')
.delete()
.eq('id', entry.id)
}
cloudlog({ requestId: c.get('requestId'), message: 'deleted manifest file from S3', s3_path: entry.s3_path })
return Promise.all([
s3.deleteObject(c, entry.s3_path),
supabaseAdmin(c)
.from('manifest')
.delete()
.eq('id', entry.id),
]) as any
}))
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix inverted S3 deletion guard

count represents other manifest rows referencing the same asset. The current condition skips deleting the S3 object when count is zero (no other references) and does delete it when count is positive, which will wipe shared artifacts and strand orphans. Please flip the guard so S3 deletion only happens when no other rows point at the file.

-              const count = v.count ?? 0
-              if (!count) {
-                return supabaseAdmin(c)
-                  .from('manifest')
-                  .delete()
-                  .eq('id', entry.id)
-              }
-              cloudlog({ requestId: c.get('requestId'), message: 'deleted manifest file from S3', s3_path: entry.s3_path })
-              return Promise.all([
-                s3.deleteObject(c, entry.s3_path),
-                supabaseAdmin(c)
-                  .from('manifest')
-                  .delete()
-                  .eq('id', entry.id),
-              ]) as any
+              const count = v.count ?? 0
+              if (count && count > 0) {
+                return supabaseAdmin(c)
+                  .from('manifest')
+                  .delete()
+                  .eq('id', entry.id)
+              }
+              cloudlog({ requestId: c.get('requestId'), message: 'deleted manifest file from S3', s3_path: entry.s3_path })
+              return Promise.all([
+                s3.deleteObject(c, entry.s3_path),
+                supabaseAdmin(c)
+                  .from('manifest')
+                  .delete()
+                  .eq('id', entry.id),
+              ]) as any
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const count = v.count ?? 0
if (!count) {
return supabaseAdmin(c)
.from('manifest')
.delete()
.eq('id', entry.id)
}
cloudlog({ requestId: c.get('requestId'), message: 'deleted manifest file from S3', s3_path: entry.s3_path })
return Promise.all([
s3.deleteObject(c, entry.s3_path),
supabaseAdmin(c)
.from('manifest')
.delete()
.eq('id', entry.id),
]) as any
}))
const count = v.count ?? 0
if (count && count > 0) {
return supabaseAdmin(c)
.from('manifest')
.delete()
.eq('id', entry.id)
}
cloudlog({ requestId: c.get('requestId'), message: 'deleted manifest file from S3', s3_path: entry.s3_path })
return Promise.all([
s3.deleteObject(c, entry.s3_path),
supabaseAdmin(c)
.from('manifest')
.delete()
.eq('id', entry.id),
]) as any
🤖 Prompt for AI Agents
In supabase/functions/_backend/triggers/on_version_update.ts around lines 139 to
154 the S3-deletion guard is inverted: currently it skips S3 deletion when count
=== 0 and deletes S3 when count > 0; flip the logic so that when count === 0 (no
other manifest rows reference the asset) you delete the S3 object and the
manifest row, and when count > 0 you only delete the manifest row (no S3
deletion). Move or add the cloudlog to the branch where S3 is actually deleted,
and ensure the function returns the appropriate Promise (Promise.all for
S3+manifest deletion, single supabaseAdmin delete for manifest-only) with
correct typing.

@WcaleNieWolny WcaleNieWolny changed the title fix: use drizzle to avoid rows soft-limit for manifest delete fix: use drizzle to avoid rows soft-limit for manifest delete && r2 fileExists fixes Oct 11, 2025
Copy link
Contributor

@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: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4ebb112 and dbbc657.

📒 Files selected for processing (1)
  • supabase/functions/_backend/utils/s3.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
{src,supabase/functions/_backend}/**/*.{ts,tsx,js,jsx,vue}

📄 CodeRabbit inference engine (CLAUDE.md)

Enforce code style per @antfu/eslint-config: single quotes and no semicolons

Files:

  • supabase/functions/_backend/utils/s3.ts
supabase/functions/_backend/utils/**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (CLAUDE.md)

Put shared utilities and database schemas under supabase/functions/_backend/utils/

Files:

  • supabase/functions/_backend/utils/s3.ts
supabase/functions/_backend/**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (CLAUDE.md)

Write backend code as Deno-compatible Supabase Edge Functions

Files:

  • supabase/functions/_backend/utils/s3.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Run tests
  • GitHub Check: Analyze (java-kotlin)

WcaleNieWolny and others added 3 commits October 11, 2025 12:37
Copy link
Contributor

@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: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
supabase/functions/_backend/utils/s3.ts (1)

76-92: Critical: Missing S3 client initialization.

The client variable is referenced on line 81 but never initialized. This will cause a ReferenceError: client is not defined at runtime.

Apply this diff to initialize the client:

 async function checkIfExist(c: Context, fileId: string | null) {
   if (!fileId) {
     return false
   }
+  const client = initS3(c)
   try {
     const url = await client.getPresignedUrl('HEAD', fileId)
🧹 Nitpick comments (2)
supabase/functions/_backend/utils/s3.ts (2)

85-85: Consider specifying radix parameter for parseInt.

While Number.parseInt defaults to base 10 for decimal strings, explicitly passing the radix parameter is a best practice that prevents edge cases and improves code clarity.

Apply this diff:

-    const contentLength = Number.parseInt(response.headers.get('content-length') || '0')
+    const contentLength = Number.parseInt(response.headers.get('content-length') || '0', 10)

67-74: Handle errors for deleteObject calls consistently
In on_version_update.ts at line 148 the call to s3.deleteObject(c, entry.s3_path) inside Promise.all isn’t wrapped in try/catch (unlike the v2 call at line 178). Wrap it or handle its rejection to match the later block and avoid unhandled promise failures.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0fd27c9 and f17e84f.

📒 Files selected for processing (1)
  • supabase/functions/_backend/utils/s3.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
{src,supabase/functions/_backend}/**/*.{ts,tsx,js,jsx,vue}

📄 CodeRabbit inference engine (CLAUDE.md)

Enforce code style per @antfu/eslint-config: single quotes and no semicolons

Files:

  • supabase/functions/_backend/utils/s3.ts
supabase/functions/_backend/utils/**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (CLAUDE.md)

Put shared utilities and database schemas under supabase/functions/_backend/utils/

Files:

  • supabase/functions/_backend/utils/s3.ts
supabase/functions/_backend/**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (CLAUDE.md)

Write backend code as Deno-compatible Supabase Edge Functions

Files:

  • supabase/functions/_backend/utils/s3.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Run tests

Copy link
Contributor

@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: 0

🧹 Nitpick comments (3)
supabase/functions/_backend/utils/s3.ts (3)

67-74: Add error handling and timeout for reliability.

The function lacks error handling, so fetch failures will throw and propagate to the caller. This is inconsistent with checkIfExist (which catches all errors and returns false). Additionally, the fetch call has no timeout, which could lead to hanging requests.

Consider adding structured error handling and a timeout:

 async function deleteObject(c: Context, fileId: string) {
-  const client = initS3(c)
-  const url = await client.getPresignedUrl('DELETE', fileId)
-  const response = await fetch(url, {
-    method: 'DELETE',
-  })
-  return response.status >= 200 && response.status < 300
+  try {
+    const client = initS3(c)
+    const url = await client.getPresignedUrl('DELETE', fileId)
+    const response = await fetch(url, {
+      method: 'DELETE',
+      signal: AbortSignal.timeout(10000), // 10 second timeout
+    })
+    return response.status >= 200 && response.status < 300
+  }
+  catch (error) {
+    cloudlog({ requestId: c.get('requestId'), message: 'deleteObject failed', fileId, error })
+    return false
+  }
}

86-87: Add radix parameter to Number.parseInt for clarity.

For clarity and to avoid potential edge cases with number parsing, specify the radix explicitly:

-    const contentLength = Number.parseInt(response.headers.get('content-length') || '0')
+    const contentLength = Number.parseInt(response.headers.get('content-length') || '0', 10)

80-88: Add timeout to prevent hanging requests.

The fetch call lacks a timeout, which could cause the request to hang indefinitely. Consider adding a timeout for better reliability:

     const url = await client.getPresignedUrl('HEAD', fileId)
     const response = await fetch(url, {
       method: 'HEAD',
+      signal: AbortSignal.timeout(10000), // 10 second timeout
     })
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f17e84f and 8d8738c.

📒 Files selected for processing (1)
  • supabase/functions/_backend/utils/s3.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
{src,supabase/functions/_backend}/**/*.{ts,tsx,js,jsx,vue}

📄 CodeRabbit inference engine (CLAUDE.md)

Enforce code style per @antfu/eslint-config: single quotes and no semicolons

Files:

  • supabase/functions/_backend/utils/s3.ts
supabase/functions/_backend/utils/**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (CLAUDE.md)

Put shared utilities and database schemas under supabase/functions/_backend/utils/

Files:

  • supabase/functions/_backend/utils/s3.ts
supabase/functions/_backend/**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (CLAUDE.md)

Write backend code as Deno-compatible Supabase Edge Functions

Files:

  • supabase/functions/_backend/utils/s3.ts
🧬 Code graph analysis (1)
supabase/functions/_backend/utils/s3.ts (1)
scripts/check_r2.ts (1)
  • initS3 (255-279)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Run tests
  • GitHub Check: Analyze (java-kotlin)

Copy link

@riderx riderx merged commit 32fc045 into main Oct 16, 2025
12 checks passed
@riderx riderx deleted the r2_fixes branch October 16, 2025 03:49
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