mikebridge opened a new pull request, #45364: URL: https://github.com/apache/superset/pull/45364
### SUMMARY **TL;DR:** the `Python-Unit` job dies at 99% on roughly one run in ten since October 12 because every unit-test module leaks a full Superset app. Each test module builds its own `SupersetApp` through the module-scoped `app` fixture, and two process-wide singletons kept every app ever built reachable, so each pytest-xdist worker's resident memory grew by about 10 MB per module and reached 5 to 7 GB per worker on the 857-module suite. Two workers plus the controller no longer fit on the hosted runner; the VM stops responding and the job sits until its 45-minute timeout. Fixes #45362. **The two retention roots** 1. `SupersetAppInitializer.configure_celery` subclassed whatever `celery_app.Task` currently was and installed the subclass. On a second initialization in the same process that was the previous `AppContextTask`, so each wrapper wrapped the last one and every app ever initialized stayed reachable through the task class hierarchy (after 8 test modules the MRO carried 9 wrappers). The wrapper now carries a marker and a reference to the base it wrapped; a re-initialization unwraps one level and subclasses the original Celery base again. A single initialization, which is every production path, finds no marker and constructs exactly what it did before. 2. `init_views` adds a set of menu items to the Flask-AppBuilder singleton on every initialization, and each item's link closure holds the app that registered it. The unit fixture only reset `appbuilder.baseviews`; it now resets `appbuilder.menu` alongside. **Fixture design.** The fixture stays module-scoped. With both roots fixed, each module's app is collectable on its own (measured: 8 apps initialized, 1 alive at the end), so changing the scope was unnecessary. A session-scoped app would also have removed the growth, but it would share one app across the suite's several hundred in-place `app.config[...]` mutations and the indirect `app` parametrizations, a much larger blast radius for the same result. The fixture body moved into a module-level `build_app()` helper so the regression test and the fixture initialize apps the same way. **Regression test.** `tests/unit_tests/test_app_reinitialization.py` initializes twice and asserts that `celery_app.Task.__mro__` does not grow (and carries exactly one wrapper) and that the first app is dead through a weakref after `gc.collect()`, naming the referrer types if not. Both tests fail on `master` (`assert 7 == 6`; `assert <SupersetApp ...> is None`) and pass here. **Evidence** Lost-runner kills of the unit-tests job (job lingered 15 minutes or more after the step ended; GitHub annotation "The job has exceeded the maximum execution time of 45m0s"), from the Actions API: zero in 278 cancelled runs between the xdist switch (#44178, 2026-09-12) and 2026-10-11; then 1, 4, 23 and 22 on October 12 through 15, against about 500 successes, including `master` itself. Sample killed runs: https://github.com/apache/superset/actions/runs/38619149658, https://github.com/apache/superset/actions/runs/38752136752 (both attempts), and `master` https://github.com/apache/superset/actions/runs/38563726398. The suite grew from 21,457 to 27,507 items between October 3 and October 15; the retained cost is per module, so growth alone crossed the ceiling. Full suite with the exact CI command at `-n 2 --dist loadfile`, per-worker resident memory (local macOS; absolute numbers differ from Linux, the before/after ratio is the point): | Tree | Worker | Peak RSS | Max RSS | Result | |---|---|---|---|---| | master e0e728ef | gw0 | 5,068 MB | 5,548 MB | 28 failed (local baseline), 27,433 passed | | master e0e728ef | gw1 | 5,467 MB | 6,348 MB | | | this branch | gw0 | 3,130 MB | 3,151 MB | 22 failed (strict subset of the baseline), 27,441 passed | | this branch | gw1 | 3,634 MB | 3,642 MB | | The branch's failures are a strict subset of master's local baseline (the six extra on master are `check_pot_drift_test.py`, which cannot run from the archive export used for the baseline). A residual climb of about 1 to 2 MB per module remains, down from 10 MB, and is left for a follow-up. **Reviews.** python-review and preset-review lenses (0 blocking; all LOW and the one MEDIUM, which asked for the full-suite numbers above, addressed before the first commit), plus an independent Codex review that approved the behaviour and asked for the annotation and docstring additions in the second commit. This PR and its description were generated by Claude (AI) on behalf of @mikebridge, who directed the investigation and reviewed the result. ### BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF Not applicable. ### TESTING INSTRUCTIONS 1. On `master`, run `pytest tests/unit_tests/test_app_reinitialization.py` with this branch's test file and conftest only: both tests fail. 2. On this branch, the same command passes. 3. Full suite: `pytest -n 2 --dist loadfile --cov=superset ./tests/common ./tests/unit_tests` while sampling each worker's RSS (for example `ps -o rss` on the `gw*` processes, or a small `pytest_runtest_protocol` hookwrapper reading `psutil.Process().memory_info().rss`): peaks drop from 5 to 7 GB per worker to about 3 to 3.6 GB, and the per-module growth drops from about 10 MB to 1 to 2 MB. ### ADDITIONAL INFORMATION - [x] Has associated issue: Fixes #45362 - [ ] Required feature flags: - [ ] Changes UI - [ ] Includes DB Migration (follow approval process in [SIP-59](https://github.com/apache/superset/issues/13351)) - [ ] Migration is atomic, supports rollback & is backwards-compatible - [ ] Confirm DB migration upgrade and downgrade tested - [ ] Runtime estimates and downtime expectations provided - [ ] Introduces new feature or API - [ ] Removes existing feature or API 🤖 Generated with [Claude Code](https://claude.com/claude-code) -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
