Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Move events to job dispatch and use deleteResources utility function everywhere #222

Merged
merged 3 commits into from
Nov 20, 2024

Conversation

adityachoudhari26
Copy link
Contributor

@adityachoudhari26 adityachoudhari26 commented Nov 20, 2024

Summary by CodeRabbit

Release Notes

  • New Features

    • Enhanced resource deletion handling with integrated event processing.
    • Streamlined build processes for Docker images across applications.
  • Bug Fixes

    • Improved action selection logic in the Edit Hook dialog for better user experience.
  • Chores

    • Removed obsolete dependencies related to @ctrlplane/events across multiple packages.
    • Updated import paths for better module resolution and organization.
  • Documentation

    • Minor adjustments to comments and code structure for clarity and maintainability.

Copy link
Contributor

coderabbitai bot commented Nov 20, 2024

Walkthrough

The pull request includes significant modifications across various files, primarily focusing on dependency management, import adjustments, and Dockerfile optimizations. Key changes involve the removal of the @ctrlplane/events dependency from multiple packages, updates to import paths for event handling functions, and enhancements to the build processes in Dockerfiles. Additionally, there are updates to component logic and event handling mechanisms within the codebase, particularly related to resource deletions and environment updates.

Changes

File Change Summary
apps/jobs/Dockerfile Removed COPY command for packages/events/package.json, added RUN pnpm add -g turbo, retained system dependencies, and maintained installation commands.
apps/jobs/package.json Removed dependency on @ctrlplane/events.
apps/jobs/src/expired-env-checker/index.ts Updated import source for getEventsForEnvironmentDeleted and handleEvent from @ctrlplane/events to @ctrlplane/job-dispatch.
apps/pty-proxy/src/controller/sockets.ts Added imports for eq and SCHEMA, modified stale agent resource deletion logic.
apps/webservice/Dockerfile Removed COPY command for packages/events/package.json, organized build stages into base, builder, and runner.
apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/deployments/[deploymentSlug]/hooks/EditHookDialog.tsx Enhanced handling of the action property and refined form submission logic.
packages/api/package.json Removed dependency on @ctrlplane/events.
packages/api/src/router/deployment.ts Updated import for event handling functions from @ctrlplane/events to @ctrlplane/job-dispatch.
packages/api/src/router/environment.ts Reorganized imports and updated logic in delete and update procedures for event handling and resource filters.
packages/api/src/router/resources.ts Introduced deleteResources function, simplifying resource deletion logic.
packages/api/src/router/runbook.ts Simplified the bySystemId query method.
packages/db/src/schema/event.ts Minor formatting change in runhook table definition.
packages/events/eslint.config.js Deleted ESLint configuration file.
packages/events/package.json Deleted package.json for @ctrlplane/events.
packages/events/src/handlers/index.ts Removed export statement for target-removed.js.
packages/events/tsconfig.json Deleted TypeScript configuration file.
packages/job-dispatch/src/events/handlers/index.ts Re-exported all members from resource-removed.js.
packages/job-dispatch/src/events/triggers/index.ts Removed export for target-deleted.js and added export for resource-deleted.js.
packages/job-dispatch/src/index.ts Added export for events/index.js.
packages/job-dispatch/src/resource.ts Updated deleteResources function to accept Resource objects and integrated event handling for deletions.
packages/job-dispatch/src/events/handlers/resource-removed.ts Updated import path for dispatchRunbook function.

Possibly related PRs

Suggested reviewers

  • jsbroks

📜 Recent review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 8ba6dc4 and 5866ca5.

📒 Files selected for processing (1)
  • packages/job-dispatch/src/events/handlers/resource-removed.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • packages/job-dispatch/src/events/handlers/resource-removed.ts

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • 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 generate unit testing code for this file.
    • @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 generate unit testing code.
    • @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.

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

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

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: 4

🧹 Outside diff range and nitpick comments (8)
packages/job-dispatch/tsconfig.json (1)

5-6: Consider reorganizing the TypeScript configuration

Having both baseUrl and rootDir set to "." while including files outside the package directory might lead to unexpected module resolution behavior. Consider:

  1. Moving shared code to a separate package
  2. Adjusting the directory structure to better reflect the module boundaries
apps/jobs/src/expired-env-checker/index.ts (1)

Line range hint 28-31: Consider using deleteResources utility function

According to the PR objectives, we should use the deleteResources utility function everywhere. The current implementation directly deletes from the database.

Consider replacing the direct database deletion with the deleteResources utility function. This would maintain consistency with the PR objectives and ensure all resource cleanup is handled uniformly.

- await db
-   .delete(SCHEMA.environment)
-   .where(inArray(SCHEMA.environment.id, envIds));
+ await deleteResources(envIds);

Note: You'll need to import the deleteResources utility function.

apps/pty-proxy/src/controller/sockets.ts (1)

Line range hint 15-17: Consider optimizing stale connection checks

The current implementation performs checks every minute for all agents, which could impact performance as the number of agents grows. Each stale agent triggers a database query.

Consider these optimizations:

  1. Increase the check interval (e.g., to 5 minutes) while keeping the 15-minute stale threshold
  2. Batch process stale agents to reduce database operations
  3. Use a more efficient data structure (like a priority queue) to track agent timestamps
packages/api/src/router/runbook.ts (2)

38-47: Great simplification of the query implementation!

The new implementation is more maintainable and leverages the type-safe query builder effectively. The inclusion of all necessary related data through the with clauses ensures complete data retrieval while maintaining good performance characteristics by avoiding N+1 queries.

Consider adding a comment documenting the shape of the returned data structure, as it might be helpful for consumers of this API.

+    // Returns runbooks with their associated runhooks, hooks, job agent, and variables
     .query(({ ctx, input }) =>

Consider using deleteResources utility function for runbook deletion

The runbook deletion currently uses direct database deletion:

ctx.db.delete(SCHEMA.runbook).where(eq(SCHEMA.runbook.id, input))

Based on the codebase analysis:

  • The deleteResources utility from @ctrlplane/job-dispatch is used across the codebase for resource deletion
  • It handles additional responsibilities like event dispatching for deleted resources
  • The runbook router already imports from @ctrlplane/job-dispatch but only uses dispatchRunbook
🔗 Analysis chain

Line range hint 119-124: Consider using deleteResources utility function

Based on the PR objectives to "use deleteResources utility function everywhere", the delete operation might need to be updated to use this utility function instead of direct database deletion.

Let's verify the existence and usage pattern of the deleteResources utility:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for the deleteResources utility function definition and its usage pattern
rg -t typescript "deleteResources" -A 5

Length of output: 74


Script:

#!/bin/bash
# Search for deleteResources without file type restriction
rg "deleteResources" -A 5

# Also search for similar delete utility patterns
rg "delete.*Resources" -A 5

# Search for any delete-related utility functions
rg "export.*delete|function.*delete" -A 5

Length of output: 11251


Script:

#!/bin/bash
# Let's check the implementation of runbook deletion to see if it's using direct DB deletion
rg -A 10 "runbook.*delete" packages/api/src/router/runbook.ts

# Also check if there are any resource-related operations for runbooks
ast-grep --pattern 'from "@ctrlplane/job-dispatch"' packages/api/src/router/runbook.ts

Length of output: 152


Script:

#!/bin/bash
# Let's check the entire content of the runbook.ts file to understand the deletion implementation
cat packages/api/src/router/runbook.ts

# Also check for any imports from job-dispatch in this file
rg -l "@ctrlplane/job-dispatch" packages/api/src/router/runbook.ts

Length of output: 3998

packages/api/src/router/deployment.ts (1)

Line range hint 479-489: Consider enhancing error handling and atomicity

While the event handling logic is correct, consider these improvements for better robustness:

  1. Add error logging for failed events
  2. Wrap the operation in a transaction for atomicity
   .mutation(async ({ ctx, input }) => {
+    return ctx.db.transaction(async (tx) => {
       const dep = await ctx.db
         .select()
         .from(deployment)
         .where(eq(deployment.id, input))
         .then(takeFirst);
       const events = await getEventsForDeploymentDeleted(dep);
-      await Promise.allSettled(events.map(handleEvent));
+      const results = await Promise.allSettled(events.map(handleEvent));
+      results.forEach((result, index) => {
+        if (result.status === 'rejected') {
+          console.error(`Failed to handle event ${index}:`, result.reason);
+        }
+      });
       return ctx.db
         .delete(deployment)
         .where(eq(deployment.id, input))
         .returning()
         .then(takeFirst);
+    });
     }),
packages/job-dispatch/src/resource.ts (1)

347-352: Simplify asynchronous operations for better readability

You can simplify the code by combining the Promise.all and await into a single statement, which enhances readability and aligns with common asynchronous patterns.

Refactored code:

 export const deleteResources = async (tx: Tx, resources: Resource[]) => {
+  try {
     const events = (
       await Promise.all(resources.map(getEventsForResourceDeleted))
     ).flat();
     await Promise.all(events.map(handleEvent));
     const resourceIds = resources.map((r) => r.id);
     await tx.delete(resource).where(inArray(resource.id, resourceIds));
+  } catch (err) {
+    log.error("Error in deleteResources", { error: err });
+    throw err;
+  }
 };
packages/api/src/router/environment.ts (1)

Line range hint 243-249: Handle rejections from Promise.allSettled in event handling

In the delete mutation, Promise.allSettled is used to handle event promises, but the code does not process the results to handle any rejections. This could lead to silent failures if any of the handleEvent calls are rejected. To improve error visibility and robustness, consider iterating over the settled promises and handling any rejected promises appropriately.

Apply this diff to handle rejections:

.then(async (env) => {
  const events = await getEventsForEnvironmentDeleted(env);
  const handleEventPromises = events.map(handleEvent);
- await Promise.allSettled(handleEventPromises);
+ const results = await Promise.allSettled(handleEventPromises);
+ results.forEach((result) => {
+   if (result.status === 'rejected') {
+     console.error('Event handling error in delete mutation:', result.reason);
+   }
+ });
  return env;
}),
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between d2d208b and 8ba6dc4.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (21)
  • apps/jobs/Dockerfile (0 hunks)
  • apps/jobs/package.json (0 hunks)
  • apps/jobs/src/expired-env-checker/index.ts (1 hunks)
  • apps/pty-proxy/src/controller/sockets.ts (2 hunks)
  • apps/webservice/Dockerfile (0 hunks)
  • apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/deployments/[deploymentSlug]/hooks/EditHookDialog.tsx (2 hunks)
  • packages/api/package.json (0 hunks)
  • packages/api/src/router/deployment.ts (1 hunks)
  • packages/api/src/router/environment.ts (1 hunks)
  • packages/api/src/router/resources.ts (2 hunks)
  • packages/api/src/router/runbook.ts (1 hunks)
  • packages/db/src/schema/event.ts (1 hunks)
  • packages/events/eslint.config.js (0 hunks)
  • packages/events/package.json (0 hunks)
  • packages/events/src/handlers/index.ts (0 hunks)
  • packages/events/tsconfig.json (0 hunks)
  • packages/job-dispatch/src/events/handlers/index.ts (1 hunks)
  • packages/job-dispatch/src/events/triggers/index.ts (1 hunks)
  • packages/job-dispatch/src/index.ts (1 hunks)
  • packages/job-dispatch/src/resource.ts (3 hunks)
  • packages/job-dispatch/tsconfig.json (1 hunks)
💤 Files with no reviewable changes (8)
  • apps/jobs/Dockerfile
  • apps/jobs/package.json
  • apps/webservice/Dockerfile
  • packages/api/package.json
  • packages/events/eslint.config.js
  • packages/events/package.json
  • packages/events/src/handlers/index.ts
  • packages/events/tsconfig.json
✅ Files skipped from review due to trivial changes (3)
  • packages/db/src/schema/event.ts
  • packages/job-dispatch/src/events/handlers/index.ts
  • packages/job-dispatch/src/events/triggers/index.ts
🔇 Additional comments (14)
packages/job-dispatch/tsconfig.json (1)

8-8: Verify the necessity of including external utils directory

Including "../api/src/utils" from outside the package directory could lead to tight coupling and potential circular dependencies. Consider moving shared utilities to a common package or copying necessary utilities into this package.

Let's check what utilities are being used:

apps/jobs/src/expired-env-checker/index.ts (1)

4-7: Verify the imported functions exist in @ctrlplane/job-dispatch

The import change aligns with the PR objective to move events to job dispatch.

Let's verify that these functions exist in the new module with the same signature:

✅ Verification successful

Import changes are correctly implemented

The functions getEventsForEnvironmentDeleted and handleEvent are properly exported from @ctrlplane/job-dispatch with matching signatures. Found in:

  • packages/job-dispatch/src/events/triggers/environment-deleted.ts
  • packages/job-dispatch/src/events/index.ts
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the functions exist in @ctrlplane/job-dispatch
# Expected: Find function declarations in the new module

# Search for function declarations
ast-grep --pattern 'export $_ getEventsForEnvironmentDeleted' packages/job-dispatch
ast-grep --pattern 'export $_ handleEvent' packages/job-dispatch

Length of output: 152


Script:

#!/bin/bash
# Let's try a different approach to find these functions
# 1. First, let's check if the directory structure is correct
fd -t d "job-dispatch"

# 2. Then search for these function names in the codebase
rg "getEventsForEnvironmentDeleted" -l
rg "handleEvent" -l

# 3. Let's also check the package.json to verify the package name
fd "package.json" -x grep -l "@ctrlplane/job-dispatch" {}

Length of output: 729


Script:

#!/bin/bash
# Let's check the actual implementation of these functions
rg "getEventsForEnvironmentDeleted" -A 5 packages/job-dispatch/src/events/triggers/environment-deleted.ts
rg "export.*handleEvent" -A 5 packages/job-dispatch/src/events/index.ts

Length of output: 539

packages/job-dispatch/src/index.ts (1)

26-26: LGTM! Export aligns with centralization of event handling.

The addition of this export statement supports the PR's objective of moving event handling to the job-dispatch module.

apps/pty-proxy/src/controller/sockets.ts (1)

3-6: LGTM! Import changes align with PR objectives

The new imports support the transition to using the job dispatch system and database utilities.

apps/webservice/src/app/[workspaceSlug]/(app)/systems/[systemSlug]/deployments/[deploymentSlug]/hooks/EditHookDialog.tsx (2)

79-79: LGTM: Type safety improvement

The explicit type casting of hook.action to HookAction enhances type safety and makes the code more maintainable.


148-148: Verify empty state handling

The button text now always displays the value without a placeholder. Please verify that this handles the initial empty state gracefully.

Consider adding a fallback for empty state:

-                          {value}
+                          {value || "Select action..."}
✅ Verification successful

Empty state is properly handled - no changes needed

The code shows that:

  1. The action field is initialized with the existing hook's action in defaultValues
  2. The component is part of an edit dialog where a hook already exists
  3. The UI uses a Command pattern with proper placeholder text "Search action..." for the input field
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for any default/initial state handling for the action field
ast-grep --pattern 'action: $value' # Search for default value assignments
rg -A 2 'defaultValues.*action' # Search for default value configurations

Length of output: 71


Script:

#!/bin/bash
# Let's look at the component implementation and surrounding context
rg -A 10 -B 10 "EditHookDialog" apps/webservice/src/app/[workspaceSlug]/\(app\)/systems/[systemSlug]/deployments/[deploymentSlug]/hooks/EditHookDialog.tsx

# Also search for any Select or similar components that might be using this value
rg -A 5 "Select.*value.*action" apps/webservice/src/app/[workspaceSlug]/\(app\)/systems/[systemSlug]/deployments/[deploymentSlug]/hooks/

Length of output: 1299


Script:

#!/bin/bash
# Let's search for the form initialization and default values
rg -A 20 "const.*form.*useForm" apps/webservice/src/app/[workspaceSlug]/\(app\)/systems/[systemSlug]/deployments/[deploymentSlug]/hooks/EditHookDialog.tsx

# Let's also look at the Select component implementation
rg -A 10 -B 5 "value={.*action" apps/webservice/src/app/[workspaceSlug]/\(app\)/systems/[systemSlug]/deployments/[deploymentSlug]/hooks/EditHookDialog.tsx

Length of output: 1196


Script:

#!/bin/bash
# Let's look for the defaultValues initialization
rg -B 10 "const form = useForm" apps/webservice/src/app/[workspaceSlug]/\(app\)/systems/[systemSlug]/deployments/[deploymentSlug]/hooks/EditHookDialog.tsx

# And let's look at the Select component with proper escaping
rg -A 10 -B 5 'value=\{.*action\}' apps/webservice/src/app/[workspaceSlug]/\(app\)/systems/[systemSlug]/deployments/[deploymentSlug]/hooks/EditHookDialog.tsx

Length of output: 1432

packages/api/src/router/resources.ts (2)

24-24: LGTM: Import statement follows conventions

The addition of deleteResources import aligns with the PR objective and maintains the existing import structure.


580-584: Verify event handling migration

Let's verify that all resource deletion events are properly migrated to the new system.

✅ Verification successful

Let me gather more information about the event handling implementation to ensure completeness of the migration.


Based on the comprehensive results, I can now provide the final response:

Resource deletion events are properly migrated

The migration from events to job dispatch is complete and properly implemented:

  • The deleteResources function in job-dispatch correctly handles resource deletion events
  • Events are properly triggered through getEventsForResourceDeleted and processed via handleEvent
  • The implementation includes proper handling of deployments and environments affected by resource deletion
  • No references to the old @ctrlplane/events system remain
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for any remaining references to the old event system
# and verify the new deleteResources implementation

# Check for any remaining references to @ctrlplane/events
echo "Checking for remaining references to @ctrlplane/events..."
rg "@ctrlplane/events"

# Check the implementation of deleteResources to ensure it handles all events
echo "Checking deleteResources implementation..."
rg -A 10 "export const deleteResources"

# Look for any remaining event handling patterns
echo "Checking for remaining event handling patterns..."
rg "getEventsForResourceDeleted|handleEvent"

Length of output: 2531


Script:

#!/bin/bash
# Check the implementation of event handling in the new system
echo "Checking event handling implementation..."
ast-grep --pattern 'export const getEventsForResourceDeleted = $_'

# Check for any resource deletion related code
echo "Checking resource deletion patterns..."
rg -A 5 "deleteResource|ResourceDeleted"

# Check the events directory structure
echo "Checking events directory structure..."
fd . packages/job-dispatch/src/events

Length of output: 13462

packages/api/src/router/deployment.ts (2)

40-43: LGTM: Import statement updated correctly

The change aligns with the PR objective to move events from '@ctrlplane/events' to '@ctrlplane/job-dispatch'.


40-43: Verify complete migration from @ctrlplane/events

Let's ensure all references to '@ctrlplane/events' have been migrated to '@ctrlplane/job-dispatch'.

Also applies to: 479-489

✅ Verification successful

Migration from @ctrlplane/events to @ctrlplane/job-dispatch is complete

The verification shows that:

  • No references to '@ctrlplane/events' package remain in the codebase
  • All event-related functions (getEventsForDeploymentDeleted, handleEvent) are now properly imported from '@ctrlplane/job-dispatch'
  • The functions are consistently used across the codebase in:
    • packages/api/src/router/deployment.ts
    • packages/api/src/router/environment.ts
    • apps/jobs/src/expired-env-checker/index.ts
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for any remaining references to @ctrlplane/events

# Search for any remaining imports from @ctrlplane/events
echo "Checking for remaining @ctrlplane/events imports..."
rg "from ['\"]@ctrlplane/events['\"]"

# Search for any remaining package.json dependencies
echo "Checking package.json files for @ctrlplane/events dependency..."
fd "package.json" --exec jq -r '.dependencies["@ctrlplane/events"] // .devDependencies["@ctrlplane/events"] // empty'

Length of output: 523


Script:

#!/bin/bash
# Let's try a different approach to check for remaining references

# Search for any string containing @ctrlplane/events
echo "Checking for any references to @ctrlplane/events..."
rg "@ctrlplane/events"

# Check package.json files with a more resilient approach
echo -e "\nChecking package.json files..."
fd "package.json" --exec cat {} \; | grep -A 2 -B 2 "@ctrlplane/events"

# Look for any event-related imports that might need migration
echo -e "\nChecking for event-related function usage..."
rg "getEventsFor|handleEvent" -A 2 -B 2

Length of output: 6919

packages/job-dispatch/src/resource.ts (2)

25-25: Import statement is correct

The import statement correctly imports getEventsForResourceDeleted and handleEvent from "./events/index.js", which are used for event handling related to resource deletions.


324-324: Error handling is consistent when deleting resources

The catch block appropriately logs the error and rethrows it, ensuring that any issues during the deletion process are handled consistently.

packages/api/src/router/environment.ts (2)

29-33: Updated imports for event handling functions

The import statements have been correctly updated to import event handling functions from @ctrlplane/job-dispatch. This aligns with the refactoring efforts to remove the @ctrlplane/events dependency and maintains consistency across the codebase.


Line range hint 167-177: Handle rejections from Promise.allSettled in event handling

Similarly, in the update mutation, Promise.allSettled is used without processing the results for rejections. This could result in silent failures when handling events for removed resources. To ensure errors are not missed, consider handling the rejected promises.

Apply this diff to handle rejections:

if (removedResources.length > 0) {
  // Existing code to generate events
  const handleEventPromises = events.map(handleEvent);
- await Promise.allSettled(handleEventPromises);
+ const results = await Promise.allSettled(handleEventPromises);
+ results.forEach((result) => {
+   if (result.status === 'rejected') {
+     console.error('Event handling error in update mutation:', result.reason);
+   }
+ });
}

@@ -23,3 +23,4 @@ export * from "./policies/concurrency-policy.js";
export * from "./policies/release-window.js";
export * from "./environment-creation.js";
export * from "./pending-job-checker.js";
export * from "./events/index.js";
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Codebase verification

Event module integration is properly structured but has a circular dependency

The events module is well-structured with proper exports and imports, but there's a circular dependency that needs attention:

  • packages/job-dispatch/src/events/handlers/resource-removed.ts imports from @ctrlplane/job-dispatch while being part of the same package
  • This creates a circular dependency as the main package exports the events module which contains this handler

Suggested fix:

  • Move the dispatchRunbook function to a separate utility module
  • Import it directly in resource-removed.ts instead of importing from the main package
🔗 Analysis chain

Verify the event module integration.

Let's ensure this export is properly integrated:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the events module integration and usage

# Check if the events/index.js file exists
echo "Checking for events/index.js file..."
fd --type f "index.js" packages/job-dispatch/src/events

# Check for potential circular dependencies
echo "Checking for potential circular dependencies..."
rg -l "from \"@ctrlplane/job-dispatch\"" packages/job-dispatch/src/events

# Check usage of the newly exported events
echo "Checking usage of exported events..."
rg --type ts "from \"@ctrlplane/job-dispatch\".*events" -A 3

Length of output: 523


Script:

#!/bin/bash
# Let's verify the events module structure and integration more thoroughly

# Check the content of events/index.ts (not .js)
echo "Checking for events/index.ts file..."
fd --type f "index.ts" packages/job-dispatch/src/events

echo "Checking content of events/index.ts if exists..."
fd --type f "index.ts" packages/job-dispatch/src/events --exec cat {}

echo "Checking for all files in events directory..."
fd . packages/job-dispatch/src/events

# Check the specific circular dependency found
echo "Checking content of resource-removed.ts..."
cat packages/job-dispatch/src/events/handlers/resource-removed.ts

# Check if events are imported elsewhere in the project
echo "Checking broader usage of events..."
rg "from.*events" packages/job-dispatch/src

Length of output: 4003

Comment on lines +31 to +34
db.query.resource
.findFirst({ where: eq(SCHEMA.resource.id, agentId) })
.then((resource) => resource && deleteResources(db, [resource]))
.then(() => logger.info("Deleted stale agent resource", { agentId }));
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add error handling and improve logging precision

While the new implementation correctly checks for resource existence before deletion, there are a few concerns:

  1. Missing error handling for the promise chain
  2. The success log message doesn't accurately reflect whether deletion succeeded
  3. Potential race condition between resource check and deletion

Consider this improved implementation:

       db.query.resource
         .findFirst({ where: eq(SCHEMA.resource.id, agentId) })
         .then((resource) => resource && deleteResources(db, [resource]))
-        .then(() => logger.info("Deleted stale agent resource", { agentId }));
+        .then((deleted) => {
+          if (deleted) {
+            logger.info("Deleted stale agent resource", { agentId });
+          } else {
+            logger.info("No resource found for stale agent", { agentId });
+          }
+        })
+        .catch((error) => {
+          logger.error("Failed to delete stale agent resource", {
+            agentId,
+            error: error.message
+          });
+        });
📝 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
db.query.resource
.findFirst({ where: eq(SCHEMA.resource.id, agentId) })
.then((resource) => resource && deleteResources(db, [resource]))
.then(() => logger.info("Deleted stale agent resource", { agentId }));
db.query.resource
.findFirst({ where: eq(SCHEMA.resource.id, agentId) })
.then((resource) => resource && deleteResources(db, [resource]))
.then((deleted) => {
if (deleted) {
logger.info("Deleted stale agent resource", { agentId });
} else {
logger.info("No resource found for stale agent", { agentId });
}
})
.catch((error) => {
logger.error("Failed to delete stale agent resource", {
agentId,
error: error.message
});
});

Comment on lines +580 to +584
.mutation(async ({ ctx, input }) =>
ctx.db.query.resource
.findMany({ where: inArray(schema.resource.id, input) })
.then((resources) => deleteResources(ctx.db, resources)),
),
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider wrapping operations in a transaction and adding error handling

While the implementation correctly uses the new deleteResources utility, consider these improvements:

  1. Wrap the query and delete operations in a transaction to ensure atomicity
  2. Add error handling for cases where resources don't exist
  3. Consider performance optimization by selecting only necessary fields instead of fetching entire resources

Here's a suggested implementation:

  .mutation(async ({ ctx, input }) =>
-   ctx.db.query.resource
-     .findMany({ where: inArray(schema.resource.id, input) })
-     .then((resources) => deleteResources(ctx.db, resources)),
+   ctx.db.transaction(async (tx) => {
+     const resources = await tx.query.resource.findMany({
+       where: inArray(schema.resource.id, input),
+       columns: {
+         id: true,
+         kind: true,
+         name: true,
+         workspaceId: true
+       }
+     });
+     
+     if (resources.length !== input.length) {
+       throw new Error('Some resources not found');
+     }
+     
+     return deleteResources(tx, resources);
+   }),
📝 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
.mutation(async ({ ctx, input }) =>
ctx.db.query.resource
.findMany({ where: inArray(schema.resource.id, input) })
.then((resources) => deleteResources(ctx.db, resources)),
),
.mutation(async ({ ctx, input }) =>
ctx.db.transaction(async (tx) => {
const resources = await tx.query.resource.findMany({
where: inArray(schema.resource.id, input),
columns: {
id: true,
kind: true,
name: true,
workspaceId: true
}
});
if (resources.length !== input.length) {
throw new Error('Some resources not found');
}
return deleteResources(tx, resources);
}),
),

Comment on lines +347 to +352
export const deleteResources = async (tx: Tx, resources: Resource[]) => {
const eventsPromises = Promise.all(
resources.map(getEventsForResourceDeleted),
);
const events = await eventsPromises.then((res) => res.flat());
await Promise.all(events.map(handleEvent));
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

Add error handling in deleteResources function

The deleteResources function lacks error handling around the asynchronous operations for fetching and handling events. If any of these promises are rejected, it could lead to unhandled promise rejections and potentially leave the system in an inconsistent state. To maintain robustness and consistency with the rest of the codebase, consider adding error handling.

Apply this diff to add error handling:

 export const deleteResources = async (tx: Tx, resources: Resource[]) => {
+  try {
     const eventsPromises = Promise.all(
       resources.map(getEventsForResourceDeleted),
     );
     const events = await eventsPromises.then((res) => res.flat());
     await Promise.all(events.map(handleEvent));
     const resourceIds = resources.map((r) => r.id);
     await tx.delete(resource).where(inArray(resource.id, resourceIds));
+  } catch (err) {
+    log.error("Error in deleteResources", { error: err });
+    throw err;
+  }
 };

Committable suggestion skipped: line range outside the PR's diff.

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