Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

System Architecture

Promovolve runs as an Apache Pekko Cluster with three distinct node roles, Cluster Sharding for entity distribution, and Distributed Data (DData) for replicated in-memory state. Persistence uses PostgreSQL via JDBC (Slick).

High-Level Components

graph TB
    subgraph Cluster["Pekko Cluster (promovolve system)"]
        subgraph Singleton["Singleton Role"]
            CD["Campaign Directory"]
            Sched["Scheduler"]
        end
        subgraph Entity["Entity Role"]
            Camp["Campaign"]
            Auct["Auctioneer"]
            CatBid["CategoryBidder"]
            TaxRnk["TaxonomyRanker"]
            Adv["Advertiser"]
        end
        subgraph API["API Role"]
            HTTP["HTTP API"]
            AdSrv["AdServer"]
            Evt["Events"]
        end
        DData["DData: ServeIndex, PacingConfig, Blocklist<br/>Replicated across all nodes via gossip<br/>Durable: LMDB for shard-* and exhausted-*"]
        PG["PostgreSQL: durable_state, snapshot tables<br/>20 connections, 5 min pool"]
    end

Cluster Configuration

From application.conf:

SettingValue
Cluster rolessingleton, entity, api (env: PEKKO_CLUSTER_ROLES)
Number of shards100
Remember entitieson (via DData store)
Passivation timeout5 minutes
Split-brain strategykeep-majority (stable after 20s)
Heartbeat interval1s, threshold 12.0, acceptable pause 10s
Remote frame size256 KiB
Seed nodepekko://promovolve@127.0.0.1:25520

DData Configuration

SettingValue
Gossip interval2s
Notify subscribers interval500ms
Max delta elements500
Durable keysshard-*, exhausted-campaigns
Durable storeLMDB (100 MiB map, 200ms write-behind)
Pruning interval120s (dissemination: 300s)

Key Design Decisions

  1. 100 shards with remember-entities-via-DData ensures entities survive node restarts and are automatically rebalanced (rebalance-absolute-limit: 20, relative: 0.1).

  2. DData for ServeIndex means every API node has a local replica of all active ad candidates. Serve-time lookups never cross the network.

  3. LMDB durability for shard metadata and exhausted-campaigns state, but ServeIndex itself is ephemeral — rebuilt from auctions on restart.

  4. Separation of roles allows scaling read (API) and write (entity) workloads independently. The singleton role hosts cluster-wide coordinators like CampaignDirectory.