Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions frameworks/django/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,14 @@ Django 6.1 as an ASGI application on Uvicorn.
| `/baseline11` | POST | Sums query parameters + request body |
| `/json/{count}?m=N` | GET | First `count` dataset items with `total = price * quantity * m` |
| `/echo` | POST | Returns the request body back verbatim |
| `/async-db` | GET | Items in a price range, read from Postgres |
| `/crud/items`, `/crud/items/{id}` | GET/POST/PUT | CRUD over Postgres, cache-aside on Redis |
| `/static/{filename}` | GET | Static asset read from `/data/static` per request |
| `/fortunes` | GET | 200 rows from Postgres + a runtime row, sorted and rendered as HTML |

## Notes

- URLconf routing with the `int` path converter, async views returning `HttpResponse` / `JsonResponse`
- Compression through `django.middleware.gzip.GZipMiddleware`
- `/fortunes` renders `templates/fortunes.html` with the Django Template Language on every request, behind the cached loader. Escaping the `<script>` row is DTL's own autoescape, not handler-side work; the sort is Python's code-point ordering, which is the byte order the profile requires
- ASGI and not WSGI because the WSGI request drops `Transfer-Encoding: chunked` bodies, which the baseline profile sends
49 changes: 49 additions & 0 deletions frameworks/django/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,38 @@
import redis.asyncio as aioredis
from django.conf import settings

BASE_DIR = os.path.dirname(os.path.abspath(__file__))

settings.configure(
DEBUG=False,
ALLOWED_HOSTS=["*"],
SECRET_KEY="httparena",
ROOT_URLCONF=__name__,
MIDDLEWARE=["django.middleware.gzip.GZipMiddleware"],
# The Django Template Language renders /fortunes. The cached loader parses
# the template once per worker instead of on every render, which is the
# production configuration for a filesystem template directory.
TEMPLATES=[
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [os.path.join(BASE_DIR, "templates")],
"OPTIONS": {
"loaders": [
(
"django.template.loaders.cached.Loader",
["django.template.loaders.filesystem.Loader"],
)
]
},
}
],
LOGGING_CONFIG=None,
)
django.setup()

from django.core.asgi import get_asgi_application # noqa: E402
from django.http import FileResponse, HttpResponse, JsonResponse # noqa: E402
from django.template.loader import get_template # noqa: E402
from django.urls import path # noqa: E402

DATASET_PATH = os.environ.get("DATASET_PATH", "/data/dataset.json")
Expand Down Expand Up @@ -298,6 +318,34 @@ async def static_file(request, filename):
)


# -- Fortunes ----------------------------------------------------------------
# Rendered per request by the Django Template Language, from a template that
# lives in its own file under templates/. DTL autoescapes every {{ }} it
# interpolates, so the <script> row in the seed comes out encoded without the
# handler touching it.

FORTUNES_TEMPLATE = get_template("fortunes.html")
RUNTIME_FORTUNE = {"id": 0, "message": "Additional fortune added at request time."}


async def fortunes(request):
pool = await _pool()
if pool is None:
return HttpResponse(status=500)
try:
rows = await pool.fetch("SELECT id, message FROM fortune")
except Exception:
return HttpResponse(status=500)
items = [{"id": r["id"], "message": r["message"]} for r in rows]
items.append(RUNTIME_FORTUNE)
# Python orders str by code point, which is the byte order of their UTF-8
# encoding - the ordinal sort the profile asks for. A locale-aware collation
# would order the rows differently from one runtime to the next.
items.sort(key=lambda fortune: fortune["message"])
# No content_type: HttpResponse already defaults to text/html; charset=utf-8.
return HttpResponse(FORTUNES_TEMPLATE.render({"fortunes": items}))


urlpatterns = [
path("pipeline", pipeline),
path("baseline11", baseline11),
Expand All @@ -307,6 +355,7 @@ async def static_file(request, filename):
path("async-db", async_db),
path("crud/items", crud_items),
path("crud/items/<int:item_id>", crud_item),
path("fortunes", fortunes),
path("static/<str:filename>", static_file),
]

