selenium
selenium copied to clipboard
[rb] Handle graceful webdriver shutdown
User description
Improve the shutdown sequence by separating a dead child pid (not running anymore) from a running child pid.
Description
When shutting down Selenium::WebDriver::ServiceManager#stop_server issues a /shutdown request against the webdriver server and the server exits cleanly, however the mechanism to assert if the child process is up or not cannot distinguish between busy or not found.
Selenium::WebDriver::ChildProcess#exited? returns false when the process is still alive because
Selenium::WebDriver::ChildProcess#waitpid2 returns nil.
However, by catching Errno::ECHILD and Errno::ESRCH and returning nil #waitpid2 masks a not running pid as "running" delaying the shutdown procedure when the server was already gone.
This patch moves the exception handling away from the Process wrappers so its closer to the decision points in
Selenium::WebDriver::ChildProcess#exited? and
Selenium::WebDriver::ChildProcess#stop.
Addresses a similar problem as in #14032. With this patch the shutdown sequence looks like:
- Send a
/shutdownsignal to the server [if supported]- Check for the server pid
- If it cannot be found (Errno::ECHILD, Errno::ESRCH), then assume the server has shutdown.
- else: wait and retry in ~100 ms.
- repeat for 20 seconds (
Selenium::WebDriver::ServiceManager::STOP_TIMEOUT).
- Check for the server pid
- Try to send a
TERMsignal to the server.- Check for the server pid
- If it cannot be found (Errno::ECHILD, Errno::ESRCH), then assume the server has shutdown.
- else: wait and retry in ~100 ms.
- repeat for 3 seconds (default value of
timeoutinSelenium::WebDriver::ChildProcess#stop).
- Check for the server pid
- Try to send a
KILLsignal to the server.- Check for the server pid
- If it cannot be found (Errno::ECHILD, Errno::ESRCH), then assume the server has shutdown.
- else: wait and retry in ~100 ms.
- Check for the server pid
Motivation and Context
Noticed that the tests will end, but it'll be hung for 10~20 seconds doing nothing. This patch reduces my test run time from ~32s to ~13s consistently.
Types of changes
- [x] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
Checklist
- [x] I have read the contributing document.
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.
Logs of a production application
with selenium-webdrivers 4.23.0
time RAILS_ENV=test bex teaspoon
# [...]
2024-08-23 16:16:10 DEBUG Selenium [:process] Starting process: ["/usr/bin/chromedriver", "--port=9515"] with {[:out, :err]=>#<IO:<STDERR>>, :pgroup=>true}
2024-08-23 16:16:10 DEBUG Selenium [:process] -> pid: 27757
# [...]
........................................................................................................................................
Finished in 1.78500 seconds
136 examples, 0 failures
2024-08-23 16:16:18 DEBUG Selenium [:command] -> DELETE session/60ed9bb863325bb8238f2cc86bee81c6
2024-08-23 16:16:19 DEBUG Selenium [:header] <<< {"content-length"=>["14"], "content-type"=>["application/json; charset=utf-8"], "cache-control"=>["no-cache"]}
2024-08-23 16:16:19 DEBUG Selenium [:command] <- {"value":null}
2024-08-23 16:16:19 DEBUG Selenium [:process] Checking if 27757 is exited:
2024-08-23 16:16:19 DEBUG Selenium [:process] Polling 20 seconds for exit of 27757
2024-08-23 16:16:19 DEBUG Selenium [:process] Checking if 27757 is exited:
2024-08-23 16:16:19 DEBUG Selenium [:process] Checking if 27757 is exited:
real 0m33.907s
user 0m6.563s
sys 0m1.045s
with this patch:
2024-08-23 16:17:55 DEBUG Selenium [:process] Starting process: ["/usr/bin/chromedriver", "--port=9515"] with {[:out, :err]=>#<IO:<STDERR>>, :pgroup=>true}
2024-08-23 16:17:55 DEBUG Selenium [:process] -> pid: 28825
........................................................................................................................................
Finished in 1.54100 seconds
136 examples, 0 failures
2024-08-23 16:18:03 DEBUG Selenium [:command] -> DELETE session/93d5a7d278d25585438a62a60a0625ca
2024-08-23 16:18:03 DEBUG Selenium [:header] <<< {"content-length"=>["14"], "content-type"=>["application/json; charset=utf-8"], "cache-control"=>["no-cache"]}
2024-08-23 16:18:03 DEBUG Selenium [:command] <- {"value":null}
2024-08-23 16:18:03 DEBUG Selenium [:process] Checking if 28825 is exited:
2024-08-23 16:18:03 DEBUG Selenium [:driver_service] Sending shutdown request to server
2024-08-23 16:18:03 DEBUG Selenium [:process] Polling 20 seconds for exit of 28825
2024-08-23 16:18:03 DEBUG Selenium [:process] Checking if 28825 is exited:
2024-08-23 16:18:03 DEBUG Selenium [:process] Checking if 28825 is exited:
2024-08-23 16:18:03 DEBUG Selenium [:process] -> process: 28825 already finished
2024-08-23 16:18:03 DEBUG Selenium [:process] Checking if 28825 is exited:
2024-08-23 16:18:03 DEBUG Selenium [:process] -> process: 28825 already finished
2024-08-23 16:18:03 DEBUG Selenium [:process] Checking if 28825 is exited:
2024-08-23 16:18:03 DEBUG Selenium [:process] -> process: 28825 already finished
2024-08-23 16:18:03 DEBUG Selenium [:process] Checking if 28825 is exited:
2024-08-23 16:18:03 DEBUG Selenium [:process] -> process: 28825 already finished
2024-08-23 16:18:03 DEBUG Selenium [:process] Checking if 28825 is exited:
2024-08-23 16:18:03 DEBUG Selenium [:process] -> process: 28825 already finished
real 0m12.858s
user 0m6.254s
sys 0m1.049s
PR Type
enhancement, tests
Description
- Refactored the process termination logic in
ChildProcessto improve clarity and error handling. - Introduced a new method
terminate_and_wait_else_killto encapsulate termination logic. - Enhanced logging for process termination and server shutdown requests to aid in debugging.
- Improved handling of
Errno::ECHILDandErrno::ESRCHexceptions to correctly identify exited processes.
Changes walkthrough 📝
| Relevant files | |||||
|---|---|---|---|---|---|
| Enhancement |
|
💡 PR-Agent usage: Comment
/helpon the PR to get a list of all available PR-Agent tools and their descriptions
PR Reviewer Guide 🔍
| ⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪ |
| 🧪 No relevant tests |
| 🔒 No security concerns identified |
| ⚡ Key issues to review Error Handling Code Duplication |
PR Code Suggestions ✨
| Category | Suggestion | Score |
| Error handling |
Add error handling for the server shutdown HTTP requestIn the rb/lib/selenium/webdriver/common/service_manager.rb [109-115]
Suggestion importance[1-10]: 8Why: Implementing error handling for the HTTP request in | 8 |
| Enhancement |
Enhance error logging by including the specific exception typeIn the rb/lib/selenium/webdriver/common/child_process.rb [86-88]
Suggestion importance[1-10]: 7Why: Adding the specific exception type to the log message enhances debugging and provides more context, which is beneficial for maintenance and troubleshooting. | 7 |
Improve method name to better reflect its functionalityConsider using a more descriptive method name for rb/lib/selenium/webdriver/common/child_process.rb [105-116]
Suggestion importance[1-10]: 6Why: The suggestion to rename the method to | 6 |
Sorry for taking too long on this one, can you please address RuboCop complaints https://github.com/SeleniumHQ/selenium/actions/runs/11849853755/job/33048613805?pr=14430 and we'll merge afterwards.
Thank you for the contribution!
Thank y'all. Until the next ~~bug~~ feature! ✌️