Skip to content

About

Multi-purpose microservice for all things performance, made for Akatsuki.

Resources

Stars

1 star

Watchers

1 watching

Forks

Repository files navigation

performance-service

Multi-purpose microservice for all things performance, made for Akatsuki.

Components

The service runs different components based on the APP_COMPONENT environment variable:

Component Description
api REST API server for PP calculation (default, port 8665)
deploy Production PP recalculation tool
processor AMQP consumer for rework recalculation queue
mass_recalc CLI to queue all users for rework recalculation
individual_recalc CLI to queue a single user for rework recalculation

Building

cargo build --release

Running

APP_COMPONENT=api cargo run --release

Configuration

Copy .env.example to .env and configure:

# Service
APP_COMPONENT=api
API_HOST=127.0.0.1
API_PORT=8665

# Database
DATABASE_HOST=localhost
DATABASE_PORT=3306
DATABASE_USERNAME=root
DATABASE_PASSWORD=password
DATABASE_NAME=akatsuki
DATABASE_POOL_MAX_SIZE=16

# AMQP (RabbitMQ) - required for processor/mass_recalc/individual_recalc
AMQP_HOST=localhost
AMQP_PORT=5672
AMQP_USERNAME=guest
AMQP_PASSWORD=guest
AMQP_POOL_MAX_SIZE=10

# Redis
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_DATABASE=0

# External Services
BEATMAPS_SERVICE_BASE_URL=http://localhost:8080
SERVICE_READINESS_TIMEOUT=60

Production PP Recalculation

The deploy component recalculates PP for all scores and updates user statistics. This uses the same PP calculation algorithm as live score submissions.

See PRACTICAL_RECALC_GUIDE.md for step-by-step instructions on running a recalculation in production.

Game Modes

Mode Value Relax Bit
osu!std 0 0 (vanilla), 1 (relax), 2 (autopilot)
osu!taiko 1 0 (vanilla), 1 (relax)
osu!catch 2 0 (vanilla), 1 (relax)
osu!mania 3 0 (vanilla)

Interactive Mode

APP_COMPONENT=deploy cargo run --release

You'll be prompted for:

  • Modes: Comma-separated list (e.g., 0,1,2,3)
  • Relax bits: Comma-separated list (e.g., 0,1,2)
  • Total PP recalc only: y to skip individual score recalc, n to recalc scores first
  • Total PP: y to recalculate user total PP and leaderboards
  • Mod value recalc only: Filter to scores with specific mods
  • Neq mod value recalc only: Filter to scores WITHOUT specific mods
  • Mapper recalc only: Filter to beatmaps by mapper name
  • Map recalc only: Filter to specific beatmap IDs

Environment Variable Mode

For automated/scripted recalculation:

# Full recalculation across all modes
DEPLOY_MODES=0,1,2,3 \
DEPLOY_RELAX_BITS=0,1,2 \
DEPLOY_TOTAL_PP_ONLY=0 \
DEPLOY_TOTAL_PP=1 \
APP_COMPONENT=deploy cargo run --release

Environment Variables

Variable Description Example
DEPLOY_MODES Comma-separated game modes 0,1,2,3
DEPLOY_RELAX_BITS Comma-separated relax bits 0,1,2
DEPLOY_TOTAL_PP_ONLY Set to 1 to skip Phase 1 (score PP recalc) 0
DEPLOY_TOTAL_PP Set to 1 to run Phase 2 (user total PP aggregation) 1
DEPLOY_PREVIEW Set to 1 to log matching score/beatmap/user counts without updates 1
DEPLOY_DRY_RUN Set to 1 to calculate and log each write without performing it; combined score+total dry-runs are capped at 100k planned score PP values 1
DEPLOY_MODS_FILTER Only scores WITH these mods (bitmask) 64 (DT)
DEPLOY_NEQ_MODS_FILTER Only scores WITHOUT these mods 64
DEPLOY_MAPPER_FILTER Filter by mapper name (fuzzy) Sotarks
DEPLOY_MAP_FILTER Comma-separated beatmap IDs 123,456,789
DEPLOY_PP_ZERO Set to 1 to only repair scores where pp = 0 1
DEPLOY_AFTER_DATE Only repair scores submitted on/after a UTC date 2026-07-01
DEPLOY_AFTER_TIME Only repair scores submitted on/after a Unix timestamp 1782864000

