diff --git a/frameworks/Python/aiohttp/aiohttp-pg-raw.dockerfile b/frameworks/Python/aiohttp/aiohttp-orm.dockerfile similarity index 91% rename from frameworks/Python/aiohttp/aiohttp-pg-raw.dockerfile rename to frameworks/Python/aiohttp/aiohttp-orm.dockerfile index f6dda6f5ef5..fe1648e3ff4 100644 --- a/frameworks/Python/aiohttp/aiohttp-pg-raw.dockerfile +++ b/frameworks/Python/aiohttp/aiohttp-orm.dockerfile @@ -7,7 +7,7 @@ WORKDIR aiohttp RUN pip3 install cython==3.0.11 && \ pip3 install -r /aiohttp/requirements.txt -ENV CONNECTION=RAW +WORKDIR /aiohttp EXPOSE 8080 diff --git a/frameworks/Python/aiohttp/aiohttp.dockerfile b/frameworks/Python/aiohttp/aiohttp.dockerfile index fe1648e3ff4..f6dda6f5ef5 100644 --- a/frameworks/Python/aiohttp/aiohttp.dockerfile +++ b/frameworks/Python/aiohttp/aiohttp.dockerfile @@ -7,7 +7,7 @@ WORKDIR aiohttp RUN pip3 install cython==3.0.11 && \ pip3 install -r /aiohttp/requirements.txt -WORKDIR /aiohttp +ENV CONNECTION=RAW EXPOSE 8080 diff --git a/frameworks/Python/aiohttp/app/main.py b/frameworks/Python/aiohttp/app/main.py index 30680138c33..6ed65ed19d7 100644 --- a/frameworks/Python/aiohttp/app/main.py +++ b/frameworks/Python/aiohttp/app/main.py @@ -64,13 +64,13 @@ async def db_ctx(app: web.Application): def setup_routes(app): if CONNECTION_ORM: - app.router.add_get('/json', json) app.router.add_get('/db', single_database_query_orm) app.router.add_get('/queries/{queries:.*}', multiple_database_queries_orm) app.router.add_get('/fortunes', fortunes) app.router.add_get('/updates/{queries:.*}', updates) - app.router.add_get('/plaintext', plaintext) else: + app.router.add_get('/json', json) + app.router.add_get('/plaintext', plaintext) app.router.add_get('/db', single_database_query_raw) app.router.add_get('/queries/{queries:.*}', multiple_database_queries_raw) app.router.add_get('/fortunes', fortunes_raw) diff --git a/frameworks/Python/aiohttp/app/views.py b/frameworks/Python/aiohttp/app/views.py index ef2b04ed7bc..932969ed23d 100644 --- a/frameworks/Python/aiohttp/app/views.py +++ b/frameworks/Python/aiohttp/app/views.py @@ -1,19 +1,37 @@ +import random from operator import attrgetter, itemgetter from pathlib import Path -from random import randint, sample +import aiohttp.web import jinja2 +import sqlalchemy +import sqlalchemy.orm import orjson -from aiohttp.web import Response -from sqlalchemy import select -from sqlalchemy.orm.attributes import flag_modified -from .models import sa_fortunes, sa_worlds, Fortune, World +from . import models + +# In current versions of Python (tested 3.9 and 3.12), from ... import ... +# creates variables that are atleast 4x slower to reference: +# > python3 -m timeit 'from random import sample' 'sample' +# 500000 loops, best of 5: 823 nsec per loop +# > python3 -m timeit 'import random' 'random.sample' +# 2000000 loops, best of 5: 161 nsec per loop +# > python3 -m timeit 'import random; sample = random.sample' 'sample' +# 2000000 loops, best of 5: 161 nsec per loop +dumps = orjson.dumps +randint = random.randint +sample = random.sample +Response = aiohttp.web.Response +select = sqlalchemy.select +flag_modified = sqlalchemy.orm.attributes.flag_modified +Fortune = models.Fortune +World = models.World ADDITIONAL_FORTUNE_ORM = Fortune(id=0, message='Additional fortune added at request time.') ADDITIONAL_FORTUNE_ROW = {'id': 0, 'message': 'Additional fortune added at request time.'} READ_ROW_SQL = 'SELECT "randomnumber", "id" FROM "world" WHERE id = $1' -READ_SELECT_ORM = select(World.randomnumber) +READ_SELECT_ORM = select(World.randomnumber).where(World.id == sqlalchemy.bindparam("id")) +READ_FORTUNES_ORM = select(Fortune.id, Fortune.message) WRITE_ROW_SQL = 'UPDATE "world" SET "randomnumber"=$2 WHERE id=$1' template_path = Path(__file__).parent / 'templates' / 'fortune.jinja' @@ -33,12 +51,14 @@ def get_num_queries(request): return 500 return num_queries + def json_response(payload): return Response( - body=orjson.dumps(payload), + body=dumps(payload), content_type="application/json", ) + async def json(request): """ Test 1 @@ -52,7 +72,7 @@ async def single_database_query_orm(request): """ id_ = randint(1, 10000) async with request.app['db_session']() as sess: - num = await sess.scalar(select(World.randomnumber).filter_by(id=id_)) + num = await sess.scalar(READ_SELECT_ORM, {"id": id_}) return json_response({'id': id_, 'randomNumber': num}) @@ -78,7 +98,7 @@ async def multiple_database_queries_orm(request): result = [] async with request.app['db_session']() as sess: for id_ in ids: - num = await sess.scalar(READ_SELECT_ORM.where(World.id == id_)) + num = await sess.scalar(READ_SELECT_ORM, {"id": id_}) result.append({'id': id_, 'randomNumber': num}) return json_response(result) @@ -107,7 +127,7 @@ async def fortunes(request): Test 4 ORM """ async with request.app['db_session']() as sess: - ret = await sess.execute(select(Fortune.id, Fortune.message)) + ret = await sess.execute(READ_FORTUNES_ORM) fortunes = ret.all() fortunes.append(ADDITIONAL_FORTUNE_ORM) fortunes.sort(key=sort_fortunes_orm) @@ -132,21 +152,18 @@ async def updates(request): Test 5 ORM """ num_queries = get_num_queries(request) - - ids = sample(range(1, 10000 + 1), num_queries) - ids.sort() - worlds = [] + update_ids = sample(range(1, 10001), num_queries) + update_ids.sort() + updates = tuple(zip(update_ids, sample(range(1, 10001), num_queries))) + worlds = [{'id': row_id, 'randomNumber': number} for row_id, number in updates] async with request.app['db_session'].begin() as sess: - for row_id in ids: - random_number = randint(1, 10000) - world = await sess.get(World, row_id, populate_existing=True) - world.randomnumber = random_number - # force sqlalchemy to UPDATE entry even if the value has not changed - # doesn't make sense in a real application, added only for pass `tfb verify` + for id_, number in updates: + world = await sess.get(World, id_, populate_existing=True) + world.randomnumber = number + # Force sqlalchemy to UPDATE entry even if the value has not changed + # doesn't make sense in a real application, added only to pass tests. flag_modified(world, "randomnumber") - worlds.append({'id': row_id, 'randomNumber': random_number}) - return json_response(worlds) async def updates_raw(request): @@ -154,16 +171,16 @@ async def updates_raw(request): Test 5 RAW """ num_queries = get_num_queries(request) - ids = sample(range(1, 10000 + 1), num_queries) - ids.sort() - updates = [(row_id, randint(1, 10000)) for row_id in ids] + update_ids = sample(range(1, 10001), num_queries) + update_ids.sort() + updates = tuple(zip(update_ids, sample(range(1, 10001), num_queries))) worlds = [{'id': row_id, 'randomNumber': number} for row_id, number in updates] async with request.app['pg'].acquire() as conn: stmt = await conn.prepare(READ_ROW_SQL) - for row_id in ids: + for id_, _ in updates: # the result of this is the int previous random number which we don't actually use - await stmt.fetchval(row_id) + await stmt.fetchval(id_) await conn.executemany(WRITE_ROW_SQL, updates) return json_response(worlds) diff --git a/frameworks/Python/aiohttp/benchmark_config.json b/frameworks/Python/aiohttp/benchmark_config.json index dad6b2fcc87..ab4257e12bf 100644 --- a/frameworks/Python/aiohttp/benchmark_config.json +++ b/frameworks/Python/aiohttp/benchmark_config.json @@ -15,15 +15,15 @@ "framework": "aiohttp", "language": "Python", "flavor": "Python3", - "orm": "Full", + "orm": "Raw", "platform": "asyncio", "webserver": "gunicorn", "os": "Linux", "database_os": "Linux", "display_name": "aiohttp", - "notes": "uses aiopg with sqlalchemy for database access" + "notes": "uses asyncpg for database access" }, - "pg-raw": { + "orm": { "db_url": "/db", "query_url": "/queries/", "fortune_url": "/fortunes", @@ -35,13 +35,13 @@ "framework": "aiohttp", "language": "Python", "flavor": "Python3", - "orm": "Raw", + "orm": "Full", "platform": "asyncio", "webserver": "gunicorn", "os": "Linux", "database_os": "Linux", - "display_name": "aiohttp-pg-raw", - "notes": "uses asyncpg for database access", + "display_name": "aiohttp-orm", + "notes": "uses sqlalchemy for database access", "versus": "default" } }] diff --git a/frameworks/Python/aiohttp/config.toml b/frameworks/Python/aiohttp/config.toml index d22b4c5bcc2..dd9088e497d 100644 --- a/frameworks/Python/aiohttp/config.toml +++ b/frameworks/Python/aiohttp/config.toml @@ -13,12 +13,12 @@ classification = "Micro" database = "Postgres" database_os = "Linux" os = "Linux" -orm = "Full" +orm = "Raw" platform = "asyncio" webserver = "gunicorn" versus = "None" -[pg-raw] +[orm] urls.db = "/db" urls.query = "/queries/" urls.update = "/updates/" @@ -28,7 +28,7 @@ classification = "Micro" database = "Postgres" database_os = "Linux" os = "Linux" -orm = "Raw" +orm = "Full" platform = "asyncio" webserver = "gunicorn" versus = "default" diff --git a/frameworks/Python/aiohttp/requirements.txt b/frameworks/Python/aiohttp/requirements.txt index aec2ed27c64..99681a2600a 100644 --- a/frameworks/Python/aiohttp/requirements.txt +++ b/frameworks/Python/aiohttp/requirements.txt @@ -2,6 +2,6 @@ aiohttp==3.11.16 asyncpg==0.30.0 gunicorn==23.0.0 jinja2==3.1.6 -SQLAlchemy==2.0.39 +SQLAlchemy==2.0.40 orjson==3.10.16 uvloop==0.21.0