|
@@ -0,0 +1,136 @@
|
|
|
+#!/usr/bin/env python3
|
|
|
+from random import randint, sample
|
|
|
+import os
|
|
|
+import os.path
|
|
|
+from typing import Any, Dict, List, Tuple
|
|
|
+from urllib.parse import parse_qs
|
|
|
+
|
|
|
+import asyncpg
|
|
|
+import jinja2
|
|
|
+import orjson
|
|
|
+
|
|
|
+from bareasgi import Application, HttpRequest, HttpResponse, LifespanRequest
|
|
|
+from bareasgi_jinja2 import add_jinja2, Jinja2TemplateProvider
|
|
|
+
|
|
|
+GET_WORLD = "SELECT id, randomnumber FROM world WHERE id = $1"
|
|
|
+UPDATE_WORLD = "UPDATE world SET randomNumber = $2 WHERE id = $1"
|
|
|
+GET_FORTUNES = "SELECT * FROM fortune"
|
|
|
+ADDITIONAL_ROW = (0, "Additional fortune added at request time.")
|
|
|
+
|
|
|
+
|
|
|
+async def on_startup(_request: LifespanRequest) -> None:
|
|
|
+ app.info['db'] = await asyncpg.create_pool(
|
|
|
+ user=os.getenv("PGUSER", "benchmarkdbuser"),
|
|
|
+ password=os.getenv("PGPASS", "benchmarkdbpass"),
|
|
|
+ database="hello_world",
|
|
|
+ host="tfb-database",
|
|
|
+ port=5432,
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
+async def on_shutdown(_request: LifespanRequest) -> None:
|
|
|
+ await app.info['db'].close()
|
|
|
+
|
|
|
+
|
|
|
+async def handle_json_request(_request: HttpRequest) -> HttpResponse:
|
|
|
+ return HttpResponse.from_json(
|
|
|
+ {"message": "Hello, World!"},
|
|
|
+ encode_bytes=orjson.dumps
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
+async def handle_plaintext_request(_request: HttpRequest) -> HttpResponse:
|
|
|
+ return HttpResponse.from_text("Hello, World!")
|
|
|
+
|
|
|
+
|
|
|
+async def handle_db_request(_request: HttpRequest) -> HttpResponse:
|
|
|
+ key = randint(1, 10000)
|
|
|
+
|
|
|
+ async with app.info['db'].acquire() as conn:
|
|
|
+ number = await conn.fetchval(GET_WORLD, key)
|
|
|
+
|
|
|
+ return HttpResponse.from_json(
|
|
|
+ {"id": key, "randomNumber": number},
|
|
|
+ encode_bytes=orjson.dumps
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
+def get_query_count(request: HttpRequest):
|
|
|
+ try:
|
|
|
+ qs = parse_qs(request.scope["query_string"])
|
|
|
+ num_queries = int(qs.get(b'queries', (1, ))[0])
|
|
|
+ except ValueError:
|
|
|
+ num_queries = 1
|
|
|
+ if num_queries < 1:
|
|
|
+ return 1
|
|
|
+ if num_queries > 500:
|
|
|
+ return 500
|
|
|
+
|
|
|
+ return num_queries
|
|
|
+
|
|
|
+
|
|
|
+async def handle_queries_request(request: HttpRequest) -> HttpResponse:
|
|
|
+ queries = get_query_count(request)
|
|
|
+
|
|
|
+ worlds: List[Dict[str, Any]] = []
|
|
|
+ async with app.info['db'].acquire() as conn:
|
|
|
+ pst = await conn.prepare(GET_WORLD)
|
|
|
+ for key in sample(range(1, 10000), queries):
|
|
|
+ number = await pst.fetchval(key)
|
|
|
+ worlds.append({"id": key, "randomNumber": number})
|
|
|
+
|
|
|
+ return HttpResponse.from_json(
|
|
|
+ worlds,
|
|
|
+ encode_bytes=orjson.dumps
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
+async def handle_updates_request(request: HttpRequest) -> HttpResponse:
|
|
|
+ queries = get_query_count(request)
|
|
|
+ updates = [(row_id, randint(1, 10000)) for row_id in sample(range(1, 10000), queries)]
|
|
|
+ updates.sort()
|
|
|
+ worlds = [{'id': row_id, 'randomNumber': number} for row_id, number in updates]
|
|
|
+
|
|
|
+ async with app.info['db'].acquire() as connection:
|
|
|
+ statement = await connection.prepare(GET_WORLD)
|
|
|
+ for row_id, number in updates:
|
|
|
+ await statement.fetchval(row_id)
|
|
|
+ await connection.executemany(UPDATE_WORLD, updates)
|
|
|
+
|
|
|
+ return HttpResponse.from_json(
|
|
|
+ worlds,
|
|
|
+ encode_bytes=orjson.dumps
|
|
|
+ )
|
|
|
+
|
|
|
+async def handle_fortunes_request(request: HttpRequest) -> HttpResponse:
|
|
|
+ async with app.info['db'].acquire() as conn:
|
|
|
+ rows = await conn.fetch(GET_FORTUNES)
|
|
|
+ rows.append(ADDITIONAL_ROW)
|
|
|
+ rows.sort(key=lambda row: row[1])
|
|
|
+
|
|
|
+ return await Jinja2TemplateProvider.apply(
|
|
|
+ request,
|
|
|
+ "fortune.html",
|
|
|
+ { "fortunes": rows }
|
|
|
+ )
|
|
|
+
|
|
|
+app = Application(
|
|
|
+ startup_handlers=[on_startup],
|
|
|
+ shutdown_handlers=[on_shutdown]
|
|
|
+)
|
|
|
+
|
|
|
+here = os.path.abspath(os.path.dirname(__file__))
|
|
|
+env = jinja2.Environment(
|
|
|
+ loader=jinja2.FileSystemLoader(os.path.join(here, 'templates')),
|
|
|
+ autoescape=jinja2.select_autoescape(['html', 'xml']),
|
|
|
+ enable_async=True
|
|
|
+)
|
|
|
+
|
|
|
+add_jinja2(app, env)
|
|
|
+
|
|
|
+app.http_router.add({"GET"}, "/json", handle_json_request)
|
|
|
+app.http_router.add({"GET"}, "/plaintext", handle_plaintext_request)
|
|
|
+app.http_router.add({"GET"}, "/db", handle_db_request)
|
|
|
+app.http_router.add({"GET"}, "/queries", handle_queries_request)
|
|
|
+app.http_router.add({"GET"}, "/updates", handle_updates_request)
|
|
|
+app.http_router.add({"GET"}, "/fortunes", handle_fortunes_request)
|