Skip to content
This repository has been archived by the owner on Feb 25, 2023. It is now read-only.

Reduce usage of module level globals #424

Merged
merged 5 commits into from
Apr 25, 2021
Merged

Conversation

jhesketh
Copy link
Contributor

@jhesketh jhesketh commented Apr 14, 2021

This refactor instantiates Tickers (ie long running sub processes that
persist across requests+sessions) as part of the app. It stores them
inside FastAPI's app.state, which in turn is available on FastAPI's
Request object.

I could not find the most "FastAPI way" of doing this. From my reading, the way we were handling state with module level globals is generally the approach that FastAPI takes. This has some difficult draw backs with mocking and testing, not to mention potential complications on instantiating multiple redundant (or even conflicting) instances, and the general developer confusion about what they are accessing. There is some good discussion on it here: tiangolo/fastapi#81

The solution, as describe in the commit above, is to create all the Tickers ourselves and register them in app.state. This will make it trivial to swap them out for fake Tickers in our testing as we can build different app's.

Follow up:

  • Write example functional tests (will be much easier with this refactor).

Closes: #276
Closes: #282

@jhesketh jhesketh added the WIP Work in progress - Do not merge label Apr 14, 2021
@jhesketh jhesketh requested a review from jecluis April 14, 2021 13:59
@github-actions github-actions bot added the gravel Related to the Aquarium Backend label Apr 14, 2021
Copy link
Member

@jecluis jecluis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM but mypy is failing badly. I suspect is partially because of comment below, and because of a lot of calls are made using app.state.gstate, which will likely confuse the static checker because, I suspect, the type of app.state is not known.

src/gravel/controllers/gstate.py Outdated Show resolved Hide resolved
Copy link
Member

@jecluis jecluis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, commit is missing the DCO.

@jhesketh jhesketh removed the WIP Work in progress - Do not merge label Apr 21, 2021
@jhesketh
Copy link
Contributor Author

FYI I haven't done a deployment to verify everything still works since rebasing this.

@kshtsk
Copy link
Contributor

kshtsk commented Apr 21, 2021

jenkins run tumbleweed

@kshtsk
Copy link
Contributor

kshtsk commented Apr 21, 2021

jenkins run leap

@kshtsk
Copy link
Contributor

kshtsk commented Apr 21, 2021

I guess you saw these multiple traces:

>>> -- Logs begin at Wed 2021-04-21 12:20:10 UTC, end at Wed 2021-04-21 12:22:32 UTC. --
>>> Apr 21 12:20:46 localhost systemd[1]: Started Project Aquarium.
>>> Apr 21 12:20:49 localhost uvicorn[1317]: INFO:     Started server process [1317]
>>> Apr 21 12:20:49 localhost uvicorn[1317]: INFO:     Waiting for application startup.
>>> Apr 21 12:20:49 localhost uvicorn[1317]: INFO:     2021-04-21 12:20:49 -- aquarium -- Aquarium startup!
>>> Apr 21 12:20:49 localhost uvicorn[1317]: INFO:     2021-04-21 12:20:49 -- aquarium -- starting node manager
>>> Apr 21 12:20:49 localhost uvicorn[1317]: ERROR:    2021-04-21 12:20:49 -- on -- Traceback (most recent call last):
>>> Apr 21 12:20:49 localhost uvicorn[1317]:   File "/usr/local/lib/python3.8/site-packages/starlette/datastructures.py", line 669, in __getattr__
>>> Apr 21 12:20:49 localhost uvicorn[1317]:     return self._state[key]
>>> Apr 21 12:20:49 localhost uvicorn[1317]: KeyError: 'gstate'
>>> Apr 21 12:20:49 localhost uvicorn[1317]: During handling of the above exception, another exception occurred:
>>> Apr 21 12:20:49 localhost uvicorn[1317]: Traceback (most recent call last):
>>> Apr 21 12:20:49 localhost uvicorn[1317]:   File "/usr/local/lib/python3.8/site-packages/starlette/routing.py", line 526, in lifespan
>>> Apr 21 12:20:49 localhost uvicorn[1317]:     async for item in self.lifespan_context(app):
>>> Apr 21 12:20:49 localhost uvicorn[1317]:   File "/usr/local/lib/python3.8/site-packages/starlette/routing.py", line 467, in default_lifespan
>>> Apr 21 12:20:49 localhost uvicorn[1317]:     await self.startup()
>>> Apr 21 12:20:49 localhost uvicorn[1317]:   File "/usr/local/lib/python3.8/site-packages/starlette/routing.py", line 502, in startup
>>> Apr 21 12:20:49 localhost uvicorn[1317]:     await handler()
>>> Apr 21 12:20:49 localhost uvicorn[1317]:   File "./aquarium.py", line 99, in on_startup
>>> Apr 21 12:20:49 localhost uvicorn[1317]:     nodemgr: NodeMgr = NodeMgr(app.state.gstate)
>>> Apr 21 12:20:49 localhost uvicorn[1317]:   File "/usr/local/lib/python3.8/site-packages/starlette/datastructures.py", line 672, in __getattr__
>>> Apr 21 12:20:49 localhost uvicorn[1317]:     raise AttributeError(message.format(self.__class__.__name__, key))
>>> Apr 21 12:20:49 localhost uvicorn[1317]: AttributeError: 'State' object has no attribute 'gstate'
>>> Apr 21 12:20:49 localhost uvicorn[1317]: ERROR:    2021-04-21 12:20:49 -- on -- Traceback (most recent call last):
>>> Apr 21 12:20:49 localhost uvicorn[1317]:   File "/usr/local/lib/python3.8/site-packages/starlette/datastructures.py", line 669, in __getattr__
>>> Apr 21 12:20:49 localhost uvicorn[1317]:     return self._state[key]
>>> Apr 21 12:20:49 localhost uvicorn[1317]: KeyError: 'gstate'
>>> Apr 21 12:20:49 localhost uvicorn[1317]: During handling of the above exception, another exception occurred:
>>> Apr 21 12:20:49 localhost uvicorn[1317]: Traceback (most recent call last):
>>> Apr 21 12:20:49 localhost uvicorn[1317]:   File "/usr/local/lib/python3.8/site-packages/starlette/routing.py", line 526, in lifespan
>>> Apr 21 12:20:49 localhost uvicorn[1317]:     async for item in self.lifespan_context(app):
>>> Apr 21 12:20:49 localhost uvicorn[1317]:   File "/usr/local/lib/python3.8/site-packages/starlette/routing.py", line 467, in default_lifespan
>>> Apr 21 12:20:49 localhost uvicorn[1317]:     await self.startup()
>>> Apr 21 12:20:49 localhost uvicorn[1317]:   File "/usr/local/lib/python3.8/site-packages/starlette/routing.py", line 502, in startup
>>> Apr 21 12:20:49 localhost uvicorn[1317]:     await handler()
>>> Apr 21 12:20:49 localhost uvicorn[1317]:   File "./aquarium.py", line 99, in on_startup
>>> Apr 21 12:20:49 localhost uvicorn[1317]:     nodemgr: NodeMgr = NodeMgr(app.state.gstate)
>>> Apr 21 12:20:49 localhost uvicorn[1317]:   File "/usr/local/lib/python3.8/site-packages/starlette/datastructures.py", line 672, in __getattr__
>>> Apr 21 12:20:49 localhost uvicorn[1317]:     raise AttributeError(message.format(self.__class__.__name__, key))
>>> Apr 21 12:20:49 localhost uvicorn[1317]: AttributeError: 'State' object has no attribute 'gstate'
>>> Apr 21 12:20:49 localhost uvicorn[1317]: ERROR:    2021-04-21 12:20:49 -- on -- Application startup failed. Exiting.
>>> Apr 21 12:20:49 localhost uvicorn[1317]: ERROR:    2021-04-21 12:20:49 -- on -- Application startup failed. Exiting.

Copy link
Member

@jecluis jecluis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks really great, but there's a few things that are mostly nits, a few other things we can change in a follow-up PR, and one thing I'm pretty sure it's a hard bug.

src/aquarium.py Outdated Show resolved Hide resolved
src/gravel/controllers/gstate.py Outdated Show resolved Hide resolved
src/gravel/controllers/resources/storage.py Outdated Show resolved Hide resolved
src/gravel/tests/conftest.py Outdated Show resolved Hide resolved
This refactor instantiates Tickers (ie long running sub processes that
persist across requests+sessions) as part of the app. It stores them
inside FastAPI's `app.state`, which in turn is available on FastAPI's
`Request` object.

Signed-off-by: Joshua Hesketh <josh@nitrotech.org>
This change also reduces usage of mocks which can cause issues between
isolated tests.

Signed-off-by: Joshua Hesketh <josh@nitrotech.org>
Signed-off-by: Joshua Hesketh <josh@nitrotech.org>
jecluis
jecluis previously approved these changes Apr 23, 2021
@jhesketh jhesketh added the WIP Work in progress - Do not merge label Apr 23, 2021
Signed-off-by: Joao Eduardo Luis <joao@suse.com>
Copy link
Member

@jecluis jecluis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pushed a patch that fixes unresolved issues.

@jecluis
Copy link
Member

jecluis commented Apr 25, 2021

jenkins run tumbleweed

@jecluis jecluis merged commit b40d0bf into aquarist-labs:main Apr 25, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
gravel Related to the Aquarium Backend WIP Work in progress - Do not merge
Projects
None yet
Development

Successfully merging this pull request may close these issues.

gravel: reenable test_services.py unit test gravel: reduce dependency on global instances
3 participants