SeleniumHQ / selenium

A browser automation framework and ecosystem.
https://selenium.dev
Apache License 2.0
30.03k stars 8.07k forks source link

[🐛 Bug]: Program exit is slow only on Windows #13850

Open marvelm opened 3 months ago

marvelm commented 3 months ago

What happened?

Hello,

Apologies for the vague title, but it completely summarizes what we've been experiencing.

When running this code with a profiler, we discovered that send_remote_shutdown_command is taking >30 seconds to finish.

To get to the bottom of the issue, we monkey-patched this function. This gave us an unexpected output. .quit() was only called 3 times, but the output shows that send_remote_shutdown_command was called 6 times. I think it's because __exit__ is being called by the with statement. https://github.com/SeleniumHQ/selenium/blob/ba27d0f7675a3d8139544e5522b8f0690a2ba4ce/py/selenium/webdriver/remote/webdriver.py#L216-L222

On macOS and Linux, calling quit() twice is quick because the default TCP socket timeout is low. However on Windows, the default timeout is long. https://github.com/SeleniumHQ/selenium/blob/ba27d0f7675a3d8139544e5522b8f0690a2ba4ce/py/selenium/webdriver/common/service.py#L126

Solution

We're working around this issue on Windows by monkey-patching the function and setting the timeout to a small value.

from selenium.webdriver.common.service import Service
from urllib import request
from urllib.error import URLError

def send_remote_shutdown_command(self) -> None:
    try:
        request.urlopen(f"{self.service_url}/shutdown", timeout=0.1)
    except URLError:
        return

Service.send_remote_shutdown_command = send_remote_shutdown_command

How can we reproduce the issue?

import threading
from concurrent.futures import ThreadPoolExecutor
from selenium.webdriver import Chrome
from selenium.webdriver.common.service import Service

fn_copy = Service.send_remote_shutdown_command

count = 0
mut = threading.Lock()

def send_remote_shutdown_command(self: Service) -> None:
    global count
    with mut:
        count += 1
        print(f"CALLED: {count} times")
    fn_copy(self)

Service.send_remote_shutdown_command = send_remote_shutdown_command

class MyObject:
    def __init__(self, id):
        self.id = id

objects = [MyObject(i) for i in range(3)]

def run(obj):
        chrome = Chrome()
        obj.browser = chrome
        chrome.get("https://www.google.com")
        print("quit", obj.id)
        chrome.quit()

with ThreadPoolExecutor(max_workers=1) as executor:
    for result in executor.map(run, objects):
        print("result")

print("script complete")

Relevant log output

quit 0
CALLED: 1 times
CALLED: 2 times
quit 1
CALLED: 3 times
CALLED: 4 times
quit 2
CALLED: 5 times
CALLED: 6 times
script complete

Operating System

Windows 10

Selenium version

4.19.0

What are the browser(s) and version(s) where you see this issue?

124.0.6367.60

What are the browser driver(s) and version(s) where you see this issue?

ChromeDriver 123.0.6312.122

Are you using Selenium Grid?

No response

github-actions[bot] commented 3 months ago

@marvelm, thank you for creating this issue. We will troubleshoot it as soon as we can.


Info for maintainers

Triage this issue by using labels.

If information is missing, add a helpful comment and then I-issue-template label.

If the issue is a question, add the I-question label.

If the issue is valid but there is no time to troubleshoot it, consider adding the help wanted label.

If the issue requires changes or fixes from an external project (e.g., ChromeDriver, GeckoDriver, MSEdgeDriver, W3C), add the applicable G-* label, and it will provide the correct link and auto-close the issue.

After troubleshooting the issue, please add the R-awaiting answer label.

Thank you!

diemol commented 3 months ago

Would you like to help us and send a PR?

github-actions[bot] commented 3 months ago

This issue is looking for contributors.

Please comment below or reach out to us through our IRC/Slack/Matrix channels if you are interested.

ptrdom commented 3 months ago

I have started to have this issue on my repo about two weeks ago too - https://github.com/ptrdom/scalajs-esbuild/actions/runs/8969102614. Only Windows jobs fail, and it seems to be related to timeouts on quitting the driver. The project with this issue is JVM-based.

ptrdom commented 3 months ago

Might also be a bug with some recent release of chromedriver v124.