app.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. #!/usr/bin/env python3
  2. from random import randint, sample
  3. import os
  4. import os.path
  5. from typing import Any, Dict, List, Tuple
  6. from urllib.parse import parse_qs
  7. import asyncpg
  8. import jinja2
  9. import orjson
  10. from bareasgi import Application, HttpRequest, HttpResponse, LifespanRequest
  11. from bareasgi_jinja2 import add_jinja2, Jinja2TemplateProvider
  12. GET_WORLD = "SELECT id, randomnumber FROM world WHERE id = $1"
  13. UPDATE_WORLD = "UPDATE world SET randomNumber = $2 WHERE id = $1"
  14. GET_FORTUNES = "SELECT * FROM fortune"
  15. ADDITIONAL_ROW = (0, "Additional fortune added at request time.")
  16. async def on_startup(_request: LifespanRequest) -> None:
  17. app.info['db'] = await asyncpg.create_pool(
  18. user=os.getenv("PGUSER", "benchmarkdbuser"),
  19. password=os.getenv("PGPASS", "benchmarkdbpass"),
  20. database="hello_world",
  21. host="tfb-database",
  22. port=5432,
  23. )
  24. async def on_shutdown(_request: LifespanRequest) -> None:
  25. await app.info['db'].close()
  26. async def handle_json_request(_request: HttpRequest) -> HttpResponse:
  27. return HttpResponse.from_json(
  28. {"message": "Hello, World!"},
  29. encode_bytes=orjson.dumps
  30. )
  31. async def handle_plaintext_request(_request: HttpRequest) -> HttpResponse:
  32. return HttpResponse.from_text("Hello, World!")
  33. async def handle_db_request(_request: HttpRequest) -> HttpResponse:
  34. key = randint(1, 10000)
  35. async with app.info['db'].acquire() as conn:
  36. number = await conn.fetchval(GET_WORLD, key)
  37. return HttpResponse.from_json(
  38. {"id": key, "randomNumber": number},
  39. encode_bytes=orjson.dumps
  40. )
  41. def get_query_count(request: HttpRequest):
  42. try:
  43. qs = parse_qs(request.scope["query_string"])
  44. num_queries = int(qs.get(b'queries', (1, ))[0])
  45. except ValueError:
  46. num_queries = 1
  47. if num_queries < 1:
  48. return 1
  49. if num_queries > 500:
  50. return 500
  51. return num_queries
  52. async def handle_queries_request(request: HttpRequest) -> HttpResponse:
  53. queries = get_query_count(request)
  54. worlds: List[Dict[str, Any]] = []
  55. async with app.info['db'].acquire() as conn:
  56. pst = await conn.prepare(GET_WORLD)
  57. for key in sample(range(1, 10000), queries):
  58. number = await pst.fetchval(key)
  59. worlds.append({"id": key, "randomNumber": number})
  60. return HttpResponse.from_json(
  61. worlds,
  62. encode_bytes=orjson.dumps
  63. )
  64. async def handle_updates_request(request: HttpRequest) -> HttpResponse:
  65. queries = get_query_count(request)
  66. updates = [(row_id, randint(1, 10000)) for row_id in sample(range(1, 10000), queries)]
  67. updates.sort()
  68. worlds = [{'id': row_id, 'randomNumber': number} for row_id, number in updates]
  69. async with app.info['db'].acquire() as connection:
  70. statement = await connection.prepare(GET_WORLD)
  71. for row_id, number in updates:
  72. await statement.fetchval(row_id)
  73. await connection.executemany(UPDATE_WORLD, updates)
  74. return HttpResponse.from_json(
  75. worlds,
  76. encode_bytes=orjson.dumps
  77. )
  78. async def handle_fortunes_request(request: HttpRequest) -> HttpResponse:
  79. async with app.info['db'].acquire() as conn:
  80. rows = await conn.fetch(GET_FORTUNES)
  81. rows.append(ADDITIONAL_ROW)
  82. rows.sort(key=lambda row: row[1])
  83. return await Jinja2TemplateProvider.apply(
  84. request,
  85. "fortune.html",
  86. { "fortunes": rows }
  87. )
  88. app = Application(
  89. startup_handlers=[on_startup],
  90. shutdown_handlers=[on_shutdown]
  91. )
  92. here = os.path.abspath(os.path.dirname(__file__))
  93. env = jinja2.Environment(
  94. loader=jinja2.FileSystemLoader(os.path.join(here, 'templates')),
  95. autoescape=jinja2.select_autoescape(['html', 'xml']),
  96. enable_async=True
  97. )
  98. add_jinja2(app, env)
  99. app.http_router.add({"GET"}, "/json", handle_json_request)
  100. app.http_router.add({"GET"}, "/plaintext", handle_plaintext_request)
  101. app.http_router.add({"GET"}, "/db", handle_db_request)
  102. app.http_router.add({"GET"}, "/queries", handle_queries_request)
  103. app.http_router.add({"GET"}, "/updates", handle_updates_request)
  104. app.http_router.add({"GET"}, "/fortunes", handle_fortunes_request)