-
Notifications
You must be signed in to change notification settings - Fork 2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add Quart * Use asyncpg connection pooling and don't use prepared statements Turns out asyncpg explodes when you try to do concurrent requests on a single connection, so use a connection pool instead * quart: steal all the good ideas from starlette * use kwargs for pool configuration instead of DSN * use prepared statements, executemany, fetch as necessary * quart: steal more good ideas from starlette * run hypercorn with python config file * enable uvloop * quart: add alternate configuration with uvicorn
- Loading branch information
1 parent
519a5de
commit e62a8f5
Showing
10 changed files
with
276 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
# [Quart](https://gitlab.com/pgjones/quart) Benchmarking Test | ||
|
||
This benchmark uses Quart with the default Hypercorn server, and asyncpg for database connectivity | ||
(because there is still no good asyncio ORM, sadly). | ||
|
||
All code is contained in [app.py](app.py), and should be fairly self-documenting. | ||
|
||
## Test URLs | ||
### JSON | ||
|
||
http://localhost:8080/json | ||
|
||
### PLAINTEXT | ||
|
||
http://localhost:8080/plaintext | ||
|
||
### DB | ||
|
||
http://localhost:8080/db | ||
|
||
### QUERY | ||
|
||
http://localhost:8080/query?queries= | ||
|
||
### UPDATE | ||
|
||
http://localhost:8080/update?queries= | ||
|
||
### FORTUNES | ||
|
||
http://localhost:8080/fortunes |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
#!/usr/bin/env python3 | ||
import random | ||
import os | ||
|
||
import asyncpg | ||
from quart import Quart, jsonify, make_response, request, render_template | ||
|
||
app = Quart(__name__) | ||
|
||
GET_WORLD = "select randomnumber from world where id = $1" | ||
UPDATE_WORLD = "update world set randomNumber = $2 where id = $1" | ||
|
||
|
||
@app.before_first_request | ||
async def connect_to_db(): | ||
app.db = await asyncpg.create_pool( | ||
user=os.getenv("PGUSER", "benchmarkdbuser"), | ||
password=os.getenv("PGPASS", "benchmarkdbpass"), | ||
database="hello_world", | ||
host="tfb-database", | ||
port=5432, | ||
) | ||
|
||
|
||
@app.route("/json") | ||
def json(): | ||
return jsonify(message="Hello, World!") | ||
|
||
|
||
@app.route("/plaintext") | ||
async def plaintext(): | ||
response = await make_response(b"Hello, World!") | ||
# Quart assumes string responses are 'text/html', so make a custom one | ||
response.mimetype = "text/plain" | ||
return response | ||
|
||
|
||
@app.route("/db") | ||
async def db(): | ||
async with app.db.acquire() as conn: | ||
key = random.randint(1, 10000) | ||
number = await conn.fetchval(GET_WORLD, key) | ||
return jsonify({"id": key, "randomNumber": number}) | ||
|
||
|
||
def get_query_count(args): | ||
qc = args.get("queries") | ||
|
||
if qc is None: | ||
return 1 | ||
|
||
try: | ||
qc = int(qc) | ||
except ValueError: | ||
return 1 | ||
|
||
qc = max(qc, 1) | ||
qc = min(qc, 500) | ||
return qc | ||
|
||
|
||
@app.route("/queries") | ||
async def queries(): | ||
queries = get_query_count(request.args) | ||
|
||
worlds = [] | ||
async with app.db.acquire() as conn: | ||
pst = await conn.prepare(GET_WORLD) | ||
for _ in range(queries): | ||
key = random.randint(1, 10000) | ||
number = await pst.fetchval(key) | ||
worlds.append({"id": key, "randomNumber": number}) | ||
|
||
return jsonify(worlds) | ||
|
||
|
||
@app.route("/updates") | ||
async def updates(): | ||
queries = get_query_count(request.args) | ||
|
||
new_worlds = [] | ||
async with app.db.acquire() as conn, conn.transaction(): | ||
pst = await conn.prepare(GET_WORLD) | ||
|
||
for _ in range(queries): | ||
key = random.randint(1, 10000) | ||
old_number = await pst.fetchval(key) | ||
new_number = random.randint(1, 10000) | ||
new_worlds.append((key, new_number)) | ||
|
||
await conn.executemany(UPDATE_WORLD, new_worlds) | ||
|
||
return jsonify( | ||
[{"id": key, "randomNumber": new_number} for key, new_number in new_worlds] | ||
) | ||
|
||
|
||
@app.route("/fortunes") | ||
async def fortunes(): | ||
async with app.db.acquire() as conn: | ||
rows = await conn.fetch("select * from fortune") | ||
rows.append((0, "Additional fortune added at request time.")) | ||
rows.sort(key=lambda row: row[1]) | ||
|
||
return await render_template("fortunes.html", fortunes=rows) | ||
|
||
|
||
if __name__ == "__main__": | ||
app.run(host="0.0.0.0", port=8080) |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
{ | ||
"framework": "quart", | ||
"tests": [ | ||
{ | ||
"default": { | ||
"json_url": "/json", | ||
"plaintext_url": "/plaintext", | ||
"db_url": "/db", | ||
"query_url": "/queries?queries=", | ||
"update_url": "/updates?queries=", | ||
"fortune_url": "/fortunes", | ||
"port": 8080, | ||
"approach": "Realistic", | ||
"classification": "Micro", | ||
"database": "Postgres", | ||
"framework": "Quart", | ||
"language": "Python", | ||
"flavor": "None", | ||
"orm": "Raw", | ||
"platform": "None", | ||
"webserver": "Hypercorn", | ||
"os": "Linux", | ||
"database_os": "Linux", | ||
"display_name": "Quart", | ||
"notes": "", | ||
"versus": "None" | ||
}, | ||
"uvicorn": { | ||
"json_url": "/json", | ||
"plaintext_url": "/plaintext", | ||
"db_url": "/db", | ||
"query_url": "/queries?queries=", | ||
"update_url": "/updates?queries=", | ||
"fortune_url": "/fortunes", | ||
"port": 8080, | ||
"approach": "Realistic", | ||
"classification": "Micro", | ||
"database": "Postgres", | ||
"framework": "Quart", | ||
"language": "Python", | ||
"flavor": "Uvicorn", | ||
"orm": "Raw", | ||
"platform": "None", | ||
"webserver": "Uvicorn", | ||
"os": "Linux", | ||
"database_os": "Linux", | ||
"display_name": "Quart", | ||
"notes": "", | ||
"versus": "uvicorn" | ||
} | ||
} | ||
] | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import multiprocessing | ||
import os | ||
|
||
_is_travis = os.environ.get('TRAVIS') == 'true' | ||
|
||
workers = multiprocessing.cpu_count() | ||
if _is_travis: | ||
workers = 2 | ||
|
||
bind = "0.0.0.0:8080" | ||
keepalive = 120 | ||
errorlog = '-' | ||
loglevel = 'error' |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import multiprocessing | ||
import os | ||
|
||
_is_travis = os.environ.get('TRAVIS') == 'true' | ||
|
||
workers = multiprocessing.cpu_count() | ||
if _is_travis: | ||
workers = 2 | ||
|
||
bind = ["0.0.0.0:8080"] | ||
keep_alive_timeout = 120 | ||
worker_class = "uvloop" |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
FROM python:3.7-stretch | ||
|
||
ADD ./ /quart | ||
|
||
WORKDIR /quart | ||
|
||
RUN pip3 install -r /quart/requirements.txt | ||
RUN pip3 install -r /quart/requirements-uvicorn.txt | ||
|
||
CMD gunicorn app:app -k uvicorn.workers.UvicornWorker -c gunicorn_conf.py | ||
|
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
FROM python:3.7-stretch | ||
|
||
ADD ./ /quart | ||
|
||
WORKDIR /quart | ||
|
||
RUN pip3 install -r /quart/requirements.txt | ||
|
||
CMD hypercorn app:app --config=python:hypercorn_conf.py |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
Click==7.0 | ||
gunicorn==19.9.0 | ||
h11==0.8.1 | ||
httptools==0.0.13 | ||
uvicorn==0.4.6 | ||
uvloop==0.12.1 | ||
websockets==7.0 |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
aiofiles==0.4.0 | ||
asyncpg==0.18.3 | ||
blinker==1.4 | ||
Click==7.0 | ||
h11==0.8.1 | ||
h2==3.1.0 | ||
hpack==3.0.0 | ||
Hypercorn==0.5.3 | ||
hyperframe==5.2.0 | ||
itsdangerous==1.1.0 | ||
Jinja2==2.10 | ||
MarkupSafe==1.1.1 | ||
multidict==4.5.2 | ||
pytoml==0.1.20 | ||
Quart==0.8.1 | ||
sortedcontainers==2.1.0 | ||
typing-extensions==3.7.2 | ||
uvloop==0.12.1 | ||
wsproto==0.13.0 |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
<!DOCTYPE html> | ||
<html> | ||
<head><title>Fortunes</title></head> | ||
<body> | ||
<table> | ||
<tr><th>id</th><th>message</th></tr> | ||
{% for row in fortunes %} | ||
<tr><td>{{ row[0] }}</td><td>{{ row[1] }}</td></tr> | ||
{% endfor %} | ||
</table> | ||
</body> | ||
</html> |