perf(robot-server): Remove slow re-exports from __init__.py and protocols/__init__.py #14480
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Overview
This rearranges some of robot-server's internal
import
statements to make it significantly faster to launch certain subprocesses. See #14465 (comment) for background. This goes towards RSS-451.This is paired with a PR in oe-core: Opentrons/oe-core#134
Benchmarks
This brings the startup time of #14465's worker processes down from ~74 sec to ~31 sec. Tested on an OT-2 with this command:
(The call to
_get_legacy_ot_types()
is to make sure we account for the cost of its dynamic import._up_to_3_helper
is a thing from my work in progress for #14465 and it may not make it into the final PR, so don't be surprised if you can't import it; but it's basically just the code for that PR's worker process, extracted to a standalone file.)Explanation
In Python, if you have a tree like this:
And then you do this:
The Python interpreter will evaluate
module_1
,module_2
, andmodule_3
, because they were mentioned insome_package/__init__.py
. When you factor in all the subdependencies ofmodule_2
andmodule_3
, this can be very very slow.In the case of #14465, worker processes for database migration code were wasting nearly 45 seconds importing things like FastAPI routers, which are irrelevant to them.
Review requests
We will definitely regress on this performance improvement unless we deliberately guard against it.
I propose, going forward, we adopt a house rule to "very rarely" re-export things from
__init__.py
files. Acceptable re-exports are ones likeopentrons.protocol_api.<...>
, where we have a strict public interface that we want to be ergonomic for users. All other re-exports are questionable.Then, without those re-exports, we need a way to delineate private stuff from public stuff. I propose we lean harder into prefixing the filenames with underscores. We've already done this in a few places, but it's been pretty ad-hoc so far. Underscore-prefixed filenames are a common, but not universal, Python convention. (Examples: h11, trio, requests; counterexamples: sqlalchemy, pydantic.)
Does all of that sound good?
Then, we could take this a lot further.
opentrons.protocol_engine
's imports, for example, are pretty heavy. You can't just import command models without also pulling in all of the execution logic, and all of the dependencies of that execution logic, including the hardware API, etc. etc.Test plan
make dev
still worksRisk assessment
Pretty low.