Skip to content

Speed up high-impact functional tests and replace slow ones with unit tests - #7997

Open
gerrod3 wants to merge 14 commits into
pulp:mainfrom
gerrod3:faster-impactful-tests
Open

Speed up high-impact functional tests and replace slow ones with unit tests#7997
gerrod3 wants to merge 14 commits into
pulp:mainfrom
gerrod3:faster-impactful-tests

Conversation

@gerrod3

@gerrod3 gerrod3 commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Speed up the functional tests that showed the largest CI wins (shared setup for replication, pulpimport, and pulp export; pagination; MIME types; checkpoints; hidden distros; cross-domain exports).
  • Replace the Redis fetch-batch functional test (60s sleep, 25 tasks) with a unit test, and add a unit test for missing-worker cleanup so the long-running functional test can stay nightly-only.
  • Timed 5× before/after on isolated lean stacks. Biggest saves: replication group ~127s (1.56x), pulpimport group ~62s (2.64x), repo-version pagination ~45s (5.76x).

Test plan

  • oci-env test -p pulpcore unit -- -k "test_missing_worker_cleanup_fails_abandoned_task or test_fetch_task_beyond_initial_batch"
  • oci-env test -p pulpcore functional -- -k "TestPulpImport or test_repo_version_pagination or TestCheckpointDistribution or test_checkpoint_retention or test_hidden_distros"
  • oci-env test -p pulp_file functional -- -k "test_content_types or TestSyncedRepoExport or test_fsexport_cross_domain"
  • Replication/cross-domain tests with PULP_DOMAIN_ENABLED=True

Made with Cursor

gerrod3 and others added 14 commits August 20, 2026 17:30
The functional test waits on the worker heartbeat cleanup interval, so
keep it long_running for e2e/nightly and cover the cleanup path with a
fast unit test for routine CI.

Assisted-By: Cursor Grok 4.5
Co-authored-by: Cursor <cursoragent@cursor.com>
The functional test slept 60s and queued 25 tasks. Cover
FETCH_TASK_LIMIT doubling in fetch_task without a live worker.

Assisted-By: Cursor Grok 4.6
Co-authored-by: Cursor <cursoragent@cursor.com>
Use a smaller page size so fewer versions are needed, and create content
with the synchronous upload API instead of task-backed creates.

Assisted-By: Cursor Grok 4.5
Co-authored-by: Cursor <cursoragent@cursor.com>
Pre-create content units so create tasks can overlap before the serial
modify and publish steps that assert distributed versions are retained.

Assisted-By: Cursor Grok 4.5
Co-authored-by: Cursor <cursoragent@cursor.com>
Dispatch content creates concurrently and use synchronous uploads,
cutting setup time for repo-version tests.

Assisted-By: Cursor Grok 4.5
Co-authored-by: Cursor <cursoragent@cursor.com>
Create units with the synchronous upload API and fetch Content-Type
headers concurrently on one HTTP session.

Assisted-By: Cursor Grok 4.5
Co-authored-by: Cursor <cursoragent@cursor.com>
Pre-create content with synchronous uploads, wait for checkpoint
timestamps only when needed, and run the class serially so xdist does
not rebuild five publications per test.

Assisted-By: Cursor Grok 4.5
Co-authored-by: Cursor <cursoragent@cursor.com>
Keep two checkpoints and retain_checkpoints=1, and skip creating an
unused distribution.

Assisted-By: Cursor Grok 4.6
Co-authored-by: Cursor <cursoragent@cursor.com>
Create two visible and two hidden distributions instead of five each;
that still covers index visibility without ten serial create tasks.

Assisted-By: Cursor Grok 4.5
Co-authored-by: Cursor <cursoragent@cursor.com>
Group consumers of import_export_repositories into a serial class with
class-scoped fixtures so the two immediate syncs run once. Keep light
import-check tests and self-contained cases parallel.

Assisted-By: Cursor Grok 4.5
Co-authored-by: Cursor <cursoragent@cursor.com>
Use a serial class with class-scoped three-repo sync and exporter fixtures
so xdist does not redo the expensive setup for each export consumer.

Assisted-By: Cursor Grok 4.5
Co-authored-by: Cursor <cursoragent@cursor.com>
Skip unused same-domain syncs and keep one uploaded file where a
version or last_export is needed for the 400 cases.

Assisted-By: Cursor Grok 4.6
Co-authored-by: Cursor <cursoragent@cursor.com>
Skip unused same-domain syncs and successful exports; keep one uploaded
file where a version or last_export is needed for the 400 cases.

Assisted-By: Cursor Grok 4.6
Co-authored-by: Cursor <cursoragent@cursor.com>
Populate upstreams with one uploaded file instead of 3-file fixture
syncs, and use fewer distributions where label/policy coverage remains.

Assisted-By: Cursor Grok 4.6
Co-authored-by: Cursor <cursoragent@cursor.com>
@dralley

dralley commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Is this officially replacing #7968? If so can that PR be closed?

@gerrod3

gerrod3 commented Aug 21, 2026

Copy link
Copy Markdown
Contributor Author

Is this officially replacing #7968? If so can that PR be closed?

Yes, once I'm done comparing the final times and ensuring the most impactful have been extracted, I'll close the old one.

@gerrod3

gerrod3 commented Aug 21, 2026

Copy link
Copy Markdown
Contributor Author

The speedup is kind of insane, a sub-20 azure run... Not sure if I just got lucky with the runner, but here's the explanation from AI:

CI is faster because the old tests were paying for setup over and over, not because the assertions got cheaper.

On test (pulp), pytest time vs a recent full-suite PR (fix/7910):

Suite Before This PR Saved
pulpcore functional parallel (-n 8) 6:33 (212 tests) 4:00 (192) 2:33
pulpcore functional serial 2:33 (36 tests) 2:24 (57) ~9s with 21 extra tests
pulp_file functional parallel 3:31 (99) 2:32 (95) 59s
pulp_file functional serial 25s (3) 34s (7) +9s (export class moved here)

Job wall clock: pulp 21 → 16 min, azure 26 → 18 min, s3 25 → 21 min. Azure dropped more because that matrix runs domains, where the replication suite was the biggest local win (~353s → ~226s).

Three mechanisms show up in the logs:

1. Stop repeating expensive setup under xdist.
Pulpimport and pulp export used to sync/export on every test. With -n 8, that setup ran many times at once and contended for the worker. They are now a serial class with class-scoped fixtures, so the two immediate syncs / three-repo export happen once. Parallel count dropped ~20 tests; serial picked them up and still got faster. Checkpoint did the same: five publications used to be rebuilt on every worker.

2. Skip the task round-trip for content create.
ContentFilesApi.create + monitor_task is a full task. upload (or overlapping creates) makes the unit appear in-process. That is most of pagination (55s → 9s locally), MIME types, checkpoints, and file_9_contents.

3. Do less work that the assertion does not need.
Replication used a 3-file fixture sync per upstream; it now uploads one file. Hidden distros created ten distributions; four still covers the index. Pagination used a large page size, so it had to create that many versions. The Redis fetch-batch test slept 60s and queued 25 tasks; that is a unit test now.

The isolated benches understated CI impact: a single pytest -k cannot show “setup once, eight workers.” CI with -n 8 is exactly where shared fixtures pay off.

@gerrod3
gerrod3 marked this pull request as ready for review August 21, 2026 15:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants