Skip to content

Conversation

@zzstoatzz
Copy link
Collaborator

@zzstoatzz zzstoatzz commented Nov 3, 2025

summary

emits events for each deployment pull step execution, enabling the UI to display exact pull step history for flow runs. one event is emitted per step.

changes

event emission

  • one event per step (not one aggregate event)
  • event types:
    • prefect.flow-run.pull-step.executed - step completed successfully
    • prefect.flow-run.pull-step.failed - step failed
  • resource: prefect.flow-run.<flow_run_id>
  • related resources: includes deployment as related resource when available
  • payload structure (per step):
    {
      "index": 0,
      "qualified_name": "prefect.deployments.steps.run_shell_script",
      "step_name": "run_shell_script",
      "id": "optional-step-id",
      "inputs": {...}
    }
  • emits only when PREFECT__FLOW_RUN_ID is set (deployment runs, not local)
  • uses get_events_client(checkpoint_every=1) to avoid buffering issues
  • logs warning on emission failure without disrupting flow execution

security

  • inputs contain pre-templated values (e.g., {{ prefect.blocks.secret.name }})
  • secrets are NOT exposed in event payloads
  • templating (which resolves secrets) happens after serialization
  • added comprehensive test coverage for secret sanitization

implementation

  • removed _EnvironmentRunContext class (over-engineered)
  • read flow_run_id directly from os.getenv("PREFECT__FLOW_RUN_ID")
  • pass flow_run and logger as parameters to run_steps() instead of using context
  • inline step serialization directly in run_steps() loop
  • keep all inputs including reserved keywords like 'requires'

testing

# run all pull steps tests
uv run pytest tests/deployment/test_steps.py::TestRunSteps -x

# specific tests:
# - test_run_steps_emits_pull_step_events (verifies one event per step)
# - test_run_steps_skips_event_without_flow_run_id
# - test_run_steps_emits_event_on_failure (verifies failed event emission)
# - test_run_steps_does_not_expose_secrets_in_event
# - test_run_steps_includes_deployment_as_related_resource

context

UI wants immutable record of executed pull steps since deployment configuration may change after flow run scheduling. emitting one event per step provides granular visibility into the execution timeline.

@codspeed-hq
Copy link

codspeed-hq bot commented Nov 3, 2025

CodSpeed Performance Report

Merging #19339 will not alter performance

Comparing pull-step-events-spike (3874fc7) with main (f59e4c8)

Summary

✅ 2 untouched

@zzstoatzz zzstoatzz force-pushed the pull-step-events-spike branch from 57afdec to 7e35bde Compare November 4, 2025 14:46
@zzstoatzz zzstoatzz added the enhancement An improvement of an existing feature label Nov 4, 2025
@zzstoatzz zzstoatzz self-assigned this Nov 4, 2025
@zzstoatzz zzstoatzz marked this pull request as ready for review November 4, 2025 18:31
@zzstoatzz zzstoatzz changed the title emit pull steps event emit prefect.flow-run.pull-steps.executed Nov 4, 2025
@zzstoatzz zzstoatzz marked this pull request as draft November 4, 2025 18:33
Copy link
Contributor

@znicholasbrown znicholasbrown left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yusss

@zzstoatzz
Copy link
Collaborator Author

yusss

🙏 looking into one potential improvement here but not in a way that'd change the structure of the event

@zzstoatzz zzstoatzz marked this pull request as ready for review November 4, 2025 22:24
deployment=deployment,
)
raise StepExecutionError(f"Encountered error while running {fqn}") from exc
_emit_pull_steps_event(serialized_steps, failed_step=None, deployment=deployment)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought we'd emit an event for each step. Is there a reason for emitting them all as one event?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the value in emitting one for each? it seemed unnecessary to do more than one

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the case @zzstoatzz made to me yesterday is that if one step fails, the rest of them aren't going to be executed anyway, so the maximum amount of data a user would need fits on a single event. I'm assuming the compromise there is observability, but pull steps are logged too. Is there a case for say, automating on pull steps outside of failure scenarios?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I doubt there's a use case for automating outside of a failure, but I think it'd be easier to write an automation against a prefect.pull-step.failed event than writing one against a prefect.pull-step.executed event with failed_step populated in the payload.

I think emitting an event for each pull step is overall more flexible with little downside.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@znicholasbrown the new event grammar in the PR body has been updated accordingly

@zzstoatzz zzstoatzz force-pushed the pull-step-events-spike branch from 9cc3b3f to 491e42f Compare November 5, 2025 16:27
@zzstoatzz zzstoatzz requested a review from desertaxle November 5, 2025 17:23
deployment=deployment,
)
raise StepExecutionError(f"Encountered error while running {fqn}") from exc
_emit_pull_steps_event(serialized_steps, failed_step=None, deployment=deployment)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I doubt there's a use case for automating outside of a failure, but I think it'd be easier to write an automation against a prefect.pull-step.failed event than writing one against a prefect.pull-step.executed event with failed_step populated in the payload.

I think emitting an event for each pull step is overall more flexible with little downside.

@zzstoatzz zzstoatzz changed the title emit prefect.flow-run.pull-steps.executed emit events for each deployment pull step execution Nov 5, 2025
zzstoatzz and others added 8 commits November 5, 2025 12:58
…rovements

- Add deployment as related resource in pull-steps.executed events
- Add security documentation explaining pre-templating prevents secret leakage
- Add test for secret sanitization in event payloads
- Add test for deployment in related resources
- Modernize type annotations to Python 3.10+ syntax (list, dict, | None)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
…ne serialization

this commit addresses alex's PR review comments #1-4:

1. keep all inputs including reserved keywords (like 'requires') in event payload
2. use get_events_client() with checkpoint_every=1 directly instead of emit_event to avoid buffering issues (similar to Runner heartbeat pattern)
3. pass flow_run and logger as params instead of relying on context
4. inline serialization logic directly in run_steps loop instead of separate function

also converts all event-related tests to use AssertingEventsClient fixture pattern instead of custom fake clients, which required using the events client as an async context manager.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
zzstoatzz and others added 4 commits November 5, 2025 12:58
- remove `from __future__ import annotations` from context.py and test file (not needed for 3.10+)
- change `Optional[X]` to `X | None` in context.py for modern type hints
- quote forward references since no longer using future annotations
- add `pytest.MonkeyPatch` type hints to all monkeypatch fixture parameters
- remove unnecessary comment about runner heartbeat events (internal monologue)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
when we removed `from __future__ import annotations`, the return type annotation for get_logger became evaluated at runtime, causing a NameError since LoggerAdapter is only imported inside the function. quote the annotation to defer evaluation.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
…ne serialization

This commit addresses PR review comments #1-4 and implements the requested change from comment #5:

1. Keep reserved keywords (like 'requires') in serialized inputs ✅
2. Use `get_events_client(checkpoint_every=1)` directly instead of `emit_event` to avoid buffering issues ✅
3. Pass `flow_run` and `logger` as parameters instead of using context ✅
4. Inline step serialization logic directly in `run_steps` ✅
5. Emit **one event per step** instead of one aggregate event ✅

Changes:
- Removed `_EnvironmentRunContext` class and tests (over-engineered)
- Read `flow_run_id` directly from `os.getenv("PREFECT__FLOW_RUN_ID")`
- Changed from single aggregate event to individual events per step:
  - `prefect.flow-run.pull-step.executed` for each successful step
  - `prefect.flow-run.pull-step.failed` when a step fails
- Updated all tests to expect one event per step
- Event payload now contains individual step data (index, qualified_name, step_name, id, inputs)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
… to top

Small improvements:
- Use `enumerate(steps)` instead of manual `step_index` tracking
- Move `from uuid import UUID` to top-level imports instead of deferring it

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@zzstoatzz zzstoatzz force-pushed the pull-step-events-spike branch from 6af755b to 3874fc7 Compare November 5, 2025 18:58
@zzstoatzz zzstoatzz merged commit 7d02236 into main Nov 5, 2025
55 checks passed
@zzstoatzz zzstoatzz deleted the pull-step-events-spike branch November 5, 2025 19:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement An improvement of an existing feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants