Skip to content

PYTHON-6114 & PYTHON-5418 Move the mod_wsgi tests from Evergreen to GitHub Actions - #30

Draft
blink1073 wants to merge 21 commits into
mainfrom
mod-wsgi-github-actions
Draft

blink1073 wants to merge 21 commits into
mainfrom
mod-wsgi-github-actions

Conversation

@blink1073

@blink1073 blink1073 commented Sep 19, 2026

Copy link
Copy Markdown
Owner

PYTHON-6114
PYTHON-5418

Changes in this PR

  • Run the mod_wsgi tests on pull requests in two GitHub Actions jobs, Mod WSGI Min Deps and Mod WSGI Latest, each covering the standalone and embedded modes.
  • Test the minimum dependencies with the oldest supported CPython against MongoDB 6.0, and the newest dependencies with the newest supported CPython against the latest MongoDB; the versions come from CPYTHONS and ALL_VERSIONS in generate_config_utils.py.
  • Install mod_wsgi from a mod_wsgi dependency group instead of consuming toolchain builds.
  • Remove the mod_wsgi tasks and build variant from the Evergreen config.
  • Set WSGIDestroyInterpreter Off in the test Apache config; mod_wsgi 6 deletes the thread states of still-running threads when it destroys interpreters as child processes exit, which crashes live pymongo monitor threads.
  • Add a just smoke-mod-wsgi recipe that runs the same steps locally in an ubuntu container.
  • Update test/mod_wsgi_test/README.rst and CONTRIBUTING.md for the new workflow.

Subinterpreter support (PYTHON-5418)

  • PeriodicExecutor now falls back to a non-daemon thread when daemon threads are disallowed (subinterpreters), and the monitor shutdown handler is registered with threading._register_atexit so executors are stopped and joined before interpreter teardown.
  • New tests: PeriodicExecutor in a real subinterpreter, concurrent MongoClients across subinterpreters, and MongoClient inside the standard InterpreterPoolExecutor (Python 3.14+).

Test Plan

  • just smoke-mod-wsgi passes: both modes, two rounds each, against a single-node replica set in an ubuntu:24.04 container with mod_wsgi 6.0.6.
  • A workflow_dispatch run of test-python.yml is green for both Mod WSGI jobs (min-deps round on MongoDB 6.0 / Python 3.10, latest round on latest MongoDB / Python 3.14).
  • just lint, just lint-manual, just typing, and uv lock --check all pass.

Checklist

Checklist for Author

  • Did you update the changelog (if necessary)?
  • Is there test coverage?
  • Is any followup work tracked in a JIRA ticket? If so, add link(s). (Upstream mod_wsgi issue for the child-exit crash pending; tracked in the PYTHON-6114 pitfalls.)

Checklist for Reviewer

  • Does the title of the PR reference a JIRA Ticket?
  • Do you fully understand the implementation? (Would you be comfortable explaining how this code works to someone else?)
  • Is all relevant documentation (README or docstring) updated?

Comment thread .github/workflows/test-python.yml Fixed
Comment thread .github/workflows/test-python.yml Fixed
Comment thread pymongo/auth_shared.py Fixed
@GrahamDumpleton

Copy link
Copy Markdown

Set WSGIDestroyInterpreter Off in the test Apache config; mod_wsgi 6 deletes the thread states of still-running threads when it destroys interpreters as child processes exit, which crashes live pymongo monitor threads.

It is not actually mod_wsgi that is doing this. Likely you are using a configuration of mod_wsgi for testing that is using Python sub interpreters and CPython as a result does it when a Python sub interpreter is destroyed. If you ignore this and don't try to shutdown the thread yourself when the interpreter is being cleaned up, then pymongo could crash with any Python application that started using Python sub interpreters and it isn't going to be a mod_wsgi specific issue. Things are a bit more forgiving when background threads run in the main interpreter context, so you likely have never seen an issue there for normal use of pymongo. For sub interpreters though, CPython has been a bit more strict about what happens with background threads in sub interpreters for at least a couple of Python versions now. So just be aware that not addressing the actual issue may come back and bite you in the future if people start actually using pymongo in Python sub interpreters using the Python APIs which allow sub interpreter access from Python code (vs from a C extension).

@GrahamDumpleton

Copy link
Copy Markdown

If you don't already, I would actually suggest you start incorporating tests which use concurrent.interpreters (https://docs.python.org/3/library/concurrent.interpreters.html) to create sub interpreters yourself from Python code and exercise pymongo in a sub interpreter and then cleanup up the sub interpreter. This will likely reveal the same issue.

@blink1073

Copy link
Copy Markdown
Owner Author

Great, thanks for the pointers @GrahamDumpleton!

The Evergreen tasks depend on toolchain builds of mod_wsgi.so for each
Python version. The new Mod WSGI jobs install mod_wsgi with pip on an
ubuntu runner and test the latest stable CPython against a replica set
in both daemon and embedded mode. mod_wsgi is pinned to 4.9.4 because
releases 5 and newer crash Apache children in embedded mode under load.
The jobs test the lowest supported MongoDB with the oldest CPython and
minimum dependencies, and the latest MongoDB with the newest CPython.
mod_wsgi moves into a dependency group pinned to 4.9.4, because releases
5 and newer crash Apache children in embedded mode under load.
mod_wsgi 6 destroys interpreters as child processes exit and deletes
the thread states of still-running threads, crashing live pymongo
monitor threads. Set WSGIDestroyInterpreter Off in the test Apache
config to keep the mod_wsgi 4 lifecycle. The smoke script raises the
nofile limit so mongod is not starved by the connection storm, and the
test client now dumps the Apache error log on any failure.
@blink1073
blink1073 force-pushed the mod-wsgi-github-actions branch from 45c7020 to bb36972 Compare September 21, 2026 17:18
Pin the action to a commit hash so the PR's CodeQL check no longer
flags the new setup-mongodb steps for using an unpinned third-party
action ref.
PeriodicExecutor now falls back to a non-daemon thread when daemon threads are disallowed (subinterpreters). The monitor shutdown handler is also registered with threading._register_atexit so executors are stopped and joined before interpreter teardown.

Adds regression tests for PeriodicExecutor in a real subinterpreter and for concurrent MongoClients across subinterpreters (Python 3.14+).
Runs live MongoClients inside interpreters managed by the standard
InterpreterPoolExecutor, mirroring the existing multi-threaded checks.
The pool's interpreters do not allow daemon threads, so this exercises
the non-daemon monitor thread fallback and its shutdown handling.
@blink1073 blink1073 changed the title PYTHON-6114 Move the mod_wsgi tests from Evergreen to GitHub Actions PYTHON-6114 & PYTHON-5418 Move the mod_wsgi tests from Evergreen to GitHub Actions Sep 21, 2026
The worker interpreters may not have the repo root on sys.path (the
stdlib test package shadows the repo's), so the pickled worker function
failed to unpickle.  Submit the builtin exec with a code string that
inserts the main interpreter's sys.path before importing pymongo.
@blink1073

Copy link
Copy Markdown
Owner Author

Moved to mongodb#3059

- Pin uv to an immutable release with checksum verification in the smoke
  test instead of piping the mutable install script into a shell.
- Install the minimum mod_wsgi version (4.9.4) in the min deps job; the
  lock file has the latest version and TEST_MIN_DEPS does not cover the
  mod_wsgi group.
- Only register the monitor shutdown handler with
  threading._register_atexit in interpreters that disallow daemon
  threads, preserving normal main interpreter shutdown ordering.
- Pin actions/checkout to a commit hash to satisfy the semgrep
  mutable-action-tag check.
- Clarify the test matrix documentation: MongoDB 6.0 is hardcoded
  because ubuntu-22.04 runners cannot install older versions.
- Add timeout-minutes and apt-get update to the new jobs, guard
  interpreter cleanup in the tests, and fix the rationale for calling
  the test scripts directly in the CONTRIBUTING instructions.
Add mod_wsgi to GROUP_MAP so the group is included in the
--resolution=lowest-direct resolution, testing the minimum version from
pyproject.toml rather than hardcoding it in the workflow.
The root phase already runs before the unprivileged re-exec and has
curl, so install the pinned uv to /usr/local/bin there instead of
installing it again as the smoke user.
Runs live AsyncMongoClients inside interpreters managed by
InterpreterPoolExecutor. The async client runs its background tasks on
the interpreter's own event loop rather than in threads, covering the
other shutdown path.
Invert the is_running guard in test_subinterpreters: closing an idle
interpreter runs threading._shutdown, which stops and joins pymongo's
monitor threads, so the successful path must close them. Note in the
changelog that both clients are covered by the subinterpreter tests.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants