-
Notifications
You must be signed in to change notification settings - Fork 1.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Subscribe to state changes in wait_for_flow_run #17243
Changes from 3 commits
34eaece
5bd5b09
53a1242
b50e37c
1f4e718
5c6b6b0
be1ba63
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,14 +14,16 @@ | |
from prefect.client.orchestration import PrefectClient, get_client | ||
from prefect.client.schemas import FlowRun | ||
from prefect.client.schemas.objects import ( | ||
StateType, | ||
State, StateType, | ||
) | ||
from prefect.client.schemas.responses import SetStateStatus | ||
from prefect.client.utilities import inject_client | ||
from prefect.context import ( | ||
FlowRunContext, | ||
TaskRunContext, | ||
) | ||
from prefect.events.clients import get_events_subscriber | ||
from prefect.events.filters import EventFilter, EventNameFilter, EventResourceFilter | ||
from prefect.exceptions import ( | ||
Abort, | ||
FlowPauseTimeout, | ||
|
@@ -54,7 +56,6 @@ | |
async def wait_for_flow_run( | ||
flow_run_id: UUID, | ||
timeout: int | None = 10800, | ||
poll_interval: int = 5, | ||
client: "PrefectClient | None" = None, | ||
log_states: bool = False, | ||
) -> FlowRun: | ||
|
@@ -64,7 +65,8 @@ async def wait_for_flow_run( | |
Args: | ||
flow_run_id: The flow run ID for the flow run to wait for. | ||
timeout: The wait timeout in seconds. Defaults to 10800 (3 hours). | ||
poll_interval: The poll interval in seconds. Defaults to 5. | ||
client: Optional Prefect client. If not provided, one will be injected. | ||
log_states: If True, log state changes. Defaults to False. | ||
|
||
Returns: | ||
FlowRun: The finished flow run. | ||
|
@@ -116,15 +118,30 @@ async def main(num_runs: int): | |
""" | ||
assert client is not None, "Client injection failed" | ||
logger = get_logger() | ||
|
||
flow_run = await client.read_flow_run(flow_run_id) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I actually don't know that this is even necessary to check before we enter the subscriber iterable, maybe we can remove this whole block? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hmm I think to avoid any missed events / race conditions due to the time it takes to make this call, we should check for a final state immediately after entering the subscriber (but before the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. got it, let me know if the new version is what you had in mind |
||
if flow_run.state and flow_run.state.is_final(): | ||
if log_states: | ||
logger.info(f"Flow run is in state {flow_run.state.name!r}") | ||
return flow_run | ||
|
||
filter = EventFilter( | ||
bnaul marked this conversation as resolved.
Show resolved
Hide resolved
|
||
event=EventNameFilter(prefix=["prefect.flow-run"]), | ||
resource=EventResourceFilter(id=[f"prefect.flow-run.{flow_run_id}"]) | ||
) | ||
|
||
with anyio.move_on_after(timeout): | ||
while True: | ||
flow_run = await client.read_flow_run(flow_run_id) | ||
flow_state = flow_run.state | ||
if log_states and flow_state: | ||
logger.info(f"Flow run is in state {flow_state.name!r}") | ||
if flow_state and flow_state.is_final(): | ||
return flow_run | ||
await anyio.sleep(poll_interval) | ||
async with get_events_subscriber(filter=filter) as subscriber: | ||
bnaul marked this conversation as resolved.
Show resolved
Hide resolved
|
||
async for event in subscriber: | ||
state_type = StateType(event.resource["prefect.state-type"]) | ||
state = State(type=state_type) | ||
|
||
if log_states: | ||
logger.info(f"Flow run is in state {state.name!r}") | ||
|
||
if state.is_final(): | ||
return await client.read_flow_run(flow_run_id) | ||
|
||
raise FlowRunWaitTimeout( | ||
f"Flow run with ID {flow_run_id} exceeded watch timeout of {timeout} seconds" | ||
) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
technically this is a breaking change (unfortunately since its likely infrequently used) so I think we should keep it and if a non-default value is provided we log some warning to say it has no effect and will be removed in a future release
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done, that's actually how I had it before so I just reverted 53a1242