|
| 1 | +--- |
| 2 | +sidebar_position: 3 |
| 3 | +title: Time Travel Debugger Quickstart |
| 4 | +--- |
| 5 | + |
| 6 | +Now that we've learned a little about programming DBOS, let's learn how to use the DBOS Time Travel Debugger! |
| 7 | + |
| 8 | +This tutorial assumes you have [Visual Studio Code](https://code.visualstudio.com/) installed. |
| 9 | +Please see the VS Code documentation for [installation instructions](https://code.visualstudio.com/docs/setup/setup-overview) if necessary. |
| 10 | + |
| 11 | +:::info |
| 12 | +If you're not a VS Code user, there is an advanced tutorial for [Time Travel Debugging with DBOS CLI](../cloud-tutorials/timetravel-debugging#time-travel-with-dbos-sdk-cli-non-vs-code-users). |
| 13 | +::: |
| 14 | + |
| 15 | +Additionally, this tutorial builds on the [DBOS Quickstart](./quickstart). |
| 16 | +For convenience, we recommend [creating a DBOS Cloud account](./quickstart#deploying-to-dbos-cloud) and |
| 17 | +[provisioning a DBOS Cloud database instance](./quickstart#provisioning-a-cloud-database-instance) before starting this tutorial. |
| 18 | + |
| 19 | +### Installing the DBOS Time Travel Debugging VS Code Extension |
| 20 | + |
| 21 | +Before we use the DBOS Time Travel debugger, we need to install its VS Code extension. |
| 22 | + |
| 23 | +To install the extension, navigate to the [extension's web page](https://marketplace.visualstudio.com/items?itemName=dbos-inc.dbos-ttdbg) and press the "Install" button. |
| 24 | +This will launch VS Code and open the DBOS Time Travel Debugger extension page inside the IDE. |
| 25 | +From there, select the "Install" button to install the extension. |
| 26 | + |
| 27 | + |
| 28 | + |
| 29 | +:::info |
| 30 | +You can also install the extension by opening the [Extension Marketplace](https://code.visualstudio.com/docs/editor/extension-marketplace) |
| 31 | +inside VS Code (default keybinding: Ctrl+Shift+X / ⇧⌘X) and searching for "DBOS". |
| 32 | +::: |
| 33 | + |
| 34 | +### Deploy the Programming Quickstart App to DBOS Cloud |
| 35 | + |
| 36 | +In this tutorial, you will time travel debug the `greeting-emails` application you built in the [Programming Quickstart](./quickstart-programming). |
| 37 | + |
| 38 | +If you finished that tutorial, remove the sleep logic from `GreetingWorkflow`. |
| 39 | +We're not going to need that sleep code for this tutorial. |
| 40 | + |
| 41 | +If you did not finished the Programming Quickstart, create a new DBOS application using `@dbos-inc/create`. |
| 42 | + |
| 43 | +``` |
| 44 | +npx -y @dbos-inc/create -n <app-name> |
| 45 | +``` |
| 46 | + |
| 47 | +And replace the logic in `src/operations.ts` with the following. |
| 48 | + |
| 49 | +```ts |
| 50 | +import { |
| 51 | + TransactionContext, Transaction, |
| 52 | + HandlerContext, GetApi, |
| 53 | + CommunicatorContext, Communicator, |
| 54 | + WorkflowContext, Workflow, |
| 55 | +} from "@dbos-inc/dbos-sdk"; |
| 56 | +import { Knex } from "knex"; |
| 57 | + |
| 58 | +export class Greetings { |
| 59 | + @Communicator() |
| 60 | + static async SendGreetingEmail(ctxt: CommunicatorContext, friend: string, content: string) { |
| 61 | + ctxt.logger.info(`Sending email "${content}" to ${friend}...`); |
| 62 | + // Code omitted for simplicity |
| 63 | + ctxt.logger.info("Email sent!"); |
| 64 | + } |
| 65 | + |
| 66 | + @Transaction() |
| 67 | + static async InsertGreeting(ctxt: TransactionContext<Knex>, friend: string, content: string) { |
| 68 | + await ctxt.client.raw( |
| 69 | + "INSERT INTO dbos_greetings (greeting_name, greeting_note_content) VALUES (?, ?)", |
| 70 | + [friend, content] |
| 71 | + ); |
| 72 | + } |
| 73 | + |
| 74 | + @Workflow() |
| 75 | + @GetApi("/greeting/:friend") |
| 76 | + static async GreetingWorkflow(ctxt: WorkflowContext, friend: string) { |
| 77 | + const noteContent = `Thank you for being awesome, ${friend}!`; |
| 78 | + await ctxt.invoke(Greetings).SendGreetingEmail(friend, noteContent); |
| 79 | + await ctxt.invoke(Greetings).InsertGreeting(friend, noteContent); |
| 80 | + ctxt.logger.info(`Greeting sent to ${friend}!`); |
| 81 | + return noteContent; |
| 82 | + } |
| 83 | +} |
| 84 | +``` |
| 85 | + |
| 86 | +Next, we are going to deploy this application to DBOS Cloud. |
| 87 | +Currently, Time Travel Debugging is only supported for applications that have been deployed to DBOS Cloud. |
| 88 | + |
| 89 | +If you finished the [DBOS quickstart](./quickstart), you should already have a DBOS Cloud account and database instance. |
| 90 | +If you didn't finish the [Deploying to DBOS Cloud](./quickstart#deploying-to-dbos-cloud) section of that tutorial, |
| 91 | +please create an account and provision a cloud database instance by running the `npx dbos-cloud` commands shown below from project's root folder. |
| 92 | +Note, it can take up to 5 minutes to provision a database instance |
| 93 | + |
| 94 | +``` |
| 95 | +npx dbos-cloud register -u <username> |
| 96 | +npx dbos-cloud db provision <database-instance-name> -U <database-username> |
| 97 | +``` |
| 98 | + |
| 99 | +You can then deploy the app to DBOS Cloud by executing these commands from project's root folder: |
| 100 | + |
| 101 | +``` |
| 102 | +npx dbos-cloud app register -d <database-instance-name> |
| 103 | +npx dbos-cloud app deploy |
| 104 | +``` |
| 105 | + |
| 106 | +:::info |
| 107 | +DBOS Cloud database instances can host multiple application databases. |
| 108 | +Even if you deployed the DBOS quickstart app, you can also deploy the `greeting-emails` app using the same database instance. |
| 109 | +::: |
| 110 | + |
| 111 | +When complete, the `npx dbos-cloud app deploy` command will print your application's URL to the console. |
| 112 | +The URL will be formatted like: `https://<username>-greeting-emails.cloud.dbos.dev/`. |
| 113 | +Visit `https://<username>-greeting-emails.cloud.dbos.dev/greeting/dbos` in your browser a few times to generate data that we can use to demonstrate the time travel debugger. |
| 114 | + |
| 115 | +### Time Travel Debugging Your Cloud Application |
| 116 | + |
| 117 | +After you have installed the DBOS VS Code extension and deployed the app to DBOS Cloud, open up the project folder in VS Code then open the `src/operations.ts` file in the editor. |
| 118 | +Set a breakpoint at the top of each of the functions in the `operations.ts` file: `GreetingWorkflow`, `InsertGreeting`, `SendGreetingEmail`. |
| 119 | +To set a breakpoint in VS Code, position the cursor on desired line and press `F9`. |
| 120 | + |
| 121 | +Notice there is a Time Travel Debug CodeLens attached to each function in the app. |
| 122 | +This CodeLens is automatically attached to every DBOS Workflow, Transaction and Communicator function in a DBOS application. |
| 123 | +Click on the CodeLens attached to the `GreetingWorkflow` function. |
| 124 | + |
| 125 | + |
| 126 | + |
| 127 | +After you click on the CodeLens, you will given a list of workflow IDs of that function to choose from. |
| 128 | + |
| 129 | + |
| 130 | + |
| 131 | +After you select a workflow ID, the DBOS Time Travel Debugger will launch the DBOS debug runtime and VS Code TypeScript debugger. |
| 132 | +The workflow will start executing and break on the breakpoint you set at the top of the `GreetingWorkflow` method. |
| 133 | + |
| 134 | + |
| 135 | + |
| 136 | +The debugging experience for your DBOS application is similar to debugging any other |
| 137 | +[Node.JS application](https://code.visualstudio.com/docs/nodejs/nodejs-debugging) in VS Code. |
| 138 | +You can [set breakpoints](https://code.visualstudio.com/docs/editor/debugging#_breakpoints), |
| 139 | +[inspect variables](https://code.visualstudio.com/docs/editor/debugging#_data-inspection) and |
| 140 | +[step through your code](https://code.visualstudio.com/docs/editor/debugging#_debug-actions) as you would expect. |
| 141 | +However, there is one significant difference that you will notice if you press the Continue (F5) in the debugger. |
| 142 | + |
| 143 | + |
| 144 | + |
| 145 | +Even though you set a breakpoint in the `SendGreetingEmail` function, it did not get hit. |
| 146 | +Instead, the debugger stopped at the breakpoint at the `InsertGreeting` function. |
| 147 | +This is by design. |
| 148 | +[Communicators](../tutorials/communicator-tutorial.md) are used for code with non-idempotent side effects, such as sending an email to a user. |
| 149 | +When debugging, DBOS skips communicators to avoid these side effects. |
| 150 | + |
| 151 | +### Debugging Your Updated Application |
| 152 | + |
| 153 | +The Time Travel Debugger executes your DBOS application locally working against a snapshot of your DBOS Cloud database _as it existed at the time the selected workflow actually ran_. |
| 154 | +Unfortunately, the programming quickstart application only writes data to the database, it does not read it. |
| 155 | +This means the execution of `GreetingWorkflow` is identical regardless which workflow ID you selected to execute. |
| 156 | +Let's modify the code to read some state from the database and see how this updated code interacts with existing workflow executions stored in DBOS Cloud. |
| 157 | + |
| 158 | +Update `InsertGreeting` function to retrieve how many greetings the friend has received before and after the new greeting is added. |
| 159 | + |
| 160 | +```ts |
| 161 | +@Transaction() |
| 162 | +static async InsertGreeting(ctxt: TransactionContext<Knex>, friend: string, content: string) { |
| 163 | + const before = await ctxt.client.raw( |
| 164 | + "SELECT count(*) FROM dbos_greetings WHERE greeting_name = ?", |
| 165 | + [friend] |
| 166 | + ); |
| 167 | + ctxt.logger.info(`before count ${before.rows[0].count}`); |
| 168 | + |
| 169 | + await ctxt.client.raw( |
| 170 | + "INSERT INTO dbos_greetings (greeting_name, greeting_note_content) VALUES (?, ?)", |
| 171 | + [friend, content] |
| 172 | + ); |
| 173 | + |
| 174 | + const after = await ctxt.client.raw( |
| 175 | + "SELECT count(*) FROM dbos_greetings WHERE greeting_name = ?", |
| 176 | + [friend] |
| 177 | + ); |
| 178 | + ctxt.logger.info(`after count ${after.rows[0].count}`); |
| 179 | +} |
| 180 | +``` |
| 181 | + |
| 182 | +Now, when we click the `GreetingWorkflow` CodeLens, the workflow execution we select will affect the log output. |
| 183 | +If we select the oldest execution, we get output that looks like this. |
| 184 | + |
| 185 | +``` |
| 186 | +2024-03-22 23:00:53 [info]: Running in debug mode! |
| 187 | +2024-03-22 23:00:53 [info]: Debugging mode proxy: localhost:2345 |
| 188 | +2024-03-22 23:00:53 [info]: Workflow executor initialized |
| 189 | +2024-03-22 23:00:57 [info]: before count 0 |
| 190 | +2024-03-22 23:00:57 [info]: after count 1 |
| 191 | +2024-03-22 23:01:01 [info]: Greeting sent to friend! |
| 192 | +``` |
| 193 | + |
| 194 | +But if we select a later execution, we get different output. |
| 195 | + |
| 196 | +``` |
| 197 | +2024-03-22 23:03:40 [info]: Running in debug mode! |
| 198 | +2024-03-22 23:03:40 [info]: Debugging mode proxy: localhost:2345 |
| 199 | +2024-03-22 23:03:40 [info]: Workflow executor initialized |
| 200 | +2024-03-22 23:03:46 [info]: before count 2 |
| 201 | +2024-03-22 23:03:47 [info]: after count 3 |
| 202 | +2024-03-22 23:03:47 [info]: Greeting sent to friend! |
| 203 | +``` |
| 204 | + |
| 205 | +To clarify what has happened here, you modified the InsertGreeting function to retrieve database state and log it. |
| 206 | +Then, you executed that updated code in the Time Travel Debugger working against _past database state_. |
| 207 | +Note, this worked even though your local code is different from the code running in DBOS Cloud! |
| 208 | + |
| 209 | +:::warning |
| 210 | +When time travel debugging, you can freely add read queries to your application and observe their results when run against past database state. |
| 211 | +This state can be viewed via the logger as described above or via VS Code's variables window. |
| 212 | +However, you cannot change code that updates database state (i.e. insert/delete/update SQL statements) or change the value returned from |
| 213 | +a workflow or transaction function. |
| 214 | +::: |
| 215 | + |
| 216 | +Now that you know the basics of DBOS Time Travel Debugging, please check out our [tutorials](../category/dbos-sdk-tutorials). |
| 217 | +To learn more about the Time Travel Debugger, check out our Time Travel Debugger [tutorial](../cloud-tutorials/timetravel-debugging) |
| 218 | +and [reference](../api-reference/time-travel-debugger). |
0 commit comments