Real-Time ARPPU Monitoring for Games with RisingWave

Real-Time ARPPU Monitoring for Games with RisingWave

RisingWave computes ARPPU (Average Revenue Per Paying User) in real time by streaming transaction events through continuously maintained materialized views. Monetization teams get live ARPPU breakdowns by game, region, platform, and product tier—without waiting for nightly batch jobs to complete.

What Is ARPPU and Why Does Real-Time Matter?

ARPPU is one of the most important monetization KPIs in gaming. Unlike ARPU (which averages revenue across all players including non-payers), ARPPU isolates the value generated by paying users specifically. It answers: "Among players who choose to spend, how much are they spending on average?"

A rising ARPPU with flat DAU might mean your whales are spending more—or that casual spenders are churning, leaving only high-spenders in the denominator. A falling ARPPU after a price change reveals that the new pricing alienated your spending base.

These signals matter in hours, not days. When a new monetization event launches—a limited skin, a flash sale bundle, a holiday offer—you need live ARPPU data to know if it's working. RisingWave provides this through streaming SQL materialized views that update continuously as purchases arrive.

Ingesting Transaction and Session Data

CREATE SOURCE transactions (
    transaction_id  VARCHAR,
    player_id       BIGINT,
    game_id         VARCHAR,
    product_id      VARCHAR,
    product_tier    VARCHAR,    -- 'starter', 'standard', 'premium', 'ultimate'
    usd_amount      DOUBLE PRECISION,
    platform        VARCHAR,
    region          VARCHAR,
    is_refund       BOOLEAN,
    purchase_time   TIMESTAMPTZ
)
WITH (
    connector = 'kafka',
    topic = 'transactions',
    properties.bootstrap.server = 'kafka:9092',
    scan.startup.mode = 'latest'
)
FORMAT PLAIN ENCODE JSON;

Core ARPPU Materialized View

ARPPU = Total Revenue / Number of Paying Users. This view computes it across hourly windows, segmented by game, platform, and region:

CREATE MATERIALIZED VIEW arppu_hourly AS
SELECT
    window_start,
    window_end,
    game_id,
    platform,
    region,
    COUNT(DISTINCT player_id)                               AS paying_users,
    SUM(usd_amount) FILTER (WHERE is_refund = false)        AS gross_revenue,
    SUM(usd_amount) FILTER (WHERE is_refund = true)         AS refund_amount,
    SUM(usd_amount) FILTER (WHERE is_refund = false)
        - ABS(SUM(usd_amount) FILTER (WHERE is_refund = true))
                                                            AS net_revenue,
    ROUND(
        (SUM(usd_amount) FILTER (WHERE is_refund = false))
        / NULLIF(COUNT(DISTINCT player_id), 0),
    2)                                                      AS arppu,
    COUNT(*)                                                AS total_transactions,
    ROUND(
        COUNT(*)::NUMERIC / NULLIF(COUNT(DISTINCT player_id), 0),
    2)                                                      AS transactions_per_payer
FROM TUMBLE(transactions, purchase_time, INTERVAL '1 hour')
WHERE usd_amount != 0
GROUP BY window_start, window_end, game_id, platform, region;

This single view gives your monetization team live ARPPU by platform and region—updated every hour as the window slides forward.

Rolling 7-Day ARPPU for Trend Analysis

Hourly ARPPU is noisy. For trend detection, a rolling 7-day window smooths out day-of-week effects and gives a cleaner picture of monetization health:

CREATE MATERIALIZED VIEW arppu_rolling_7d AS
SELECT
    game_id,
    platform,
    window_end::DATE                                AS snapshot_date,
    COUNT(DISTINCT player_id)                       AS paying_users_7d,
    SUM(usd_amount) FILTER (WHERE is_refund = false) AS revenue_7d,
    ROUND(
        SUM(usd_amount) FILTER (WHERE is_refund = false)
        / NULLIF(COUNT(DISTINCT player_id), 0),
    2)                                              AS arppu_7d,
    AVG(usd_amount) FILTER (WHERE is_refund = false) AS avg_transaction_value,
    PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY usd_amount)
        FILTER (WHERE is_refund = false)            AS median_transaction_value
FROM HOP(transactions, purchase_time, INTERVAL '1 day', INTERVAL '7 days')
WHERE usd_amount > 0
GROUP BY game_id, platform, window_end;

The 7-day rolling ARPPU is your primary trend line. The gap between avg_transaction_value and median_transaction_value reveals whale skew—when your top 5% of spenders are pulling the average far above the median, your monetization is whale-dependent.