Expand Down
5 changes: 3 additions & 2 deletions frameworks/django/meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"response": true
},
"engine": "uvicorn",
"description": "Django 6.1 as an ASGI application on Uvicorn. URLconf routing and path converters through the Django API, JsonResponse for the JSON payload, GZipMiddleware for compression.",
"description": "Django 6.1 as an ASGI application on Uvicorn. URLconf routing and path converters through the Django API, JsonResponse for the JSON payload, GZipMiddleware for compression. Postgres through asyncpg for async-db and fortunes, the latter rendered per request by the Django Template Language from a template file, with the cached loader in front of it.",
"repo": "https://github.com/django/django",
"enabled": true,
"tests": [
Expand All @@ -20,7 +20,8 @@
"json-comp",
"json-tls",
"8gbit",
"async-db"
"async-db",
"fortunes"
],
"maintainers": []
}
10 changes: 10 additions & 0 deletions frameworks/django/templates/fortunes.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head><meta charset="utf-8"><title>Fortunes</title></head>
<body>
<table>
<tr><th>id</th><th>message</th></tr>
{% for fortune in fortunes %}<tr><td>{{ fortune.id }}</td><td>{{ fortune.message }}</td></tr>
{% endfor %}</table>
</body>
</html>
2 changes: 1 addition & 1 deletion site/data/frameworks.json
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@
},
"django": {
"dir": "django",
"description": "Django 6.1 as an ASGI application on Uvicorn. URLconf routing and path converters through the Django API, JsonResponse for the JSON payload, GZipMiddleware for compression.",
"description": "Django 6.1 as an ASGI application on Uvicorn. URLconf routing and path converters through the Django API, JsonResponse for the JSON payload, GZipMiddleware for compression. Postgres through asyncpg for async-db and fortunes, the latter rendered per request by the Django Template Language from a template file, with the cached loader in front of it.",
"repo": "https://github.com/django/django",
"type": "flagship",
"engine": "uvicorn",
Expand Down
51 changes: 35 additions & 16 deletions site/data/results/django.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,31 @@
{
"framework": "django",
"results": {
"8gbit-512": {
"framework": "django",
"language": "Python",
"rps": 27527,
"avg_latency": "935603.5us",
"p99_latency": "3001344.0us",
"cpu": "5152.2%",
"memory": "5.3GiB",
"connections": 512,
"threads": 64,
"duration": "5s",
"pipeline": 1,
"bandwidth": "286.75MB/s",
"input_bw": "268.82MB/s",
"reconnects": 0,
"status_2xx": 137750,
"status_3xx": 0,
"status_4xx": 0,
"status_5xx": 0,
"cpu_usec": 288488329,
"cpu_per_req_us": 2094.2891,
"target_rate": 50000,
"rate_ratio": 0.5505,
"p99_9_latency": "3546112.0us"
},
"async-db-1024": {
"framework": "django",
"language": "Python",
Expand Down Expand Up @@ -41,30 +66,24 @@
"status_4xx": 0,
"status_5xx": 0
},
"8gbit-512": {
"fortunes-1024": {
"framework": "django",
"language": "Python",
"rps": 27527,
"avg_latency": "935603.5us",
"p99_latency": "3001344.0us",
"cpu": "5152.2%",
"memory": "5.3GiB",
"connections": 512,
"rps": 5500,
"avg_latency": "139.17ms",
"p99_latency": "412.20ms",
"cpu": "5117.8%",
"memory": "5.4GiB",
"connections": 1024,
"threads": 64,
"duration": "5s",
"pipeline": 1,
"bandwidth": "286.75MB/s",
"input_bw": "268.82MB/s",
"bandwidth": "132.21MB/s",
"reconnects": 0,
"status_2xx": 137750,
"status_2xx": 27503,
"status_3xx": 0,
"status_4xx": 0,
"status_5xx": 0,
"cpu_usec": 288488329,
"cpu_per_req_us": 2094.2891,
"target_rate": 50000,
"rate_ratio": 0.5505,
"p99_9_latency": "3546112.0us"
"status_5xx": 8654
},
"json-comp-16384": {
"framework": "django",
Expand Down
Loading