Cluster Mode

Cluster infrastructure view

A single-node AuraBoot is fine for development and small deployments. Cluster mode targets production workloads where availability, throughput, and rolling upgrades matter. The platform is stateless at the application layer, so horizontal scaling is the default growth path.

Topology

                  +-----------------+
                  |   Load Balancer |
                  +--------+--------+
                           |
        +------------------+------------------+
        |                  |                  |
   +----v----+        +----v----+        +----v----+
   |  app-1  |        |  app-2  |        |  app-3  |   (stateless JVMs)
   +----+----+        +----+----+        +----+----+
        |                  |                  |
   +----+------------------+------------------+----+
   |                                               |
+--v-----+   +---------+   +---------+   +---------v-+
| Redis  |   |  Kafka  |   |  MinIO  |   | Postgres   |
| (lock+ |   | (event  |   | (files) |   | primary +  |
| cache) |   |  bus)   |   |         |   | standby)   |
+--------+   +---------+   +---------+   +-----------+

Application nodes hold no durable state. All shared state lives in Redis, Kafka, PostgreSQL, and MinIO.

Shared infrastructure

ComponentRoleNotes
RedisDistributed lock (SET NX EX, ~3-minute lease by default), cache, rate limitsNeeded for multi-node deployment; falls back to a JVM-local lock (single-instance safe only) when unavailable
KafkaMessage queue (one option for aura.mq.type: domain events, webhook dispatch, crawler and other background work)For multi-node, choose Kafka / Redis / RabbitMQ (the default memory is single-node only)
PostgreSQLPrimary write node + one or more streaming standbysApplication targets primary; standbys for read-heavy analytics
MinIOS3-compatible object storageRun a 4-node cluster for production durability

When to use

  • Production traffic above what one JVM can serve.
  • High availability requirements (zero-downtime rolling upgrade).
  • Long-running background work (crawler, automation) that must survive node restart.
  • Tenants on the same physical install needing isolation; see Tenancy.

How it works

Stateless application nodes share work via the message queue and coordinate exclusive operations via Redis locks. The PostgreSQL primary is the single write authority; read replicas exist for reporting only. A health probe at /actuator/health is wired to the load balancer; when draining a node, remove it from the load balancer first and wait for in-flight requests to bleed off before stopping it.

Session affinity is optional. Tokens are stateless JWTs, so requests can land on any node. SSE / WebSocket connections do benefit from sticky routing because the chokepoint ConversationTurnService keeps a ResponseSink registered on the node that owns the open stream.

Configuration

spring:
  # Redis is optional — set the SPRING_DATA_REDIS_HOST env var to enable it (required for multi-node).
  # When unset, the app runs with a local lock + in-process data sync (single-instance safe only).
  data:
    redis:
      host: "${REDIS_HOST:localhost}"
      port: "${REDIS_PORT:6379}"
  datasource:
    url: "jdbc:postgresql://${PG_PRIMARY}:5432/aura_boot"
    hikari:
      maximum-pool-size: "${HIKARI_MAX_POOL_SIZE:30}"

aura:
  mq:
    type: kafka                  # memory | redis | kafka | rabbitmq
    kafka:
      bootstrap-servers: "${KAFKA_BOOTSTRAP:localhost:9092}"
      consumer-group: aura-group
  scheduler:
    engine: "${AURA_SCHEDULER_ENGINE:local}"   # local (database-backed TaskScheduler) | xxl

Horizontal scale knobs:

KnobDefaultWhen to raise
App replica count1Latency p95 climbs under load
HikariCP maximum-pool-size per node30DB pg_stat_activity shows queueing
Kafka partition count per topic6Consumer lag grows
Crawler worker count1Frontier admit rate exceeds fetch throughput
Redis maxmemory2 GBEviction events appear

Rolling upgrade

# For each node, in order:
# 1) Remove the node from the load balancer backend pool first (exact command depends on your LB)
sleep 20                                                  # drain SSE / commands
ssh "$NODE" 'systemctl restart auraboot'
curl -sS "https://$NODE/actuator/health" | jq -e '.status == "UP"'  # put it back in the LB once healthy

Coordinate with the load balancer so only one node is drained at a time.

Verify

  • All app nodes return UP on /actuator/health and serve traffic.
  • A lock-protected resource is held by exactly one node at any instant (no double execution).
  • Message-queue consumer lag for webhook delivery and crawler background work is bounded.
  • Killing one app node does not produce user-visible errors after the drain window.
  • PostgreSQL standby replication lag stays under one second under normal load.

Related