Synchronization Across Distributed Servers: DB vs Redis vs Actor

In distributed systems, one of the most common and tricky challenges is ensuring synchronized access to shared resources across multiple server instances. For example, when a user initiates a money transfer, updates a shared task list, or interacts with a counter, the request may hit any instance in your cluster. Unless synchronized properly, this can result in race conditions, inconsistent state, or data corruption. Traditional Synchronization Techniques Developers typically reach for one of two well-known approaches: 1. Database Locking Using pessimistic locking (SELECT ... FOR UPDATE) or JPA's @Lock(PESSIMISTIC_WRITE): Simple and widely supported Ties up database connections Increases contention and slows down unrelated queries 2. Redis Locks Using a distributed lock pattern (e.g., Redlock via Redisson): Offloads lock state from the DB Requires Redis infrastructure Needs careful retry/backoff handling to be safe and reliable Both methods require additional complexity and infrastructure to maintain, especially at scale. A Simpler Alternative: Clustered Sharded Actors Thanks to spring-boot-starter-actor, there's a third option: actor-based synchronization with no external system required. The library integrates Apache Pekko cluster sharding into Spring Boot, enabling you to model shared entities as singleton actors across your cluster. If two concurrent requests targeting the same logical ID (like user-123) arrive at different servers, the actor system ensures that: Only one instance of the actor is created for that ID All messages for that ID are routed to and handled by that actor, sequentially This eliminates the need for locks or external coordination. Real-World Comparison: DB vs Redis vs Actor We compared three implementations of a synchronized counter increment: db: Uses pessimistic DB locking redis: Uses Redis-based distributed lock actor: Uses spring-boot-starter-actor’s sharded actor system Test Setup 3 Spring Boot instances (ports 8080, 8081, 8082) Each instance received 3,000 concurrent POST requests (total: 9,000) Each method was benchmarked independently using Apache Bench (ab) Final value was retrieved via: GET /counter/{method}/{counterId} Results Summary Method Counter Final Value External Infra Notes DB Lock 9,000 MySQL High DB load; Redis 9,000 Redis Required retry logic; slower throughput under load Actor 9,000 None High throughput, simple implementation Although many requests technically "failed" at the HTTP layer (due to thin responses or benchmarking artifacts), the counter logic worked perfectly in all cases. Why the Actor Model Is Effective Using actors for synchronization offers several advantages: Correctness: Each logical ID maps to a single actor that serializes access Simplicity: No retry logic or lock expiration handling Infrastructure-free: No Redis or lock coordination service needed Scalability: Naturally distributed across nodes without contention This model fits naturally into workloads where each entity (e.g., user, session, document) can be isolated and updated independently. How to Get Started Refer to the examples. Summary Feature DB Lock Redis Lock Actor External Infra Required Yes Yes No Lock Coordination Overhead High Medium Low Error-Prone Retry Logic No Yes No Throughput Under Load Degrades Moderate Stable Code Complexity Low Medium Low Horizontal Scalability Limited Good Excellent If you're building a distributed Spring Boot system and need reliable, low-latency synchronization without operational burden, consider giving the actor model a try. Source Code You can find the complete working example, including the benchmarking script, on GitHub:

May 11, 2025 - 04:26
 0
Synchronization Across Distributed Servers: DB vs Redis vs Actor

In distributed systems, one of the most common and tricky challenges is ensuring synchronized access to shared resources across multiple server instances.

For example, when a user initiates a money transfer, updates a shared task list, or interacts with a counter, the request may hit any instance in your cluster. Unless synchronized properly, this can result in race conditions, inconsistent state, or data corruption.

Traditional Synchronization Techniques

Developers typically reach for one of two well-known approaches:

1. Database Locking

Using pessimistic locking (SELECT ... FOR UPDATE) or JPA's @Lock(PESSIMISTIC_WRITE):

  • Simple and widely supported
  • Ties up database connections
  • Increases contention and slows down unrelated queries

2. Redis Locks

Using a distributed lock pattern (e.g., Redlock via Redisson):

  • Offloads lock state from the DB
  • Requires Redis infrastructure
  • Needs careful retry/backoff handling to be safe and reliable

Both methods require additional complexity and infrastructure to maintain, especially at scale.

A Simpler Alternative: Clustered Sharded Actors

Thanks to spring-boot-starter-actor, there's a third option: actor-based synchronization with no external system required.

The library integrates Apache Pekko cluster sharding into Spring Boot, enabling you to model shared entities as singleton actors across your cluster.

If two concurrent requests targeting the same logical ID (like user-123) arrive at different servers, the actor system ensures that:

  • Only one instance of the actor is created for that ID
  • All messages for that ID are routed to and handled by that actor, sequentially

This eliminates the need for locks or external coordination.

Real-World Comparison: DB vs Redis vs Actor

We compared three implementations of a synchronized counter increment:

  • db: Uses pessimistic DB locking
  • redis: Uses Redis-based distributed lock
  • actor: Uses spring-boot-starter-actor’s sharded actor system

Test Setup

  • 3 Spring Boot instances (ports 8080, 8081, 8082)
  • Each instance received 3,000 concurrent POST requests (total: 9,000)
  • Each method was benchmarked independently using Apache Bench (ab)
  • Final value was retrieved via: GET /counter/{method}/{counterId}

Results Summary

Method Counter Final Value External Infra Notes
DB Lock 9,000 MySQL High DB load;
Redis 9,000 Redis Required retry logic; slower throughput under load
Actor 9,000 None High throughput, simple implementation

Although many requests technically "failed" at the HTTP layer (due to thin responses or benchmarking artifacts), the counter logic worked perfectly in all cases.

Why the Actor Model Is Effective

Using actors for synchronization offers several advantages:

  • Correctness: Each logical ID maps to a single actor that serializes access
  • Simplicity: No retry logic or lock expiration handling
  • Infrastructure-free: No Redis or lock coordination service needed
  • Scalability: Naturally distributed across nodes without contention

This model fits naturally into workloads where each entity (e.g., user, session, document) can be isolated and updated independently.

How to Get Started

Refer to the examples.

Summary

Feature DB Lock Redis Lock Actor
External Infra Required Yes Yes No
Lock Coordination Overhead High Medium Low
Error-Prone Retry Logic No Yes No
Throughput Under Load Degrades Moderate Stable
Code Complexity Low Medium Low
Horizontal Scalability Limited Good Excellent

If you're building a distributed Spring Boot system and need reliable, low-latency synchronization without operational burden, consider giving the actor model a try.

Source Code

You can find the complete working example, including the benchmarking script, on GitHub: