-
-
Notifications
You must be signed in to change notification settings - Fork 748
Garbage collect Scheduler Instances during testing #6281
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
Changes from all commits
5181bbf
0221a0f
8f39d6d
98fbb38
72276d5
0bca52a
79e43bb
5279f2a
f734038
d7549b0
6d7ab9a
2a9e17d
a5a82b9
7b68b22
e285770
decefdd
30956cb
cbe992d
26eae50
11de7b7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6697,7 +6697,7 @@ async def get_story(self, keys=()): | |
|
|
||
| transition_story = story | ||
|
|
||
| def reschedule(self, key=None, worker=None): | ||
| def reschedule(self, key=None, worker=None, stimulus_id=None): | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's a smaller PR addressing this here: #6307 |
||
| """Reschedule a task | ||
|
|
||
| Things may have shifted and this task may now be better suited to run | ||
|
|
@@ -6715,7 +6715,7 @@ def reschedule(self, key=None, worker=None): | |
| return | ||
| if worker and ts.processing_on.address != worker: | ||
| return | ||
| self.transitions({key: "released"}, f"reschedule-{time()}") | ||
| self.transitions({key: "released"}, stimulus_id or f"reschedule-{time()}") | ||
|
|
||
| ##################### | ||
| # Utility functions # | ||
|
|
@@ -7268,6 +7268,27 @@ def request_remove_replicas(self, addr: str, keys: list, *, stimulus_id: str): | |
| } | ||
| ) | ||
|
|
||
| def clear_instance(self): | ||
| self.extensions.clear() | ||
| self.plugins.clear() | ||
| self.services.clear() | ||
| self.listeners.clear() | ||
| self.handlers.clear() | ||
| self.periodic_callbacks.clear() | ||
| self.stream_handlers.clear() | ||
| self.stream_comms.clear() | ||
| self.transitions_table.clear() | ||
| self.log.clear() | ||
| self.transition_log.clear() | ||
|
|
||
| del self.http_application | ||
| del self.http_server | ||
|
|
||
| @classmethod | ||
| def clear_instances(cls): | ||
| for instance in cls._instances: | ||
| instance.clear_instance() | ||
|
|
||
|
|
||
| def _remove_from_processing( | ||
| state: SchedulerState, ts: TaskState | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -28,7 +28,9 @@ | |
| from typing import Any, Generator, Literal | ||
|
|
||
| from distributed.compatibility import MACOS | ||
| from distributed.profile import wait_profiler | ||
| from distributed.scheduler import Scheduler | ||
| from distributed.utils import has_keyword | ||
|
|
||
| try: | ||
| import ssl | ||
|
|
@@ -138,9 +140,11 @@ async def cleanup_global_workers(): | |
| await worker.close(report=False, executor_wait=False) | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def loop(): | ||
| with check_instances(): | ||
| @pytest.fixture(params=[{"instances": True}]) | ||
| def loop(request): | ||
| instances = request.param["instances"] | ||
|
|
||
| with check_instances() if instances else nullcontext(): | ||
| with pristine_loop() as loop: | ||
| # Monkey-patch IOLoop.start to wait for loop stop | ||
| orig_start = loop.start | ||
|
|
@@ -1768,6 +1772,8 @@ def check_instances(): | |
|
|
||
| _global_clients.clear() | ||
|
|
||
| Scheduler.clear_instances() | ||
|
|
||
| for w in Worker._instances: | ||
| with suppress(RuntimeError): # closed IOLoop | ||
| w.loop.add_callback(w.close, report=False, executor_wait=False) | ||
|
|
@@ -1796,22 +1802,39 @@ def check_instances(): | |
| for n in Nanny._instances | ||
| ), {n: n.status for n in Nanny._instances} | ||
|
|
||
| # assert not list(SpecCluster._instances) # TODO | ||
| assert all(c.status == Status.closed for c in SpecCluster._instances), list( | ||
| SpecCluster._instances | ||
| ) | ||
| SpecCluster._instances.clear() | ||
| SpecCluster.clear_instances() | ||
| DequeHandler.clear_instances() | ||
|
|
||
| from _pytest.logging import LogCaptureHandler | ||
|
|
||
| for v in logging.Logger.manager.loggerDict.values(): | ||
| if not isinstance(v, logging.PlaceHolder): | ||
| for h in v.handlers: | ||
| if isinstance(h, LogCaptureHandler): | ||
| h.reset() | ||
|
|
||
| has_keyword.cache_clear() | ||
|
|
||
| wait_profiler() | ||
| gc.collect() | ||
|
|
||
| if Scheduler._instances: | ||
| s = next(iter(Scheduler._instances)) | ||
| import objgraph | ||
|
|
||
| objgraph.show_backrefs([s], filename="scheduler.png") | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The objgraph import should be removed upon PR approval |
||
| assert not Scheduler._instances | ||
|
|
||
| SpecCluster._instances.clear() | ||
| Nanny._instances.clear() | ||
| DequeHandler.clear_all_instances() | ||
|
|
||
|
|
||
| @contextmanager | ||
| def clean(threads=True, instances=True, timeout=1, processes=True): | ||
| with check_thread_leak() if threads else nullcontext(): | ||
| with pristine_loop() as loop: | ||
| with check_process_leak(check=processes): | ||
| with check_instances() if instances else nullcontext(): | ||
| with check_instances() if instances else nullcontext(): | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The IOLoop needs to be cleaned up first, otherwise callbacks on the loop like I'm not aware of any drawbacks to this ordering. |
||
| with pristine_loop() as loop: | ||
| with check_process_leak(check=processes): | ||
| with check_active_rpc(loop, timeout): | ||
| reset_config() | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
instance checks are disabled on a variety of tests largely due to #6308.
Garbage collecting Scheduler objects is useful, but secondary relative to clearing up TaskStates and their expensive run_spec attributes.
We should log an issue that tracks re-enabling them.