-
Notifications
You must be signed in to change notification settings - Fork 7
/
workflow_saga.ts
75 lines (64 loc) · 2.32 KB
/
workflow_saga.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
/*
* Copyright (c) 2024 - Restate Software, Inc., Restate GmbH
*
* This file is part of the Restate examples,
* which is released under the MIT license.
*
* You can find a copy of the license in the file LICENSE
* in the root directory of this repository or package or at
* https://github.com/restatedev/examples/
*/
import * as restate from "@restatedev/restate-sdk";
import { flights } from "./services/flights";
import { cars } from "./services/cars";
import { payments } from "./services/payments";
//
// An example of a trip reservation workflow, using the SAGAs pattern to
// undo previous steps in case of an error.
//
// The durable execution's guarantee to run code to the end in the presence
// of failures, and to deterministically recover previous steps from the
// journal, makes SAGAs easy.
// Every step pushes a compensation action (an undo operation) to a stack.
// in the case of an error, those operations are run.
//
// The main requirement is that steps are implemented as journald
// operations, like `ctx.run()` or rpc/messaging.
export default async (ctx: restate.Context, tripID: string) => {
// create an compensation stack
const compensations = [];
try {
//
// call the flight API to reserve, keeping track of how to cancel
//
const flightBooking = await ctx.run("reserve a flight", () =>
flights.reserve(tripID)
);
compensations.push(() => flights.cancel(tripID, flightBooking));
//
// call the car rental service API to reserve, keeping track of how to cancel
//
const carBooking = await ctx.run("rent a car", () => cars.reserve(tripID));
compensations.push(() => cars.cancel(tripID, carBooking));
//
// call the payments API, keeping track of how to refund
//
const paymentId = await ctx.run("process payment", () =>
payments.process({ tripID })
);
compensations.push(() => payments.refund({ tripID, paymentId }));
//
// confirm the flight and car reservations
//
await flights.confirm(tripID, flightBooking);
await cars.confirm(tripID, carBooking);
} catch (e) {
if (e instanceof restate.TerminalError) {
// undo all the steps up to this point by running the compensations
for (const compensation of compensations.reverse()) {
await compensation();
}
}
throw e;
}
};