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

Key Innovations

Promovolve’s design choices form a coherent system where each innovation enables the next.

1. Content-Based, Not User-Based

Traditional: User profiles, cookies, device fingerprinting power ad targeting.

Promovolve: Targeting uses LLM-based content classification (Gemini/OpenAI/Anthropic) to match ads to page topics. No user tracking, no cookies.

Why it matters: Privacy-preserving (no GDPR/CCPA data collection), simpler infrastructure (no profile database), content-value alignment (ads match what the user is currently reading).

2. Recency-Only Monetization

Traditional: Ads on any page, regardless of publication date.

Promovolve: Only content within the 48-hour recency window participates. AuctioneerEntity prunes older classifications every 5 minutes.

Why it matters: Fresh content has higher engagement → higher CTR → better outcomes for all participants. Reduces low-quality inventory.

3. Periodic Batch Auctions

Traditional: One auction per page load.

Promovolve: One auction per crawl (scheduled via Quartz cron) + 5-minute re-auctions.

Why it matters: Decouples auction cost from traffic. Sub-millisecond serving via DData local replica. Enables multi-candidate caching.

4. Fair Selection + Multi-Candidate MAB

Traditional: Single winner per auction.

Promovolve: Per-campaign diversity guarantee at auction time (one creative per campaign first), then Thompson Sampling explores among cached candidates at serve time.

Why it matters: Discovers which creative actually engages users. Graceful degradation on budget exhaustion. Self-correcting (poor creatives lose share naturally).

5. Pure-Scala Double DQN

Traditional: DSPs use bid-shading algorithms (often in Python/C++ ML frameworks).

Promovolve: Each campaign has a dedicated Double DQN agent (8→64→64→5, ~4,800 parameters) implemented in pure Scala. No TensorFlow, no PyTorch.

Why it matters: Lives inside the Pekko actor, no inter-process communication. Weights serialize as Array[Double] in CampaignEntity state. JVM-only deployment.

6. Self-Tuning PI Pacing

Traditional: Simple rules (“spend X% by noon”) or fixed-gain controllers.

Promovolve: PI controller with:

  • Adaptive gains scaled by traffic volatility (CV)
  • Self-tuning overpace multiplier (1.5x-5.0x, adjusts every 20 samples)
  • Oscillation detection (stddev threshold 0.08 → dampening)
  • Leaky integrator (decay 0.995, anti-windup)
  • Cross-day learning (boosts multiplier if budget exhausted early)
  • Traffic shape awareness (separate weekday/weekend 24-hour profiles)

Why it matters: Adapts to any traffic pattern without manual tuning. Learns from past days’ mistakes.

7. Buffered At-Least-Once Spend Recording

Traditional: Database writes per impression.

Promovolve: Spend events buffered (500ms timer OR batch of 20), deduplicated via Bloom filter (50K entries, 0.01% FPP), at-least-once delivery with exponential backoff retries.

Why it matters: Reduces persistence load by ~20x while maintaining correctness guarantees.

The Unified Picture

graph TD
    LLM["LLM content classification"] --> Auction["Periodic batch auction<br/>(content changes slowly)"]
    Auction --> Fair["Fair selection<br/>(per-campaign diversity guarantee)"]
    Fair --> DData["DData cache<br/>(multi-candidate, replicated locally)"]
    DData --> TS["Thompson Sampling<br/>(explore among cached candidates)"]
    TS --> DQN["DQN bid optimization<br/>(learn to pace across days)"]
    DQN --> PI["Self-tuning PI pacing<br/>(smooth delivery within days)"]
    PI --> Spend["Buffered spend recording<br/>(correctness at scale)"]

Each choice enables the next. Remove one, and the system loses coherence. Together, they create an ad platform that is fast, learning, privacy-preserving, and publisher-aligned.