atomkraft icon indicating copy to clipboard operation
atomkraft copied to clipboard

Adopt async runtime

Open rnbguy opened this issue 2 years ago • 0 comments

Async runtime provides a friendlier interface to handle concurrent tasks. Python's asyncio library reduces the code burden and offers more straightforward methods. The main cost is tagging methods with async or await accordingly.

Asyncio vs Multiprocess vs Threads

First, let us go through its comparison with other options to evaluate it properly. This blog post discusses the ideal case scenarios among these options. I roughly listed them in the following table.

Process Threads I/O CPU
Async few few slow -
Thread single multi fast -
Multiprocess multi multi - heavy

This StackOverflow reply may also be worth reading.

Atomkraft works with few processes and slow I/O tasks. There is no thread involved and no extensive computations while Atomkraft runs. It mainly,

  • Setups a distributed system - spawning binary processes.
  • Orchestrates the test - communicating with remote servers (over RPC, GRPC, REST, etc.), which are de-facto async use cases.

Leading example

Consider the following sample code.

import asyncio


async def run_test():
    await asyncio.sleep(10)
    raise RuntimeError


async def run_node(i: int):
    await asyncio.sleep(i)
    if i == 5:
        raise RuntimeError(f"node error: id {i}")
    await asyncio.sleep(i)


async def runtime(n_node: int):
    try:
        await asyncio.gather(*[run_node(i + 1) for i in range(n_node)], run_test())
    except RuntimeError as e:
        print(e)
    print("all good")


if __name__ == "__main__":
    asyncio.run(runtime(6))

The only concise way to rewrite the above would be using .apply_async() or ProcessPoolExecutor, which has more code complexity in my opinion. I don't know any other way to rewrite the same concisely in blocking runtime.

Few ways Atomkraft can benefit

  • Most of the modern I/O libraries are packaged in Async.
  • Remove blocking libraries, which are just ad-hoc wrappers around async methods.
  • Timeout-less polling on concurrent tasks.
    • #171 can be resolved cleanly with asyncio.gather.
  • Concurrent tasks can be canceled gracefully.
  • Thread support if needed.
  • ?? (more)

Rough plan of Async migration

  • Adopt pytest-asyncio
  • Support Async reactors along with blocking reactors
  • Modify Atomkraft's pytest plugin to work with async procedures

rnbguy avatar Oct 19 '22 16:10 rnbguy