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: prevent page reload on run trigger to open remote browser #389

Open
wants to merge 15 commits into
base: develop
Choose a base branch
from

Conversation

amhsirak
Copy link
Member

@amhsirak amhsirak commented Jan 24, 2025

Summary by CodeRabbit

  • New Features

    • Added support for tracking robot run states in browser pool management.
    • Enhanced workflow interpretation with improved event emissions.
    • Implemented additional checks for active robot runs during browser recording sessions.
    • Introduced job queue management for run workflows.
  • Bug Fixes

    • Improved handling of recording interpretation and run completion notifications.
  • Chores

    • Updated local storage management for run information.
    • Refined socket connection and event handling mechanisms.

Copy link

coderabbitai bot commented Jan 24, 2025

Walkthrough

The pull request introduces enhancements to browser management and workflow interpretation across multiple files. The changes focus on adding a new isRobotRun property to track robot run states in the BrowserPool, implementing a middleware to prevent multiple simultaneous robot runs, and improving event handling for run completions. The modifications aim to provide better control and monitoring of remote browser instances during automated workflows.

Changes

File Change Summary
server/src/browser-management/classes/BrowserPool.ts - Added isRobotRun optional property to BrowserPoolInfo
- Updated addRemoteBrowser method with new parameter
- Added hasActiveRobotRun() and clearRobotRunState() methods
server/src/browser-management/controller.ts - Modified addRemoteBrowser call to include isRobotRun parameter
server/src/routes/record.ts - Added middleware to check for active robot runs
- Prevent browser initialization if an active run exists
server/src/workflow-management/classes/Interpreter.ts - Added 'run-completed' event emission after interpretation
src/pages/MainPage.tsx - Removed conditional notifications in readyForRunHandler
- Added useEffect for handling stored run information
- Enhanced socket event handling
server/src/routes/storage.ts - Commented out previous browser creation logic
- Assigned browserId from req.params.id
- Added job to workflowQueue for run processing
server/src/worker.ts - Introduced new import alias for handleRunRecording
- Modified job data structure to include isScheduled property

Possibly related PRs

Suggested labels

Type: Bug

Poem

🐇 In the realm of code where browsers play,
A new flag is set to guide the way.
Robot runs now in check,
With careful steps, we’ll not wreck.
Hopping through workflows, bright and spry,
Together we’ll reach for the sky! 🌟

✨ Finishing Touches
  • 📝 Generate Docstrings (Beta)

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 generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

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

CodeRabbit Configuration File (.coderabbit.yaml)

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

Documentation and Community

  • Visit our Documentation 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

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (3)
src/pages/MainPage.tsx (2)

93-98: Consider adding metadata when storing run info in local storage.
Right now, only the browserId, runId, and recordingName are stored, which is fine for basic use. However, storing timestamps or versioning could help identify stale or concurrent runs.


109-118: Review additional run statuses beyond 'success'.
Currently, the code only notifies on 'success' or defaults to 'error'. You might consider special handling for other statuses (e.g., 'aborted', 'partial', 'cancelled') if they exist in the backend logic.

server/src/workflow-management/classes/Interpreter.ts (1)

335-336: Add error handling for interpretation failures.
Currently, 'run-completed' is hardcoded to "success" only. In cases of interpretation errors or exceptions, consider emitting a different status (e.g., "error") so the frontend can properly display a failure notification.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 3520ddc and 3fe2033.

📒 Files selected for processing (5)
  • server/src/browser-management/classes/BrowserPool.ts (4 hunks)
  • server/src/browser-management/controller.ts (2 hunks)
  • server/src/routes/record.ts (3 hunks)
  • server/src/workflow-management/classes/Interpreter.ts (2 hunks)
  • src/pages/MainPage.tsx (5 hunks)
🔇 Additional comments (8)
server/src/browser-management/classes/BrowserPool.ts (5)

18-19: Clarify the optional isRobotRun behavior.

Adding isRobotRun?: boolean; provides an easy flag for identifying robot-run browsers. However, ensure that any logic that relies on this property's truthiness also accounts for when it's undefined.


51-57: Validate the new isRobotRun parameter in addRemoteBrowser.

Passing isRobotRun as a distinct boolean argument helps control the browser’s run state more explicitly. Just make sure callers consistently provide the correct value based on their flow.


63-65: Confirm performance sufficiency of hasActiveRobotRun.

Using Object.values(this.pool).some(...) to check for an active run is fine for smaller pools. If usage scales, you might consider an alternative approach to avoid scanning the entire pool each time.


67-73: Ensure no concurrency conflicts in clearRobotRunState.

Resetting isRobotRun to false is a simple way to clear the run state. Confirm that there’s no parallel code path that might set it back to true after this method is called.


84-85: Good cleanup of the run state before deletion.

Calling clearRobotRunState prior to removing the browser ensures consistent and predictable state in the pool. Nice approach!

server/src/routes/record.ts (1)

19-19: Ensure browserPool is a valid singleton.

Importing browserPool allows you to share the same pool references across modules. Double-check that it’s indeed a singleton or properly scoped to avoid initialization issues.

server/src/browser-management/controller.ts (1)

62-62: Explicitly marking the browser run as a robot run.

Changing browserPool.addRemoteBrowser(id, browserSession, true, true); clarifies that these instances are indeed active and designated for robot runs. Make sure all relevant teardown/notification hooks also respect this setting.

src/pages/MainPage.tsx (1)

71-78: Clarify or consolidate success logic within readyForRunHandler.
By commenting out the success/failure notifications and only stopping the browser on failure, the success path now resides solely in the 'run-completed' event. This separation could introduce confusion or duplication. Consider either removing the remaining failure handling here, or reintroducing success-handling to keep logic consistent.

Use this script to check if any other places also rely on interpretation return values besides 'run-completed':

Comment on lines +37 to +45
router.use('/', requireSignIn, (req: AuthenticatedRequest, res: Response, next) => {
if (browserPool.hasActiveRobotRun()) {
logger.log('debug', 'Preventing browser initialization - robot run in progress');
return res.status(403).json({
error: 'Cannot initialize recording browser while a robot run is in progress'
});
}
next();
});
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

Race condition risk confirmed in browser pool management

The current implementation of BrowserPool lacks proper synchronization mechanisms. Multiple concurrent requests could potentially pass the hasActiveRobotRun check before any of them sets the robot run state, as the operations are not atomic. While Node.js's event loop provides some protection, it's not guaranteed to be safe in high-concurrency scenarios.

Suggested improvements:

  • Implement proper locking mechanism or semaphore for robot run state changes
  • Consider using atomic operations for state transitions
  • Add request queuing to serialize the robot run initialization
🔗 Analysis chain

Preventing parallel sessions on robot runs.

Blocking new browser sessions when hasActiveRobotRun is true is a solid approach. For high-traffic scenarios, consider if multiple requests might pass this check simultaneously (race condition) before the run is registered. If needed, use a lock or queue to serialize these events and avoid edge cases.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Gather usage references to see how `hasActiveRobotRun` is being checked.
rg -A 4 "hasActiveRobotRun"

Length of output: 878


Script:

#!/bin/bash
# Find where robot runs are initiated and how isRobotRun is set
ast-grep --pattern 'isRobotRun = true'
rg -B 2 -A 4 "isRobotRun.*=.*true"

# Look for any synchronization mechanisms
rg -i "lock|mutex|semaphore|atomic"

Length of output: 3944


Script:

#!/bin/bash
# Get the complete BrowserPool implementation
cat server/src/browser-management/classes/BrowserPool.ts

# Find robot run related code
rg -B 2 -A 4 "robotRun|robot.*run"

Length of output: 14625

Comment on lines 145 to 186
useEffect(() => {
const storedRunInfo = localStorage.getItem('runInfo');
console.log('storedRunInfo', storedRunInfo);

if (storedRunInfo) {
// Parse the stored info
const { browserId, runId, recordingName } = JSON.parse(storedRunInfo);

// Reconnect to the specific browser's namespace
setIds({ browserId, runId });
const socket = io(`${apiUrl}/${browserId}`, {
transports: ["websocket"],
rejectUnauthorized: false
});

// Update component state with stored info
setRunningRecordingName(recordingName);
setSockets(sockets => [...sockets, socket]);

// Set up event listeners
socket.on('ready-for-run', () => readyForRunHandler(browserId, runId));
socket.on('debugMessage', debugMessageHandler);
socket.on('run-completed', (status) => {
if (status === 'success') {
notify('success', t('main_page.notifications.interpretation_success', { name: recordingName }));
} else {
notify('error', t('main_page.notifications.interpretation_failed', { name: recordingName }));
}
setRunningRecordingName('');
setCurrentInterpretationLog('');
setRerenderRuns(true);
localStorage.removeItem('runInfo'); // Clean up stored info
});

// Cleanup function
return () => {
socket.off('ready-for-run', () => readyForRunHandler(browserId, runId));
socket.off('debugMessage', debugMessageHandler);
socket.off('run-completed');
};
}
}, []);
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Ensure cleanup functions use the same listener references.
Inside the useEffect, the cleanup relies on arrow callbacks that differ from those in socket.on(...). Reacting to new runs or re-renders may result in unremoved listeners. As with line 130, store references to your event handlers, then pass them consistently to on and off.

@@ -108,6 +127,7 @@ export const MainPage = ({ handleEditRecording, initialContent }: MainPageProps)
return (socket: Socket, browserId: string, runId: string) => {
socket.off('ready-for-run', () => readyForRunHandler(browserId, runId));
socket.off('debugMessage', debugMessageHandler);
socket.off('run-completed');
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Use identical function references to remove socket listeners.
Calling socket.off('run-completed', () => ...) with a new inline arrow function won’t remove the original listener. This can lead to memory leaks or multiple event fires. Store the listener in a variable and pass the same reference to both on and off.

- socket.on('run-completed', (status) => { ... });
- socket.off('run-completed', () => { ... });
+ const handleRunCompleted = (status) => { ... };
+ socket.on('run-completed', handleRunCompleted);
+ socket.off('run-completed', handleRunCompleted);

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

@amhsirak amhsirak added the Status: 🚫🚫 DO NOT MERGE 🚫🚫 Don't merge this PR. It is not perfect. label Jan 24, 2025
Copy link

@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 (2)
server/src/routes/storage.ts (1)

480-480: Remove commented-out code if unneeded.
Leaving this line in the codebase may cause confusion about whether it should be enabled. If you no longer need to call createRemoteBrowserForRun, consider removing it:

-    // const id = createRemoteBrowserForRun(req.user.id);
server/src/worker.ts (1)

40-59: Use optional chaining for safer schedule checks.
You can simplify this condition by applying optional chaining on robot.schedule. For instance:

-    if (robot.schedule && robot.schedule.cronExpression && robot.schedule.timezone) {
+    if (robot.schedule?.cronExpression && robot.schedule?.timezone) {
🧰 Tools
🪛 Biome (1.9.4)

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

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 3fe2033 and 058d512.

📒 Files selected for processing (3)
  • server/src/routes/storage.ts (3 hunks)
  • server/src/worker.ts (3 hunks)
  • src/pages/MainPage.tsx (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/pages/MainPage.tsx
🧰 Additional context used
🪛 Biome (1.9.4)
server/src/worker.ts

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

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

🔇 Additional comments (6)
server/src/routes/storage.ts (3)

491-491: Verify browserId alignment with actual browser creation.
This line implies that the id of the recording in req.params.id is reused for the browser ID. Confirm that the front end or the rest of the system does not rely on a different ID for browsers, which could lead to mismatches.


500-504: Ensure consistent handling of the isScheduled flag.
The job is added with isScheduled: false, which triggers the unscheduled workflow path in the worker. Double-check that other parts of the code correctly handle this flag and that no re-scheduling logic is executed inadvertently.


508-508: Duplicate assignment of browserId as req.params.id.
This is the same assignment as on line 491, which may cause confusion between robot/recording IDs and browser IDs if the meaning differs. Ensure both references are intentional.

server/src/worker.ts (3)

4-5: Confirm distinction between scheduled vs. direct run recordings.
Using different function names for scheduled vs. direct runs can improve clarity. Ensure that both implementations stay synchronized if they share logic.
[approve]


26-26: Evaluate defaulting isScheduled to true.
This fallback is helpful, but confirm that manual runs always pass false to avoid erroneously calling the scheduled-run handler.


28-30: Conditional call effectively separates scheduled vs. on-demand runs.
This is a clean approach. Ensure that both functions handle unexpected job data states gracefully.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Status: 🚫🚫 DO NOT MERGE 🚫🚫 Don't merge this PR. It is not perfect.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants