Blog
-
Tech Talks

Stateless, Stateful, and Serverless Processing: Making Sense of the Buzzwords

Reading time: ca.
5
minutes

Ever written a function so clean and predictable you could set your watch to it? It takes an input, does its job, and gives you the exact same output every single time. It’s simple, reliable, and easy to scale. But then, the requirements change. Suddenly, that function needs to remember what happened the last time it ran. It needs a memory. It needs state.

The choice between stateless and stateful processing impacts everything from scalability and complexity to reliability. And with the rise of serverless computing, understanding these concepts is essential for building modern, efficient applications. Let's break down what these terms actually mean for a developer, what happens when they collide, and how you can get the best of both worlds.

Stateless Processing

Let's start with the simplest option: stateless processing. Think of it like a basic calculator. If you type in 2 + 2, you get 4. It doesn't matter if you did it a second ago or a year ago. The output depends only on the current input.

In development, this means the function has no memory of past events and doesn't rely on any external data to do its job. Examples unclide:

  • Converting a text document to a PDF.
  • Resizing an image.
  • Calculating the total price of items passed into a function.

As long as the input is the same, the output will always be the same. Developers call these pure functions.

Why does this matter? Because there's no shared memory or state to manage, you can run a million instances of a stateless function side-by-side without them ever interfering with each other. This makes horizontal scaling a breeze and retrying a failed operation completely safe (a property known as idempotency).

Stateful Processing

Stateful processing is the opposite. It has a memory. The outcome of an operation depends not just on the input, but also on a pre-existing, external state.

The classic example is an e-commerce shopping cart. When a user adds an item to their cart, the function needs to know what’s already in the cart to calculate the new total. The "cart" is the external state. Counting website clicks or managing a user's session are other common examples.

This introduces a side effect: the function modifies something outside of itself. Adding the same item to the cart twice results in a different state than adding it once.

And that's where the big challenge with stateful processing comes in: consistency. You have to protect that state from being corrupted. What happens if two users try to book the last available seat on a flight at the exact same time? Your application needs to handle that gracefully, which adds complexity.

Serverless Processing

So where does "serverless" fit into all this?

Despite the name, there are still servers. The difference is that as a developer, you don't have to manage them. Serverless platforms like AWS Lambda, Azure Functions, or frameworks like Spring Cloud Functions let you write and deploy individual functions, and the platform handles the rest. It automatically spins up resources when your function is called and scales them down when it's not.

You get to focus purely on the code. For example, here's a complete Spring Cloud Function application. We only need to provide a simple function, and the framework takes care of running it on demand.

@SpringBootApplication
public class Application {
   @Bean
   public Function<Flux<String>, Flux<String>> uppercase() {
       return flux -> flux.map(value -> value.toUpperCase());
   }
   public static void main(String[] args) {
       SpringApplication.run(Application.class, args);
   }
}

You might notice that this uppercase function is stateless. And that’s no coincidence. Serverless architectures and stateless functions are a perfect match because they scale so effortlessly. Need to handle more traffic? The platform just spins up more independent instances. This automatic scaling is often called elasticity.

Stateful Applications at Scale

Now that we’ve explored the differences between stateless, stateful and serverless processing, let’s look at an interesting use case where they intersect. What happens when your application needs to be both stateful and elastic?

You can absolutely run a stateful application in a serverless model. The common approach is to have each function instance connect to a shared database (the external state), either when the application starts or when the data arrives. Both approaches have their pros and cons, but you will end up with something like this when you spin up multiple instances of your application.

While this works, you quickly run into bottlenecks. As you scale up, your database becomes the limiting factor. You start asking tough questions:

  • How many simultaneous connections can our database handle before it falls over?
  • Does connecting to the database on every single request slow things down too much?
  • How do we prevent two function instances from overwriting the same piece of data at the same time? Using database locks to prevent this can kill the very performance you were trying to gain.

In other words: serverless solves the computational scaling problem, but it doesn't magically solve the state scaling problem.

Stateful Serverless Applications

So, are we stuck choosing between simple scaling and complex state management? Not necessarily. A new wave of "stateful serverless" platforms promises to tackle this exact problem. One interesting example we've worked with is Kalix (formerly Akka Serverless).

The core idea is to bring the state closer to the function. Instead of you having to manage database connections, the platform "injects" the relevant state directly into the function alongside the event that triggers it. Your job is simply to write a function that defines how an event changes the current state to produce a new one.

public ApplicationState stationRegistered(ApplicationState currentState, Event event) {
   ApplicationState newState = //calculate the new state based on the current state and the event
   return newState;
}

The Kalix platform handles all the heavy lifting behind the scenes:

  • Setting up and connecting to the database.
  • Fetching the correct currentState for a given request.
  • Ensuring only one operation modifies a piece of state at a time.
  • Storing the newState once the function completes successfully.

This approach hides the state management complexity from the developer, letting you build elastic, stateful applications without getting bogged down in database bottlenecks and concurrency issues.

Obviously, there is more to it than just this small code snippet, but this illustrates what people mean when they talk about stateful serverless applications.

It's All About the Right Tool for the Job

Understanding the trade-offs between stateless and stateful design is crucial for anyone building scalable systems. To recap, here's what each processing option does and when you should use it.

  • Stateless gives you simplicity and massive scalability, perfect for pure data transformations.
  • Stateful gives you memory but introduces the complexity of managing consistency.
  • Serverless excels with stateless workloads but can hit a wall when state is involved.

Luckily, emerging platforms like Kalix show a promising future where we don't have to make such a hard choice. By abstracting away state management, they let us build powerful, elastic applications that can handle complex, real-world requirements.

Not sure which processing option would be best for your next project? Need an expert's eye to take a critical look at your architecture? Get in touch to discuss how we can help.

Mathi

22.08.2023

Mathi

Read the highlights of our blog

"Each project pushes our skills farther and expands our expertise"
We value your privacy! We use cookies to enhance your browsing experience and analyse our traffic.
By clicking "Accept All", you consent to our use of cookies.