app.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. import asyncpg
  2. import os
  3. import jinja2
  4. from logging import getLogger
  5. from random import randint, sample
  6. from operator import itemgetter
  7. import multiprocessing
  8. from wsgiref.handlers import format_date_time
  9. import sanic
  10. from sanic import response
  11. from orjson import dumps
  12. logger = getLogger(__name__)
  13. READ_ROW_SQL = 'SELECT "randomnumber", "id" FROM "world" WHERE id = $1'
  14. READ_ROW_SQL_TO_UPDATE = 'SELECT "id", "randomnumber" FROM "world" WHERE id = $1'
  15. WRITE_ROW_SQL = 'UPDATE "world" SET "randomnumber"=$1 WHERE id=$2'
  16. ADDITIONAL_ROW = [0, 'Additional fortune added at request time.']
  17. def load_fortunes_template():
  18. path = os.path.join('templates', 'fortune.html')
  19. with open(path, 'r') as template_file:
  20. template_text = template_file.read()
  21. return jinja2.Template(template_text)
  22. def get_num_queries(queries):
  23. try:
  24. query_count = int(queries)
  25. except (ValueError, TypeError):
  26. return 1
  27. if query_count < 1:
  28. return 1
  29. if query_count > 500:
  30. return 500
  31. return query_count
  32. sort_fortunes_key = itemgetter(1)
  33. template = load_fortunes_template()
  34. app = sanic.Sanic(name=__name__, dumps=dumps)
  35. @app.listener('before_server_start')
  36. async def setup_database(app, loop):
  37. app.ctx.pool = await asyncpg.create_pool(
  38. user=os.getenv('PGUSER', 'benchmarkdbuser'),
  39. password=os.getenv('PGPASS', 'benchmarkdbpass'),
  40. database='hello_world',
  41. host='tfb-database',
  42. port=5432
  43. )
  44. @app.listener('after_server_stop')
  45. async def close_database(app, loop):
  46. app.ctx.pool.close()
  47. @app.get('/json')
  48. def json_view(request):
  49. return response.json({'message': 'Hello, world!'}, headers=get_headers())
  50. @app.get('/db')
  51. async def single_database_query_view(request):
  52. row_id = randint(1, 10000)
  53. async with request.app.ctx.pool.acquire() as connection:
  54. number = await connection.fetchval(READ_ROW_SQL, row_id)
  55. return response.json(
  56. {'id': row_id, 'randomNumber': number},
  57. headers=get_headers()
  58. )
  59. @app.get('/queries')
  60. async def multiple_database_queries_view(request):
  61. num_queries = get_num_queries(request.args.get('queries', 1))
  62. row_ids = sample(range(1, 10000), num_queries)
  63. worlds = []
  64. async with request.app.ctx.pool.acquire() as connection:
  65. statement = await connection.prepare(READ_ROW_SQL)
  66. for row_id in row_ids:
  67. number = await statement.fetchval(row_id)
  68. worlds.append(
  69. dict(
  70. id=row_id,
  71. randomNumber=number
  72. )
  73. )
  74. return response.json(worlds, headers=get_headers())
  75. @app.get('/fortunes')
  76. async def fortunes_view(request):
  77. async with request.app.ctx.pool.acquire() as connection:
  78. fortunes = await connection.fetch('SELECT * FROM Fortune')
  79. fortunes.append(ADDITIONAL_ROW)
  80. fortunes.sort(key=sort_fortunes_key)
  81. content = template.render(fortunes=fortunes)
  82. return response.html(content, headers=get_headers())
  83. @app.get('/updates')
  84. async def database_updates_view(request):
  85. queries = request.args.get('queries', 1)
  86. num_queries = get_num_queries(queries)
  87. # To avoid deadlock
  88. ids = sorted(sample(range(1, 10000 + 1), num_queries))
  89. numbers = sorted(sample(range(1, 10000), num_queries))
  90. updates = list(zip(ids, numbers))
  91. worlds = [
  92. {"id": row_id, "randomNumber": number} for row_id, number in updates
  93. ]
  94. async with request.app.ctx.pool.acquire() as connection:
  95. statement = await connection.prepare(READ_ROW_SQL)
  96. for row_id, _ in updates:
  97. await statement.fetchval(row_id)
  98. await connection.executemany(WRITE_ROW_SQL, updates)
  99. return response.json(worlds, headers=get_headers())
  100. @app.get('/plaintext')
  101. def plaintext_view(request):
  102. return response.text('Hello, world!', headers=get_headers())
  103. def get_headers(server='Sanic/{}'.format(sanic.__version__)):
  104. return {
  105. 'Server': server,
  106. 'Date': format_date_time(None),
  107. }
  108. if __name__ == '__main__':
  109. app.run('0.0.0.0', 8080, access_log=False,
  110. workers=multiprocessing.cpu_count())