FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

fix: avoid blocking the event loop during file upload retries by 1fanwang · Pull Request #2939 · googleapis/python-genai · GitHub

fix: avoid blocking the event loop during file upload retries - #2939

Open
1fanwang wants to merge 5 commits into
googleapis:mainfrom
1fanwang:1fannnw/async-upload-retry
Open

1fanwang wants to merge 5 commits into
googleapis:mainfrom
1fanwang:1fannnw/async-upload-retry

Conversation

1fanwang commented Sep 3, 2026 •
edited
Loading

Copy link
Copy Markdown

Summary

Async HTTPX upload retries blocked other coroutines. The retry now awaits its delay, and CI runs the existing upload regressions.

Fixes #2938

Testing Done

  • Local code review completed

Save as repro.py; run PYTHONPATH=. python repro.py on baseline and this PR. The peer is local; the SDK delay is unchanged.

Public HTTPX reproducer
import asyncio, io, time
from concurrent.futures import ThreadPoolExecutor
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from google.genai import Client, types
import httpx

class Peer(BaseHTTPRequestHandler):
  attempts: int = 0

  def do_POST(self) -> None:
    self.rfile.read(int(self.headers['Content-Length']))
    start = self.headers['X-Goog-Upload-Command'] == 'start'
    if not start:
      Peer.attempts += 1
    payload = b'{"file":{"name":"files/local"}}'
    self.send_response(200)
    if start:
      self.send_header('X-Goog-Upload-URL',
                       f'http://{self.headers["Host"]}/upload/v1beta/files')
    elif Peer.attempts == 2:
      self.send_header('X-Goog-Upload-Status', 'final')
    self.send_header('Content-Type', 'application/json')
    self.send_header('Content-Length', str(len(payload)))
    self.end_headers()
    self.wfile.write(payload)

async def check_upload(base_url: str) -> None:
  with Client(
          vertexai=False, api_key='local-only',
          http_options=types.HttpOptions(
              base_url=base_url,
              httpx_async_client=httpx.AsyncClient(trust_env=False),
          ),
      ) as client, io.BytesIO(b'local') as payload:
    async with client.aio:
      upload = asyncio.create_task(client.aio.files.upload(
          file=payload, config={'mime_type': 'text/plain'}))
      gap = 0.0
      while not upload.done():
        last = time.monotonic()
        await asyncio.sleep(0.01)
        gap = max(gap, time.monotonic() - last)
      print((await upload).name, round(gap, 3))
      assert gap < 0.2

with ThreadingHTTPServer(('127.0.0.1', 0), Peer) as server, ThreadPoolExecutor(max_workers=1) as pool:
  pool.submit(server.serve_forever)
  try:
    asyncio.run(check_upload(f'http://127.0.0.1:{server.server_port}'))
  finally:
    server.shutdown()
Before: files/local 1.011
AssertionError
After: files/local 0.013

Signed-off-by: 1fanwang <1fannnw@gmail.com>
Venkaiahbabuneelam self-assigned this Sep 3, 2026
Venkaiahbabuneelam added the size:M Code changes between 10-40 lines label Sep 3, 2026
Signed-off-by: 1fanwang <1fannnw@gmail.com>
Signed-off-by: 1fanwang <1fannnw@gmail.com>
1fanwang changed the title fix: keep async file upload retries cancellable fix: avoid blocking the event loop during file upload retries Sep 12, 2026
Signed-off-by: 1fanwang <1fannnw@gmail.com>
Signed-off-by: 1fanwang <1fannnw@gmail.com>

This branch has not been deployed

No deployments
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M Code changes between 10-40 lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Async file upload retry blocks the event loop

2 participants


Back | FazBrowse Home | New Git URL