Lab 11

Performance und Diagnostik

N+1, ungebremste Listen und fehlende Messpunkte systematisch verbessern.

PerformanceJPAJFRMetrics
SchwierigkeitFortgeschritten
Dauer90–120 Min
LernstufeStufe 6 – Praxislabor und Capstone
Arbeitsweise: Erst den Vorher-Code öffnen, dann Analyse lesen, anschließend die Nachher-Lösung und den Test vergleichen. Alle Abschnitte sind standardmäßig geschlossen.

Workshop-Slice

Dieses Lab zeigt einen konkreten Enterprise-Fehler mit Vorher/Nachher-Code. Die Beispiele sind bewusst fachlich benannt, damit du Architekturentscheidungen und nicht nur Syntax übst.

Lernziel

Du analysierst einen realistischen Performance-Fehler und ersetzt Vermutung durch Messung und gezieltes Refactoring.

Ausgangslage

Eine Order-Liste ist in Produktion langsam. Der Code lädt Orders und Positionen unkontrolliert und liefert beliebig große Listen.

Vorher: problematischer Code

Die API lädt zu viel und erzeugt N+1-Queries.

OrderQueryService.javaJAVA
public List<OrderDto> findAllOrders() {
    return orderRepository.findAll().stream()
        .map(order -> new OrderDto(
            order.getBusinessId(),
            order.getItems().stream().map(OrderItemEntity::getSku).toList()))
        .toList();
}
Analyse: Was ist daran schlecht?
  • Unbegrenzte Ergebnisliste.
  • Lazy `items` erzeugen pro Order zusätzliche Queries.
  • Keine Projektion für Listenansicht.
  • Keine Metrik, um Verbesserung objektiv zu sehen.
Nachher: bessere Lösung

Die Zielversion nutzt Pagination, Query-Projektion und Messpunkte.

OrderListProjection.javaJAVA
public record OrderListProjection(
    String orderId,
    String customerId,
    BigDecimal totalAmount,
    String status
) {}
OrderReadRepository.javaJAVA
public interface OrderReadRepository extends Repository<OrderEntity, UUID> {
    @Query("""
        select new com.acme.order.query.OrderListProjection(
          o.businessId, o.customerId, o.totalAmount, o.status
        )
        from OrderEntity o
        where o.createdAt >= :from
        order by o.createdAt desc
    """)
    Page<OrderListProjection> findRecentOrders(@Param("from") Instant from, Pageable pageable);
}
OrderQueryController.javaJAVA
@GetMapping("/orders")
public Page<OrderListProjection> recent(
        @RequestParam Instant from,
        @PageableDefault(size = 50, sort = "createdAt", direction = DESC) Pageable pageable) {
    return metrics.timer("order.query.recent").record(() ->
        repository.findRecentOrders(from, pageable));
}
Test / Prüfnachweis

Dieser Abschnitt zeigt, wie du die Verbesserung nachweist. Es ist bewusst kein reiner Happy-Path-Test, sondern prüft ein Risiko aus dem Vorher-Teil.

diagnostics-commands.shBASH
# Lasttest mit begrenzter Ergebnisgröße
hey -z 60s -c 20 'https://order-service.apps.local/orders?from=2026-01-01T00:00:00Z&size=50'

# JFR-Aufzeichnung für Hotspots
jcmd $(pgrep -f order-service) JFR.start name=order-query duration=120s filename=/tmp/order-query.jfr
Typische Fehler
  • Performance ohne Messung optimieren.
  • Fetch Join überall einsetzen und Pagination zerstören.
  • Unbegrenzte Listen im REST-API erlauben.
  • JPA SQL-Logging dauerhaft in Produktion aktivieren.
Deep-Learning-Bezug

Die Links führen zum ausführlichen Inhalt; die Deep-Learning-Seite bleibt nur die Lernlandkarte und kopiert den Inhalt nicht doppelt.

Prüfcheckliste
  • Endpoint ist paginiert.
  • Listenansicht nutzt Projektion.
  • Messpunkt vor/nach Änderung vorhanden.
  • JFR oder Metrikbeweis ist dokumentiert.
⌂ Cockpit