Skip to content

Repository files navigation

Reward Decision Service

Low-latency FastAPI microservice that returns deterministic reward decisions for transactions.

Features

  • POST /reward/decide API with request/response validation
  • Deterministic reward decisioning using config-driven rules
  • Persona-based reward logic with multipliers (NEW, RETURNING, POWER)
  • Deterministic reward selection using hash-based weighted distribution
  • Idempotency (txn_id + user_id + merchant_id) with lock/wait handling
  • Distributed locking to ensure safe concurrent processing
  • Cache-first architecture with Redis backend and in-memory fallback
  • Daily CAC cap enforcement with XP fallback
  • Atomic CAC budget tracking to prevent race conditions
  • Reward cooldown mechanism to limit frequent monetary rewards
  • Feature flags:
    • prefer_xp_mode
    • cooldown_on_last_reward
  • Router-level rate limiting with Retry-After header
  • Unit tests using pytest
  • Custom threaded load-test script with latency (p50, p95, p99) and throughput metrics

Request Flow

Flow for POST /reward/decide:

Reward decision flow diagram

Project Structure

app/
  app.py
  routers/reward.py
  services/reward_service.py
  models/schemas.py
  cache/cache.py
  core/config.py
config/
  policy.json
tests/
  test_reward_service.py
scripts/
  load_test.py
docs/
  images/
    .gitkeep
  performance_report_template.md

API

POST /reward/decide

Request fields:

  • txn_id (string)
  • user_id (string)
  • merchant_id (string)
  • amount (number, > 0)
  • txn_type (string)
  • ts (string)

Response fields:

  • decision_id (UUID string)
  • policy_version (string)
  • reward_type (XP / CHECKOUT / GOLD)
  • reward_value (integer)
  • xp (integer)
  • reason_codes (list of strings)
  • meta (object)

Configuration

Policy is loaded from config/policy.json by default.

Override path with:

export POLICY_CONFIG_PATH=/absolute/path/to/policy.json

Cache/rate-limit environment variables:

export CACHE_BACKEND=memory        # memory | redis
export REDIS_HOST=localhost
export REDIS_PORT=6379
export REDIS_DB=0
export RATE_LIMIT_MAX_REQUESTS=30
export RATE_LIMIT_WINDOW_SECONDS=60

Run With Docker Compose (API + Redis)

docker compose up --build

Services:

  • API: http://localhost:8000
  • Redis: localhost:6379

Stop containers:

docker compose down

Optional: create a .env file if you want custom values (not required).
If no .env is provided, docker-compose.yml defaults are used.
You can start from .env.example.

Run With Docker (API Only)

Build:

docker build -t reward-decision-service .

Run (expects Redis already available):

docker run --rm -p 8000:8000 \
  -e CACHE_BACKEND=redis \
  -e REDIS_HOST=host.docker.internal \
  -e REDIS_PORT=6379 \
  reward-decision-service

Local Setup

python3 -m venv .venv
source .venv/bin/activate
pip install -e .
pip install pytest

Run Service

uvicorn main:app --host 0.0.0.0 --port 8000 --reload

Swagger docs:

  • http://localhost:8000/docs

Quick API Check

curl -X POST "http://localhost:8000/reward/decide" \
  -H "Content-Type: application/json" \
  -d '{
    "txn_id": "txn-1",
    "user_id": "user_1",
    "merchant_id": "m1",
    "amount": 100,
    "txn_type": "PURCHASE",
    "ts": "2026-03-18T12:00:00Z"
  }'

Run Tests

python3 -m pytest -q

Load Test

python3 scripts/load_test.py \
  --url http://localhost:8000/reward/decide \
  --duration 30 \
  --workers 120 \
  --timeout 2 \
  --user-pool 10000

Outputs include:

  • attempt_throughput_rps
  • response_throughput_rps
  • latency_p50_ms
  • latency_p95_ms
  • latency_p99_ms
  • status_counts
  • error_counts

Use docs/performance_report_template.md for reporting.

Assumptions

  1. Persona is mocked in service with an in-memory map and cached per user.
  2. Time-based counters (CAC/rate limit) are handled in UTC.
  3. Idempotent response consistency is guaranteed within configured idempotency TTL.
  4. Redis is optional for local development due to in-memory fallback.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages