fix: keep HTTP session open while visit() and streaming query generators are consumed - #1347
Conversation
…erators are consumed Vespa.visit() and Vespa.query(streaming=True) returned lazy generators from inside a `with VespaSync(...)` block, so the httpr client was closed before the first request was sent. httpr<=0.6.0 treated close() as a no-op and hid this; httpr>=0.7.0 releases the pool on close() and raises httpr.ClientClosed, which broke every app.visit(...) call. visit() now reference-counts the session across the outer generator and every slice generator, closing the client when the last one is exhausted or garbage collected. Streaming queries run in a generator that holds the session until the stream is drained. Regression tests cover both paths with a mock client that raises after close(). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Context from the httpr side (posted on behalf of @thomasht86): httpr 0.7.2 and pyvespa: what happened and what's next httpr 0.7.0 made What was done on the httpr side:
Next steps on the pyvespa side:
|
|
Verification with
|
Bump uv.lock so the uv-based CI jobs test the current httpr release, which reopens a closed client on use instead of raising ClientClosed. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2172e0c to
fe04895
Compare
|
Cloud integration suite on this branch with
Note: the earlier |
Summary
Vespa.visit()andVespa.query(streaming=True)returned lazy generators from inside awith VespaSync(...)block, so the underlyinghttpr.Clientwas closed before the first request was ever sent. This went unnoticed becausehttpr<=0.6.0treatedclose()as a no-op and a "closed" client kept working.httpr0.7.0/0.7.1 released the pool onclose()and raisedhttpr.ClientClosedon any later request, which broke everyapp.visit(...)call:Status on the httpr side: 0.7.1 is yanked, 0.7.0 was never published, and 0.7.2 reopens a closed client on use while emitting
httpr.ClientReopenedWarning(aResourceWarningsubclass), so released pyvespa versions keep working. This PR fixes the latent pyvespa bug regardless of httpr version, and no version bound is changed.Changes
Vespa.visit()now delegates to_visit_with_session(), which reference-counts the session: the outer generator and every slice generator hold one reference, and the client is closed when the last one is exhausted or garbage collected. This also keeps patterns likelist(app.visit(...))or handing each slice to a worker thread working, which a plainyield frominside thewithblock would not.Vespa.query(streaming=True)now runs in_query_streaming_with_session(), which keeps the session open until the stream is drained.tests/unit/test_application.py(TestSessionOutlivesLazyGenerators) use a mock client that raises afterclose(). They fail onmasterand pass with this change.uv.lockbumps httpr to 0.7.2 so theuv syncbased cloud jobs exercise the current release (the pip based jobs already resolve to it).Testing
See PR comments for the full runs. In short, with
httpr==0.7.1master fails 7 Docker and 2 Cloud integration tests viavisit, all passing with this fix; withhttpr==0.7.2the unit suite (696 tests) and the same 7 Docker tests pass, with noClientReopenedWarningemitted.🤖 Generated with Claude Code