Skip to content

Overview

PerfShop is a pedagogical chaos engineering platform: an e-commerce shop deliberately instrumented so that an instructor can inject performance, security, business, functional or scripting anomalies on the fly, and so that students can observe the consequences in real time through a complete observability stack.

This page gives the level 1 view: who interacts with what, and what the major functional blocks are. The following pages (stack, deployment, database, auth, multi-session, docker, network) progressively dive into the details.

Source of truth

This entire section reflects the actual state of the deployment Docker Compose files (docker-compose.desktop.yml for Docker Desktop, docker-compose.build.yml for Linux/VPS), the JPA backend entities, and the configuration files of the observability and QA services. No information has been invented; every detail is verifiable in the source code.

Actors and systems

C4Context
  title PerfShop — Functional context (C4 level 1)

  Person(student, "Student", "Follows a pedagogical journey<br/>(BAC1 to BAC5)")
  Person(instructor, "Instructor", "Activates chaos types, follows sessions,<br/>analyzes metrics")
  Person(client, "End customer<br/>(role played)", "Browses the shop,<br/>places an order")

  System_Boundary(perfshop, "PerfShop") {
    System(shop, "E-commerce shop", "Spring Boot + React<br/>Catalog, cart, checkout")
    System(chaos, "Chaos Engine", "6 anomaly families<br/>injectable on the fly")
    System(obs, "Observability", "Prometheus, Grafana,<br/>Loki, Tempo, Pyroscope,<br/>OpenSearch")
    System(qa, "QA stack", "Squash TM, Selenium,<br/>JMeter, Forgejo,<br/>Polyglot Test Runner")
    System(ped, "Pedagogical engine", "Enigmas, sessions,<br/>agent code, stars")
  }

  Rel(client, shop, "Buys", "HTTPS")
  Rel(student, ped, "Plays the enigmas", "HTTPS")
  Rel(student, obs, "Watches metrics", "Student Grafana")
  Rel(instructor, chaos, "Activates / deactivates", "X-Admin-Token")
  Rel(instructor, obs, "Analyzes", "Instructor Grafana")
  Rel(instructor, qa, "Runs tests", "Web UI")

  Rel(chaos, shop, "Injects anomalies", "Spring Interceptor")
  Rel(shop, obs, "Metrics + traces + logs", "Prometheus / OTLP / Docker")
  Rel(qa, shop, "Selenium / JMeter tests", "HTTP")

Three usages cross paths in the same platform:

  • The end customer is a character played by the student or by a test script: they browse the e-commerce shop, add products to the cart, place an order. This is the "normal user role".
  • The student follows a pedagogical journey structured in 5 levels (BAC1 → BAC5). They have their own page (chaos student page) where they discover the enigmas, validate steps and observe the consequences of the chaos types activated by the instructor. They access the "Student" Grafana dashboards without authentication.
  • The instructor drives the entire session via the chaos-admin interface and the admin portal. They activate anomalies, manage accounts, open/close access to the student page, analyze sessions and access the reserved "Instructor" Grafana dashboards.

Major functional blocks

flowchart TB
  subgraph CORE["Application core"]
    direction LR
    FE["perfshop-frontend<br/>React 18 + Vite"]
    BE["perfshop-app<br/>Spring Boot 3.2 + Java 21"]
    DB[("perfshop-db<br/>MySQL 8")]
    FE -->|REST + session cookies| BE
    BE --> DB
  end

  subgraph CHAOS["Chaos control"]
    direction LR
    CADM["perfshop-chaos-admin<br/>static nginx"]
    ADM["perfshop-admin<br/>static nginx"]
    MON["perfshop-monitoring<br/>Node + Express"]
  end

  subgraph OBS["Observability"]
    direction LR
    PROM["Prometheus"]
    GRAF["Grafana"]
    LOKI["Loki + Promtail"]
    TEMPO["Tempo"]
    PYRO["Pyroscope"]
    OS["OpenSearch + Vector"]
  end

  subgraph QA["QA stack"]
    direction LR
    SQ["Squash TM + Orchestrator"]
    SEL["Selenium Grid"]
    TR["Test Runner<br/>RF + pytest"]
    JM["JMeter + JMeter UI"]
    FG["Forgejo"]
    SU["Scripts UI"]
  end

  CADM -->|X-Admin-Token| BE
  ADM -->|X-Admin-Token| BE
  MON -->|2s polling| BE

  BE -->|"/actuator/prometheus<br/>5s scrape"| PROM
  BE -->|OTLP gRPC 4317| TEMPO
  BE -->|JFR push| PYRO
  PROM --> GRAF
  LOKI --> GRAF
  TEMPO --> GRAF
  PYRO --> GRAF
  OS -->|Dashboards| GRAF

  SQ --> SEL
  SQ --> TR
  TR -->|HTTP| FE
  JM -->|HTTP| BE
  SU --> FG
  SU --> TR

