question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Implement multithreading with subscriptions (concurrent execution) like Flask routes

See original GitHub issue

Hi! I am moving my code from a flask app to NATS. Flask, by default is multi-threaded and I am trying to do that as well with nats but have struggled to run co-routines concurrently. As far as I understand, when you execute a co-routine, it blocks the event loop until it is completed even if that co-routine contains I/O operations.

The flask app had two routes:

  1. /train: Large task of training a ML model and takes time.
  2. /predict: Small high priority task.

In my flask app, I could call the /predict route while /train was running and get the result immediately. However, with the asyncio approach in NATS, the predict callback does not even start until the train callback is completed. They are always executed sequentially. I have tried to execute the LARGE TASK and SMALL TASK as threads, but in Python you cannot get a return value of the thread unless you use a Queue. However, Queue is not a good option when you start threads randomly.

Is there a possible solution to execute multiple callbacks concurrently? I have attached a sample code to better understand the terms I am using to describe my problem.

     async def train(msg):
        """
        The bot train requests come here
        :return:
        """
        subject = msg.subject
        reply = msg.reply
        data_train = json.loads(msg.data.decode())
        routes.logger.info("Received a message on '{subject} {reply}': {data}".format(
            subject=subject, reply=reply, data=data_train))

        routes.logger.debug('subscribe/train: Train data: {}'.format(data_train))
        data = routes.train(data_train) // LARGE TASK
        routes.logger.info(data)
        await nc.publish(msg.reply, json.dumps(data).encode())
        subscriber = 'nlu.{}.predict.{}'.format(lang, data['model'].split('_')[1])
        if subscriber not in current_subscribers:
            current_subscribers[subscriber] = await nc.subscribe(subscriber, cb=predict)
            routes.logger.info('Added subscription: {}'.format(subscriber))

    async def predict(msg):
        """
        The bot predict requests come here
        :return:
        """
        subject = msg.subject
        reply = msg.reply
        data_predict = json.loads(msg.data.decode())
        routes.logger.info("Received a message on '{subject} {reply}': {data}".format(
            subject=subject, reply=reply, data=data_predict))
        data = routes.predict(data_predict) // SMALL TASK
        routes.logger.info(data)
        await nc.publish(msg.reply, json.dumps(data).encode())

await nc.subscribe('{}.train'.format(connection), cb=train)
await nc.subscribe('{}.predict'.format(connection), cb=predict)

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:5 (3 by maintainers)

github_iconTop GitHub Comments

2reactions
charliestrawncommented, Oct 2, 2020

It’s an interesting idea and worth looking into, but IMO that brings a lot of potential complexity that should probably not live in this repo. Flask is a (micro) web framework, while this is a library for talking to a nats-server or clusters. I think we want to keep this repo simple, focused, and flexible. This allows consumers to wrap or extend it in their own projects or custom frameworks that handle things like threading.

1reaction
wallyqscommented, Oct 2, 2020

Hi @shayan09 yes this is something that can look into in the near future, like binding each handler into its own thread pool executor maybe. One thing to consider as well when there are blocking issues is to wrap the NATS connection into its own component and with its own thread (something like: https://github.com/nats-io/nats.py/blob/master/examples/coroutine_threadsafe.py#L31) and then whenever receive a task use syncio.run_coroutine_threadsafe so that the client can be called from another thread.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Python Multithreading and Multiprocessing Tutorial
In this article, we will take a look at threading and a couple of other strategies for building concurrent programs in Python, as...
Read more >
How to use threads within a Flask route
I would like to use concurrency when responding to a specific route without creating extra threads on every request. There's a route defined...
Read more >
How to Perform Multitasking in Flask and Tornado
Here is the implementation of ThreadPoolExecutor in Flask. The performance is as high as the previous example. Concurrent requests in Flask. And ...
Read more >
Using Python Threading and Returning Multiple Results ...
It allows you to manage concurrent threads doing work at the same time. The library is called “threading“, you create “Thread” objects, and...
Read more >
Deep Learning in Production: A Flask Approach
Sequential Processing with multiple cores ... Multiple processes allow the app to spawn multiple threads as and when needed. The number of ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found