ARPPU by Product Tier

Understanding which product tiers drive ARPPU helps optimize your catalog:

CREATE MATERIALIZED VIEW arppu_by_product_tier AS
SELECT
    window_start,
    window_end,
    game_id,
    product_tier,
    COUNT(DISTINCT player_id)                               AS buyers,
    SUM(usd_amount) FILTER (WHERE is_refund = false)        AS tier_revenue,
    ROUND(
        SUM(usd_amount) FILTER (WHERE is_refund = false)
        / NULLIF(COUNT(DISTINCT player_id), 0),
    2)                                                      AS tier_arppu
FROM TUMBLE(transactions, purchase_time, INTERVAL '1 day')
WHERE usd_amount > 0 AND is_refund = false
GROUP BY window_start, window_end, game_id, product_tier;

Comparison: ARPPU Monitoring Approaches

DimensionManual BI ReportsData Warehouse RefreshStreaming (RisingWave)
Update frequencyDaily/weeklyHourly at bestPer transaction
Regional breakdownAggregatedAvailable but delayedLive, per-hour per-region
Whale skew detectionPost-hocDelayedReal-time median/mean gap
Flash sale effectivenessNext-day reportHours laterLive conversion tracking
Platform comparisonManual exportsETL-dependentAlways-current
Refund impactReconciled monthlyDelayedNetted in real time

Sinking ARPPU to Your BI System

CREATE SINK arppu_to_analytics_db
FROM arppu_rolling_7d
WITH (
    connector = 'jdbc',
    jdbc.url = 'jdbc:postgresql://analytics-db:5432/monetization',
    table.name = 'arppu_rolling_7d',
    type = 'upsert',
    primary_key = 'game_id,platform,snapshot_date'
);

Your existing Grafana dashboards now show always-current 7-day ARPPU without any pipeline changes—just point them at the analytics database that RisingWave keeps updated.

Monetization Signals to Watch

ARPPU divergence by platform: If iOS ARPPU is running 40% above Android, it's either audience mix or price sensitivity. Stream both to live dashboards and correlate with A/B test variants running on each platform.

ARPPU vs. paying user count: Healthy monetization grows both. If ARPPU rises while paying user count falls, you're extracting more from fewer spenders—a warning sign for long-term sustainability.

Regional ARPPU for pricing strategy: Lower-income regions often show lower absolute ARPPU but higher transaction counts. Use live regional ARPPU to validate localized pricing experiments.

Post-event ARPPU decay: After a special event ends, watch ARPPU decay rate. Fast decay suggests players were event-motivated, not permanently monetizable. Slow decay indicates the event converted players into habitual spenders.

FAQ

Q: How is ARPPU different from ARPU, and when should I track each? A: ARPU (Average Revenue Per User) divides total revenue by all active users—free and paying. ARPPU divides by paying users only. Track ARPU for overall monetization efficiency relative to your player base. Track ARPPU to understand spender behavior and optimize for your paying audience.

Q: Can I track ARPPU by player cohort (e.g., players who started in Q1)? A: Yes. Store player cohort data in a player_cohorts reference table, then join transactions with cohort assignments in your materialized view. This gives you cohort-adjusted ARPPU—essential for comparing monetization across acquisition campaigns.

Q: How do refunds affect the ARPPU calculation? A: The arppu_hourly view uses is_refund = false to compute gross revenue and separately tracks refund amounts. The net_revenue column subtracts refunds. Query net_revenue / paying_users for net ARPPU, which is what you should report to finance.

Q: What's a good ARPPU benchmark for mobile games? A: Industry benchmarks vary widely by genre. Casual mobile games typically see $5-15 monthly ARPPU. Mid-core and strategy games: $20-50. MMORPGs and hardcore titles: $50-200+. RisingWave helps you track your own ARPPU trends and measure the impact of changes—the absolute number matters less than the direction.

Q: Can I set up automated alerts when ARPPU drops below a threshold? A: Yes. Create a Kafka sink from arppu_rolling_7d. Your alerting service consumes this and compares against a threshold table. Alternatively, query the view directly from Grafana Alerting using a PostgreSQL data source with a threshold rule.


Get Started

Monitor ARPPU in real time and make smarter monetization decisions:

Best-in-Class Event Streaming
for Agents, Apps, and Analytics
GitHubXLinkedInSlackYouTube
Sign up for our to stay updated.