Modular Server doesn't handle multiple connections properly
Describe the bug If you open multiple tabs in your browser to the Mesa server you still only get one model running. Altering the paramenters on one tab affects the model output on all the others.
Expected behavior Each tab should run its own instance of the model. Messages from the client should be applied to the model associated with the web socket.
To Reproduce Open multiple tabs and point them at the mesa server. Adjusting the parameters on one tab will alter the output on the others.
Additional context This fault stops the mesa server from being used as a public facing application server. It's fairly easy to fix. The model just has to be lifted from the application to the socket handler and the socket can act as the session layer. I've made the necessary alterations here: https://github.com/newwayland/baseline-economy/blob/master/BaselineEconomy/ModularVisualization.py
Thank you for the bug report! Could you submit your patch as a Pull Request??
@NeilW I saw that you have a fix for this issue on your fork. Can you submit a PR with that? Don't worry about failing tests or something, I can add/modify them if you don't want to do that. I could also copy paste your fix, but I would like to give the credit to you
I've added a PR. Apologies for not getting onto the tests. They looked like they may need a bit of refactoring and I haven't had the bandwidth to tackle them
I tested running multiple tabs for the Solara viz, and one of the tabs crashed. It looks like the only way for the Solara viz to be accessed in parallel in public is via Jupyter.
Interesting, what was the error message?
It's a deep traceback:
File "/venv/lib/python3.10/site-packages/ipywidgets/widgets/widget.py", line 220, in m
return(method(self, *args, **kwargs))
File "/venv/lib/python3.10/site-packages/ipywidgets/widgets/widget.py", line 773, in _handle_msg
self.set_state(state)
File "/venv/lib/python3.10/site-packages/ipywidgets/widgets/widget.py", line 650, in set_state
with self._lock_property(**sync_data), self.hold_trait_notifications():
File "/run/current-system/sw/lib/python3.10/contextlib.py", line 142, in __exit__
next(self.gen)
File "/venv/lib/python3.10/site-packages/reacton/core.py", line 369, in hold_trait_notifications_extra
with rc, hold_trait_notifications(*args, **kwargs):
File "/venv/lib/python3.10/site-packages/reacton/core.py", line 1143, in __exit__
self._possible_rerender()
File "/venv/lib/python3.10/site-packages/reacton/core.py", line 1335, in _possible_rerender
self.render(self.element, self.container)
File "/venv/lib/python3.10/site-packages/reacton/core.py", line 1433, in render
widget = self._reconsolidate(self.element, default_key="/", parent_key=ROOT_KEY)
File "/venv/lib/python3.10/site-packages/reacton/core.py", line 1794, in _reconsolidate
widget = self._reconsolidate(child_context.root_element_next, "/", new_parent_key)
File "/venv/lib/python3.10/site-packages/reacton/core.py", line 1794, in _reconsolidate
widget = self._reconsolidate(child_context.root_element_next, "/", new_parent_key)
File "/venv/lib/python3.10/site-packages/reacton/core.py", line 1914, in _reconsolidate
kwargs = reconsolidate_children()
File "/venv/lib/python3.10/site-packages/reacton/core.py", line 1882, in reconsolidate_children
new_kwargs = self._visit_children_values(kwargs, key, parent_key, self._reconsolidate)
File "/venv/lib/python3.10/site-packages/reacton/core.py", line 2120, in _visit_children_values
return {k: self._visit_children_values(v, f"{key}{k}/", parent_key, f) for k, v in value.items()}
File "/venv/lib/python3.10/site-packages/reacton/core.py", line 2120, in <dictcomp>
return {k: self._visit_children_values(v, f"{key}{k}/", parent_key, f) for k, v in value.items()}
File "/venv/lib/python3.10/site-packages/reacton/core.py", line 2115, in _visit_children_values
values = [self._visit_children_values(v, f"{key}{index}/", parent_key, f) for index, v in enumerate(value)]
File "/venv/lib/python3.10/site-packages/reacton/core.py", line 2115, in <listcomp>
values = [self._visit_children_values(v, f"{key}{index}/", parent_key, f) for index, v in enumerate(value)]
File "/venv/lib/python3.10/site-packages/reacton/core.py", line 2112, in _visit_children_values
return f(value, key, parent_key)
File "/venv/lib/python3.10/site-packages/reacton/core.py", line 1794, in _reconsolidate
widget = self._reconsolidate(child_context.root_element_next, "/", new_parent_key)
File "/venv/lib/python3.10/site-packages/reacton/core.py", line 1794, in _reconsolidate
widget = self._reconsolidate(child_context.root_element_next, "/", new_parent_key)
File "/venv/lib/python3.10/site-packages/reacton/core.py", line 1794, in _reconsolidate
widget = self._reconsolidate(child_context.root_element_next, "/", new_parent_key)
File "/venv/lib/python3.10/site-packages/reacton/core.py", line 1914, in _reconsolidate
kwargs = reconsolidate_children()
File "/venv/lib/python3.10/site-packages/reacton/core.py", line 1882, in reconsolidate_children
new_kwargs = self._visit_children_values(kwargs, key, parent_key, self._reconsolidate)
File "/venv/lib/python3.10/site-packages/reacton/core.py", line 2120, in _visit_children_values
return {k: self._visit_children_values(v, f"{key}{k}/", parent_key, f) for k, v in value.items()}
File "/venv/lib/python3.10/site-packages/reacton/core.py", line 2120, in <dictcomp>
return {k: self._visit_children_values(v, f"{key}{k}/", parent_key, f) for k, v in value.items()}
File "/venv/lib/python3.10/site-packages/reacton/core.py", line 2115, in _visit_children_values
values = [self._visit_children_values(v, f"{key}{index}/", parent_key, f) for index, v in enumerate(value)]
File "/venv/lib/python3.10/site-packages/reacton/core.py", line 2115, in <listcomp>
values = [self._visit_children_values(v, f"{key}{index}/", parent_key, f) for index, v in enumerate(value)]
File "/venv/lib/python3.10/site-packages/reacton/core.py", line 2112, in _visit_children_values
return f(value, key, parent_key)
File "/venv/lib/python3.10/site-packages/reacton/core.py", line 1794, in _reconsolidate
widget = self._reconsolidate(child_context.root_element_next, "/", new_parent_key)
File "/venv/lib/python3.10/site-packages/reacton/core.py", line 1794, in _reconsolidate
widget = self._reconsolidate(child_context.root_element_next, "/", new_parent_key)
File "/venv/lib/python3.10/site-packages/reacton/core.py", line 1914, in _reconsolidate
kwargs = reconsolidate_children()
File "/venv/lib/python3.10/site-packages/reacton/core.py", line 1882, in reconsolidate_children
new_kwargs = self._visit_children_values(kwargs, key, parent_key, self._reconsolidate)
File "/venv/lib/python3.10/site-packages/reacton/core.py", line 2120, in _visit_children_values
return {k: self._visit_children_values(v, f"{key}{k}/", parent_key, f) for k, v in value.items()}
File "/venv/lib/python3.10/site-packages/reacton/core.py", line 2120, in <dictcomp>
return {k: self._visit_children_values(v, f"{key}{k}/", parent_key, f) for k, v in value.items()}
File "/venv/lib/python3.10/site-packages/reacton/core.py", line 2115, in _visit_children_values
values = [self._visit_children_values(v, f"{key}{index}/", parent_key, f) for index, v in enumerate(value)]
File "/venv/lib/python3.10/site-packages/reacton/core.py", line 2115, in <listcomp>
values = [self._visit_children_values(v, f"{key}{index}/", parent_key, f) for index, v in enumerate(value)]
File "/venv/lib/python3.10/site-packages/reacton/core.py", line 2112, in _visit_children_values
return f(value, key, parent_key)
File "/venv/lib/python3.10/site-packages/reacton/core.py", line 1794, in _reconsolidate
widget = self._reconsolidate(child_context.root_element_next, "/", new_parent_key)
File "/venv/lib/python3.10/site-packages/reacton/core.py", line 1794, in _reconsolidate
widget = self._reconsolidate(child_context.root_element_next, "/", new_parent_key)
File "/venv/lib/python3.10/site-packages/reacton/core.py", line 1914, in _reconsolidate
kwargs = reconsolidate_children()
File "/venv/lib/python3.10/site-packages/reacton/core.py", line 1882, in reconsolidate_children
new_kwargs = self._visit_children_values(kwargs, key, parent_key, self._reconsolidate)
File "/venv/lib/python3.10/site-packages/reacton/core.py", line 2120, in _visit_children_values
return {k: self._visit_children_values(v, f"{key}{k}/", parent_key, f) for k, v in value.items()}
File "/venv/lib/python3.10/site-packages/reacton/core.py", line 2120, in <dictcomp>
return {k: self._visit_children_values(v, f"{key}{k}/", parent_key, f) for k, v in value.items()}
File "/venv/lib/python3.10/site-packages/reacton/core.py", line 2115, in _visit_children_values
values = [self._visit_children_values(v, f"{key}{index}/", parent_key, f) for index, v in enumerate(value)]
File "/venv/lib/python3.10/site-packages/reacton/core.py", line 2115, in <listcomp>
values = [self._visit_children_values(v, f"{key}{index}/", parent_key, f) for index, v in enumerate(value)]
File "/venv/lib/python3.10/site-packages/reacton/core.py", line 2112, in _visit_children_values
return f(value, key, parent_key)
File "/venv/lib/python3.10/site-packages/reacton/core.py", line 1794, in _reconsolidate
widget = self._reconsolidate(child_context.root_element_next, "/", new_parent_key)
File "/venv/lib/python3.10/site-packages/reacton/core.py", line 1998, in _reconsolidate
self._remove_element(self.context.elements[key], key, parent_key=parent_key)
File "/venv/lib/python3.10/site-packages/reacton/core.py", line 2066, in _remove_element
assert widget.model_id in widgets.Widget.widgets
AssertionError
I just tested with 2 tabs (one in incognito) via Jupyter. The 2 simulations in the 2 tabs run in parallel, independently. This means that we can offload #481 to point to how to expose a Jupyter server to be web public.
One complication is that, some users probably don't want to expose their full source code. I will ask at Solara Discord about this topic.
It seems to be a fixable problem. From Solara Discord:
Solara should work in multiple tabs. I can be that somewhere, your app has global shared state that's accessed from multiple threads (each tab runs in it's own thread)
Fixed in #1759 for Solara.