app.py 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. import asyncio
  2. import asyncpg
  3. import jinja2
  4. import os
  5. import ujson as json
  6. from functools import partial
  7. from random import randint
  8. from operator import itemgetter
  9. from urllib import parse
  10. async def setup():
  11. global pool
  12. pool = await asyncpg.create_pool(
  13. user=os.getenv('PGUSER', 'benchmarkdbuser'),
  14. password=os.getenv('PGPASS', 'benchmarkdbpass'),
  15. database='hello_world',
  16. host='tfb-database',
  17. port=5432
  18. )
  19. pool = None
  20. additional = [0, 'Additional fortune added at request time.']
  21. key = itemgetter(1)
  22. template = None
  23. path = os.path.join('templates', 'fortune.html')
  24. with open(path, 'r') as template_file:
  25. template_text = template_file.read()
  26. template = jinja2.Template(template_text)
  27. loop = asyncio.get_event_loop()
  28. loop.run_until_complete(setup())
  29. def get_query_count(query_string):
  30. # helper to deal with the querystring passed in
  31. queries = parse.parse_qs(query_string).get(b'queries', [None])[0]
  32. if queries:
  33. try:
  34. query_count = int(queries)
  35. if query_count < 1:
  36. return 1
  37. if query_count > 500:
  38. return 500
  39. return query_count
  40. except ValueError:
  41. pass
  42. return 1
  43. random_int = partial(randint, 1, 10000)
  44. class JSONSerialization:
  45. """
  46. Test type 1: JSON Serialization
  47. """
  48. def __init__(self, scope):
  49. pass
  50. async def __call__(self, receive, send):
  51. content = json.dumps({'message': 'Hello, world!'}).encode('utf-8')
  52. await send({
  53. 'type': 'http.response.start',
  54. 'status': 200,
  55. 'headers': [
  56. [b'content-type', b'application/json'],
  57. [b'content-length', str(len(content)).encode()]
  58. ]
  59. })
  60. await send({
  61. 'type': 'http.response.body',
  62. 'body': content,
  63. 'more_body': False
  64. })
  65. class SingleDatabaseQuery:
  66. """
  67. Test type 2: Single database object
  68. """
  69. def __init__(self, scope):
  70. pass
  71. async def __call__(self, receive, send):
  72. connection = await pool.acquire()
  73. try:
  74. row = await connection.fetchrow('SELECT id, "randomnumber" FROM "world" WHERE id = ' + str(random_int()))
  75. world = {'id': row[0], 'randomNumber': row[1]}
  76. content = json.dumps(world).encode('utf-8')
  77. await send({
  78. 'type': 'http.response.start',
  79. 'status': 200,
  80. 'headers': [
  81. [b'content-type', b'application/json'],
  82. [b'content-length', str(len(content)).encode()]
  83. ]
  84. })
  85. await send({
  86. 'type': 'http.response.body',
  87. 'body': content,
  88. 'more_body': False
  89. })
  90. finally:
  91. await pool.release(connection)
  92. class MultipleDatabaseQueries:
  93. """
  94. Test type 3: Multiple database queries
  95. """
  96. def __init__(self, scope):
  97. self.queries = get_query_count(scope.get('query_string', {}))
  98. async def __call__(self, receive, send):
  99. connection = await pool.acquire()
  100. try:
  101. worlds = []
  102. for i in range(self.queries):
  103. sql = 'SELECT id, "randomnumber" FROM "world" WHERE id = ' + str(random_int())
  104. row = await connection.fetchrow(sql)
  105. worlds.append({'id': row[0], 'randomNumber': row[1]})
  106. content = json.dumps(worlds).encode('utf-8')
  107. await send({
  108. 'type': 'http.response.start',
  109. 'status': 200,
  110. 'headers': [
  111. [b'content-type', b'application/json'],
  112. [b'content-length', str(len(content)).encode()]
  113. ]
  114. })
  115. await send({
  116. 'type': 'http.response.body',
  117. 'body': content,
  118. 'more_body': False
  119. })
  120. finally:
  121. await pool.release(connection)
  122. class Fortunes:
  123. """
  124. Test type 4: Fortunes
  125. """
  126. def __init__(self, scope):
  127. pass
  128. async def __call__(self, receive, send):
  129. connection = await pool.acquire()
  130. try:
  131. fortunes = await connection.fetch('SELECT * FROM Fortune')
  132. fortunes.append(additional)
  133. fortunes.sort(key=key)
  134. content = template.render(fortunes=fortunes).encode('utf-8')
  135. await send({
  136. 'type': 'http.response.start',
  137. 'status': 200,
  138. 'headers': [
  139. [b'content-type', b'text/html; charset=utf-8'],
  140. [b'content-length', str(len(content)).encode()]
  141. ]
  142. })
  143. await send({
  144. 'type': 'http.response.body',
  145. 'body': content,
  146. 'more_body': False
  147. })
  148. finally:
  149. await pool.release(connection)
  150. class DatabaseUpdates:
  151. """
  152. Test type 5: Database updates
  153. """
  154. def __init__(self, scope):
  155. self.queries = get_query_count(scope.get('query_string', {}))
  156. async def __call__(self, receive, send):
  157. connection = await pool.acquire()
  158. try:
  159. worlds = []
  160. for i in range(self.queries):
  161. row = await connection.fetchrow('SELECT id FROM "world" WHERE id=' + str(random_int()))
  162. worlds.append({'id': row[0], 'randomNumber': random_int()})
  163. await connection.execute('UPDATE "world" SET "randomnumber"=%s WHERE id=%s' % (random_int(), row[0]))
  164. content = json.dumps(worlds).encode('utf-8')
  165. await send({
  166. 'type': 'http.response.start',
  167. 'status': 200,
  168. 'headers': [
  169. [b'content-type', b'application/json'],
  170. [b'content-length', str(len(content)).encode()]
  171. ]
  172. })
  173. await send({
  174. 'type': 'http.response.body',
  175. 'body': content,
  176. 'more_body': False
  177. })
  178. finally:
  179. await pool.release(connection)
  180. class Plaintext:
  181. """
  182. Test type 6: Plaintext
  183. """
  184. def __init__(self, scope):
  185. pass
  186. async def __call__(self, receive, send):
  187. content = b'Hello, world!'
  188. await send({
  189. 'type': 'http.response.start',
  190. 'status': 200,
  191. 'headers': [
  192. [b'content-type', b'text/plain'],
  193. [b'content-length', str(len(content)).encode()]
  194. ]
  195. })
  196. await send({
  197. 'type': 'http.response.body',
  198. 'body': content,
  199. 'more_body': False
  200. })
  201. class Handle404:
  202. def __init__(self, scope):
  203. pass
  204. async def __call__(self, receive, send):
  205. content = b'Not found'
  206. await send({
  207. 'type': 'http.response.start',
  208. 'status': 200,
  209. 'headers': [
  210. [b'content-type', b'text/plain'],
  211. [b'content-length', str(len(content)).encode()]
  212. ]
  213. })
  214. await send({
  215. 'type': 'http.response.body',
  216. 'body': content,
  217. 'more_body': False
  218. })
  219. routes = {
  220. '/json': JSONSerialization,
  221. '/db': SingleDatabaseQuery,
  222. '/queries': MultipleDatabaseQueries,
  223. '/fortunes': Fortunes,
  224. '/updates': DatabaseUpdates,
  225. '/plaintext': Plaintext,
  226. }
  227. def main(scope):
  228. path = scope['path']
  229. return routes.get(path, Handle404)(scope)