Skip to content

Project Story

mkuch edited this page Jul 29, 2026 · 1 revision

Project Story

pytest-poolwatch began with a scheduler graph that looked wrong.

Observation

A cooperative pytest workload had a configured concurrency limit, runnable tests still waiting, and yet active concurrency repeatedly fell far below the limit. Ordinary pytest duration reports could show slow tests, but they could not answer whether the scheduler was keeping all execution slots occupied.

Measurement

The first analysis reconstructed overlapping test intervals and plotted active tests over time. That made the failure mode visible:

configured capacity: high
queued work:          still present
active tests:         unexpectedly low

This was not normal end-of-suite drain because the queue was not empty.

Root cause and upstream fix

Investigation found a refill loop in pytest-asyncio-cooperative that replaced only one task after asyncio.wait(..., FIRST_COMPLETED) returned several completed tasks together.

The minimal fix changed one-shot replacement into refill-until-full behavior. The accompanying regression test forced several slots to become free in one batch so the bug could not hide behind event-loop timing.

That work became pytest-asyncio-cooperative PR #86.

Generalization

Once the specific bug was fixed upstream, building another scheduler would have solved the wrong problem. The reusable missing tool was observability:

Given pytest reports, configured capacity, and collected work, did the active scheduler remain work-conserving?

That became pytest-poolwatch.

The original product direction

The initial project brief proposed a broad concurrency diagnostic tool for ordinary pytest, pytest-asyncio, cooperative async scheduling, concurrent async groups, xdist, and custom schedulers. Its central distinction was:

queued > 0 and active < target  → scheduler underfill
queued = 0 and active < target  → natural drain

It also imagined later event-loop watchdogs, named async phases, blocking-work markers, and safe thread offload helpers.

Deliberate v0.1 scope

Version 0.1 implements the evidence that can be reconstructed reliably from public pytest reports:

  • start and finish collection;
  • phase-aware intervals;
  • configured or detected capacity;
  • active and queued timelines;
  • utilization and idle slot-seconds;
  • scheduler-underfill windows;
  • terminal, JSON, and HTML reports;
  • deterministic before/after examples.

It deliberately does not claim event-loop starvation detection. An active async test might be progressing, waiting on an API, or blocking the loop; report timestamps alone cannot distinguish those states.

Engineering principle

The project follows the complete diagnostic cycle:

observation → measurement → analysis → root cause → upstream fix → reusable tool

PoolWatch should continue in that spirit: add a diagnosis only when a stable, public signal can support it.

Name

“PoolWatch” describes the narrow promise: observe the execution pool. It is not a runner, scheduler, fixture manager, or cloud service.

See Examples for the reproducible regression and Roadmap for future work.

Clone this wiki locally