Five blocks coexist on the same perfshop-network Docker network. Each block is documented on its own page:

Block Reference page Number of services
Application core Technical stack, Database schema 3
Chaos control Authentication 3
Observability Overview 9
QA stack Overview 8
Cross-cutting tools Docker Compose 9

Main data flows

PerfShop handles four parallel telemetry flows, each with its own protocol and storage.

flowchart LR
  APP["perfshop-app<br/>Spring Boot"]

  APP -->|"/actuator/prometheus<br/>HTTP scrape 5s"| MET[("Prometheus<br/>retention 7d / 5GB")]
  APP -->|"OTLP gRPC<br/>:4317"| TRC[("Tempo<br/>local blocks")]
  APP -->|"JFR push HTTP<br/>:4040"| PRF[("Pyroscope<br/>filesystem")]
  APP -->|"stdout / stderr"| DOC{"Docker<br/>logging driver"}

  DOC --> PT["Promtail<br/>(docker SD)"] --> LOG[("Loki<br/>retention 7d")]
  DOC --> VEC["Vector<br/>(VRL transform)"] --> OS[("OpenSearch<br/>perfshop-spring*")]

  MET --> GRAF["Grafana<br/>dashboards"]
  LOG --> GRAF
  TRC --> GRAF
  PRF --> GRAF
  OS  --> OSD["OpenSearch<br/>Dashboards"]
  • Metrics: Prometheus scrapes perfshop-app:9090/actuator/prometheus every 5 seconds (perfshop-backend job), perfshop-monitoring:3001/metrics for Docker metrics (perfshop-docker job), and perfshop-jmeter:9270 during JMeter test runs (jmeter job). Details in observability/prometheus.md.
  • Traces: the OpenTelemetry Java agent is embedded in the perfshop-backend image and exports spans via OTLP gRPC to perfshop-tempo:4317. Details in observability/tempo.md.
  • Continuous profiling: the Pyroscope Java agent is also embedded in the image and pushes JFR-format profiles to perfshop-pyroscope:4040. Details in observability/pyroscope.md.
  • Logs: they are collected twice in parallel. Promtail reads the Docker socket to feed Loki; Vector reads the same socket to feed OpenSearch (with deeper VRL parsing that isolates the chaos_family, chaos_level, scenario_id fields). The two sinks deliberately coexist as a pedagogical demonstration.

Request lifecycle

An HTTP request reaching the backend goes through several layers before reaching the business controller.

sequenceDiagram
  autonumber
  participant N as Browser
  participant FE as perfshop-frontend<br/>(nginx)
  participant BE as perfshop-app<br/>(Tomcat)
  participant LI as LicenseInterceptor
  participant CI as ChaosInterceptor
  participant CT as Controller
  participant DB as MySQL
  participant T as Tempo / Pyroscope

  N->>FE: GET /products
  FE->>BE: GET /api/products
  BE->>LI: handle()
  alt License missing
    LI-->>N: HTTP 402 Payment Required
  else License valid
    LI->>CI: handle()
    CI->>CI: Inject active anomalies
    CI->>CT: ProductController.list()
    CT->>DB: SELECT (HikariCP)
    DB-->>CT: rows
    CT-->>N: 200 JSON
    BE->>T: OTLP span + CPU sample
  end

The interceptor order is locked in WebConfig.java:

  1. LicenseInterceptor (order 1) — verifies that a valid license is present. Without a license, any request to /api/** is rejected with an HTTP 402 Payment Required. Only the /actuator/** endpoints are excluded from this check.
  2. ChaosInterceptor (order 2) — only reached if the license is valid. It inspects the state of the active chaos types and injects the anomalies (artificial latency, random exception, response corruption, etc.) before letting the request reach the controller.

CORS is handled separately by CorsConfig.java, which dynamically authorizes the origins listed in the CORS_ALLOWED_ORIGINS compose variable and explicitly exposes the headers used by Scripting Chaos (X-Session-Token, X-Action-Token, X-CSRF-Token, X-Step-Token, X-Signature, X-Request-ID, X-Key-Hint).

Pedagogical games hub

PerfShop also embeds a pedagogical games hub unlocked at the end of the BAC journey. It is a static web application separate from the shop, built in modern JavaScript (ES modules, Vite 5) and based on the Phaser 3.80 (2D games, e.g. Server Defender) and Three.js 0.160 (3D rendering) frameworks. The hub is served by nginx as a standalone container and has no backend dependency: all gameplay runs in the browser. See the future games-dedicated section for the details of the internal architecture.

To go further