Skip to content

Commit a259841

Browse files
krisctlprabhakk-mw
authored andcommitted
Fixes issues related to updates from web logging API from AIOHTTP, and refactors shutdown process to use AIOHTTP's shutdown hooks instead of the cleanup hook.
1 parent bec8206 commit a259841

File tree

4 files changed

+48
-30
lines changed

4 files changed

+48
-30
lines changed

.vscode/settings.json

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@
77
"source.fixAll": "explicit",
88
"source.organizeImports": "explicit"
99
},
10-
"editor.defaultFormatter": "charliermarsh.ruff"
11-
}
10+
"editor.defaultFormatter": "ms-python.black-formatter"
11+
},
12+
"python.testing.pytestArgs": [
13+
"tests"
14+
],
15+
"python.testing.unittestEnabled": false,
16+
"python.testing.pytestEnabled": true
1217
}

matlab_proxy/app.py

Lines changed: 37 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -432,25 +432,38 @@ async def shutdown_integration_delete(req):
432432
req (HTTPRequest): HTTPRequest Object
433433
"""
434434
state = req.app["state"]
435-
436435
logger.info(f"Shutting down {state.settings['integration_name']}...")
437436

438-
# Send response manually because this has to happen before the application exits
439437
res = create_status_response(req.app, "../")
440-
await res.prepare(req)
441-
await res.write_eof()
442438

443-
# Gracefully shutdown the server
444-
await req.app.shutdown()
445-
await req.app.cleanup()
439+
# Schedule the shutdown to happen after the response is sent
440+
asyncio.create_task(_shutdown_after_response(req.app))
441+
442+
return res
443+
444+
445+
async def _shutdown_after_response(app):
446+
# Shutdown the application after a short delay to allow the response to be fully sent
447+
# back to the client before the server is stopped
448+
await asyncio.sleep(0.1)
449+
450+
# aiohttp shutdown to be invoked before cleanup -
451+
# https://docs.aiohttp.org/en/stable/web_reference.html#aiohttp.web.Application.shutdown
452+
await app.shutdown()
453+
await app.cleanup()
446454

447455
loop = util.get_event_loop()
448-
# Run the current batch of coroutines in the event loop and then exit.
449-
# This completes the loop.run_forever() blocking call and subsequent code
450-
# in create_and_start_app() resumes execution.
451-
loop.stop()
452456

453-
return res
457+
# Cancel remaining tasks (except this one: _shutdown_after_response)
458+
running_tasks = asyncio.all_tasks(loop)
459+
current_task = asyncio.current_task()
460+
if current_task:
461+
running_tasks.discard(current_task)
462+
463+
await util.cancel_tasks(running_tasks)
464+
465+
# Stop the event loop from this task
466+
loop.call_soon_threadsafe(loop.stop)
454467

455468

456469
# @token_auth.authenticate_access_decorator
@@ -873,8 +886,6 @@ def configure_and_start(app):
873886
"""
874887
loop = util.get_event_loop()
875888

876-
web_logger = None if not mwi_env.is_web_logging_enabled() else logger
877-
878889
# Setup the session storage,
879890
# Uniqified per session to prevent multiple proxy servers on the same FQDN from interfering with each other.
880891
uniqify_session_cookie = secrets.token_hex()
@@ -888,7 +899,9 @@ def configure_and_start(app):
888899
)
889900

890901
# Setup runner
891-
runner = web.AppRunner(app, logger=web_logger, access_log=web_logger)
902+
runner = web.AppRunner(
903+
app, access_log=logger if mwi_env.is_web_logging_enabled() else None
904+
)
892905
loop.run_until_complete(runner.setup())
893906

894907
# Prepare site to start, then set port of the app.
@@ -961,7 +974,7 @@ def create_app(config_name=matlab_proxy.get_default_config_name()):
961974
app.router.add_route("*", f"{base_url}", root_redirect)
962975

963976
app.router.add_route("*", f"{base_url}/{{proxyPath:.*}}", matlab_view)
964-
app.on_cleanup.append(cleanup_background_tasks)
977+
app.on_shutdown.append(cleanup_background_tasks)
965978

966979
return app
967980

@@ -1011,15 +1024,15 @@ def create_and_start_app(config_name):
10111024

10121025
# After handling the interrupt, proceed with shutting down the server gracefully.
10131026
try:
1027+
# aiohttp shutdown to be invoked before cleanup -
1028+
# https://docs.aiohttp.org/en/stable/web_reference.html#aiohttp.web.Application.shutdown
1029+
loop.run_until_complete(app.shutdown())
1030+
loop.run_until_complete(app.cleanup())
1031+
10141032
running_tasks = asyncio.all_tasks(loop)
1015-
loop.run_until_complete(
1016-
asyncio.gather(
1017-
app.shutdown(),
1018-
app.cleanup(),
1019-
util.cancel_tasks(running_tasks),
1020-
return_exceptions=False,
1021-
)
1022-
)
1033+
1034+
# Gracefully cancel all running background tasks
1035+
loop.run_until_complete(util.cancel_tasks(running_tasks))
10231036

10241037
except Exception:
10251038
pass

matlab_proxy/util/mwi/logger.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 2020-2024 The MathWorks, Inc.
1+
# Copyright 2020-2025 The MathWorks, Inc.
22
"""Functions to access & control the logging behavior of the app"""
33

44
import logging
@@ -8,7 +8,6 @@
88

99
from . import environment_variables as mwi_env
1010

11-
1211
logging.getLogger("aiohttp_session").setLevel(logging.ERROR)
1312

1413

tests/unit/test_app.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,10 @@ def test_create_app(event_loop):
7979
# Verify router is configured with some routes
8080
assert test_server.router._resources is not None
8181

82-
# Verify app server has a cleanup task
82+
# Verify app server has a shutdown task
8383
# By default there is 1 for clean up task
84-
assert len(test_server.on_cleanup) > 1
84+
assert len(test_server.on_shutdown) == 1
85+
assert len(test_server.on_cleanup) == 1
8586
event_loop.run_until_complete(test_server["state"].stop_server_tasks())
8687

8788

0 commit comments

Comments
 (0)