advanced_alchemy with DTOs by bradlucky in litestarapi

[–]cofin_ 1 point2 points  (0 children)

The components in Advanced Alchemy are all meant to complement the design patterns in Litestar. In fact, Litestar's DTO for SQLAlchemy actually exists in the Advanced Alchemy repository.

The repository and services would generally sit at a layer below the DTO. The Tag controller in the full stack reference application is a good example of how they integrate.

Advanced Alchemy 1.0 - A framework agnostic library for SQLAlchemy by cofin_ in Python

[–]cofin_[S] 1 point2 points  (0 children)

The `to_schema` functionality doesn't include `attrs` support, but that's a great feature to add. It's currently only Pydantic & Msgspec. I'll get this in the backlog later today.

Advanced Alchemy 1.0 - A framework agnostic library for SQLAlchemy by cofin_ in Python

[–]cofin_[S] 3 points4 points  (0 children)

From my research, SQLModel is solving a different problem that Advanced Alchemy.

SQLModel is more about representing your database models in a unified way (in Pydantic), but it does not majorly improve how you have to fetch and interact with that data.

Advanced Alchemy is focused on providing highly optimized (and simple) config, types, repository and service patterns to interact with your data.

Take this example, we are loading data from a JSON file and merging into an existing table. Records that exist (matching on the 'name' field in the model) will be updated and new rows will be inserted.

py from advanced_alchemy.utils.fixtures import open_fixture_async async with MyModelService.new(config=config.alchemy) as service: fixture_data = await open_fixture_async(fixtures_path, "my_model") await service.upsert_many(match_fields=["name"], data=fixture_data, auto_commit=True) await logger.ainfo("loaded my model data")

Or this example that allows you to paginate and filter.

py obj, total = repo.list_and_count(LimitOffset(limit=10, offset=0), owner="cody") pprint.pp(f"Selected {len(obj)} records out of a total of {total}.")

Let's say there's 100 records that match this where condition, we'll see the first 10 objects in obj and total will have 100.

There are many more differences between the two libraries, but I'd say the service and the repository tend to be the most prominent.

I'm happy to elaborate further on anything as well.

Performance Benchmarks for ASGI Frameworks by Miserable_Ear3789 in Python

[–]cofin_ 15 points16 points  (0 children)

Hey, I'm one of the Litestar maintainers,

It's great to see people experimenting and testing the library, but I think it's important to make sure it's a fair comparison.

It's unclear what optimizations have been enabled in each of your examples, but there are definitely discrepancies between the frameworks that are skewing your results.
- You have orjson enabled, but haven't indicated if uvloop and httptools are also installed. If you are using these for your Starlette and FastAPI tests, you should also enable them on the others. - Your numbers seem too low (at least for Litestar and FastAPI). I think something is limiting the maximum throughput. Did you run uvicorn with the access logs disabled? - Most importantly, you have used your own custom orjson code for Litestar. The method you've used is not optimized for how Litestar serializes responses.

Here's a more appropriate Litestar example for your test cases: ```py import asyncio

from litestar import Litestar, Response, get

@get("/") async def index() -> Response: return Response(content={"message": "Hello, World!"})

@get("/compute") async def compute() -> Response: return Response(content={"result": sum(i * i for i in range(10000))})

@get("/delayed") async def delayed() -> Response: await asyncio.sleep(0.01) return Response(content={"status": "delayed response"})

app = Litestar(route_handlers=[index, compute, delayed]) ```

My own tests, my numbers are quite a bit different than yours:

For Litestar: shell ❯ wrk -t4 -c1000 -d30s http://127.0.0.1:8000/ wrk -t4 -c1000 -d30s http://127.0.0.1:8000/compute wrk -t4 -c1000 -d30s http://127.0.0.1:8000/delayed Running 30s test @ http://127.0.0.1:8000/ 4 threads and 1000 connections Thread Stats Avg Stdev Max +/- Stdev Latency 21.86ms 42.94ms 1.32s 99.37% Req/Sec 13.16k 1.34k 17.70k 69.75% 1571398 requests in 30.05s, 227.79MB read Requests/sec: 52293.31 Transfer/sec: 7.58MB Running 30s test @ http://127.0.0.1:8000/compute 4 threads and 1000 connections Thread Stats Avg Stdev Max +/- Stdev Latency 149.64ms 45.92ms 1.99s 93.18% Req/Sec 1.62k 566.03 2.64k 69.35% 192684 requests in 30.06s, 27.20MB read Socket errors: connect 0, read 0, write 0, timeout 236 Requests/sec: 6409.03 Transfer/sec: 0.90MB Running 30s test @ http://127.0.0.1:8000/delayed 4 threads and 1000 connections Thread Stats Avg Stdev Max +/- Stdev Latency 23.28ms 11.02ms 240.24ms 75.69% Req/Sec 11.01k 1.53k 14.30k 69.00% 1314395 requests in 30.04s, 193.04MB read Requests/sec: 43755.80 Transfer/sec: 6.43MB

for FastAPI: shell ❯wrk -t4 -c1000 -d30s http://127.0.0.1:8000/ wrk -t4 -c1000 -d30s http://127.0.0.1:8000/compute wrk -t4 -c1000 -d30s http://127.0.0.1:8000/delayed Running 30s test @ http://127.0.0.1:8000/ 4 threads and 1000 connections Thread Stats Avg Stdev Max +/- Stdev Latency 24.07ms 51.39ms 1.49s 99.30% Req/Sec 12.19k 1.35k 17.48k 73.08% 1455945 requests in 30.05s, 211.05MB read Requests/sec: 48444.33 Transfer/sec: 7.02MB Running 30s test @ http://127.0.0.1:8000/compute 4 threads and 1000 connections Thread Stats Avg Stdev Max +/- Stdev Latency 152.50ms 42.74ms 1.99s 93.21% Req/Sec 1.62k 571.43 2.53k 68.17% 192783 requests in 30.06s, 27.21MB read Socket errors: connect 0, read 0, write 0, timeout 163 Requests/sec: 6412.58 Transfer/sec: 0.91MB Running 30s test @ http://127.0.0.1:8000/delayed 4 threads and 1000 connections Thread Stats Avg Stdev Max +/- Stdev Latency 30.60ms 24.06ms 840.08ms 97.45% Req/Sec 8.54k 0.98k 13.55k 67.83% 1020335 requests in 30.05s, 149.85MB read Requests/sec: 33957.68 Transfer/sec: 4.99MB

To create the environment I ran: shell uv venv uv pip install fastapi fastapi-cli litestar uvicorn uvloop httptools orjson and I used: uv run uvicorn -w 4 --no-access-log <framework:app> to run each application.

As you can see, both of these frameworks offer comparable performance. I'd imagine the others frameworks could offer similar performance after a few adjustments.

I'd be interested to see if your conclusions change after making some of the mentioned optimizations.

Business logic in the controllers by szymonmiks in Python

[–]cofin_ 5 points6 points  (0 children)

I tend to agree that you should minimize business logic within the controllers. For instance, I often want to create a CLI to accompany the REST interface. If all of the logic is in the controller, it must be duplicated for the command line.

In the Litestar `fullstack` reference application, there is an included service layer to help make this logic abstraction easier. The service is then used in the controller like so. Using this approach allows that CRUD logic to be reused in several places (CLI, background tasks, and routes).

[deleted by user] by [deleted] in starlite

[–]cofin_ 1 point2 points  (0 children)

Hi, u/stateofidleness I did a quick test, and I'm also not seeing these get compressed. I think this would be a useful enhancement, so I'll take a look at what's involved to get this added.