Common Recalculation Scenarios

Full server recalculation (all modes, all variants):

DEPLOY_MODES=0,1,2,3 \
DEPLOY_RELAX_BITS=0,1,2 \
DEPLOY_TOTAL_PP_ONLY=0 \
DEPLOY_TOTAL_PP=1 \
APP_COMPONENT=deploy cargo run --release

Recalculate only osu!std (vanilla + relax + autopilot):

DEPLOY_MODES=0 \
DEPLOY_RELAX_BITS=0,1,2 \
DEPLOY_TOTAL_PP_ONLY=0 \
DEPLOY_TOTAL_PP=1 \
APP_COMPONENT=deploy cargo run --release

Recalculate only DT scores (all modes):

DEPLOY_MODES=0,1,2,3 \
DEPLOY_RELAX_BITS=0,1,2 \
DEPLOY_MODS_FILTER=64 \
DEPLOY_TOTAL_PP_ONLY=0 \
DEPLOY_TOTAL_PP=1 \
APP_COMPONENT=deploy cargo run --release

Re-aggregate user totals only (skip Phase 1, use existing score PP):

DEPLOY_MODES=0,1,2,3 \
DEPLOY_RELAX_BITS=0,1,2 \
DEPLOY_TOTAL_PP_ONLY=1 \
DEPLOY_TOTAL_PP=1 \
APP_COMPONENT=deploy cargo run --release

Recalculate specific beatmaps:

DEPLOY_MODES=0 \
DEPLOY_RELAX_BITS=0,1 \
DEPLOY_MAP_FILTER=75,129891,1816113 \
DEPLOY_TOTAL_PP_ONLY=0 \
DEPLOY_TOTAL_PP=1 \
APP_COMPONENT=deploy cargo run --release

Repair recent 0pp relax scores:

DEPLOY_MODES=0,1,2 \
DEPLOY_RELAX_BITS=1 \
DEPLOY_PP_ZERO=1 \
DEPLOY_AFTER_DATE=2026-07-01 \
DEPLOY_TOTAL_PP_ONLY=0 \
DEPLOY_TOTAL_PP=1 \
APP_COMPONENT=deploy cargo run --release

Preview recent 0pp relax score repair counts without updates:

DEPLOY_MODES=0,1,2 \
DEPLOY_RELAX_BITS=1 \
DEPLOY_PP_ZERO=1 \
DEPLOY_AFTER_DATE=2026-07-01 \
DEPLOY_TOTAL_PP_ONLY=0 \
DEPLOY_TOTAL_PP=1 \
DEPLOY_PREVIEW=1 \
APP_COMPONENT=deploy cargo run --release

Dry-run recent 0pp relax score repair and log skipped writes:

DEPLOY_MODES=0,1,2 \
DEPLOY_RELAX_BITS=1 \
DEPLOY_PP_ZERO=1 \
DEPLOY_AFTER_DATE=2026-07-01 \
DEPLOY_TOTAL_PP_ONLY=0 \
DEPLOY_TOTAL_PP=1 \
DEPLOY_DRY_RUN=1 \
APP_COMPONENT=deploy cargo run --release

Recalculation Phases

The deploy component runs in two phases, controlled by environment variables:

Phase 1: Score PP Recalculation (runs unless DEPLOY_TOTAL_PP_ONLY=1)

  • Fetches the .osu file for each beatmap from beatmaps-service
  • Re-runs the PP calculation algorithm on each individual score
  • Updates the pp column in scores, scores_relax, or scores_ap tables
  • This is the computationally expensive phase

Phase 2: User Total PP Aggregation (runs if DEPLOY_TOTAL_PP=1)

  • Uses the existing pp values already in the score tables (does NOT recalculate them)
  • Recalculates which score is "best" per beatmap (completed=3 vs completed=2)
  • Aggregates each user's top 100 scores using the weighted formula
  • Updates user_stats.pp
  • Updates Redis leaderboards (global and country)
  • Publishes peppy:update_cached_stats for each user

Common configurations:

TOTAL_PP_ONLY TOTAL_PP What happens
0 1 Full recalc: Phase 1 then Phase 2
0 0 Score PP only: Phase 1 only (no user totals)
1 1 Aggregation only: Phase 2 only (uses existing score PP)

When to use aggregation only (DEPLOY_TOTAL_PP_ONLY=1):

  • A previous recalc crashed during Phase 2 and you need to resume
  • You fixed a bug in the aggregation/leaderboard logic (not the PP algorithm)
  • You want to rebuild Redis leaderboards without changing score PP values

Performance

  • Beatmaps are processed with 10 concurrent tasks
  • Users are processed in batches of 1000 with 100 concurrent tasks
  • Progress is logged every 100 beatmaps/users

Rework Recalculation

The mass_recalc and processor components are for testing experimental PP algorithms (reworks). These use different calculation formulas than live score submission.

Available Reworks

The processor supports the following rework IDs, all for osu!std relax (mode = 0, rx = 1). The names below identify their calculator implementations in src/processor/mod.rs; display names come from the reworks database table.

ID Name Description
19 improved_miss_penalty Improved miss penalty formula
21 flashlight_hotfix Flashlight mod adjustments
22 remove_accuracy_pp Removes accuracy PP component
23 stream_nerf_speed_value Nerfs stream speed value
24 remove_manual_adjustments Removes manual map adjustments
25 fix_inconsistent_powers Fixes inconsistent power calculations
26 aim_accuracy_fix Aim and accuracy fixes
27 improved_miss_penalty_and_acc_rework Combined miss penalty + accuracy rework
28 everything_at_once All experimental changes combined
29 kippy_attempt Kippy's experimental relax PP formula
30 merge_test Merge Test formula from the merge-test rosu-pp fork
31 relax_pp_update Relax calculation from the relax-pp-update rosu-pp branch

Recalculation also requires a matching entry in the reworks table. IDs without an implemented calculator are rejected by the API and CLI enqueue paths.

Running a Rework Recalculation

  1. Start the processor (listens to AMQP queue):

    APP_COMPONENT=processor cargo run --release
  2. Queue all users for a rework:

    APP_COMPONENT=mass_recalc cargo run --release
    # Enter the rework ID when prompted

    Or via environment:

    MASS_RECALC_REWORK_ID=19 APP_COMPONENT=mass_recalc cargo run --release
  3. Queue a single user:

    APP_COMPONENT=individual_recalc cargo run --release
    # Enter user ID and rework ID when prompted

Rework Data Storage

  • rework_scores - Individual score PP calculations
  • rework_stats - User total PP for each rework
  • rework_queue - Processing queue status
  • Redis: rework:leaderboard:{rework_id} - Rework leaderboards

API Endpoints

POST /api/v1/calculate

Calculate PP for one or more scores.

Request:

[
  {
    "beatmap_id": 75,
    "beatmap_md5": "a5b99395a42bd55bc5eb1d2411cbdf8b",
    "mode": 0,
    "mods": 0,
    "max_combo": 314,
    "accuracy": 98.5,
    "miss_count": 1
  }
]

Or with hit counts instead of accuracy:

[
  {
    "beatmap_id": 75,
    "beatmap_md5": "a5b99395a42bd55bc5eb1d2411cbdf8b",
    "mode": 0,
    "mods": 0,
    "max_combo": 314,
    "count_300": 200,
    "count_100": 10,
    "count_50": 2,
    "miss_count": 1
  }
]

Response:

[
  {
    "stars": 5.23,
    "pp": 234.56,
    "ar": 9.0,
    "od": 8.0,
    "max_combo": 315
  }
]

PP Calculation Algorithm

  • osu!std Relax: Uses akatsuki_pp_rs::osu_2019::OsuPP (2019 algorithm)
  • All other modes: Uses rosu-pp via beatmap.performance()

Total PP formula:

Total PP = SUM(score.pp * 0.95^index) + bonus_pp
Bonus PP = 416.6667 * (1 - 0.995^score_count)

Where index is the score's position when sorted by PP descending (top 100 scores only).

About

Multi-purpose microservice for all things performance, made for Akatsuki.

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages