NashTech Blog

Part 1 – What is Akka Persistence? From Coffee Shops to Game Save/Load

Table of Contents
the coffee shop

Have you ever heard of Akka and the power of its “Actors”? Imagine them as diligent employees, working independently and communicating with each other through messages. But here’s a big question: if an “employee” (Actor) suddenly suffers from “amnesia” (due to a system crash, server failure, etc.), how can they resume their unfinished work?
The answer lies in the ability to “keep a diary” – and that’s precisely where Akka Persistence shines. This article will guide you through a story to understand why and how Akka Persistence helps your Actors become immortal.

1. When Do You Need Akka Persistence? A Tale of a Forgetful Barista

Meet Nga, a barista at a coffee shop called “The Actor’s Brew.” Nga is a superstar employee; she can remember every customer’s order in her head: “An iced Americano for the lady at table 2,” “A sugar-free turmeric latte for the gentleman at table 5,” “A peach-lemongrass iced tea for the young woman by the window.” Everything is perfect.

Every Actor in your system is just like Nga, holding its current state in memory (RAM). For example, an OrderActor would hold information about items in a shopping cart, the total amount, and the delivery address.

The Problem: One day, exhausted from the rush, Nga suddenly… forgets everything! She can’t remember what the lady at table 2 ordered or what the gentleman at table 5 wanted. Chaos ensues. Customers complain, and Nga has to go back to each table and ask for their orders again. It’s time-consuming and unprofessional.

This is the exact disaster that happens when an Actor “dies” (restarts) and loses all its state from memory. Any ongoing process is lost.

Nga’s Solution: After that incident, Nga decides to get a notebook. Now, whenever a customer places an order, instead of just memorizing it, she carefully notes it down:

  • 8:05 AM: Table 2 ordered 1 Iced Americano.
  • 8:07 AM: Table 5 ordered 1 Sugar-free Turmeric Latte.
  • 8:10 AM: Table 2 added 1 Croissant.

This notebook is her event log. If she ever feels forgetful, she can simply look at her notebook, read it from top to bottom, and know the exact state of every order. She can “reconstruct” her memory perfectly.

This is the core idea behind Event Sourcing in Akka Persistence. Instead of just saving the final state, we save a sequence of events that have occurred. When an Actor restarts, it simply “re-reads its diary” (replays events) to restore its state to exactly what it was before the crash.

And occasionally, to avoid reading through a thick notebook from the very beginning, Nga summarizes the order states at the end of the morning. For instance: “12:00 PM: Table 2 has 1 Americano, 1 Croissant. Table 5 has 1 Latte. Total bill…” This action is like creating a Snapshot – a quick record of the state at a specific point in time, which makes recovery even faster.

Conclusion: You need Akka Persistence when your Actors must “remember” their state durably, even when the system fails. It’s like equipping your employees with a diary so they never forget what they were doing.

2. Traditional CRUD vs Durable Actors – A Real-Time Auction System 💰

Imagine you’re building a real-time auction platform. Users can:

  • Place bids.
  • Receive outbid notifications instantly.
  • See live price updates.
  • Set automatic bid limits.

The Requirements:

  • Handle 10,000 concurrent auctions running in parallel.
  • Never lose a bid – money is on the line!
  • Real-time updates (< 100ms latency)
  • Perfect audit trail (for disputes)

The Traditional Approach: REST API + Database

In a typical CRUD architecture:

  • Each bid = a row in the database.
  • Placing a bid = an INSERT or UPDATE.
  • Checking the current price = SELECT MAX(bid) from the table.
  • Notifications = triggered by polling or DB triggers.

This approach works for low scale, but at 10,000 concurrent auctions:

  • Database contention becomes a nightmare (constant SELECT and UPDATE collisions).
  • Latency spikes as queries fight for locks.
  • If the DB hiccups, you risk losing bids or delivering stale prices.
  • Maintaining real-time push notifications requires an extra pub/sub system bolted on.

The Akka Persistence – Durable State Approach

With Akka Persistence, we can model each Auction as an Actor:

  • State in memory: The actor tracks the current highest bid, bidders, and automatic bid limits.
  • Durable state persistence: Every accepted bid is persisted so it can be recovered after a crash.
  • Real-time updates: Messages to the actor are processed in milliseconds, and the actor can instantly push updates to connected clients.
  • Race-Condition Free: Each actor processes messages sequentially.
  • Audit trail: Each bid can also be stored as an event, guaranteeing a complete history for dispute resolution.
// Each auction is a separate actor - natural isolation
class AuctionActor extends PersistentActor {
  // State is kept in memory for lightning-fast access
  case class AuctionState(
    currentBid: Double = 0,
    currentWinner: Option[String] = None,
    bidders: Set[String] = Set.empty,
    endTime: Instant,
    bids: List[Bid] = List.empty
  )
  
  var state = AuctionState(endTime = context.system.settings.endTime)
  
  def receiveCommand: Receive = {
    case PlaceBid(userId, amount) =>
      // All validation happens in memory - microseconds!
      if (amount <= state.currentBid) {
        sender() ! BidRejected("Bid too low")
      } else if (Instant.now().isAfter(state.endTime)) {
        sender() ! BidRejected("Auction ended")
      } else {
        // Create immutable event
        val event = BidPlaced(userId, amount, Instant.now())
        
        // Persist event - guarantees durability
        persist(event) { evt =>
          // Update state only after successful persistence
          updateState(evt)
          
          // Notify all interested parties
          notifyBidders(evt)
          sender() ! BidAccepted(evt.amount)
        }
      }
  }
  
  def receiveRecover: Receive = {
    // Replay all events when actor restarts
    case evt: BidPlaced => updateState(evt)
  }
  
  def updateState(event: BidPlaced): Unit = {
    state = state.copy(
      currentBid = event.amount,
      currentWinner = Some(event.userId),
      bidders = state.bidders + event.userId,
      bids = event :: state.bids
    )
  }
  
  def notifyBidders(event: BidPlaced): Unit = {
    // Real-time notifications to all watchers
    state.bidders.foreach { bidderId =>
      if (bidderId != event.userId) {
        context.system.eventStream.publish(
          OutbidNotification(bidderId, event.amount)
        )
      }
    }
  }
}

In short:

  • CRUD = centralized bottleneck, fragile under load.
  • Akka Durable Actors = distributed, resilient, and real-time by design.

3. Event Sourcing Explained: It’s Just Like a Save/Load in a Video Game 🎮

Many people hear terms like “Event Sourcing,” “Replay,” and “Snapshot” and think they sound academic and complex. But in reality, if you’ve ever played a video game, you already understand the core concepts!

Imagine you are playing an adventure game.

  • State: This is all the information about your character at a given moment: Health Points (HP), Mana Points (MP), items in your inventory, your position on the map, and completed quests.
  • Events: These are the actions you took that changed your state: Picked up the Excalibur sword, Defeated the dragon, Drank a health potion, Learned a new spell.

Replaying the Event Log – Loading from the Beginning

Let’s say the game has no “Save” feature. Every time you turn off the console, you have to start over from level 1. Extremely frustrating!

Now, imagine the game has a “magical” mechanism: it automatically records every single action you take into a log file.

  • 00:01 – Game started.
  • 00:05 – Moved left.
  • 00:10 – Picked up wooden sword.
  • 00:15 – Defeated a slime.
  • 02:30 – Defeated the final boss.

When you want to continue playing, instead of starting over, you hit the “Replay” button. The game reads this log file and re-enacts all your actions at super-speed (fast-forward). In just a few seconds, your character is restored to the exact state it was in before you quit.

This is precisely how Akka Persistence replays events from the journal to recover an Actor’s state.

Snapshots – Your Game Save Points (Checkpoints)

Replaying from the very beginning would take a long time if you’ve played for hundreds of hours. That’s why games have a Save Game feature.

When you “Save,” the game takes a picture of your character’s entire state (HP, inventory, location, etc.) at that moment and stores it in a file. This is a Snapshot.

The next time you “Load Game,” the process is:

  1. The game loads the Snapshot file (your state at the 100-hour mark). Your character is instantly restored to that point in time.
  2. Then, the game only needs to replay the events that happened after you saved (from hour 100 until you quit).

This process is much faster than replaying the entire 100 hours of gameplay.

In Akka Persistence, you can configure an Actor to automatically create a Snapshot after a certain number of events. When the Actor restarts, it finds the most recent Snapshot, loads its state from there, and then only replays the events that occurred after that snapshot was taken.

In summary: Event Sourcing isn’t complicated. It’s simply a change in perspective: instead of asking “What is the current state?”, we ask “What happened to lead to this state?”. By recording the journey, we can recreate the destination at any time.

Event Sourcing vs Durable State

So which is better?

  • Durable State
    • Think of it as a snapshot or photo.
    • You only care about the latest version of your data.
    • Great for CRUD-like services (e.g., todo lists, user profiles).
  • Event Sourcing
    • Think of it as a movie recording.
    • You care about the entire sequence of events, not just the final result.
    • Essential when you need audit trails, reproducibility, or reactive integrations.

Both approaches are supported by Akka Persistence. And both rely on the same principle: actors shouldn’t forget.

Wrapping Up

In this first article, we:

  • Looked at why Akka Persistence matters (coffee shop analogy).
  • Compared CRUD vs Durable State with a simple Scala example.
  • Demystified Event Sourcing using the game save/load analogy.
  • Understood the trade-off between snapshots (durable state) and event logs.

Hopefully, through these stories and examples, you now have a more intuitive and clearer understanding of Akka Persistence. Try applying this “diary-keeping” mindset to your Actors and see just how powerful and resilient they can become!

This is just the beginning.

In the next article, we’ll go deeper:

  • When to pick EventSourcedBehavior vs DurableStateBehavior.
  • How to use snapshots effectively.
  • Why projections matter.
  • And how Akka ensures idempotency and exactly-once delivery.

Stay tuned – we’re just getting started with Akka Persistence.

References & Further Reading

Picture of Vang Do

Vang Do

I’m a software engineer and technical architect who enjoys designing scalable systems, creating clean APIs, and solving real-world problems with code. With a background in Java, Scala, and TypeScript, and hands-on experience across AWS and Azure Cloud, I focus on building reliable backend services and integrating well-performing systems. On this blog, I want to share what I’ve learned from years of building and breaking things so others can do more of the former and less of the latter.

2 thoughts on “Part 1 – What is Akka Persistence? From Coffee Shops to Game Save/Load”

Leave a Comment

Your email address will not be published. Required fields are marked *

Suggested Article

Scroll to Top