atomkraft
atomkraft copied to clipboard
Adopt async runtime
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 aroundasync
methods. - Timeout-less polling on concurrent tasks.
- #171 can be resolved cleanly with
asyncio.gather
.
- #171 can be resolved cleanly with
- 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