Falcon API - process respond after client gets it

245 Views Asked by At

I have a falcon API (WSGI) that I wrote that needs to be as fast as possible.

At one part of the code I have the respond that I need to send to the client, but I have a datalake where I send all of responses + some more calculation results using kafka. I want to separate the extra calculations + the send to kafka as the client does not need wait for it and it takes more time then I want it to.

Is there a way to do it in Falcon without handling the threads by myself like this:

class Compute(Thread):
    def __init__(self, request):
        Thread.__init__(self)
        self.request = request

    def run(self):
        print("start")
        time.sleep(5)
        print(self.request)
        print("done")


class FalconApi
    def on_post(self, request: falcon.Request, response: falcon.Response):
        thread_a = Compute(request.__copy__())
        thread_a.start()

I run the API using gunicorn so I thought maybe I could use the hook post_request but I can't seem to be able to get the response data in the hook. I also tried using asyncio but it seems like it does not like when I do use it because my app is WSGI.

My async code:

class FalconApi
        @staticmethod
    def http_post(response: falcon.Response) -> None:
        requests.post(consts.ENDPOINT, headers=response.headers, data=json.dumps(response.data))

    async def http_post_async(self, response: falcon.Response) -> None:
        await asyncio.to_thread(self.http_post, response)

    def on_post(self, request: falcon.Request, response: falcon.Response):
        self.http_post_async(response)

and the error that I got: RuntimeWarning: coroutine 'http_post_async' was never awaited

and when I changed it to:

    async def on_post(self, request: falcon.Request, response: falcon.Response):
        self.http_post_async(response)

I got: TypeError: The <bound methodon_post of object at 0x7f7cf0c4ffd0>> responder must be a regular synchronous method to be used with a WSGI app.

1

There are 1 best solutions below

0
Ema Il On

This is what I ended up doing which worked for me.

I added a gunicorn hook post_request and I added the response I want to post process to the falcon.request.env attribute and then I extracted it in the hook.

My code looks something like:

api.py:

class FalconApi
    def on_post(self, request: falcon.Request, response: falcon.Response):
        response = <something>
        request.env['response'] = response

hooks.py:

def post_request(worker: SyncWorker, request: gunicorn.http.Request, environ: dict,
                 response: gunicorn.http.wsgi.Response) -> None:
    response = environ.get('response')
    if not response:
        logger.error(f'could not post process')
    PostProcessingRoute().post_process_response(response_body)

and then I run it using gunicorn -c hooks.py