How to Start Learning Actor-Based Programming

Discovering and appreciating differences between Akka.NET actors and traditional Object-Oriented Programming

One of the most frequent pieces of feedback we get from developers who are new to Akka.NET is that the “learning curve” is high. I want to explore that today and why I think this isn’t actually true, but what developers are saying is that working with actors is unfamiliar, not difficult.

I’d also argue that really, really bad pre-existing habits many software developers have, such as frameworkism, become a hundred-fold more destructive when you introduce unfamiliar paradigms, such as stateful programming with actors, to the mix. You could substitute “stateful programming with actors” with “using NoSQL” or “going cloud native” and that sentence would be equally true.

Let’s assume you’ve already decided that learning actors is worth your time and now you want to know: “how can I begin to learn how to work with actors for my own purposes?” That’s what this post is about.

Why IActorRef.Tell Doesn't Return a Task

Nuances of actor-based programming in .NET

A really, really common question Akka.NET beginners have within the first couple of hours of looking at Akka.NET: “how is the IActorRef.Tell method asynchronous if it’s a void method? Shouldn’t it return a Task I can await on?”

Today I’m going to clarify Akka.NET’s API design and explain, in terms of Akka.NET’s behavior and benefits to users, why we don’t do this.

Even if you’re a seasoned Akka.NET user, you might find some value in this post.

We put together a new YouTube video on our Petabridge channel (which you should subscribe to) yesterday, “Akka.NET Actors’ Hidden Super Power: Switchable Behaviors” - all about one of my favorite features in Akka.NET: the Become method for swapping actor message-processing behavior dynamically at runtime.

In addition to the video, I wanted to expand on why behavior-switching is so powerful and how you can use it to transform inherently complex domain problems into something that is approachable, understandable, and expressable with a very small amount of purpose-built code.

The Worst .NET Bug I've Ever Fixed

A 70+ hour journey into fixing a ~10 year old Akka.NET bug.

The month of July, 2024 was pretty rough for me and the Petabridge crew from a workload standpoint. Our office got hit with a hurricane. My entire family and I were out sick with COVID in the middle of being hit by a hurricane. Fun times.

But the worst of it was a pair of truly nasty Akka.NET issues that both turned out to both be the same bug:

I ended up spending about 70+ hours myself investigating and eventually solving this bug starting from June 27th onwards.

If you’re an Akka.NET user and don’t want to read any further, just upgrade to Akka.NET v1.5.27 or later - the bugs are fixed in those versions of the software. Every other earlier version of the software, dating back to when Akka.Cluster.Tools was first introduced in 2015, has this bug. Please upgrade now.

Otherwise, read on.

How Akka.NET Actors Process Messages

The fundamentals of Akka.NET actor construction and message processing.

It’s been a little while since we covered Akka.NET 101 on our blog, so we decided it was time to visit one of the most fundamental and significant matters users need to understand about Akka.NET in order to leverage it properly: how actor message processing actually works.

Akka.NET, ASP.NET Core, Hosted Services, and Dependency Injection

Akka.Hosting trivializes integrating Akka.NET with everything else in .NET.

We first introduced Akka.Hosting a couple of years ago and released it to market one of the major pillars of Akka.NET v1.5. Without any exaggeration, it is the single best usability improvement we have ever made to Akka.NET and we are furiously rewriting all of the official Akka.NET documentation and training courses to prioritize it.

In that same vein, we’re going to introduce how you can use Akka.Hosting to easily integration Akka.NET with:

  • Microsoft.Extensions.DependencyInjection - injecting things into actors and actors into things;
  • ASP.NET Core / SignalR / gRPC / etc - anything that can be injected via MSFT.EXT.DI really; and
  • Running Akka.NET in headless IHostedServices for things like stand-alone Windows Services.

Read on.

Distributing State Reliably with Akka.Cluster.Sharding

A straightforward introduction to Akka.Cluster.Sharding

We’ve had a number of posts on Akka.Cluster.Sharding on this blog:

Both of those were written by Bartosz Sypytkowski, who originally contributed those features to Akka.NET. We’ve had a lot of demand for an updated introduction video to Akka.Cluster.Sharding that explores the concepts, behavior, code, and configuration of Akka.Cluster.Sharding in more detail than previously - and so I’ve produced a video doing just that: “Distributing State Reliably with Akka.Cluster.Sharding

This is my companion blog post to go along with the video - I’m not going to cover everything in the video, but I’ll provide a high-level synopsis that explains:

  • What Akka.Cluster.Sharding does;
  • When you should use it; and
  • How it works.

Watch “Distributing State Reliably with Akka.Cluster.Sharding” for the complete details, or read on for the high level overview.

.NET Performance Optimization: Deferred Allocations

How We Accelerated Phobos 2.5's Throughput by 161%

We just finished shipping Phobos 2.5 and it’s a massive performance upgrade over previous versions of Phobos.

For those that are not aware: Phobos is our commerical OpenTelemetry add-on for Akka.NET.

This past summer we posted about Phobos 2.4’s performance being 62% faster than Phobos 2.3.1 Phobos 2.5 is 161% faster than Phobos 2.3.1 - and in this post we’re going to share the generalized .NET performance optimization technique we’ve been using to accomplish these improvements: deferred allocations.

Problem Context

Imagine you have a performance-critical hotpath in your application, such as an Akka.NET actor or an ASP.NET Controller - we ideally want to keep the latency in this critical path as low as possible in order to maximize responsiveness and per-process throughput.

But, we are also given a secondary requirement - maybe we have to add logging or OpenTelemetry tracing here for observability purposes, or maybe we have to push some data points into an internal-facing analytics / reporting system for internal stakeholders.

Critical processing pipeline with fully allocated telemetry

Implementing that secondary requirement along the critical path is going to increase our processing time at the expense of our mission-critical processing and ultimately, our end-users. Deferring allocations to outside the critical path is how we can avoid this problem.

New Akka.NET Training Courses and Support Plans

New offerings from Petabridge to help teams in development and in production with Akka.NET.

It’s been a very long time since we’ve refreshed our Akka.NET training courses or Akka.NET support plans - so I’m pleased to announce that we are doing both effective immediately.

You can watch my video summary below or read on:

Watch "New Akka.NET Training and Support Offerings" by @Petabridge on YouTube

Solving Major Database Contention Problems with Throttling and Akka.NET Streams

Alleviate strain on production systems with in-process Akka.NET streams.

When troubleshooting performance problems in distributed systems or locally-run, high-throughput-required software I tell our users “your most severe performance problems are almost always going to be caused by flow control issues.”

My preferred batting order for troubleshooting performance issues is:

  1. Improve or resolve flow control issues;
  2. Eliminate wasteful I/O and round-trips; and
  3. Technical improvements - improve how efficiently work is performed leveraging mechanical sympathy.

This list is ranked in the order of “most likely to have largest real-world performance impact.”

In this post we’re going to address how you can use Akka.NET actors and Akka.Streams to easily resolve some one of the most painful flow control issues: database contention and bottlenecking.