From 2e43510ef533adab9f75f03c6ccc091764e7e173 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Fri, 25 Mar 2022 15:09:44 +0100 Subject: [PATCH] Ensure gc.collect is not excessively called (#3259) --- panel/io/server.py | 25 +++++++++++++++++++++++++ panel/io/state.py | 2 +- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/panel/io/server.py b/panel/io/server.py index e59ab76c9b..c19b249e7d 100644 --- a/panel/io/server.py +++ b/panel/io/server.py @@ -2,6 +2,7 @@ Utilities for creating bokeh Server instances. """ import datetime as dt +import gc import html import logging import os @@ -165,6 +166,28 @@ def autoload_js_script(doc, resources, token, element_id, app_path, absolute_url return AUTOLOAD_JS.render(bundle=bundle, elementid=element_id) + +def destroy_document(self, session): + """ + Override for Document.destroy() without calling gc.collect directly. + The gc.collect() call is scheduled as a task, ensuring that when + multiple documents are destroyed in quick succession we do not + schedule excessive garbage collection. + """ + self.remove_on_change(session) + del self._roots + del self._theme + del self._template + self._session_context = None + + self.callbacks.destroy() + self.models.destroy() + self.modules.destroy() + + at = dt.datetime.now() + dt.timedelta(seconds=5) + state.schedule_task('gc.collect', gc.collect, at=at) + + # Patch Application to handle session callbacks class Application(BkApplication): @@ -349,7 +372,9 @@ def handle_exception(handler, e): def _log_session_destroyed(session_context): logger.info(LOG_SESSION_DESTROYED, id(doc)) + doc.on_session_destroyed(_log_session_destroyed) + doc.destroy = partial(destroy_document, doc) finally: state._launching.remove(doc) if config.profiler: diff --git a/panel/io/state.py b/panel/io/state.py index b1d22eee45..8a945d6321 100644 --- a/panel/io/state.py +++ b/panel/io/state.py @@ -530,7 +530,7 @@ def schedule_task(self, name, callback, at=None, period=None, cron=None): "A separate task was already scheduled under the " f"name {name!r}. The new task will be ignored. If " "you want to replace the existing task cancel it " - "with `state.cancel_task({name!r})` before adding " + f"with `state.cancel_task({name!r})` before adding " "adding a new task under the same name." ) return