pydantic / pydantic/httpx2

httpx + http2 raises a lot of exceptions when parallelized with ThreadPoolExecuton

Open
#802 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
1.5k
Forks
76
Avg merge
8h 59m
Merged PRs (30d)
24

Description

Originally opened by @takumi2786 on 2023-12-13 09:22:04 in encode/httpx

The starting point for issues should usually be a discussion...

https://github.com/encode/httpx/discussions

Possible bugs may be raised as a "Potential Issue" discussion, feature requests may be raised as an "Ideas" discussion. We can then determine if the discussion needs to be escalated into an "Issue" or not.

This will help us ensure that the "Issues" list properly reflects ongoing or needed work on the project.


  • Initially raised as discussion #...

Problem

I tried to use httpx as http2 client and I want to execute multiple requests in parallel.

First, I tried to parallelize with ThreadPoolExecutor, but it threw a lot of exceptions.
Then I used Asyncio and it worked.

reproducion

server

package main

import (
	"fmt"
	"log"
	"net/http"
	"time"

	"golang.org/x/net/http2"
)

func main() {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		time.Sleep(1 * time.Second)
		fmt.Println("Hello, HTTP/2!")
		fmt.Fprintf(w, "Hello, HTTP/2!")
	})

	server := &http.Server{
		Addr: ":8080",
	}

	http2.ConfigureServer(server, &http2.Server{})
	log.Fatal(server.ListenAndServeTLS("../server.crt", "../server.key"))
}

client

import asyncio
import httpx
from concurrent.futures import ThreadPoolExecutor, as_completed
from logging import basicConfig
basicConfig(level='DEBUG')

PARRALEL = 50

def request(client: httpx.Client):
    response = client.get(
        'https://localhost:8080/',
        timeout=httpx.Timeout(1.5),
    )
    print(response.http_version)
    print(response.text)
    return response

def batch_request():
    client = httpx.Client(
        http2=True,
        verify=False,
    )
    futures = []
    with ThreadPoolExecutor(max_workers=100) as executor:
        for i in range(PARRALEL):
            futures.append(executor.submit(request, client))    
    for future in as_completed(futures, timeout=5.0):
        result = future.result()
        print(result.http_version)
        print(result.text)

async def request_async(client: httpx.AsyncClient):
    response = await client.get(
        'https://localhost:8080/',
        timeout=httpx.Timeout(1.5),
    )
    print(response.http_version)
    print(response.text)
    return response

async def batch_request_async():
    async with httpx.AsyncClient(
        http2=True,
        verify=False,
    ) as client:
        tasks = []
        for i in range(PARRALEL):
            task = asyncio.create_task(request_async(client))
            tasks.append(task)

        for task in tasks:
            try:
                await task
            except Exception as e:
                print("exception!!!!", e)

def main_v2():
    loop = asyncio.get_event_loop()
    loop.run_until_complete(batch_request_async())
            
if __name__ == "__main__":
    # raise many error
    batch_request()

    # this works well
    # loop = asyncio.get_event_loop()
    # loop.run_until_complete(batch_request_async())

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the Python client reproduction using httpx.Client(http2=True) and ThreadPoolExecutor, then compare its behavior with the provided asyncio version and Go HTTP/2 server. The issue does not name repository files or tests; done means isolating the parallel-client failure and documenting or correcting the behavior with coverage for the reported scenario.

Written by the indexing model from the issue text.

Assessment

Tech stack
go, python
Domain
api, networking
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.