The way we offer concurrency in the plugin API is with the Batch() object which is built on code that we have to carry and maintain. Concurrent I/O (such as concurrent downloads) is a great fit for "asyncio":https://docs.python.org/3/library/asyncio.html and we should: * Create a ConcurrentHTTPDownloader which is designed to be managed by an asyncio event loop created by the plugin writer * Create a ConcurrentFTPDownloader which is designed to be managed by an asyncio event loop created by the plugin writer * Delete the Batch() object and it's supporting machinery. I think that is effectively everything in "batch.py":https://github.com/pulp/pulp/blob/3.0-dev/platform/pulpcore/download/batch.py machinery One side-effect of this switch is that we need to switch from the requests library to the http library. This is required because the fetching of remote content has to be "asyncio" aware. In other words it needs to support to co-routine pattern required by asyncio.