-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add `ConcurrentExecution` step Based on the preexisting internal `_ConcurrentJobExecution` class. With support for three concurrency mechanisms: asyncio, threading, and multiprocessing. * Fix typo * Expose `ConcurrentExecution` * Allow `ConcurrentExecution` to pass context to user function * Fix list append * Fail early on non-serializable context * Fix passing of default `max_in_flight` as `max_workers` * Fix * Support passing context to multiprocessing step * Minor refactoring * Change event processor back to function because of mlrun serialization issues * Move function to avoid serialization issue * Add documentation * Revert attempts to pass context to multiprocessing, add docs * Remove dill requirement
- Loading branch information
Showing
3 changed files
with
150 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
import asyncio | ||
import time | ||
|
||
import pytest | ||
|
||
from storey import AsyncEmitSource | ||
from storey.flow import ConcurrentExecution, Reduce, build_flow | ||
from tests.test_flow import append_and_return | ||
|
||
event_processing_duration = 0.5 | ||
|
||
|
||
class SomeContext: | ||
def __init__(self): | ||
self.fn = lambda x: x | ||
|
||
|
||
async def process_event_slow_asyncio(event, context): | ||
assert isinstance(context, SomeContext) and callable(context.fn) | ||
await asyncio.sleep(event_processing_duration) | ||
return event | ||
|
||
|
||
def process_event_slow_io(event, context): | ||
assert isinstance(context, SomeContext) and callable(context.fn) | ||
time.sleep(event_processing_duration) | ||
return event | ||
|
||
|
||
def process_event_slow_processing(event): | ||
start = time.monotonic() | ||
while time.monotonic() - start < event_processing_duration: | ||
pass | ||
return event | ||
|
||
|
||
async def async_test_concurrent_execution(concurrency_mechanism, event_processor, pass_context): | ||
controller = build_flow( | ||
[ | ||
AsyncEmitSource(), | ||
ConcurrentExecution( | ||
event_processor=event_processor, | ||
concurrency_mechanism=concurrency_mechanism, | ||
pass_context=pass_context, | ||
max_in_flight=10, | ||
context=SomeContext(), | ||
), | ||
Reduce([], append_and_return), | ||
] | ||
).run() | ||
|
||
num_events = 8 | ||
|
||
start = time.monotonic() | ||
for counter in range(num_events): | ||
await controller.emit(counter) | ||
|
||
await controller.terminate() | ||
result = await controller.await_termination() | ||
end = time.monotonic() | ||
|
||
assert result == list(range(num_events)) | ||
assert end - start > event_processing_duration, "Run time cannot be less than the time to process a single event" | ||
assert ( | ||
end - start < event_processing_duration * num_events | ||
), "Run time must be less than the time to process all events in serial" | ||
|
||
|
||
@pytest.mark.parametrize( | ||
["concurrency_mechanism", "event_processor", "pass_context"], | ||
[ | ||
("asyncio", process_event_slow_asyncio, True), | ||
("threading", process_event_slow_io, True), | ||
("multiprocessing", process_event_slow_processing, False), | ||
], | ||
) | ||
def test_concurrent_execution(concurrency_mechanism, event_processor, pass_context): | ||
asyncio.run(async_test_concurrent_execution(concurrency_mechanism, event_processor, pass_context)) |