Kaynağa Gözat

[aiohttp] Tweaks (#9783)

* [aiohttp] Tweaks

* Rename aiohttp.dockerfile to aiohttp-orm.dockerfile

* Rename aiohttp-pg-raw.dockerfile to aiohttp.dockerfile

* Update benchmark_config.json

* Update config.toml

* Update benchmark_config.json

* Update requirements.txt

* Update frameworks/Python/aiohttp/requirements.txt

* Update config.toml

* Update benchmark_config.json

* Update views.py

* Update views.py

* Update views.py

* Update views.py

* Update views.py

* Update main.py

* Update views.py

* Update views.py

* Update views.py

* Update views.py

* Update views.py

* Update views.py

* Update views.py

* aiohtp orm use bindparam

---------

Co-authored-by: s_kovalev <[email protected]>
Sam Bull 4 ay önce
ebeveyn
işleme
22a6a52350

+ 1 - 1
frameworks/Python/aiohttp/aiohttp-pg-raw.dockerfile → 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
 

+ 1 - 1
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
 

+ 2 - 2
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)

+ 44 - 27
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)

+ 6 - 6
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"
     }
   }]

+ 3 - 3
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"

+ 1 - 1
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