Skip to content

Commit

Permalink
Changed attempts to be in UI and now adding invalid to flight booking…
Browse files Browse the repository at this point in the history
… in UI causing rollback/compensation

Signed-off-by: ktenzer <keith.tenzer@gmail.com>
  • Loading branch information
ktenzer committed May 12, 2023
1 parent d0ab8d0 commit 3db8485
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 13 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ Notice each is executed via an activity.

### Demo: Recover Forward (retries)

In the `run_workflow.py` modify the global variable `ATTEMPTS = 1` to `ATTEMPTS = 3`, so that the `book_flight` Activity attempts a retry 3 times.
Modify `Attempts = 1` to `Attempts = 3` in UI, so that the booking activities attempt a retry 3 times.
Render your booking information in the Flask app <http://127.0.0.1:5000>, then see the tasks in the Web UI at <http://localhost:8233/>.

Select your running or completed Workflow ID.
Expand All @@ -60,7 +60,7 @@ Then notice how the Workflow executes the compensations.

### Demo: Recover Backward (rollback)

In the `run_workflow.py` modify the global variable `ATTEMPTS = 3` to `ATTEMPTS = 5`, so that the `book_flight` Activity attempts a retry 5 times.
Modify `Flight = Alaska Airlines 123` to `Flight = invalid` in the UI, so that the `book_flight` Activity fails and a rollback occurs.
Render your booking information in the Flask app <http://127.0.0.1:5000>, then see the tasks in the Web UI at <http://localhost:8233/>.

Select your running or completed Workflow ID.
Expand All @@ -69,7 +69,7 @@ Under **Recent** events, select the failed Activity, `book_flight` (in compact v

Under **ActivityTaskStarted** you'll see the Attempts (5), and the stack trace message letting you know the last failed attempt.

Under **ActivityTaskFailed** you'll see error `Too many retries, flight booking not possible at this time!`. You will also see that since the booking cannot be completed, rollback (undo) is performed using compensation.
Under **ActivityTaskFailed** you'll see error `Invalid flight booking, rolling back!`. You will also see that since the booking cannot be completed, rollback (undo) is performed using compensation.

## Design

Expand Down
34 changes: 28 additions & 6 deletions activities.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,28 +15,50 @@ class BookVacationInput:

@activity.defn
async def book_car(input: BookVacationInput) -> str:
await asyncio.sleep(3)
if activity.info().attempt < input.attempts:
activity.heartbeat(
f"Invoking activity, attempt number {activity.info().attempt}"
)
await asyncio.sleep(3)
raise RuntimeError("Car service is down")

if "invalid" in input.book_car_id:
raise Exception("Invalid car booking, rolling back!")

print(f"Booking car: {input.book_car_id}")
return f"Booked car: {input.book_car_id}"


@activity.defn
async def book_hotel(input: BookVacationInput) -> str:
await asyncio.sleep(3)
if activity.info().attempt < input.attempts:
activity.heartbeat(
f"Invoking activity, attempt number {activity.info().attempt}"
)
await asyncio.sleep(3)
raise RuntimeError("Hotel service is down")

if "invalid" in input.book_hotel_id:
raise Exception("Invalid hotel booking, rolling back!")

print(f"Booking hotel: {input.book_hotel_id}")
return f"Booked hotel: {input.book_hotel_id}"


@activity.defn
async def book_flight(input: BookVacationInput) -> str:
await asyncio.sleep(3)
if activity.info().attempt < input.attempts:
activity.heartbeat(
f"Invoking activity, attempt number {activity.info().attempt}"
)
await asyncio.sleep(1)
raise RuntimeError("Service is down")
elif activity.info().attempt > 3:
raise RuntimeError(
"Too many retries, flight booking not possible at this time!"
)
await asyncio.sleep(3)
raise RuntimeError("Flight service is down")

if "invalid" in input.book_flight_id:
raise Exception("Invalid flight booking, rolling back!")

print(f"Booking flight: {input.book_flight_id}")
return f"Booking flight: {input.book_flight_id}"
Expand Down
9 changes: 6 additions & 3 deletions book_workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,20 @@ async def run(self, input: BookVacationInput):
book_car,
input,
start_to_close_timeout=timedelta(seconds=10),
retry_policy=RetryPolicy(
non_retryable_error_types=["Exception"],
),
)
compensations.append("undo_book_hotel")
output += " " + await workflow.execute_activity(
book_hotel,
input,
start_to_close_timeout=timedelta(seconds=10),
retry_policy=RetryPolicy(
non_retryable_error_types=["Exception"],
),
)

# Sleep to simulate flight booking taking longer, allowing for worker restart while workflow running
await asyncio.sleep(15)

compensations.append("undo_book_flight")
output += " " + await workflow.execute_activity(
book_flight,
Expand Down
3 changes: 2 additions & 1 deletion run_workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,17 @@ async def display_form():
@app.route("/book", methods=["POST"])
async def book_vacation():
user_id = f'{request.form.get("name").replace(" ", "-").lower()}-{str(uuid.uuid4().int)[:6]}'
attempts = request.form.get("attempts")
car = request.form.get("car")
hotel = request.form.get("hotel")
flight = request.form.get("flight")

input = BookVacationInput(
attempts=int(attempts),
book_user_id=user_id,
book_car_id=car,
book_hotel_id=hotel,
book_flight_id=flight,
attempts=ATTEMPTS,
)

if (
Expand Down
9 changes: 9 additions & 0 deletions templates/book_vacation.html
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,15 @@ <h2>GitHub: <a href="https://github.com/temporalio">github.com/temporalio</a></h
<h2>Docs: <a href="https://docs.temporal.io">docs.temporal.io</a></h2>
<p>Eliminate complex error or retry logic, avoid callbacks, and ensure that every workflow you start, completes. Temporal delivers durable execution for your services and applications.</p>
<form method="post" action="/book">
<label for="name">Attempts:</label>
<input
type="text"
name="attempts"
id="attempts"
value="1"
required
placeholder="Your Temporal User"
/><br />
<label for="name">Name:</label>
<input
type="text"
Expand Down

0 comments on commit 3db8485

Please sign in to comment.