app_asgi_tortoise.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. #!/usr/bin/env python
  2. import orjson
  3. import falcon.asgi
  4. from falcon import media
  5. from helpers import load_template, FortuneTuple, generate_ids, sanitize
  6. from operator import attrgetter
  7. from random import randint
  8. from tortoise import Tortoise
  9. from tortoise_models import World, Fortune
  10. from tortoise.transactions import in_transaction, atomic
  11. from tortoise.exceptions import OperationalError
  12. # Middleware
  13. class TortoiseInit:
  14. async def process_startup(self, scope, event):
  15. await Tortoise.init(
  16. db_url="postgres://benchmarkdbuser:benchmarkdbpass@tfb-database:5432/hello_world",
  17. modules={"models": ["tortoise_models"]}
  18. )
  19. await Tortoise.generate_schemas(safe=True)
  20. async def process_shutdown(self, scopre, event):
  21. await Tortoise.close_connections()
  22. tortoise_init = TortoiseInit()
  23. # resource endpoints
  24. class JSONResource(object):
  25. async def on_get(self, request, response):
  26. response.media = {'message': "Hello, world!"}
  27. class SingleQuery(object):
  28. # Note: There's much improvement when we decorate
  29. # the query, even just for retreiving data
  30. @atomic()
  31. async def on_get(self, request, response):
  32. resp = await World.get(id=randint(1, 10000))
  33. response.media = resp.to_dict()
  34. class MultipleQueries(object):
  35. # Note: Not much different between using atomic or
  36. # in_transaction decorator here.
  37. @atomic()
  38. async def on_get(self, request, response, num):
  39. num = sanitize(num)
  40. worlds = []
  41. for ids in generate_ids(num):
  42. data = await World.get(id=ids)
  43. worlds.append(data.to_dict())
  44. response.media = worlds
  45. class UpdateQueries(object):
  46. async def on_get(self, request, response, num):
  47. try:
  48. async with in_transaction():
  49. num = sanitize(num)
  50. items = await World.filter(id__in=generate_ids(num))
  51. worlds = []
  52. for ids in items:
  53. ids.randomNumber = randint(1, 10000)
  54. await ids.save()
  55. worlds.append({'id': ids.id, 'randomNumber': ids.randomNumber})
  56. response.media = worlds
  57. except OperationalError:
  58. pass
  59. class Fortunes(object):
  60. _template = load_template()
  61. async def on_get(self, request, response):
  62. fortunes = [FortuneTuple(id=f.id, message=f.message) for f in await Fortune.all()]
  63. fortunes.append(FortuneTuple(id=0, message="Additional fortune added at request time."))
  64. fortunes.sort(key=attrgetter("message"))
  65. content = self._template.render(fortunes=fortunes)
  66. response.content_type = falcon.MEDIA_HTML
  67. response.text = content
  68. class PlaintextResource(object):
  69. async def on_get(self, request, response):
  70. response.content_type = falcon.MEDIA_TEXT
  71. response.text = 'Hello, world!'
  72. asgi = falcon.asgi.App(middleware=[tortoise_init])
  73. # Change JSON handler to orjson
  74. JSONHandler = media.JSONHandler(dumps=orjson.dumps, loads=orjson.loads)
  75. extra_handlers = {
  76. "application/json": JSONHandler,
  77. "application/json; charset=UTF-8": JSONHandler
  78. }
  79. asgi.req_options.media_handlers.update(extra_handlers)
  80. asgi.resp_options.media_handlers.update(extra_handlers)
  81. # register resources
  82. asgi.add_route("/json", JSONResource())
  83. asgi.add_route("/db", SingleQuery())
  84. asgi.add_route("/queries/{num}", MultipleQueries())
  85. asgi.add_route("/updates/{num}", UpdateQueries())
  86. asgi.add_route("/fortunes", Fortunes())
  87. asgi.add_route("/plaintext", PlaintextResource())