Yeah, so I think that one of the things is that a big part of... I mean it's said that sixty to eighty percent of the code that is built for applications and distributed systems is failure handling code.
And that is something that I don't see us doing on the platform side as much because what we do is we build these automations. We even build like... I love the transition. I like you, I'm not Erlang, but I'm a functional programmer at heart and I love declarative systems. That's why Cloud Foundry. And Cloud Foundry was before Kubernetes, so it was one of these... it was maybe around the same time that Terraform was coming on the scene... and so this is my love letter to Terraform.
Terraform did something amazing. It was this declarative system. It was like, "Okay, we're going to go out and take a look at what the current state is. We're going to compare the state and then we're going to update it." Love all of that, but things break.
And so this system where we're making calls to AWS and Cloudflare and some APIs in our own internal data centers, those things break. And so what we end up is... we end up with systems that are inconsistent, we end up with orphans, all of those types of things.
And then we try to apply, like, if you will, maybe brute force hammers to trying to fix those things. "Oh, I'll have a cron job that goes and looks for orphans every once in a while," that type of thing. But the reality is that there's patterns that could keep you from getting into a state where you've got orphans.
I think that we tend to be reactive. And so it's largely around that failure handling is that... yeah, we can put together orchestrations, we can do declarative configurations, but then realizing that to stitch it all together... and by the way, in the platform space, nothing runs in a half a second, in five hundred milliseconds, everything runs for not just minutes, but hours, days, weeks and months. And so the longer that something runs, the more likely something's going to fail. And so it's that failure handling, that implicit inherent failure handling that has to be there for everything.
And it's hard, by the way, because you don't want to spend sixty to eighty percent of your cycles working on making the system as durable as you can, because you've got a whole team of developers in your organization that are like, "When are you going to give me the next feature I'm waiting for?"
Yeah. So durable execution is a term. It's gaining a little bit of popularity. But the way that I would describe it, because most of your listeners probably aren't super familiar with it, is that it's... and I'm a nerd, so I'm going to explain it from a nerdy perspective... it's a programming model.
It's a programming model that allows you to write your code as if those failures didn't exist.
You see the potential huge value here in the platform space is that the platform space, because you say they come from a systems administration background often, and they have gone through tremendous pains to clean up after failures. They do program as if failures don't exist, and then they compensate for that.
Well, what this is, is it's a programming model that allows you to program as if process boundaries don't exist, as if failures don't exist. And the system, the durable execution platform will actually put all those compensations in place for you.
So for example, it will do automatic retries for you. And by the way, those retries... now I know you're probably thinking, "Retries? Well, everybody does retries." The thing about durable execution is that the retries themselves are durable. And what I mean by that is that even if you're in the middle of retrying something, you've got a something rate limited, or you've got some network issue and you're in the middle of retrying and then some other failure happens and your retry logic itself is in process and goes away. Oopsie.
But by durable retry, I mean that if the system that's doing the retrying, so you're at step three and you're doing the retrying, and now your orchestration itself goes down. When your orchestration comes back up, we remember exactly that you were on retry number three and we continue on with that.
So durable retries, for example, is one of the examples... or state management, like managing state... all of that stuff is what we call durability. So you get to program as if failures don't exist. But the runtime behaviors... and by the way, a lot of that is distributed systems, and we'll dig into that, I'm sure, in just a moment... the runtime behaviors make the system resilient to those failures. It's not that the failures don't exist. It's like a waterproof watch. Doesn't mean it won't get wet, it means that if it gets wet, it'll still be okay.
Yep. So I'll answer that question in a couple of different ways.
From a theoretical perspective, durable execution does not imply a graph model or imply not a graph model. You can do durability with either of those programming models.
The one that we do... and I work, as you said in the intro, I work for Temporal, which by the way, is an open source, hundred percent open source - it's not open core, it's not like you can run some stuff on the open source, it's 100% open source. That's what I'm going to be talking about today is the open source, super cool technology... Our approach there is that we don't want to introduce a DAG, we don't want to introduce a DSL, that you have to program a different programming model. We believe that the most natural programming models are the ones that you're already familiar with. They are the languages that everybody uses. It's Python, it is Typescript, it is Java, it is .Net. We support seven different languages. There's even a Swift SDK.
Whatever programming models that you're familiar with, you can continue to use those. Getting back to your question, what the code looks like is, it looks like regular old code.
Now, there's two fundamental abstractions that we use. One is called an activity. And an activity is basically a unit of work, but it's a unit of work where there's a possibility of failure. Then the other abstraction is what we call a workflow, which stitches together all of those units of work in the flow that you want. The workflow, if you think of it, is the overall application. The activities are the units of work where there could potentially be failure.
You program that. You basically say, "Okay, here's my..." basically activities, or decorators on functions. If you've already structured your code in a really great modular way, you've already got functions that encapsulate the units of work. You put activity decorators on those which tells our SDK, "Oh, hang on, pay attention to this. When this function is called, we want you to do some magic for us.We want you to step in, we want you to put that durable retry in there. We want to do some state management around that." So the SDK basically acts as, if you will, a bit of proxy around that function.
It turns it into a distributed system, which maybe we should talk about next.
Yeah. The first thing is that the durable execution layer doesn't actually know the details of your application logic. So you just described a really nuanced application specific thing.
The way that I've created my unit of work is it bundles up a number of different things... which by the way I'm going to go off on a little bit of a tangent here. One of the things that we've seen is obviously in the platform space, everybody uses Terraform. We've seen customers that are using Terraform and they're creating these Terraform configurations that are very composite. And it's like, "Do this, do this, do this and do this." And that's part of the reason why you end up in this very complex nuanced scenario that you did. Which is like, "Okay, this composite object, part of it worked, part of it didn't. How do we deal with all of that?"
One of the things that we're seeing is that people are starting to, because they have durable execution, they're able to break up their Terraform resources into smaller units, because the reason that they had them in a composite unit was so that they wouldn't have to deal with the potential failures between those different components. But when you have a different solution like durable execution, that's orchestrating those lower level units, now you can break your monolithic Terraform configurations into smaller pieces and use durable execution for that.
So it kind of simplifies the scenario that you talked about because part of the nuance of what you described was because you had a multitude of different things. It wasn't just one resource. It was like, "Okay, I got through part of it, but I didn't get through the rest of it because of this nuanced error."
So back to your original question - "How do you deal with that?" Well, you would have to still deal with that. That would still have to be part of the return codes of your activity. And then you would have to create, of course, a mapping... and so you would decide like, I'm going to return things out of here that indicate retryable versus non retryable. And that would dovetail with your policy.
I want to make one other comment though, because I love your quota example. Because what sometimes people would have naturally thought of that... and you're already picking up on this really interesting thing, which is that quotas, you might say, "Well, that's not retryable, because I'm just going to go back and ask and the quota is still going to be not satisfied," or "It's going to take me... I have to file a request to up my quota, so I need to stop retrying this until my quota goes... like, how do I deal with all that? I don't want to retry it every five seconds because now I've got a human in the loop process to increase my quota."
The interesting thing is when you start working with durable execution, you start thinking a little bit harder about retryable versus not. I would suggest that the quota error is a retryable error because you can actually side effect the system, where now this is going to continue when the quota is updated.
We'll get a little bit further into the durable execution... I'll come back to this example when we get a little further into the technology.
Yep. So there's two things.
I'll get back to the pause in just a moment because you also used another magic word, which is eventing, and I hadn't described that yet. So one of the things that we do... I mentioned that you have these units of work, they're activities, and then you have a workflow that stitches them together, right? So we talked about the retry around the activity. But the other thing that I want to point out that's very, very important is that when I have this workflow that's orchestrating these activities, every single one of those calls from the workflow into an activity and the return happens via message queue. Happens via task queue.
No longer does this... remember I said the programming model says that you can program as if failures don't exist? Another way of putting that is you can program as if everything is running in the same process. Like a function call, you don't have to worry about that because it's running in the same process. Of course you can have out of memory errors and things like that, but the higher level programming languages have done a pretty good job not letting you shoot yourself in the foot by not having the right pointer type of a thing. So a function call is a pretty safe thing. You don't really have to wrap every single function call with a whole bunch of error handling code.
So what the SDKs do in the durable execution case is they intercept and they're handling some retry. But in fact that retry itself is being handled over a task queue. And so all of this is happening with an eventing system. So now in this quota example that you talked about, now I want to get to your dead-letter queue, because that is a perfect example, because this is the way that we have programmed these distributed systems in the past.
We have an eventing system and at some point when we hit some failure scenario, we don't let it sit in the queue anymore, we send it to a different queue, which is the dead-letter queue, which says, "Hang on, I'm stuck here, I can't do...." And then that's typically where application engineers... or if you're applying this in the platform space, platform engineers... have to occasionally go, they have to write automation that goes across that dead-letter queue, gives you some observability, and you've got to handle these things. And that's where a lot of orphans end up, right? Like orphaned infrastructure ends up somehow manifesting itself into the dead-letter queue.
With durable execution, another way of expressing it is that once you have started a process, so once you've started one of these workflows with durable execution, it will live until it either completes or you decide to terminate it. What that means is that if something's going wrong. Like I hit this quota problem. I don't have to actually send things to a dead-letter queue. I can basically say, "You know what, I'm stuck. I got an error. It's a quota error. And so now I am going to put this flow into a wait state."
Yep. And this is a perfect spot to talk about it.
So we said that the things that the platform engineer is dealing with, these things live for a long time. And it's not only the provisioning process. We know, especially with the infrastructure as code and some of these declarative systems, there's definitely this notion of like, okay, it's not that we do all the orchestration and then we're done. We recognize that things change, so that environment will change. I've provisioned the environment, but now I need to provision more. I need to scale capacity or I need to add some other component into the architecture. Those types of things. I need to cycle credentials, all of those types of things. And this is where entity workflows come in.
I actually prefer a different term. It's a term, I think that's going to resonate with more of your audience, which is the notion of a digital twin.
So, I've got a thing, it is an application team has come along and they've said, "I need an environment. I need an environment provisioned. I actually need Dev, Staging, and Prod. And they all need to have this database, this message queue... the Temporal... It needs to have all of these different components that needs to be tied into the IAM system in the following way." Then we go ahead and the infrastructure is code, the orchestration logic, it provisions all of that.
The notion of a digital twin is that you always have a logical and digital analog to this very real thing that is out there. The very real thing, of course here in this case is abstract, it's infrastructure and all of those things. But the cool thing is now you have a digital twin. And that digital twin is basically sitting there saying, "okay, I have a representation of what that infrastructure is. And I'm also the place where you can interact instead of interacting directly with the physical thing. You're interacting with me as the digital twin." And so the entity workflow... remember I was describing how these workflows can live forever... you can basically design a workflow so that it provisions everything and then it goes into a wait state. Now you can send signals into that workflow and say, "Hey, I need you to scale capacity," or "Hey, I'm adding additional users to this project."
It basically allows you to not create a brand new orchestration to make changes to an existing thing. It says, "Alright, here's the orchestration that is going to make changes to the existing thing."
So it's a great place for audit. It is a great place for observability. What are the things that happened? That's what we mean by an entity workflow.
I think of it as a digital twin for the infrastructure that you're orchestrating and that's a super powerful thing in the platform space.