Back to Prefect

How to emit and use custom events

docs/v3/advanced/use-custom-event-grammar.mdx

3.8.2.dev43.2 KB
Original Source

Motivating custom events

Imagine you are running an e-commerce platform and you want to trigger a deployment when a customer completes an order.

There might be a number of events that occur during an order on your platform, for example:

  • order.created
  • order.item.added
  • order.payment-method.confirmed
  • order.shipping-method.added
  • order.complete
<Tip> **Event grammar**

The above choices of event names are arbitrary. With Prefect events, you're free to select any event grammar that best represents your use case. </Tip>

In this case, we want to trigger a deployment when a user completes an order, so our trigger should:

  • expect an order.complete event
  • after an order.created event
  • evaluate these conditions for_each user id

Finally, it should pass the user_id as a parameter to the deployment.

Define the trigger

Here's how this looks in code:

python
from prefect import flow
from prefect.events.schemas.deployment_triggers import DeploymentEventTrigger

order_complete = DeploymentEventTrigger(
    expect={"order.complete"},
    after={"order.created"},
    for_each={"prefect.resource.id"},
    parameters={"user_id": "{{ event.resource.id }}"},
)


@flow(log_prints=True)
def post_order_complete(user_id: str):
    print(f"User {user_id} has completed an order -- doing stuff now")


if __name__ == "__main__":
    post_order_complete.serve(triggers=[order_complete])
<Tip> **Specify multiple events or resources**

The expect and after fields accept a set of event names, so you can specify multiple events for each condition.

Similarly, the for_each field accepts a set of resource ids. </Tip>

Simulate events

To simulate users causing order status events, run the following in a Python shell or script:

python
from prefect.events import emit_event

user_id_1, user_id_2 = "123", "456"

order_created_1 = emit_event(
    event="order.created",
    resource={"prefect.resource.id": user_id_1},
)
emit_event(
    event="order.created",
    resource={"prefect.resource.id": user_id_2},  # other user
)
emit_event(
    event="order.complete",
    resource={"prefect.resource.id": user_id_1},
    follows=order_created_1,
)

In the above example:

  • user_id_1 creates and then completes an order, triggering a run of our deployment.
  • user_id_2 creates an order, but no completed event is emitted so no deployment is triggered.
<Warning> **Declare `follows` for events emitted close together**

Because the trigger uses order.created in after and order.complete in expect, it only counts an order.complete event that arrives after an order.created event. Events emitted within a second or two of each other may arrive at the system in either order unless the later event declares follows—which is why the order.complete emission above passes follows=order_created_1. In a real system where minutes pass between creating and completing an order, follows isn't strictly needed, though it still documents the relationship between the events (note that emit_event only sets it when the two events occur within five minutes of each other). </Warning>