Skip to content

Conversation

@ItzNotABug
Copy link
Member

@ItzNotABug ItzNotABug commented Jan 2, 2026

What does this PR do?

Handles retries smartly!

Test Plan

Manual.

Related PRs and Issues

N/A.

Have you read the Contributing Guidelines on issues?

Yes.

Summary by CodeRabbit

  • New Features

    • Single-backup delete option and app-wide batched deletion for faster multi-item removal.
    • New analytics tracking for database backup deletions.
  • Chores

    • Refactored deletion handlers to use batched operations with improved error handling and clearer success reporting for analytics.

✏️ Tip: You can customize this high-level summary in your review settings.

@ItzNotABug ItzNotABug self-assigned this Jan 2, 2026
@appwrite
Copy link

appwrite bot commented Jan 2, 2026

Console (appwrite/console)

Project ID: 688b7bf400350cbd60e9

Sites (1)
Site Status Logs Preview QR
 console-stage
688b7cf6003b1842c9dc
Ready Ready View Logs Preview URL QR Code

Tip

Realtime gives you live updates for users, storage, functions, and databases

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 2, 2026

Walkthrough

The PR adds a new Submit enum member DatabaseBackupDelete = 'submit_database_backup_delete' and refactors deletion flows to a batched pattern. MultiSelectionTable introduces DeleteOperation and a richer DeleteOperationState; its onDelete callback now receives a batchDelete function plus selected rows. Many route/component handlers were updated to accept a DeleteOperation and call batchDelete with a per-item delete callback, then inspect the returned result (error and deleted IDs) instead of constructing and awaiting Promise.all. A single-file change replaces a prior batch path with a single-item delete for database backups while also adding a batch-delete variant.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Pre-merge checks

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Fix: race conditions for deletions' directly summarizes the main objective of the PR, which is to address race conditions in deletion operations across the codebase.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

Caution

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

⚠️ Outside diff range comments (1)
src/routes/(console)/project-[region]-[project]/messaging/topics/topic-[topic]/table.svelte (1)

40-52: Store mutation inside the batch callback could cause false failures.

If subscribers[subscriberId] is undefined (e.g., due to stale state), accessing target on line 49 will throw, causing batchDelete to mark this item as failed even though the API deletion succeeded. Consider moving the store cleanup outside the callback or adding a guard.

🔎 Proposed fix
     async function handleDelete(batchDelete: DeleteOperation): Promise<DeleteOperationState> {
         const result = await batchDelete(async (subscriberId) => {
             await sdk
                 .forProject(page.params.region, page.params.project)
                 .messaging.deleteSubscriber({
                     topicId: page.params.topic,
                     subscriberId
                 });
-
-            const { target } = subscribers[subscriberId];
-            const { [target.$id]: _, ...rest } = $targetsById;
-            $targetsById = rest;
         });

+        // Clean up targetsById for successfully deleted subscribers
+        for (const subscriberId of result.deleted) {
+            const subscriber = subscribers[subscriberId];
+            if (subscriber?.target?.$id) {
+                const { [subscriber.target.$id]: _, ...rest } = $targetsById;
+                $targetsById = rest;
+            }
+        }
+
         try {
🧹 Nitpick comments (7)
src/routes/(console)/project-[region]-[project]/sites/site-[site]/logs/table.svelte (1)

34-53: Consider simplifying error handling.

The try block wraps only analytics calls that check result.error conditionally. Unless trackError or trackEvent can throw exceptions, this try-catch is unnecessary. You can simplify to:

 async function deleteLogs(batchDelete: DeleteOperation): Promise<DeleteOperationState> {
     const result = await batchDelete((logId) =>
         sdk.forProject(page.params.region, page.params.project).sites.deleteLog({
             siteId: page.params.site,
             logId
         })
     );
 
-    try {
-        if (result.error) {
-            trackError(result.error, Submit.LogDelete);
-        } else {
-            trackEvent(Submit.LogDelete, { total: result.deleted.length });
-        }
-    } finally {
-        await invalidate(Dependencies.EXECUTIONS);
-    }
+    if (result.error) {
+        trackError(result.error, Submit.LogDelete);
+    } else {
+        trackEvent(Submit.LogDelete, { total: result.deleted.length });
+    }
+    await invalidate(Dependencies.EXECUTIONS);
 
     return result;
 }

That said, the current implementation is correct and ensures cleanup via finally.

src/routes/(console)/project-[region]-[project]/auth/user-[user]/targets/table.svelte (1)

27-45: Consider simplifying error handling.

Same observation as in other files: the try block is unnecessary if trackError and trackEvent don't throw. Consider:

 async function handleDelete(batchDelete: DeleteOperation): Promise<DeleteOperationState> {
     const result = await batchDelete((id) =>
         sdk
             .forProject(page.params.region, page.params.project)
             .users.deleteTarget({ userId: page.params.user, targetId: id })
     );
 
-    try {
-        if (result.error) {
-            trackError(result.error, Submit.UserTargetDelete);
-        } else {
-            trackEvent(Submit.UserTargetDelete, { total: result.deleted.length });
-        }
-    } finally {
-        await invalidate(Dependencies.USER_TARGETS);
-    }
+    if (result.error) {
+        trackError(result.error, Submit.UserTargetDelete);
+    } else {
+        trackEvent(Submit.UserTargetDelete, { total: result.deleted.length });
+    }
+    await invalidate(Dependencies.USER_TARGETS);
 
     return result;
 }
src/routes/(console)/project-[region]-[project]/storage/bucket-[bucket]/+page.svelte (1)

69-88: Consider simplifying error handling.

The try-finally block can be simplified since only conditional checks are performed:

 async function handleBulkDelete(batchDelete: DeleteOperation): Promise<DeleteOperationState> {
     const result = await batchDelete((fileId) =>
         sdk.forProject(page.params.region, page.params.project).storage.deleteFile({
             bucketId: page.params.bucket,
             fileId
         })
     );
 
-    try {
-        if (result.error) {
-            trackError(result.error, Submit.FileDelete);
-        } else {
-            trackEvent(Submit.FileDelete, { total: result.deleted.length });
-        }
-    } finally {
-        await invalidate(Dependencies.FILES);
-    }
+    if (result.error) {
+        trackError(result.error, Submit.FileDelete);
+    } else {
+        trackEvent(result.FileDelete, { total: result.deleted.length });
+    }
+    await invalidate(Dependencies.FILES);
 
     return result;
 }
src/routes/(console)/project-[region]-[project]/auth/user-[user]/memberships/+page.svelte (1)

27-52: Consider simplifying error handling.

The try-finally wrapping analytics can be simplified:

-    try {
-        if (result.error) {
-            trackError(result.error, Submit.MembershipUpdate);
-        } else {
-            trackEvent(Submit.MembershipUpdate, { total: result.deleted.length });
-        }
-    } finally {
-        await invalidate(Dependencies.MEMBERSHIPS);
-    }
+    if (result.error) {
+        trackError(result.error, Submit.MembershipUpdate);
+    } else {
+        trackEvent(Submit.MembershipUpdate, { total: result.deleted.length });
+    }
+    await invalidate(Dependencies.MEMBERSHIPS);
src/routes/(console)/project-[region]-[project]/auth/user-[user]/identities/table.svelte (1)

29-47: Consider simplifying error handling.

The try-finally block can be simplified:

 async function handleDelete(batchDelete: DeleteOperation): Promise<DeleteOperationState> {
     const result = await batchDelete((id) =>
         sdk
             .forProject(page.params.region, page.params.project)
             .users.deleteIdentity({ identityId: id })
     );
 
-    try {
-        if (result.error) {
-            trackError(result.error, Submit.UserIdentityDelete);
-        } else {
-            trackEvent(Submit.UserIdentityDelete, { total: result.deleted.length });
-        }
-    } finally {
-        await invalidate(Dependencies.USER_IDENTITIES);
-    }
+    if (result.error) {
+        trackError(result.error, Submit.UserIdentityDelete);
+    } else {
+        trackEvent(Submit.UserIdentityDelete, { total: result.deleted.length });
+    }
+    await invalidate(Dependencies.USER_IDENTITIES);
 
     return result;
 }
src/routes/(console)/project-[region]-[project]/sites/site-[site]/deployments/table.svelte (1)

45-67: Consider simplifying the try/finally block.

The try/finally wrapping analytics calls (lines 53-63) seems unnecessary since trackError and trackEvent are synchronous fire-and-forget calls that shouldn't throw. The invalidation could be called directly after the conditional tracking.

That said, the batch deletion logic itself is correct and properly handles error tracking via result.error and success tracking via result.deleted.length.

🔎 Proposed simplification
     const result = await batchDelete((deploymentId) =>
         sdk.forProject(page.params.region, page.params.project).sites.deleteDeployment({
             siteId: page.params.site,
             deploymentId
         })
     );

-    try {
-        if (result.error) {
-            trackError(result.error, Submit.DeploymentDelete);
-        } else {
-            trackEvent(Submit.DeploymentDelete, { total: result.deleted.length });
-        }
-    } finally {
-        await Promise.all([
-            invalidate(Dependencies.DEPLOYMENTS),
-            invalidate(Dependencies.SITE)
-        ]);
+    if (result.error) {
+        trackError(result.error, Submit.DeploymentDelete);
+    } else {
+        trackEvent(Submit.DeploymentDelete, { total: result.deleted.length });
     }
+
+    await Promise.all([
+        invalidate(Dependencies.DEPLOYMENTS),
+        invalidate(Dependencies.SITE)
+    ]);

     return result;
src/routes/(console)/project-[region]-[project]/databases/database-[database]/backups/table.svelte (1)

141-149: Consider adding the total count to the success event for consistency.

Other batch delete handlers in this PR (e.g., executions, platforms, messaging) include { total: result.deleted.length } in the success tracking event. This one omits it.

🔎 Proposed fix
             if (result.error) {
                 trackError(result.error, Submit.DatabaseBackupDelete);
             } else {
-                trackEvent(Submit.DatabaseBackupDelete);
+                trackEvent(Submit.DatabaseBackupDelete, { total: result.deleted.length });
             }
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 936666d and f6b5615.

📒 Files selected for processing (21)
  • src/lib/actions/analytics.ts
  • src/lib/components/multiSelectTable.svelte
  • src/routes/(console)/project-[region]-[project]/auth/+page.svelte
  • src/routes/(console)/project-[region]-[project]/auth/teams/+page.svelte
  • src/routes/(console)/project-[region]-[project]/auth/teams/team-[team]/members/+page.svelte
  • src/routes/(console)/project-[region]-[project]/auth/user-[user]/identities/table.svelte
  • src/routes/(console)/project-[region]-[project]/auth/user-[user]/memberships/+page.svelte
  • src/routes/(console)/project-[region]-[project]/auth/user-[user]/targets/table.svelte
  • src/routes/(console)/project-[region]-[project]/databases/database-[database]/backups/table.svelte
  • src/routes/(console)/project-[region]-[project]/databases/database-[database]/table.svelte
  • src/routes/(console)/project-[region]-[project]/functions/function-[function]/executions/table.svelte
  • src/routes/(console)/project-[region]-[project]/functions/function-[function]/table.svelte
  • src/routes/(console)/project-[region]-[project]/messaging/+page.svelte
  • src/routes/(console)/project-[region]-[project]/messaging/providers/table.svelte
  • src/routes/(console)/project-[region]-[project]/messaging/topics/table.svelte
  • src/routes/(console)/project-[region]-[project]/messaging/topics/topic-[topic]/table.svelte
  • src/routes/(console)/project-[region]-[project]/overview/(components)/table.svelte
  • src/routes/(console)/project-[region]-[project]/overview/platforms/+page.svelte
  • src/routes/(console)/project-[region]-[project]/sites/site-[site]/deployments/table.svelte
  • src/routes/(console)/project-[region]-[project]/sites/site-[site]/logs/table.svelte
  • src/routes/(console)/project-[region]-[project]/storage/bucket-[bucket]/+page.svelte
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{ts,tsx,js,jsx,svelte}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx,svelte}: Import reusable modules from the src/lib directory using the $lib alias
Use minimal comments in code; reserve comments for TODOs or complex logic explanations
Use $lib, $routes, and $themes aliases instead of relative paths for module imports

Files:

  • src/routes/(console)/project-[region]-[project]/messaging/topics/table.svelte
  • src/routes/(console)/project-[region]-[project]/auth/+page.svelte
  • src/routes/(console)/project-[region]-[project]/storage/bucket-[bucket]/+page.svelte
  • src/lib/actions/analytics.ts
  • src/routes/(console)/project-[region]-[project]/functions/function-[function]/executions/table.svelte
  • src/routes/(console)/project-[region]-[project]/databases/database-[database]/table.svelte
  • src/routes/(console)/project-[region]-[project]/auth/user-[user]/targets/table.svelte
  • src/routes/(console)/project-[region]-[project]/sites/site-[site]/deployments/table.svelte
  • src/routes/(console)/project-[region]-[project]/messaging/+page.svelte
  • src/routes/(console)/project-[region]-[project]/messaging/providers/table.svelte
  • src/lib/components/multiSelectTable.svelte
  • src/routes/(console)/project-[region]-[project]/sites/site-[site]/logs/table.svelte
  • src/routes/(console)/project-[region]-[project]/auth/user-[user]/identities/table.svelte
  • src/routes/(console)/project-[region]-[project]/messaging/topics/topic-[topic]/table.svelte
  • src/routes/(console)/project-[region]-[project]/overview/platforms/+page.svelte
  • src/routes/(console)/project-[region]-[project]/auth/teams/team-[team]/members/+page.svelte
  • src/routes/(console)/project-[region]-[project]/auth/user-[user]/memberships/+page.svelte
  • src/routes/(console)/project-[region]-[project]/auth/teams/+page.svelte
  • src/routes/(console)/project-[region]-[project]/overview/(components)/table.svelte
  • src/routes/(console)/project-[region]-[project]/functions/function-[function]/table.svelte
  • src/routes/(console)/project-[region]-[project]/databases/database-[database]/backups/table.svelte
src/routes/**/*.svelte

📄 CodeRabbit inference engine (AGENTS.md)

Use SvelteKit file conventions: +page.svelte for components, +page.ts for data loaders, +layout.svelte for wrappers, +error.svelte for error handling, and dynamic route params in square brackets like [param]

Files:

  • src/routes/(console)/project-[region]-[project]/messaging/topics/table.svelte
  • src/routes/(console)/project-[region]-[project]/auth/+page.svelte
  • src/routes/(console)/project-[region]-[project]/storage/bucket-[bucket]/+page.svelte
  • src/routes/(console)/project-[region]-[project]/functions/function-[function]/executions/table.svelte
  • src/routes/(console)/project-[region]-[project]/databases/database-[database]/table.svelte
  • src/routes/(console)/project-[region]-[project]/auth/user-[user]/targets/table.svelte
  • src/routes/(console)/project-[region]-[project]/sites/site-[site]/deployments/table.svelte
  • src/routes/(console)/project-[region]-[project]/messaging/+page.svelte
  • src/routes/(console)/project-[region]-[project]/messaging/providers/table.svelte
  • src/routes/(console)/project-[region]-[project]/sites/site-[site]/logs/table.svelte
  • src/routes/(console)/project-[region]-[project]/auth/user-[user]/identities/table.svelte
  • src/routes/(console)/project-[region]-[project]/messaging/topics/topic-[topic]/table.svelte
  • src/routes/(console)/project-[region]-[project]/overview/platforms/+page.svelte
  • src/routes/(console)/project-[region]-[project]/auth/teams/team-[team]/members/+page.svelte
  • src/routes/(console)/project-[region]-[project]/auth/user-[user]/memberships/+page.svelte
  • src/routes/(console)/project-[region]-[project]/auth/teams/+page.svelte
  • src/routes/(console)/project-[region]-[project]/overview/(components)/table.svelte
  • src/routes/(console)/project-[region]-[project]/functions/function-[function]/table.svelte
  • src/routes/(console)/project-[region]-[project]/databases/database-[database]/backups/table.svelte
**/*.{ts,tsx,js,jsx,svelte,json}

📄 CodeRabbit inference engine (AGENTS.md)

Use 4 spaces for indentation, single quotes, 100 character line width, and no trailing commas per Prettier configuration

Files:

  • src/routes/(console)/project-[region]-[project]/messaging/topics/table.svelte
  • src/routes/(console)/project-[region]-[project]/auth/+page.svelte
  • src/routes/(console)/project-[region]-[project]/storage/bucket-[bucket]/+page.svelte
  • src/lib/actions/analytics.ts
  • src/routes/(console)/project-[region]-[project]/functions/function-[function]/executions/table.svelte
  • src/routes/(console)/project-[region]-[project]/databases/database-[database]/table.svelte
  • src/routes/(console)/project-[region]-[project]/auth/user-[user]/targets/table.svelte
  • src/routes/(console)/project-[region]-[project]/sites/site-[site]/deployments/table.svelte
  • src/routes/(console)/project-[region]-[project]/messaging/+page.svelte
  • src/routes/(console)/project-[region]-[project]/messaging/providers/table.svelte
  • src/lib/components/multiSelectTable.svelte
  • src/routes/(console)/project-[region]-[project]/sites/site-[site]/logs/table.svelte
  • src/routes/(console)/project-[region]-[project]/auth/user-[user]/identities/table.svelte
  • src/routes/(console)/project-[region]-[project]/messaging/topics/topic-[topic]/table.svelte
  • src/routes/(console)/project-[region]-[project]/overview/platforms/+page.svelte
  • src/routes/(console)/project-[region]-[project]/auth/teams/team-[team]/members/+page.svelte
  • src/routes/(console)/project-[region]-[project]/auth/user-[user]/memberships/+page.svelte
  • src/routes/(console)/project-[region]-[project]/auth/teams/+page.svelte
  • src/routes/(console)/project-[region]-[project]/overview/(components)/table.svelte
  • src/routes/(console)/project-[region]-[project]/functions/function-[function]/table.svelte
  • src/routes/(console)/project-[region]-[project]/databases/database-[database]/backups/table.svelte
**/*.svelte

📄 CodeRabbit inference engine (AGENTS.md)

Use Svelte 5 + SvelteKit 2 syntax with TypeScript for component development

Files:

  • src/routes/(console)/project-[region]-[project]/messaging/topics/table.svelte
  • src/routes/(console)/project-[region]-[project]/auth/+page.svelte
  • src/routes/(console)/project-[region]-[project]/storage/bucket-[bucket]/+page.svelte
  • src/routes/(console)/project-[region]-[project]/functions/function-[function]/executions/table.svelte
  • src/routes/(console)/project-[region]-[project]/databases/database-[database]/table.svelte
  • src/routes/(console)/project-[region]-[project]/auth/user-[user]/targets/table.svelte
  • src/routes/(console)/project-[region]-[project]/sites/site-[site]/deployments/table.svelte
  • src/routes/(console)/project-[region]-[project]/messaging/+page.svelte
  • src/routes/(console)/project-[region]-[project]/messaging/providers/table.svelte
  • src/lib/components/multiSelectTable.svelte
  • src/routes/(console)/project-[region]-[project]/sites/site-[site]/logs/table.svelte
  • src/routes/(console)/project-[region]-[project]/auth/user-[user]/identities/table.svelte
  • src/routes/(console)/project-[region]-[project]/messaging/topics/topic-[topic]/table.svelte
  • src/routes/(console)/project-[region]-[project]/overview/platforms/+page.svelte
  • src/routes/(console)/project-[region]-[project]/auth/teams/team-[team]/members/+page.svelte
  • src/routes/(console)/project-[region]-[project]/auth/user-[user]/memberships/+page.svelte
  • src/routes/(console)/project-[region]-[project]/auth/teams/+page.svelte
  • src/routes/(console)/project-[region]-[project]/overview/(components)/table.svelte
  • src/routes/(console)/project-[region]-[project]/functions/function-[function]/table.svelte
  • src/routes/(console)/project-[region]-[project]/databases/database-[database]/backups/table.svelte
src/routes/**

📄 CodeRabbit inference engine (AGENTS.md)

Configure dynamic routes using SvelteKit convention with [param] syntax in route directory names

Files:

  • src/routes/(console)/project-[region]-[project]/messaging/topics/table.svelte
  • src/routes/(console)/project-[region]-[project]/auth/+page.svelte
  • src/routes/(console)/project-[region]-[project]/storage/bucket-[bucket]/+page.svelte
  • src/routes/(console)/project-[region]-[project]/functions/function-[function]/executions/table.svelte
  • src/routes/(console)/project-[region]-[project]/databases/database-[database]/table.svelte
  • src/routes/(console)/project-[region]-[project]/auth/user-[user]/targets/table.svelte
  • src/routes/(console)/project-[region]-[project]/sites/site-[site]/deployments/table.svelte
  • src/routes/(console)/project-[region]-[project]/messaging/+page.svelte
  • src/routes/(console)/project-[region]-[project]/messaging/providers/table.svelte
  • src/routes/(console)/project-[region]-[project]/sites/site-[site]/logs/table.svelte
  • src/routes/(console)/project-[region]-[project]/auth/user-[user]/identities/table.svelte
  • src/routes/(console)/project-[region]-[project]/messaging/topics/topic-[topic]/table.svelte
  • src/routes/(console)/project-[region]-[project]/overview/platforms/+page.svelte
  • src/routes/(console)/project-[region]-[project]/auth/teams/team-[team]/members/+page.svelte
  • src/routes/(console)/project-[region]-[project]/auth/user-[user]/memberships/+page.svelte
  • src/routes/(console)/project-[region]-[project]/auth/teams/+page.svelte
  • src/routes/(console)/project-[region]-[project]/overview/(components)/table.svelte
  • src/routes/(console)/project-[region]-[project]/functions/function-[function]/table.svelte
  • src/routes/(console)/project-[region]-[project]/databases/database-[database]/backups/table.svelte
**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

**/*.ts: Define types inline or in .d.ts files, avoid creating separate .types.ts files
Use TypeScript in non-strict mode; any type is tolerated in this project

Files:

  • src/lib/actions/analytics.ts
src/lib/components/**/*.svelte

📄 CodeRabbit inference engine (AGENTS.md)

Use PascalCase for component file names and place them in src/lib/components/[feature]/ directory structure

Files:

  • src/lib/components/multiSelectTable.svelte
🧠 Learnings (7)
📚 Learning: 2025-11-25T03:15:27.539Z
Learnt from: CR
Repo: appwrite/console PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T03:15:27.539Z
Learning: Applies to src/routes/**/*.svelte : Use SvelteKit file conventions: +page.svelte for components, +page.ts for data loaders, +layout.svelte for wrappers, +error.svelte for error handling, and dynamic route params in square brackets like [param]

Applied to files:

  • src/routes/(console)/project-[region]-[project]/auth/+page.svelte
  • src/routes/(console)/project-[region]-[project]/sites/site-[site]/logs/table.svelte
  • src/routes/(console)/project-[region]-[project]/auth/teams/+page.svelte
📚 Learning: 2025-10-07T14:16:31.893Z
Learnt from: ItzNotABug
Repo: appwrite/console PR: 2413
File: src/routes/(console)/project-[region]-[project]/databases/database-[database]/(entity)/helpers/analytics.ts:17-18
Timestamp: 2025-10-07T14:16:31.893Z
Learning: In the console codebase analytics helpers at `src/routes/(console)/project-[region]-[project]/databases/database-[database]/(entity)/helpers/analytics.ts`, the dynamically constructed enum lookups (like `Submit[enumKey as keyof typeof Submit]`) are designed to return `undefined` safely when a terminology entry doesn't exist, because the corresponding views won't be rendered in those scenarios (e.g., DocumentsDB columns/attributes have no view), so the analytics code paths won't be reached.

Applied to files:

  • src/lib/actions/analytics.ts
📚 Learning: 2025-10-13T05:13:54.542Z
Learnt from: ItzNotABug
Repo: appwrite/console PR: 2413
File: src/routes/(console)/project-[region]-[project]/databases/table.svelte:33-39
Timestamp: 2025-10-13T05:13:54.542Z
Learning: In Svelte 5, `import { page } from '$app/state'` provides a reactive state proxy that can be accessed directly (e.g., `page.params`), unlike the older `import { page } from '$app/stores'` which returns a readable store requiring the `$page` syntax for auto-subscription in components.

Applied to files:

  • src/routes/(console)/project-[region]-[project]/databases/database-[database]/table.svelte
  • src/routes/(console)/project-[region]-[project]/messaging/+page.svelte
  • src/routes/(console)/project-[region]-[project]/messaging/topics/topic-[topic]/table.svelte
  • src/routes/(console)/project-[region]-[project]/overview/platforms/+page.svelte
📚 Learning: 2025-10-05T09:41:40.439Z
Learnt from: ItzNotABug
Repo: appwrite/console PR: 2398
File: src/routes/(console)/verify-email/+page.svelte:48-51
Timestamp: 2025-10-05T09:41:40.439Z
Learning: In SvelteKit 5, `page` imported from `$app/state` is a reactive state object (using runes), not a store. It should be accessed as `page.data` without the `$` prefix, unlike the store-based `$page` from `$app/stores` in earlier versions.

Applied to files:

  • src/routes/(console)/project-[region]-[project]/messaging/+page.svelte
  • src/routes/(console)/project-[region]-[project]/overview/platforms/+page.svelte
📚 Learning: 2025-10-13T05:16:07.656Z
Learnt from: ItzNotABug
Repo: appwrite/console PR: 2413
File: src/routes/(console)/project-[region]-[project]/databases/database-[database]/header.svelte:54-58
Timestamp: 2025-10-13T05:16:07.656Z
Learning: In SvelteKit apps, shared layout components (like headers) that use `$derived(page.data.*)` should use optional chaining when accessing properties that may not be present on all routes. During page transitions, reactive statements can briefly evaluate with different page.data structures, so optional chaining prevents runtime errors when navigating between routes with different data shapes (e.g., between `/databases` and `/databases/database-[database]`).

Applied to files:

  • src/routes/(console)/project-[region]-[project]/messaging/topics/topic-[topic]/table.svelte
📚 Learning: 2025-11-19T11:22:42.553Z
Learnt from: atharvadeosthale
Repo: appwrite/console PR: 2512
File: src/routes/(console)/project-[region]-[project]/overview/platforms/llmBanner.svelte:51-83
Timestamp: 2025-11-19T11:22:42.553Z
Learning: In src/routes/(console)/project-[region]-[project]/overview/platforms/llmBanner.svelte, the Cursor integration URL format `https://cursor.com/link/prompt` with the `text` query parameter is correct and functional.

Applied to files:

  • src/routes/(console)/project-[region]-[project]/overview/platforms/+page.svelte
📚 Learning: 2025-10-26T10:20:29.792Z
Learnt from: ItzNotABug
Repo: appwrite/console PR: 2509
File: src/routes/(console)/project-[region]-[project]/auth/teams/+page.svelte:47-61
Timestamp: 2025-10-26T10:20:29.792Z
Learning: When deleting teams in the Appwrite Console codebase, only `Dependencies.TEAMS` needs to be invalidated. Additional invalidations for `Dependencies.TEAM` (detail route) and `Dependencies.MEMBERSHIPS` are not necessary because the deleted teams and their memberships no longer exist.

Applied to files:

  • src/routes/(console)/project-[region]-[project]/auth/teams/team-[team]/members/+page.svelte
  • src/routes/(console)/project-[region]-[project]/auth/teams/+page.svelte
⏰ 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: build
  • GitHub Check: e2e
🔇 Additional comments (22)
src/routes/(console)/project-[region]-[project]/sites/site-[site]/deployments/table.svelte (1)

2-7: LGTM!

Import changes correctly bring in the DeleteOperation type along with the existing exports, following the $lib alias convention.

src/routes/(console)/project-[region]-[project]/databases/database-[database]/table.svelte (2)

6-11: LGTM!

Import changes correctly add the DeleteOperation type while maintaining the existing component and type imports.


28-48: Batch deletion pattern correctly implemented.

The refactored onDelete function properly uses the batchDelete callback pattern. Error and success tracking are handled appropriately based on the batch result. The finally block ensures Dependencies.TABLES is invalidated and subNavigation is updated regardless of the outcome.

src/lib/components/multiSelectTable.svelte (4)

2-10: LGTM!

The new type definitions are well-structured:

  • DeleteOperationState with optional error and deleted array covers both success and partial failure scenarios
  • DeleteOperation signature properly accepts a deletion callback and optional batch size

84-125: Well-designed batch deletion implementation.

The batchDelete function correctly:

  • Uses Promise.allSettled to handle partial failures gracefully
  • Tracks successfully deleted items separately from errors
  • Captures only the first error (reasonable for user-facing messaging)
  • Supports optional batching for large deletion sets

One minor observation: the batching condition at line 112 (batchSize && batchSize < ids.length) means batching only kicks in when there are more items than the batch size. This is correct behavior.


127-146: Solid state management in consumeDeleteOperation.

The function properly:

  • Invokes the consumer's onDelete with the batch helper
  • Filters out successfully deleted items from selectedRows (line 137)
  • Handles partial failures by keeping undeleted items selected
  • Returns a boolean to indicate complete success vs partial/failure

199-205: Modal state management is correct.

The modal correctly:

  • Waits for the delete operation to complete
  • Only closes on full success (allDeleted)
  • Re-enables the modal buttons after the operation finishes

However, there's a potential UX issue: if partial deletion occurs, disableModal is set to false but the modal stays open with an error. This seems intentional to let users retry or cancel.

src/routes/(console)/project-[region]-[project]/overview/(components)/table.svelte (1)

70-73: Signature adaptation is correct, but consider leveraging batchDelete.

The handler correctly adapts to the new two-parameter signature by ignoring the batchDelete function. Since this component uses a custom DeleteBatch modal (line 138) and sets confirmDeletion={false}, the built-in batch deletion is bypassed.

This works, but note that the custom DeleteBatch component won't benefit from the race condition fixes unless it also uses similar batching logic internally.

src/lib/actions/analytics.ts (1)

280-280: LGTM!

The new DatabaseBackupDelete analytics event follows the established naming convention and is properly placed in the Submit enum.

src/routes/(console)/project-[region]-[project]/auth/teams/+page.svelte (2)

14-16: LGTM!

Import correctly adds DeleteOperation type alongside existing component imports.


48-64: Batch deletion correctly implemented.

The handleDelete function properly uses the batch pattern and correctly invalidates only Dependencies.TEAMS after deletion. Based on learnings, additional invalidations for Dependencies.TEAM or Dependencies.MEMBERSHIPS are unnecessary since deleted teams and their memberships no longer exist.

src/routes/(console)/project-[region]-[project]/auth/+page.svelte (2)

9-19: LGTM!

Import changes correctly add DeleteOperation type while maintaining alphabetical ordering of the component imports.


64-80: Batch deletion pattern correctly applied.

The handleDelete function follows the same pattern as other files in this PR:

  • Uses batchDelete callback for deletions
  • Tracks errors via trackError or success via trackEvent
  • Invalidates Dependencies.USERS in the finally block
  • Returns the batch result for the component to handle

The implementation is consistent and correct.

src/routes/(console)/project-[region]-[project]/messaging/topics/table.svelte (1)

28-46: LGTM!

The batch deletion implementation is consistent with the pattern used across the codebase. Error handling and dependency invalidation are correctly implemented.

src/routes/(console)/project-[region]-[project]/messaging/providers/table.svelte (1)

28-46: LGTM!

The batch deletion implementation follows the established pattern. Error handling via result.error and success tracking via result.deleted.length are correctly implemented.

src/routes/(console)/project-[region]-[project]/messaging/+page.svelte (1)

103-121: LGTM!

The batch-based deletion is correctly implemented with appropriate error handling and dependency invalidation.

src/routes/(console)/project-[region]-[project]/functions/function-[function]/table.svelte (1)

60-79: LGTM!

The batch deletion pattern is consistently applied. The implementation correctly handles errors and tracks analytics based on the batch operation result.

src/routes/(console)/project-[region]-[project]/auth/teams/team-[team]/members/+page.svelte (1)

33-52: Missing retry logic contradicts PR objective.

The batchDelete implementation in MultiSelectionTable handles concurrent deletions well using Promise.allSettled(), allowing all deletions to execute in parallel with partial success support. However, the code has no retry mechanism, which contradicts the PR objective stating "handling retries smartly" to fix race conditions.

Currently, each deletion is attempted only once. If a deletion fails due to transient errors or race conditions (e.g., concurrent access issues), there is no automatic retry. The batchSize parameter handles batching for large volumes, but not recovery from failures.

Implement retry logic (with exponential backoff or similar strategy) in the batchDelete function to align with the stated PR objective, especially for handling potential race conditions in team membership deletion scenarios.

⛔ Skipped due to learnings
Learnt from: ItzNotABug
Repo: appwrite/console PR: 2509
File: src/routes/(console)/project-[region]-[project]/auth/teams/+page.svelte:47-61
Timestamp: 2025-10-26T10:20:29.792Z
Learning: When deleting teams in the Appwrite Console codebase, only `Dependencies.TEAMS` needs to be invalidated. Additional invalidations for `Dependencies.TEAM` (detail route) and `Dependencies.MEMBERSHIPS` are not necessary because the deleted teams and their memberships no longer exist.
src/routes/(console)/project-[region]-[project]/functions/function-[function]/executions/table.svelte (1)

38-57: LGTM!

The refactored deleteExecutions function correctly implements the batch-based deletion pattern. The error handling with result.error and success tracking with result.deleted.length is consistent with the PR's approach across other components.

src/routes/(console)/project-[region]-[project]/databases/database-[database]/backups/table.svelte (1)

114-134: LGTM!

The deleteSingleBackup function properly handles the single-item deletion flow triggered from the action menu, with appropriate notifications and state cleanup.

src/routes/(console)/project-[region]-[project]/overview/platforms/+page.svelte (1)

135-156: LGTM!

The handlePlatformDelete function correctly implements the batch-based deletion pattern with proper error tracking, success analytics, and dependency invalidation.

src/routes/(console)/project-[region]-[project]/messaging/topics/topic-[topic]/table.svelte (1)

54-64: LGTM!

The error tracking and success analytics follow the established pattern, and dependency invalidation is correctly placed in the finally block.

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

Caution

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

⚠️ Outside diff range comments (1)
src/lib/components/multiSelectTable.svelte (1)

199-210: Wrap consumeDeleteOperation in try-finally to prevent stuck UI state.

If consumeDeleteOperation() throws an unexpected error (e.g., implementation bug in onDelete), line 209 never executes, leaving disableModal = true permanently. The modal becomes unresponsive and the user must reload the page.

🔎 Proposed fix: Use try-finally to ensure state cleanup
             onSubmit={async () => {
                 disableModal = true;
                 onDeleteError = null;
-
-                const allDeleted = await consumeDeleteOperation();
-
-                if (allDeleted) {
-                    showConfirmDeletion = false;
+
+                try {
+                    const allDeleted = await consumeDeleteOperation();
+
+                    if (allDeleted) {
+                        showConfirmDeletion = false;
+                    }
+                } catch (err) {
+                    onDeleteError = err instanceof Error ? err.message : String(err);
+                } finally {
+                    disableModal = false;
                 }
-
-                disableModal = false;
             }}>
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f6b5615 and 7b74d42.

📒 Files selected for processing (1)
  • src/lib/components/multiSelectTable.svelte
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,js,jsx,svelte}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx,svelte}: Import reusable modules from the src/lib directory using the $lib alias
Use minimal comments in code; reserve comments for TODOs or complex logic explanations
Use $lib, $routes, and $themes aliases instead of relative paths for module imports

Files:

  • src/lib/components/multiSelectTable.svelte
src/lib/components/**/*.svelte

📄 CodeRabbit inference engine (AGENTS.md)

Use PascalCase for component file names and place them in src/lib/components/[feature]/ directory structure

Files:

  • src/lib/components/multiSelectTable.svelte
**/*.{ts,tsx,js,jsx,svelte,json}

📄 CodeRabbit inference engine (AGENTS.md)

Use 4 spaces for indentation, single quotes, 100 character line width, and no trailing commas per Prettier configuration

Files:

  • src/lib/components/multiSelectTable.svelte
**/*.svelte

📄 CodeRabbit inference engine (AGENTS.md)

Use Svelte 5 + SvelteKit 2 syntax with TypeScript for component development

Files:

  • src/lib/components/multiSelectTable.svelte
🧠 Learnings (1)
📚 Learning: 2025-11-25T03:15:27.539Z
Learnt from: CR
Repo: appwrite/console PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T03:15:27.539Z
Learning: Applies to **/*.svelte : Use Svelte 5 + SvelteKit 2 syntax with TypeScript for component development

Applied to files:

  • src/lib/components/multiSelectTable.svelte
⏰ 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: build
  • GitHub Check: e2e
🔇 Additional comments (2)
src/lib/components/multiSelectTable.svelte (2)

2-10: LGTM: Well-designed types for partial deletion tracking.

The type definitions clearly support partial success scenarios, which aligns with the PR's goal of handling race conditions in deletions. The DeleteOperation type provides a clean API for batch operations.


131-150: LGTM: Partial success handling is correct.

The function properly tracks deleted items and handles partial failures, allowing users to retry only the failed deletions. The filtering logic at line 141 ensures selectedRows accurately reflects items that still need deletion.

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

🧹 Nitpick comments (2)
src/lib/components/multiSelectTable.svelte (2)

96-99: Consider throwing an error for invalid batchSize instead of silent correction.

The validation prevents the infinite loop flagged in previous reviews by ensuring batchSize >= 1. However, silently correcting invalid inputs (negative, zero, or decimal values) may hide bugs in calling code. Consider throwing a descriptive error instead:

if (batchSize !== undefined && (batchSize <= 0 || !Number.isInteger(batchSize))) {
    throw new Error('batchSize must be a positive integer');
}

This fails fast and makes debugging easier.


148-150: Error message could be more informative.

The error message at line 149 provides the first error's message but doesn't indicate how many items failed or which specific items. Consider enhancing the message:

onDeleteError = `Failed to delete ${selectedRows.length - deletedCount} of ${selectedRows.length} ${getPluralResource()}. Error: ${state.error.message}`;

This gives users a clearer picture of the failure scope.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7b74d42 and f8a04a2.

📒 Files selected for processing (1)
  • src/lib/components/multiSelectTable.svelte
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,js,jsx,svelte}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx,svelte}: Import reusable modules from the src/lib directory using the $lib alias
Use minimal comments in code; reserve comments for TODOs or complex logic explanations
Use $lib, $routes, and $themes aliases instead of relative paths for module imports

Files:

  • src/lib/components/multiSelectTable.svelte
src/lib/components/**/*.svelte

📄 CodeRabbit inference engine (AGENTS.md)

Use PascalCase for component file names and place them in src/lib/components/[feature]/ directory structure

Files:

  • src/lib/components/multiSelectTable.svelte
**/*.{ts,tsx,js,jsx,svelte,json}

📄 CodeRabbit inference engine (AGENTS.md)

Use 4 spaces for indentation, single quotes, 100 character line width, and no trailing commas per Prettier configuration

Files:

  • src/lib/components/multiSelectTable.svelte
**/*.svelte

📄 CodeRabbit inference engine (AGENTS.md)

Use Svelte 5 + SvelteKit 2 syntax with TypeScript for component development

Files:

  • src/lib/components/multiSelectTable.svelte
⏰ 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: e2e
  • GitHub Check: build
🔇 Additional comments (7)
src/lib/components/multiSelectTable.svelte (7)

2-10: LGTM! Well-structured types for batch deletion.

The new DeleteOperationState and DeleteOperation types clearly express the batch deletion contract. The union with void allows for no-op scenarios, and the use of Exclude ensures the return type is always defined.


51-58: Breaking API change enables batch deletion control.

The updated onDelete signature provides both a batchDelete helper and raw selectedRows, giving consumers flexibility to use the batched helper or implement custom logic. The inline comment clearly explains the use case for accessing selectedRows directly.


67-77: LGTM! Parameter change supports accurate notifications for partial deletions.

Accepting count as a parameter allows the notification to reflect the actual number of items deleted, which is crucial when some deletions may fail in a batch.


121-128: Batch processing logic is correct.

The conditional batching at line 121 correctly handles both batched and non-batched scenarios. The loop at line 122 safely increments by the validated batchSize, and the slice operation at line 123 correctly extracts each batch.


136-155: Orchestration logic is well-structured.

The consumeDeleteOperation function correctly:

  • Wraps batchDelete with the current selectedRows context
  • Updates selectedRows by filtering deleted IDs (line 146)
  • Handles the void case (no-op)
  • Returns a boolean to control modal visibility

The curry pattern at lines 137-139 elegantly closes over selectedRows while giving the callback control over deleteFn and batchSize.


185-191: LGTM! Delete button correctly uses the new flow.

The button properly routes through consumeDeleteOperation when confirmDeletion is false, ensuring consistent batch deletion behavior regardless of the confirmation setting.


204-215: Confirmation flow correctly handles partial failures.

The modal submission logic properly:

  • Disables the modal during processing (line 205)
  • Calls consumeDeleteOperation and captures the result (line 208)
  • Keeps the modal open if deletions fail (lines 210-212), allowing users to see the error
  • Re-enables the modal after processing (line 214)

This provides good UX for handling partial deletion failures.

@ItzNotABug ItzNotABug merged commit 11a42cf into main Jan 2, 2026
4 checks passed
@ItzNotABug ItzNotABug deleted the fix-dat-860 branch January 2, 2026 12:39
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