app.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. import multiprocessing
  2. from pathlib import Path
  3. from random import randint, sample
  4. import asyncpg
  5. import jinja2
  6. from panther import Panther
  7. from panther.app import API
  8. from panther.events import Event
  9. from panther.request import Request
  10. from panther.response import Response, PlainTextResponse, HTMLResponse
  11. READ_ROW_SQL = 'SELECT "id", "randomnumber" FROM "world" WHERE id = $1'
  12. WRITE_ROW_SQL = 'UPDATE "world" SET "randomnumber"=$1 WHERE id=$2'
  13. ADDITIONAL_ROW = [0, 'Additional fortune added at request time.']
  14. MAX_POOL_SIZE = 1000 // multiprocessing.cpu_count()
  15. MIN_POOL_SIZE = max(int(MAX_POOL_SIZE / 2), 1)
  16. pool = None
  17. @Event.startup
  18. async def create_db_pool():
  19. global pool
  20. pool = await asyncpg.create_pool(
  21. user='benchmarkdbuser',
  22. password='benchmarkdbpass',
  23. database='hello_world',
  24. host='tfb-database',
  25. port=5432,
  26. min_size=MIN_POOL_SIZE,
  27. max_size=MAX_POOL_SIZE,
  28. )
  29. @Event.shutdown
  30. async def clean_db_pool():
  31. await pool.close()
  32. with Path('templates/fortune.html').open() as f:
  33. fortune_template = jinja2.Template(f.read())
  34. def get_num_queries(request):
  35. value = request.query_params.get('queries')
  36. if value is None:
  37. return 1
  38. try:
  39. query_count = int(value)
  40. except ValueError:
  41. return 1
  42. if query_count < 1:
  43. return 1
  44. if query_count > 500:
  45. return 500
  46. return query_count
  47. @API()
  48. async def json_serialization():
  49. return Response(data={'message': 'Hello, world!'})
  50. @API()
  51. async def single_database_query():
  52. row_id = randint(1, 10000)
  53. async with pool.acquire() as connection:
  54. number = await connection.fetchval(READ_ROW_SQL, row_id)
  55. return Response(data={'id': row_id, 'randomNumber': number})
  56. @API()
  57. async def multiple_database_queries(request: Request):
  58. num_queries = get_num_queries(request)
  59. row_ids = sample(range(1, 10000), num_queries)
  60. async with pool.acquire() as connection:
  61. statement = await connection.prepare(READ_ROW_SQL)
  62. worlds = [{'id': i, 'randomNumber': await statement.fetchval(i)} for i in row_ids]
  63. return Response(data=worlds)
  64. @API()
  65. async def fortunes():
  66. async with pool.acquire() as connection:
  67. fortune_records = await connection.fetch('SELECT * FROM Fortune')
  68. fortune_records.append(ADDITIONAL_ROW)
  69. fortune_records.sort(key=lambda row: row[1])
  70. data = fortune_template.render(fortunes=fortune_records)
  71. return HTMLResponse(data=data)
  72. @API()
  73. async def database_updates(request: Request):
  74. num_queries = get_num_queries(request)
  75. updates = list(zip(
  76. sample(range(1, 10000), num_queries),
  77. sorted(sample(range(1, 10000), num_queries))
  78. ))
  79. worlds = [{'id': row_id, 'randomNumber': number} for row_id, number in updates]
  80. async with pool.acquire() as connection:
  81. statement = await connection.prepare(READ_ROW_SQL)
  82. for _, row_id in updates:
  83. await statement.fetchval(row_id)
  84. await connection.executemany(WRITE_ROW_SQL, updates)
  85. return Response(data=worlds)
  86. @API()
  87. async def plaintext():
  88. return PlainTextResponse(b'Hello, world!')
  89. url_routing = {
  90. 'json': json_serialization,
  91. 'db': single_database_query,
  92. 'queries': multiple_database_queries,
  93. 'fortunes': fortunes,
  94. 'updates': database_updates,
  95. 'plaintext': plaintext,
  96. }
  97. app = Panther(__name__, configs=__name__, urls=url_routing)