# Event History walkthrough with the Python SDK

In order to understand how Workflow Replay works, this page will go through the following walkthroughs:

1. [How Workflow Code Maps to Commands](#How-Workflow-Code-Maps-To-Commands)
2. [How Workflow Commands Map to Events](#How-Workflow-Commands-Map-To-Events)
3. [How History Replay Provides Durable Execution](#How-History-Replay-Provides-Durable-Execution)
4. [Example of a Non-Deterministic Workflow](#Example-of-Non-Deterministic-Workflow)

## How Workflow Code Maps to Commands 

This walkthrough will cover how the Workflow code maps to Commands that get sent to the Temporal Service, letting the
Temporal Service know what to do.

![NA](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/code-commands/code-commands.001.jpeg)
![Here is code for a basic Temporal Workflow Definition, which does the items described on the right side of the screen.](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/code-commands/code-commands.003.jpeg)
![Some steps are internal to the Workflow and do not involve interaction with the Service.](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/code-commands/code-commands.004.jpeg)
![On the other hand, some steps do involve interaction with the Temporal Service. For example, when the code requests execution of an Activity, it generates a Command to schedule the Activity Task. Another example is when the code returns a value from the Workflow, the Worker notifies the Temporal Service that the Workflow Execution is complete.](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/code-commands/code-commands.005.jpeg)
![The code walkthrough will now begin. In this Workflow Definition, setting a variable is an internal step. That is, this step doesn](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/code-commands/code-commands.006.jpeg)
![,
    ](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/code-commands/code-commands.007.jpeg)
![s a request to execute an Activity. This causes the Worker to issue a Command to the Temporal Service and provides the details needed. For example, the `ScheduleActivityTask` Command contains details such as the Task Queue name, the Activity Type, and the input parameter values. Even though an Activity can take hours or days to complete, the Worker does not use resources.](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/code-commands/code-commands.008.jpeg)
![After the `get_distance` Activity has successfully completed, the Worker continues executing the Workflow code. The next line, highlighted here, evaluates a variable. Depending on the outcome, it may throw an exception, which would send a Command to the Server to request it to fail the Workflow Execution. However, this example assumes that this is a delivery for a nearby customer. The execution will continue.](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/code-commands/code-commands.009.jpeg)
![The Worker now reaches the call to start a Timer, which is another statement that involves interaction with the Temporal Service. This causes the Worker to issue another Command, one which requests the Temporal Service to start a Timer. The duration is one of the details specified in this Command. Further execution of this Workflow will now pause for 30 minutes until the Timer fires.](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/code-commands/code-commands.010.jpeg)
![The Timer then fires. The next few lines, highlighted here, create and populate a data structure that represents the input for the next Activity. While it is related to the Activity, it doesn](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/code-commands/code-commands.011.jpeg)
![,
    ](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/code-commands/code-commands.012.jpeg)

## How Workflow Commands Map to Events 

The Commands that are sent to the Temporal Service are then turned into Events, which build up the Event History. The
Event History is a detailed log of Events that occur during the lifecycle of a Workflow Execution, such as the execution
of Workflow Tasks or Activity Tasks. Event Histories are persisted to the database used by the Temporal Service, so
they're durable, and will even survive a crash of the Temporal Service itself.

These Events are what are used to recreate a Workflow Execution's state in the case of failure.

![NA](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/commands-events/commands-events.001.jpeg)
![In this walkthrough, there will be a running list of the Commands issued with the corresponding Event to the right.](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/commands-events/commands-events.002.jpeg)
![The call to the Activity is the first line of code in the Workflow that causes a Command to be issued. In response to this Command, the Temporal Service creates an Activity Task, adds it to the Task Queue, and appends the `ActivityTaskScheduled` Event to the Event History. This Event is colored blue to indicate that it](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/commands-events/commands-events.003.jpeg)
![,
    ](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/commands-events/commands-events.004.jpeg)
![,
    ](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/commands-events/commands-events.005.jpeg)
![,
    ](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/commands-events/commands-events.006.jpeg)
![s an indirect result of the Command. By the way, the `Start-to-Close` Timeout indicates the amount of time that the Activity has to complete.](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/commands-events/commands-events.007.jpeg)
![The Worker executes the code within the Activity Definition, and when that function returns a result, the Worker sends a message to the Temporal Service, notifying it that the Task is complete. To reiterate, this is just a notification, not a Command, because it](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/commands-events/commands-events.008.jpeg)
![,
    ](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/commands-events/commands-events.009.jpeg)
![,
    ](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/commands-events/commands-events.010.jpeg)
![,
    ](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/commands-events/commands-events.011.jpeg)
![,
    ](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/commands-events/commands-events.012.jpeg)
![,
    ](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/commands-events/commands-events.013.jpeg)
![,
    ](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/commands-events/commands-events.014.jpeg)

## How History Replay Provides Durable Execution 

Now that you have seen how code maps to Commands, and how Commands map to Events, this next walkthrough will take a look
at how Temporal uses Replay with the Events to provide Durable Execution and restore a Workflow Execution in the case of
a failure.

This code walkthrough will begin by walking through a Workflow Execution, describing how the code maps to Commands and
Events. There will then be a Worker crash halfway through, explaining how Temporal uses Replay to recover the state of
the Workflow Execution, ultimately resulting in a completed execution that's identical to one that had not crashed.

![NA](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.001.jpeg)
![This walkthrough begins with a request to execute this Workflow Definition, passing in some input data. In this case, the input data contains information about the customer and the pizzas ordered.](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.009.jpeg)
![This request to execute the Workflow Definition results in the Temporal Service recording a `WorkflowExecutionStarted` Event into the Event History. It](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.010.jpeg)
![,
    ](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.011.jpeg)
![,
    ](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.012.jpeg)
![,
    ](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.013.jpeg)
![,
    ](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.014.jpeg)
![,
    ](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.015.jpeg)
![,
    ](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.016.jpeg)
![,
    ](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.018.jpeg)
![,
    ](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.019.jpeg)
![,
    ](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.020.jpeg)
![,
    ](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.022.jpeg)
![,
    ](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.023.jpeg)
![s the indirect result of the Command. The `ActivityTaskStarted` Event is not written to the Event History until a Task closes, because the number of retry attempts is an attribute of the `ActivityTaskStarted` Event.](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.026.jpeg)
![When the Activity function returns - with a result of 15 - the Worker notifies the Service that the Activity Execution is complete.](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.027.jpeg)
![The Temporal Service records an `ActivityTaskCompleted` Event, which contains the result of the Activity.](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.028.jpeg)
![In order to deliver the Activity Task result, 15, back to the Workflow, the Temporal Service creates another Workflow Task which includes the result of this Activity. `WorkflowTaskScheduled` is appended to the Event History.](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.029.jpeg)
![The Service will then dispatch this Activity to an available Worker.](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.030.jpeg)
![The Worker dequeues the Task and resumes execution of the Workflow. The `WorkflowTaskStarted` Event gets appended to the Event History.](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.031.jpeg)
![The Worker then executes the next few lines of code, evaluating the distance.](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.032.jpeg)
![The Worker reaches the request to start the Timer. Therefore, it notifies the Service to complete the current Workflow Task.](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.033.jpeg)
![The Worker will complete the current Workflow Task, adding `WorkflowTaskCompleted` to the Event History. This Event includes the `StartTimer` Command.](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.034.jpeg)
![The Worker issues a `StartTimer` Command to the Service, requesting it to set the Timer for 30 minutes. The Service records a `TimerStarted` Event in response.](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.035.jpeg)
![The Workflow does not progress until the Timer fires.](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.037.jpeg)
![After 30 minutes has elapsed, the Timer fires, and the Service records a `TimerFired` Event.](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.038.jpeg)
![The Service now adds a new Workflow Task to the Queue in order to deliver the `TimerFired` Event to the Workflow, so `WorkflowTaskScheduled` is added to the Event History to drive the Workflow progress forward.](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.039.jpeg)
![The Worker polls for the Task, dequeues it, and continues execution of the Workflow code.](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.043.jpeg)
![However, the Worker happens to crash right here. How does Temporal recover the state of the Workflow? But first, how do you know when a Worker has crashed?](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.045.jpeg)
![Once a Worker has accepted a Task, it is expected to complete that task within a predefined duration, known as a Timeout. This timeout is available to recognize whether a Worker has gone down. This results in a Workflow Task Timeout, which has a default value of 10 seconds.](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.046.jpeg)
![Therefore, if the Worker failed to complete this Workflow Task within that time, the Service will schedule a new Workflow Task.](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.048.jpeg)
![The Worker polling might be done by another Worker that](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.049.jpeg)
![,
    ](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.050.jpeg)
![,
    ](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.051.jpeg)
![,
    ](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.054.jpeg)
![,
    ](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.055.jpeg)
![,
    ](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.056.jpeg)
![,
    ](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.057.jpeg)
![,
    ](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.058.jpeg)
![,
    ](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.059.jpeg)
![s using the result stored in the Event History, so there is no way that the Activity behaves differently during History Replay than the original execution.](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.061.jpeg)
![Replay continues replaying the code.](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.062.jpeg)
![NA](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.063.jpeg)
![The Worker then reaches the request to start a Timer. It creates a Command, `StartTimer`. Again, the Worker does not issue the Command to the Service.](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.064.jpeg)
![Instead, the Worker checks the Event History to see whether the Timer was started and fired during the previous execution. The Event History indicates that the Timer was started, because there is a `TimerStarted` Event.](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.066.jpeg)
![The Event History also indicates that the Timer was fired, because there is a `TimerFired` Event.](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.068.jpeg)
![At this point, the Worker has reached the point where the crash occurred, and replaying the code has completely restored the state of the Workflow Execution prior to the crash.](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.069.jpeg)
![For example, the `distance` variable was set using the value that was stored in the Event History from the previous Execution.](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.071.jpeg)
![Since Replay uses the same input data as before, this also means that the conditional statement evaluates to `false`, like it did before.](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.072.jpeg)
![The Worker has now reached a statement beyond where the crash occurred, which is evident because the Event History does not contain any Events related to this `send_bill` Activity. Further execution of this Workflow continues on as if the crash never happened.](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.074.jpeg)
![Because the Worker encounters a request to execute an Activity, the Worker completes the current Workflow Task.](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.075.jpeg)
![The Worker issues a Command to the Service, requesting execution of the Activity.](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.076.jpeg)
![The Worker adds the Activity Task to the Task queue, adding `ActivityTaskScheduled` to the Event History. The Worker polls for the Task.](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.078.jpeg)
![The Worker dequeues the Task, adding `ActivityTaskStarted` to the Event History.](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.079.jpeg)
![When the Activity returns a result, the Worker notifies the Service.](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.080.jpeg)
![The Worker records an `ActivityTaskCompleted` Event, which includes the result from the `send_bill` Activity.](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.081.jpeg)
![But since the Service hasn](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.082.jpeg)
![,
    ](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.084.jpeg)
![,
    ](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.085.jpeg)
![,
    ](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.086.jpeg)
![,
    ](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/history-replay/history-replay.087.jpeg)

## Example of a Non-Deterministic Workflow 

Now that Replay has been covered, this section will explain why Workflows need to be
[deterministic](https://docs.temporal.io/workflow-definition#deterministic-constraints) in order for Replay to work.

A Workflow is deterministic if every execution of its Workflow Definition produces the same Commands in the same
sequence given the same input.

As mentioned in the [`How History Replay Provides Durable Execution`](#How-History-Replay-Provides-Durable-Execution)
walkthrough, in the case of a failure, a Worker requests the Event History to replay it. During Replay, the Worker runs
the Workflow code again to produce a set of Commands which is compared against the sequence of Commands in the Event
History. When there’s a mismatch between the expected sequence of Commands the Worker expects based on the Event History
and the actual sequence produced during Replay (due to non-determinism), Replay will be unable to continue.

To better understand why Workflows need to be deterministic, it's helpful to look at a Workflow Definition that violates
it. In this case, this code will walk through a Workflow Definition that breaks the determinism constraint with a random
number generator.

![NA](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/nondeterministic-workflow/nondeterministic-workflow.001.jpeg)
![Imagine the following Workflow Definition is being executed.](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/nondeterministic-workflow/nondeterministic-workflow.002.jpeg)
![As the Workflow executes step by step, the first line that results in a Command is the call to the `import_sales_data` Activity. The Worker issues the `ScheduleActivityTask` Command to the Service. In this case, the execution of the Activity is successful, so the Service logs three Events to the Event History: `ActivityTaskScheduled`, `ActivityTaskStarted`, `ActivityTaskCompleted`.](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/nondeterministic-workflow/nondeterministic-workflow.004.jpeg)
![Now, the Worker reaches a conditional statement which evaluates the value of a random-generated number. The random number generator happens to return the value of 84 during this execution. Since the expression evaluates to `true`, execution continues with the next line.](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/nondeterministic-workflow/nondeterministic-workflow.006.jpeg)
![The next line is a request to start a Timer, so the Worker issues a Command to the Service - `StartTimer` - requesting that it starts a Timer.](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/nondeterministic-workflow/nondeterministic-workflow.008.jpeg)
![The Service starts the Timer, records an Event - `TimerStarted` - and then records another Event when the Timer fires - `TimerFired`.](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/nondeterministic-workflow/nondeterministic-workflow.009.jpeg)
![Now imagine that the Worker happens to crash once it reaches to next line, so another Worker takes over, using Replay to restore the current state before continuing execution of the lines that follow.](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/nondeterministic-workflow/nondeterministic-workflow.010.jpeg)
![The Worker then requests the Event History to replay it. Once the Worker has the Event History, the Worker determines the expected sequence of Commands needed to restore the current state. The Worker is expecting to encounter the `ScheduleActivityTask` and `StartTimer` Commands.](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/nondeterministic-workflow/nondeterministic-workflow.011.jpeg)
![As the Worker executes the code during Replay, it reaches the first call to execute an Activity and creates a `ScheduleActivityTask` Command. This Command matches the one expected based on the Event History. It](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/nondeterministic-workflow/nondeterministic-workflow.013.jpeg)
![,
    ](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/nondeterministic-workflow/nondeterministic-workflow.015.jpeg)
![,
    ](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/nondeterministic-workflow/nondeterministic-workflow.016.jpeg)
![,
    ](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/nondeterministic-workflow/nondeterministic-workflow.017.jpeg)
![,
    ](https://learn.temporal.io/courses/temporal-102/python/event-history-walkthrough/nondeterministic-workflow/nondeterministic-workflow.018.jpeg)

Note that non-deterministic failures do not fail the Workflow Execution by default. A non-deterministic failure is
considered a [Workflow Task Failure](https://docs.temporal.io/references/failures#workflow-task-failures) which is
considered a transient failure, meaning it retries over and over. Users can also fix the source of non-determinism,
perhaps by removing the Activity, and then restart the Workers. This means that this type of failure can recover by
itself. You can also use a strategy called versioning to address this non-determinism error. See
[versioning](/develop/python/workflows/versioning) to learn more.

For more information on how Temporal handles Durable Execution or to see these slides in a video format with more
explanation, check out our free, self-paced courses: [Temporal 102](https://learn.temporal.io/courses/temporal_102/) and
[Versioning Workflows](https://learn.temporal.io/courses/versioning/).

## Temporal Applications Support Non-Deterministic Operations

We want to emphasize that although your Workflows themselves need to be deterministic, your application itself does not!

Remember that pretty much anything that interacts with the external world is inherently non-deterministic:

- Calling LLM APIs
- Querying databases
- Reading or writing files
- Making HTTP requests to external services

**Good news**: Your Temporal application can absolutely handle all of these operations. While your Workflow must be
deterministic, your application absolutely can handle any type of non-deterministic operation, including those listed
above. This gives you the best of both worlds—the crash-proof reliability of a Workflow and the resiliency of Activities
which have built-in support for retries.
