<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Dominik Tornow</title>
    <description>The latest articles on DEV Community by Dominik Tornow (@dtornow).</description>
    <link>https://dev.to/dtornow</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F772879%2Fd33e6c04-b9e1-4171-92a6-80d25af6bf8c.jpeg</url>
      <title>DEV Community: Dominik Tornow</title>
      <link>https://dev.to/dtornow</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dtornow"/>
    <language>en</language>
    <item>
      <title>Paper Summary: Sagas</title>
      <dc:creator>Dominik Tornow</dc:creator>
      <pubDate>Sat, 18 Feb 2023 03:30:24 +0000</pubDate>
      <link>https://dev.to/temporalio/paper-summary-sagas-4bb6</link>
      <guid>https://dev.to/temporalio/paper-summary-sagas-4bb6</guid>
      <description>&lt;p&gt;&lt;em&gt;H. Garcia-Molina and K. Salem. 1987. Sagas. In Proceedings of the 1987 ACM SIGMOD international conference on management of data (SIGMOD ‘87). Association for Computing Machinery, New York, NY, USA, 249–259.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keywords:&lt;/strong&gt; Long Running Processes, Sagas, Transactions, Compensation, Failure Mitigation, Failure Tolerance, Failure Transparency&lt;/p&gt;

&lt;p&gt;Although the original paper on Sagas was written 35 years ago, the problem is still just as relevant today. This post explores the origins of the concept of Sagas, long-running processes that span multiple database transactions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In their paper Sagas, Hector Garcia-Molina and Kenneth Salem introduce the concept of sagas to implement “long-running” operations.&lt;/p&gt;

&lt;p&gt;Molina and Salem refer to processes that consist of a single step as “short-running” or “short-lived” processes and processes that consist of multiple steps as “long-running” or “long-lived”. Therefore, short-running or long-running is not a statement about physical time but a statement about logical time with each step being a clock tick (Figure 1).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mDHdxRhc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/opzyov1gopusrf5ngwuy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mDHdxRhc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/opzyov1gopusrf5ngwuy.png" alt="Figure 1 Short-running &amp;amp; Long-running" width="562" height="241"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;In an ecosystem that largely rejects the idea of distributed transactions or holding locks for an extended period of time, any long-running process cannot simply be mapped to a long-running, distributed transaction. Instead, we need a different mechanism to compose long-running processes from short-running, non-distributed transactions.&lt;/p&gt;

&lt;p&gt;The paper defines a process P as a sequence of steps a, b, etc, where each step is a database transaction. To use more familiar terms, you can think of a process as a function and the sequence of steps as a sequence of API calls to the database.&lt;/p&gt;

&lt;p&gt;We refer to the sequence of steps a, b, etc as a trace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;trace(P) = a • b • …
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The authors identify that the trace, that is, the sequential composition of transactions is not closed under atomicity (Figure 2). Here, the term atomicity denotes that an operation happens entirely or not at all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In other words: A sequence of two transactions is not a transaction!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_Ks3IjYU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/81aisbdgrn4yjsdycnh0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_Ks3IjYU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/81aisbdgrn4yjsdycnh0.png" alt="Figure 2. The sequential composition of two transactions is not itself a transaction." width="558" height="290"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Trivially, if a process consists of a single transaction, the process exhibits the atomicity guarantees of a transaction (Figure 3).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YMR3DJvC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uwvh91p452kcoopcv86f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YMR3DJvC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uwvh91p452kcoopcv86f.png" alt="Figure 3. A process P consists of one step a" width="880" height="349"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, if a process consists of multiple transactions, the process does not exhibit the atomicity guarantees of a transaction: The process may execute partially (Figure 4).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--K_REklkj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4yntq4ned00efjfa6vdo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--K_REklkj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4yntq4ned00efjfa6vdo.png" alt="Figure 4. A process P consists of two steps a and b" width="880" height="349"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, unlike other transactions, a process’ transactions are related and should be executed as a unit, any partial executions of a process are undesirable.&lt;/p&gt;

&lt;p&gt;The paper discusses a travel reservation application, where a trip consists of multiple flights potentially on different airlines. Here, booking a trip involves multiple transactions, one per trip leg. As travelers, we want all transactions to be executed as a unit: We either want to book all flights or no flights, booking some flights is unacceptable.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;

&lt;p&gt;The paper introduces Sagas, a protocol for implementing long-running processes. The Saga protocol ensures that a process is atomic, that is, a process executes observably equivalent to completely or not at all.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0QlrvKb4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m6tujis3bt5ayxlw0m1k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0QlrvKb4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m6tujis3bt5ayxlw0m1k.png" alt="Figure 5. Backward &amp;amp; Forward Recovery" width="880" height="439"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To mitigate partial execution, a process may implement either forward or backward recovery [2]:&lt;/p&gt;

&lt;h3&gt;
  
  
  Forward Recovery
&lt;/h3&gt;

&lt;p&gt;Forward failure recovery refers to failure mitigation strategies that transition the system from the intermediary state to a state that is equivalent to the final state (moving the process forward).&lt;/p&gt;

&lt;h3&gt;
  
  
  Backward Recovery
&lt;/h3&gt;

&lt;p&gt;Backward failure recovery refers to failure mitigation strategies that transition the system from the intermediary state to a state that is equivalent to the initial state (moving the process backward).&lt;/p&gt;

&lt;h3&gt;
  
  
  Backward Recovery &amp;amp; Compensation
&lt;/h3&gt;

&lt;p&gt;To enable backward recovery, each process should register a compensating step for each step (Figure 6).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wyx6_MQu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zodkykfz0lg4yb0fuba8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wyx6_MQu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zodkykfz0lg4yb0fuba8.png" alt="Figure 6. Transaction sᵢ &amp;amp; Compensation cᵢ" width="880" height="369"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The compensating step undoes — from a semantic point of view — the step, but does not necessarily return the database to the state that existed when the step began. Compensation will restore the world to a state which is an acceptable approximation to the state that it had before the start of the transaction.&lt;/p&gt;

&lt;p&gt;For example, coming back to the travel reservation application, if a trip consists of two flights and reserving the first flight succeeded yet reserving the second flight failed, the saga needs to cancel the first reservation to return the database to an acceptable approximation to the state it had before the start of booking the trip.&lt;/p&gt;

&lt;h2&gt;
  
  
  Execution
&lt;/h2&gt;

&lt;p&gt;So in the absence of failure, a process P produces the desired trace&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;trace(P) = s₁ • s₂ • … sₙ • ✔️
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;while in the presence of failure after step sᵢ, the process P produces the less desirable yet correct trace&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;trace(P) = s₁ • s₂ • … sᵢ • × • cᵢ … • c₂ • c₁ • ✔️
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Implementation Considerations
&lt;/h2&gt;

&lt;p&gt;When a failure interrupts a Saga, a Saga must be able to recover its current state and either perform forward or backward recovery. Therefore, a Saga must be able to record the beginning of a Saga, the completion of a Saga, and record checkpoints and compensations.&lt;/p&gt;

&lt;p&gt;If you own the database you may record checkpoints and compensations transactionally with each step. If you cannot record checkpoints and compensations transactionally with each step, you have to devise a mechanism to record checkpoints compensations reliably.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discussion
&lt;/h2&gt;

&lt;p&gt;Although the paper was written more than 35 years ago, the problem and Sagas as the proposed solution are still relevant today. However, attention has shifted from composing database transactions to composing service invocations.&lt;br&gt;
Keep in mind that services do not exhibit transactional properties: Services may not be atomic, after a service invocation, and if the service crashes, you may not know if the service took effect or not.&lt;/p&gt;

&lt;p&gt;Therefore, you should register a compensating action before invoking a service and design compensation logic to be correct whether the service took effect or not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Sagas remain relevant, despite significant advances in theory and practice since the paper’s publication. The basic concept of a Saga as a protocol for implementing atomicity in a distributed environment across transactions or services the techniques described in the paper continue to be widely used today.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;[1] A trace semantics for long-running transactions, M. Butler, C. A. R. Hoare &amp;amp; C. Ferreira, 2005&lt;br&gt;
[2] Handling Failures From First Principles, Dominik Tornow, 2022&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Introducing the Linear Developer Experience</title>
      <dc:creator>Dominik Tornow</dc:creator>
      <pubDate>Wed, 11 Jan 2023 19:05:01 +0000</pubDate>
      <link>https://dev.to/temporalio/introducing-the-linear-developer-experience-141l</link>
      <guid>https://dev.to/temporalio/introducing-the-linear-developer-experience-141l</guid>
      <description>&lt;p&gt;&lt;em&gt;Many complex systems exhibit a phenomenon known as a tipping point. A tipping point is a non-linear event, when a small quantitative change triggers a significant qualitative change that is often irreversible. These moments of rapid and dramatic change play a prominent role in diverse fields of study, such as physics, sociology, economics, epidemiology, as well as software engineering.  Knowing about tipping points, understanding where and when they occur, helps organizations and individuals anticipate and mitigate the significant consequences for teams and the applications they work on.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As history repeatedly demonstrates, the complexity of software continues to grow as software becomes more broadly and deeply integrated into every aspect of our lives. Companies are quickly recognizing the need to provide a good developer experience to manage that complexity in order to boost productivity and increase developer satisfaction. While the need for a good developer experience is widely recognized, there is no consensus on its definition.&lt;/p&gt;

&lt;p&gt;However, a common sentiment among software developers is that a project’s developer experience starts out delightful but eventually and suddenly becomes dreadful.&lt;/p&gt;

&lt;p&gt;Let’s take a Django web application as an example: After you set up a Django project and generate and configure the initial application, development follows a straight path. First, you create models, controllers, views, and unit tests. Django does the heavy lifting to create an admin interface as well as database migrations when your models change. As the application evolves, you add more models, more controllers, more views, and more unit tests. Django makes updates to the admin interface and performs the database migrations.&lt;/p&gt;

&lt;p&gt;We could describe the Django developer experience as linear: While the application grows quantitatively (more models, controllers, views, etc.), it remains the same qualitatively (the architecture of the application is still expressed as models, controllers, views, etc.). In other words, even though your application gets bigger, your developer experience does not change. Hence, the application's developer experience grows linearly.&lt;/p&gt;

&lt;p&gt;Until you hit the tipping point! Just one more requirement, just one more feature, a small quantitative change that triggers a significant qualitative change, the straw that breaks the camel’s back, the last drop that overflows the barrel.&lt;/p&gt;

&lt;p&gt;There are many factors that push us over the tipping point, but some are consistently at play: Implementing a multi-step process that spans multiple database transactions or orchestrates multiple services, a process that outlives a single request-response lifecycle, or time-based actions.&lt;/p&gt;

&lt;p&gt;For example, Django, like most web frameworks, supports only single-step processes well. Per request, the Django framework executes one request handler, which is a plain function. In turn, that request handler invokes one database transaction. However, how do we approach a task that does not solely execute one database transaction or one service call, or a task that outlives the duration of a single request? How do we implement a task that requires two, five, or ten transactions, that orchestrates two, five, or ten services, or that runs for minutes, hours, days, weeks, months, or years?&lt;/p&gt;

&lt;p&gt;Every project I’ve worked on hits a tipping point. We’ve all had the meeting where the tipping point becomes apparent. Sometimes we were junior engineers, and quietly listened. Other times we acted as senior engineers, and hotly debated terms such as orchestration, choreography, commands, events, job queues, job scheduling, or outbox patterns. Our arguments flooded the room, but regardless of our role, the imminent turn for the worse quickly became apparent. We had arrived at the tipping point!&lt;/p&gt;

&lt;p&gt;At this point, Django no longer feels like my ally. Django feels like my adversary. Instead of empowering me, Django holds me back. I find myself needing to work around restrictions and limitations. Databases, queues, and timers are the duct tape holding my application together. Sagas, commands, and events are the crutches on which I drag myself through the day.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fefebvos73d5k2xk75lrw.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fefebvos73d5k2xk75lrw.jpg" alt="Image illustrating a non-linear progression" width="769" height="712"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I have two options: I can embrace the tipping point as inevitable, or I can reject the tipping point as inevitable.&lt;/p&gt;

&lt;p&gt;By embracing the tipping point, I can either declare defeat now or later. If I am willing to declare defeat now, I begin building my application as if I’ve reached the tipping point already. From day one, I implement my application in terms of sagas, commands, and events, I orchestrate and choreograph, and I enqueue and dequeue in anticipation that eventually I will have to go down that path anyways. Well, if my developer experience is dreadful from day one, at least I can’t be missing what I once had. If I am willing to declare defeat later, I begin my application as if no tipping point looms on the horizon. I soothe myself with euphemisms, I tell myself that if I don’t have to re-architect and re-implement my applications later, I am making a mistake now. KISS—keep it simple, stupid. YAGNI—you ain’t gonna need it. I can find comfort in an ecosystem armed with acronyms assuring me all of this is fine.&lt;/p&gt;

&lt;p&gt;However, it’s not. If I embrace the tipping point, whether I declare defeat now or later, eventually quality, productivity, and satisfaction will suffer. Engineers will be writing scores of code to “plug into” hunting and fixing heisenbugs in the edge cases of the application, barely able to predict or reason about the consequences of releasing that bug fix. Unexpectedly, I will “lose” the best engineers on the team, and they will no longer be doing creative work on the application; instead, the best engineers will now be doing creative work on libraries, frameworks, and platforms.&lt;/p&gt;

&lt;p&gt;Nobody will be delighted.&lt;/p&gt;

&lt;p&gt;Instead, I should reject the tipping point as inevitable. At this point in time, when most software runs in the cloud and on the edge, integrates with multiple and various third-party APIs, and executes processes that run from a few seconds to many days, I should demand libraries, frameworks, and platforms that offer a linear developer experience—one that I will not outgrow as my project scales.&lt;/p&gt;

&lt;p&gt;One that starts delightful and stays delightful.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fumt48tqqfgcyi9cmqj0q.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fumt48tqqfgcyi9cmqj0q.jpg" alt="Image illustrating a linear progression" width="773" height="711"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tipping points can be a devastating event causing a sudden and consequential decline in the experience of your team, leading to a decline in quality, productivity, and satisfaction. By knowing about tipping points and understanding where and when they occur you are able to anticipate, mitigate and outright avoid their consequences.&lt;/p&gt;

&lt;p&gt;Don’t let a tipping point derail your project. Be proactive and be aware of your developer experience. Adopt libraries, frameworks, and platforms that start delightful and stay delightful.&lt;/p&gt;

&lt;p&gt;Look for a Linear Developer Experience.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Learn more in Temporal's on-demand webinar &lt;a href="https://pages.temporal.io/webinar-linear-developer-experience-on-demand.html" rel="noopener noreferrer"&gt;Introducing the Linear Developer Experience&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>watercooler</category>
    </item>
    <item>
      <title>Introduction to Temporal Workflows</title>
      <dc:creator>Dominik Tornow</dc:creator>
      <pubDate>Mon, 24 Jan 2022 20:20:43 +0000</pubDate>
      <link>https://dev.to/dtornow/introduction-to-temporal-workflows-5bn0</link>
      <guid>https://dev.to/dtornow/introduction-to-temporal-workflows-5bn0</guid>
      <description>&lt;p&gt;For the past 45 years, the database community has enjoyed an unparalleled developer experience: transactions mitigate failure in totality on a platform level, guaranteeing correctness on an application level.&lt;/p&gt;

&lt;p&gt;Despite many advancements in the past 20 years, the distributed systems community has not enjoyed an equivalent developer experience: There is no abstraction that mitigates failure in totality on a platform level, guaranteeing correctness on an application level.&lt;/p&gt;

&lt;p&gt;However, &lt;a href="//temporal.io"&gt;Temporal&lt;/a&gt; changes that equation! &lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Temporal’s core abstraction, its unit of execution, reliability, and scalability, is the Workflow. Therefore understanding the Workflow is key to understanding Temporal in general. In this blog post series we deep dive into the world of Temporal Workflows.&lt;/p&gt;

&lt;p&gt;A Temporal Workflow is Temporal’s core abstraction. You may think of a Temporal Workflow Definition as a regular Function Definition—in fact, that is the developer experience that Temporal provides to its users—but a Workflow Execution provides stunning improvements over a regular Function Execution.&lt;/p&gt;

&lt;p&gt;Let’s get to know Workflow Executions and contrast them to regular Function Executions with a straightforward example: sending reminder emails.&lt;/p&gt;

&lt;p&gt;Our use case requires that our application sends a reminder email once a month to any user who signed up for a trial period to upgrade their plan. In pseudo code our use case can be expressed as:&lt;/p&gt;

&lt;pre&gt;
  &lt;code&gt;
&lt;b&gt;function&lt;/b&gt; send monthly reminder (user) &lt;b&gt;do&lt;/b&gt;
  &lt;b&gt;while&lt;/b&gt; user has not signed up &lt;b&gt;do&lt;/b&gt;
    send reminder to user
    sleep for 1 month
  &lt;b&gt;end&lt;/b&gt;
&lt;b&gt;end&lt;/b&gt;
  &lt;/code&gt;
&lt;/pre&gt;

&lt;h2&gt;
  
  
  Regular Functions
&lt;/h2&gt;

&lt;p&gt;The pseudo code looks fairly straightforward; in fact, the pseudo code looks less like an implementation and more like a specification. Could we use the pseudo code as a blueprint for a regular Function Definition?&lt;/p&gt;

&lt;p&gt;No, not at all:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In a typical environment we cannot just invoke a function and expect the resulting function execution to reliably execute to completion — or like in this case execute indefinitely until cancelation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As a result, we have to “break up” the process of Send Reminder Email into many different pieces, scattered across the tech stack: A cron job here, a message in a queue there, maybe a row in some database table, you know, for good measure.&lt;/p&gt;

&lt;p&gt;On top of that, now we need to worry about failures, retries, duplication, and idempotence.&lt;/p&gt;

&lt;p&gt;An implementation on top of services like AWS Lambda Functions and like AWS Simple Queueing Service might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="c1"&gt;// Lambda function is bound to&lt;/span&gt;
&lt;span class="c1"&gt;// a. input queue "Reminder"&lt;/span&gt;
&lt;span class="c1"&gt;// b. output queue "Reminder"&lt;/span&gt;

&lt;span class="c1"&gt;// We assume that&lt;/span&gt;
&lt;span class="c1"&gt;// - messages are never lost&lt;/span&gt;
&lt;span class="c1"&gt;// - messages may be duplicated&lt;/span&gt;
&lt;span class="c1"&gt;// - messages are retried on failure&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;SendReminderEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Message&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="c1"&gt;// UserSignup, SendEmail, Get, Set will throw an&lt;/span&gt;
  &lt;span class="c1"&gt;// Exception on failure&lt;/span&gt;

  &lt;span class="c1"&gt;// event.user Current user&lt;/span&gt;
  &lt;span class="c1"&gt;// event.iter Current iteration to limit retries (here 2)&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;UserSignedUp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;// Retrieve the k/v pair for this user and iteration&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;kv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`$(event.user)-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;iter&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Try at most twice&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;kv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Conditionally set the key. If the tag does not &lt;/span&gt;
      &lt;span class="c1"&gt;// match we are racing with another instance of&lt;/span&gt;
      &lt;span class="c1"&gt;// SendReminderEmail&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;kv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;kv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;kv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// This does not prevent us from calling SendEmail&lt;/span&gt;
        &lt;span class="c1"&gt;// twice. Do you see why?&lt;/span&gt;
        &lt;span class="nx"&gt;SendEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;iter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;iter&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;after&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1month&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Start by queueing the message {user: "&amp;lt;User&amp;gt;", iter: 0} on&lt;/span&gt;
&lt;span class="c1"&gt;// the message queue "Reminder"&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Listing 2 looks nothing like the pseudo code in Listing 1. &lt;/p&gt;

&lt;p&gt;Listing 2 does not tell the story of our use case—while not overly long or verbose, it is obscure and hard to reason about.&lt;/p&gt;

&lt;h2&gt;
  
  
  Temporal Workflows
&lt;/h2&gt;

&lt;p&gt;Obviously we cannot use the pseudo code as a blueprint for a regular Function Definition. However, could we use the pseudo code as a blueprint for a Temporal Workflow Definition?&lt;/p&gt;

&lt;p&gt;Well, yes, yes we can!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;proxyActivities&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sleep&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@temporalio/workflow&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;sendReminderEmail&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;hasSignedUp&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;proxyActivities&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;scheduleToCloseTimeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;10 seconds&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;retry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;maximumAttempts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;SendReminderEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;hasSignedUp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sendReminderEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Thanks to Temporal's retry policy, we already &lt;/span&gt;
        &lt;span class="c1"&gt;// tried twice, better luck next month 🍀&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1 month&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;In Temporal we can just invoke a Workflow Definition, and the resulting Workflow Execution reliably executes to completion — or like in this case execute indefinitely until cancelation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Temporal Workflow Executions are to distributed systems what transactions are to databases: A great developer experience and (or maybe because of) peace of mind.&lt;/p&gt;

&lt;p&gt;Doubts? Disbelief? Check out Part II and Part III to explore how Temporal implements this game-changing execution model.&lt;/p&gt;

&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@8moments?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Simon Berger&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/zen?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>temporal</category>
      <category>workflows</category>
      <category>distributedsystems</category>
    </item>
  </channel>
</rss>
