mirror of
https://github.com/Chia-Network/chia-blockchain.git
synced 2024-09-11 18:58:59 +03:00
avoid AttributeError when shutting down plotting (#3954)
* Remove valid # type: ignore A user ran into an exception on this line that mypy should have caught. Let's see what it says. ```python-traceback 2021-05-06T07:31:41.595 full_node full_node_server : ERROR Exception , exception Stack: Traceback (most recent call last): File "chia\server\server.py", line 356, in start_client File "aiohttp\client.py", line 763, in _ws_connect File "aiohttp\client.py", line 521, in _request File "aiohttp\connector.py", line 535, in connect File "aiohttp\connector.py", line 892, in _create_connection File "aiohttp\connector.py", line 1032, in _create_direct_connection File "aiohttp\connector.py", line 969, in _wrap_create_connection File "asyncio\base_events.py", line 949, in create_connection File "asyncio\selector_events.py", line 473, in sock_connect concurrent.futures._base.CancelledError 2021-05-06T07:31:45.016 daemon asyncio : ERROR Task exception was never retrieved future: <Task finished coro=<kill_service() done, defined at chia\daemon\server.py:833> exception=AttributeError("'list' object has no attribute 'pid'")> Traceback (most recent call last): File "chia\daemon\server.py", line 841, in kill_service File "chia\daemon\server.py", line 805, in kill_process AttributeError: 'list' object has no attribute 'pid' 2021-05-06T07:32:09.965 full_node full_node_server : ERROR Exception: <class 'concurrent.futures._base.CancelledError'>, closing connection None. Traceback (most recent call last): File "chia\server\server.py", line 531, in api_call File "asyncio\tasks.py", line 435, in wait_for concurrent.futures._base.CancelledError 2021-05-06T07:33:20.573 full_node full_node_server : ERROR Exception , exception Stack: Traceback (most recent call last): File "chia\server\server.py", line 356, in start_client File "aiohttp\client.py", line 763, in _ws_connect File "aiohttp\client.py", line 521, in _request File "aiohttp\connector.py", line 535, in connect File "aiohttp\connector.py", line 892, in _create_connection File "aiohttp\connector.py", line 1032, in _create_direct_connection File "aiohttp\connector.py", line 969, in _wrap_create_connection ``` * fix plotter service killing * just make it always be a list of processes for all services * catch up tests * Update chia/daemon/server.py
This commit is contained in:
parent
d84917f5df
commit
c4c165eabd
@ -133,7 +133,7 @@ class WebSocketServer:
|
||||
):
|
||||
self.root_path = root_path
|
||||
self.log = log
|
||||
self.services: Dict = dict()
|
||||
self.services: Dict[str, List[subprocess.Popen]] = dict()
|
||||
self.plots_queue: List[Dict] = []
|
||||
self.connections: Dict[str, Set[WebSocketResponse]] = dict() # service name : {WebSocketResponse}
|
||||
self.ping_job: Optional[asyncio.Task] = None
|
||||
@ -1053,7 +1053,7 @@ class WebSocketServer:
|
||||
run_next = True
|
||||
config["state"] = PlotState.REMOVING
|
||||
self.state_changed(service_plotter, self.prepare_plot_state_message(PlotEvent.STATE_CHANGED, id))
|
||||
await kill_process(process, self.root_path, service_plotter, id)
|
||||
await kill_processes([process], self.root_path, service_plotter, id)
|
||||
|
||||
config["state"] = PlotState.FINISHED
|
||||
config["deleted"] = True
|
||||
@ -1089,9 +1089,8 @@ class WebSocketServer:
|
||||
error = "unknown service"
|
||||
|
||||
if service_command in self.services:
|
||||
service = self.services[service_command]
|
||||
r = service is not None and service.poll() is None
|
||||
if r is False:
|
||||
processes = self.services[service_command]
|
||||
if all(process.poll() is not None for process in processes):
|
||||
self.services.pop(service_command)
|
||||
error = None
|
||||
else:
|
||||
@ -1111,7 +1110,7 @@ class WebSocketServer:
|
||||
if testing is True:
|
||||
exe_command = f"{service_command} --testing=true"
|
||||
process, pid_path = launch_service(self.root_path, exe_command)
|
||||
self.services[service_command] = process
|
||||
self.services[service_command] = [process]
|
||||
success = True
|
||||
except (subprocess.SubprocessError, IOError):
|
||||
log.exception(f"problem starting {service_command}")
|
||||
@ -1127,12 +1126,13 @@ class WebSocketServer:
|
||||
return response
|
||||
|
||||
def is_service_running(self, service_name: str) -> bool:
|
||||
processes: List[subprocess.Popen]
|
||||
if service_name == service_plotter:
|
||||
processes = self.services.get(service_name)
|
||||
is_running = processes is not None and len(processes) > 0
|
||||
processes = self.services.get(service_name, [])
|
||||
is_running = len(processes) > 0
|
||||
else:
|
||||
process = self.services.get(service_name)
|
||||
is_running = process is not None and process.poll() is None
|
||||
processes = self.services.get(service_name, [])
|
||||
is_running = any(process.poll() is None for process in processes)
|
||||
if not is_running:
|
||||
# Check if we have a connection to the requested service. This might be the
|
||||
# case if the service was started manually (i.e. not started by the daemon).
|
||||
@ -1297,31 +1297,39 @@ def launch_service(root_path: Path, service_command) -> Tuple[subprocess.Popen,
|
||||
return process, pid_path
|
||||
|
||||
|
||||
async def kill_process(
|
||||
process: subprocess.Popen, root_path: Path, service_name: str, id: str, delay_before_kill: int = 15
|
||||
async def kill_processes(
|
||||
processes: List[subprocess.Popen],
|
||||
root_path: Path,
|
||||
service_name: str,
|
||||
id: str,
|
||||
delay_before_kill: int = 15,
|
||||
) -> bool:
|
||||
pid_path = pid_path_for_service(root_path, service_name, id)
|
||||
|
||||
if sys.platform == "win32" or sys.platform == "cygwin":
|
||||
log.info("sending CTRL_BREAK_EVENT signal to %s", service_name)
|
||||
# pylint: disable=E1101
|
||||
kill(process.pid, signal.SIGBREAK)
|
||||
|
||||
for process in processes:
|
||||
kill(process.pid, signal.SIGBREAK)
|
||||
else:
|
||||
log.info("sending term signal to %s", service_name)
|
||||
process.terminate()
|
||||
for process in processes:
|
||||
process.terminate()
|
||||
|
||||
count: float = 0
|
||||
while count < delay_before_kill:
|
||||
if process.poll() is not None:
|
||||
if all(process.poll() is not None for process in processes):
|
||||
break
|
||||
await asyncio.sleep(0.5)
|
||||
count += 0.5
|
||||
else:
|
||||
process.kill()
|
||||
for process in processes:
|
||||
process.kill()
|
||||
log.info("sending kill signal to %s", service_name)
|
||||
r = process.wait()
|
||||
log.info("process %s returned %d", service_name, r)
|
||||
for process in processes:
|
||||
r = process.wait()
|
||||
log.info("process %s returned %d", service_name, r)
|
||||
|
||||
try:
|
||||
pid_path_killed = pid_path.with_suffix(".pid-killed")
|
||||
if pid_path_killed.exists():
|
||||
@ -1334,13 +1342,13 @@ async def kill_process(
|
||||
|
||||
|
||||
async def kill_service(
|
||||
root_path: Path, services: Dict[str, subprocess.Popen], service_name: str, delay_before_kill: int = 15
|
||||
root_path: Path, services: Dict[str, List[subprocess.Popen]], service_name: str, delay_before_kill: int = 15
|
||||
) -> bool:
|
||||
process = services.get(service_name)
|
||||
if process is None:
|
||||
processes = services.get(service_name)
|
||||
if processes is None:
|
||||
return False
|
||||
del services[service_name]
|
||||
result = await kill_process(process, root_path, service_name, "", delay_before_kill)
|
||||
result = await kill_processes(processes, root_path, service_name, "", delay_before_kill)
|
||||
return result
|
||||
|
||||
|
||||
|
@ -165,9 +165,9 @@ def mock_daemon_with_services():
|
||||
# Mock daemon server with a couple running services, a plotter, and one stopped service
|
||||
return Daemon(
|
||||
services={
|
||||
"my_refrigerator": Service(True),
|
||||
"the_river": Service(True),
|
||||
"your_nose": Service(False),
|
||||
"my_refrigerator": [Service(True)],
|
||||
"the_river": [Service(True)],
|
||||
"your_nose": [Service(False)],
|
||||
"chia_plotter": [Service(True), Service(True)],
|
||||
},
|
||||
connections={},
|
||||
@ -179,9 +179,9 @@ def mock_daemon_with_services_and_connections():
|
||||
# Mock daemon server with a couple running services, a plotter, and a couple active connections
|
||||
return Daemon(
|
||||
services={
|
||||
"my_refrigerator": Service(True),
|
||||
"my_refrigerator": [Service(True)],
|
||||
"chia_plotter": [Service(True), Service(True)],
|
||||
"apple": Service(True),
|
||||
"apple": [Service(True)],
|
||||
},
|
||||
connections={
|
||||
"apple": [1],
|
||||
|
Loading…
Reference in New Issue
Block a user