mirror of
https://github.com/s-frick/effigenix.git
synced 2026-03-28 10:09:35 +01:00
feat: Paginierung für alle GET-List-Endpoints (#61)
Einheitliches Paginierungs-Pattern mit page, size und Multi-Field sort
für alle 14 List-Endpoints. Response-Format ändert sich von [...] zu
{ content: [...], page: { number, size, totalElements, totalPages } }.
Backend:
- Shared Kernel: Page<T>, PageRequest, SortField, SortDirection
- PaginationHelper (SQL ORDER BY mit Whitelist), PageResponse DTO
- Paginated Methoden in allen 14 Domain-Repos + JDBC-Implementierungen
- Safety-Limit (500) für findAllBelowMinimumLevel/ExpiryRelevantBatches
- Alle List-Use-Cases akzeptieren PageRequest, liefern Page<T>
- Alle Controller mit page/size/sort Query-Params + PageResponse
Frontend:
- PagedResponse<T> Type auf nested page-Format aktualisiert
- Alle 14 API-Client-Resourcen liefern PagedResponse mit PaginationParams
- Alle Hooks mit Pagination-State (currentPage, totalPages, pageSize)
- Alle List-Screens mit Seiten-Navigation (Pfeiltasten) und Footer
Loadtest:
- Podman-Support im justfile (DOCKER_HOST auto-detect)
- Verschärfte Performance-Schwellwerte basierend auf Ist-Werten
This commit is contained in:
parent
fc4faafd57
commit
72979c9537
151 changed files with 2880 additions and 1120 deletions
|
|
@ -100,8 +100,7 @@ public final class LoadTestInfrastructure {
|
|||
// Podman rootless Socket
|
||||
Path podmanSocket = Path.of("/run/user/" + getUid() + "/podman/podman.sock");
|
||||
if (Files.exists(podmanSocket)) {
|
||||
System.setProperty("DOCKER_HOST", "unix://" + podmanSocket);
|
||||
System.setProperty("TESTCONTAINERS_RYUK_DISABLED", "true");
|
||||
configurePodman("unix://" + podmanSocket);
|
||||
System.out.println("=== Podman erkannt: " + podmanSocket + " ===");
|
||||
return;
|
||||
}
|
||||
|
|
@ -109,12 +108,17 @@ public final class LoadTestInfrastructure {
|
|||
// Podman root Socket
|
||||
Path podmanRootSocket = Path.of("/run/podman/podman.sock");
|
||||
if (Files.exists(podmanRootSocket)) {
|
||||
System.setProperty("DOCKER_HOST", "unix://" + podmanRootSocket);
|
||||
System.setProperty("TESTCONTAINERS_RYUK_DISABLED", "true");
|
||||
configurePodman("unix://" + podmanRootSocket);
|
||||
System.out.println("=== Podman (root) erkannt: " + podmanRootSocket + " ===");
|
||||
}
|
||||
}
|
||||
|
||||
private static void configurePodman(String dockerHost) {
|
||||
System.setProperty("DOCKER_HOST", dockerHost);
|
||||
System.setProperty("TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE", dockerHost.replace("unix://", ""));
|
||||
System.setProperty("TESTCONTAINERS_RYUK_DISABLED", "true");
|
||||
}
|
||||
|
||||
private static String getUid() {
|
||||
try {
|
||||
var process = new ProcessBuilder("id", "-u").start();
|
||||
|
|
|
|||
|
|
@ -78,62 +78,61 @@ public class FullWorkloadSimulation extends Simulation {
|
|||
)
|
||||
).protocols(httpProtocol)
|
||||
.assertions(
|
||||
// Global: Login (BCrypt ~230ms) hebt den Schnitt
|
||||
global().responseTime().percentile(95.0).lt(500),
|
||||
global().responseTime().percentile(99.0).lt(1000),
|
||||
global().failedRequests().percent().lt(5.0),
|
||||
// ── Global ──────────────────────────────────────────
|
||||
global().responseTime().percentile(95.0).lt(250),
|
||||
global().responseTime().percentile(99.0).lt(400),
|
||||
global().failedRequests().percent().lt(1.0),
|
||||
|
||||
// Login darf langsam sein (BCrypt strength 12)
|
||||
details("Login [admin]").responseTime().mean().lt(350),
|
||||
details("Login [admin]").responseTime().percentile(95.0).lt(500),
|
||||
// ── Login (BCrypt strength 12 ~230ms) ───────────────
|
||||
details("Login [admin]").responseTime().mean().lt(280),
|
||||
details("Login [admin]").responseTime().percentile(95.0).lt(350),
|
||||
|
||||
// Einzeldatensatz-Reads: streng (mean < 20ms)
|
||||
details("Rezept laden").responseTime().mean().lt(20),
|
||||
details("Artikel laden").responseTime().mean().lt(20),
|
||||
details("Lieferant laden").responseTime().mean().lt(20),
|
||||
details("Kunde laden").responseTime().mean().lt(20),
|
||||
// ── Einzeldatensatz-Reads: mean < 10ms ─────────────
|
||||
details("Rezept laden").responseTime().mean().lt(10),
|
||||
details("Artikel laden").responseTime().mean().lt(10),
|
||||
details("Lieferant laden").responseTime().mean().lt(10),
|
||||
details("Kunde laden").responseTime().mean().lt(10),
|
||||
details("Inventur laden").responseTime().mean().lt(10),
|
||||
details("Lagerort laden").responseTime().mean().lt(10),
|
||||
|
||||
// Listen mit wenig Daten (< 30 Einträge): mean < 35ms
|
||||
details("Rezepte auflisten").responseTime().mean().lt(35),
|
||||
details("Lagerorte auflisten").responseTime().mean().lt(35),
|
||||
details("Bestände auflisten").responseTime().mean().lt(35),
|
||||
details("Bestände unter Minimum").responseTime().mean().lt(35),
|
||||
details("Bestände nach Lagerort").responseTime().mean().lt(35),
|
||||
details("Lieferanten auflisten").responseTime().mean().lt(35),
|
||||
details("Kategorien auflisten").responseTime().mean().lt(35),
|
||||
details("Bestandsbewegungen auflisten").responseTime().mean().lt(35),
|
||||
details("Bestandsbewegungen nach Bestand").responseTime().mean().lt(35),
|
||||
details("Bestandsbewegungen nach Charge").responseTime().mean().lt(35),
|
||||
details("Bestandsbewegungen nach Zeitraum").responseTime().mean().lt(35),
|
||||
details("Inventuren auflisten").responseTime().mean().lt(35),
|
||||
details("Inventur laden").responseTime().mean().lt(20),
|
||||
// ── Paginated Listen (20er Seiten): mean < 15ms ────
|
||||
details("Rezepte auflisten").responseTime().mean().lt(15),
|
||||
details("Lagerorte auflisten").responseTime().mean().lt(15),
|
||||
details("Bestände auflisten").responseTime().mean().lt(15),
|
||||
details("Bestände unter Minimum").responseTime().mean().lt(15),
|
||||
details("Bestände nach Lagerort").responseTime().mean().lt(15),
|
||||
details("Lieferanten auflisten").responseTime().mean().lt(15),
|
||||
details("Kategorien auflisten").responseTime().mean().lt(15),
|
||||
details("Bestandsbewegungen auflisten").responseTime().mean().lt(15),
|
||||
details("Bestandsbewegungen nach Bestand").responseTime().mean().lt(15),
|
||||
details("Bestandsbewegungen nach Charge").responseTime().mean().lt(15),
|
||||
details("Bestandsbewegungen nach Zeitraum").responseTime().mean().lt(15),
|
||||
details("Inventuren auflisten").responseTime().mean().lt(15),
|
||||
details("Produktionsaufträge auflisten").responseTime().mean().lt(15),
|
||||
details("Produktionsaufträge nach Status").responseTime().mean().lt(15),
|
||||
|
||||
// Listen mit viel Daten (50-300 Einträge): mean < 75ms
|
||||
details("Chargen auflisten").responseTime().mean().lt(75),
|
||||
details("Artikel auflisten").responseTime().mean().lt(75),
|
||||
details("Kunden auflisten").responseTime().mean().lt(75),
|
||||
// ── Listen mit Children-Loading: mean < 25ms ───────
|
||||
details("Chargen auflisten").responseTime().mean().lt(25),
|
||||
details("Artikel auflisten").responseTime().mean().lt(25),
|
||||
details("Kunden auflisten").responseTime().mean().lt(25),
|
||||
|
||||
// Garantiert vorkommende Write-Requests: moderat (mean < 50ms)
|
||||
details("Charge planen").responseTime().mean().lt(50),
|
||||
details("Charge starten").responseTime().mean().lt(50),
|
||||
details("Charge abschließen").responseTime().mean().lt(50),
|
||||
details("Produktionsauftrag anlegen").responseTime().mean().lt(50),
|
||||
details("Produktionsauftrag freigeben").responseTime().mean().lt(50),
|
||||
details("Produktionsauftrag abschließen").responseTime().mean().lt(50),
|
||||
details("Produktionsauftrag stornieren").responseTime().mean().lt(50),
|
||||
details("Produktionsauftrag umterminieren").responseTime().mean().lt(50),
|
||||
details("Bestandsbewegung erfassen").responseTime().mean().lt(50),
|
||||
details("Inventur anlegen").responseTime().mean().lt(50),
|
||||
details("Inventur starten").responseTime().mean().lt(50),
|
||||
details("Ist-Menge erfassen").responseTime().mean().lt(50),
|
||||
// ── Write-Requests: mean < 20ms ────────────────────
|
||||
details("Charge planen").responseTime().mean().lt(20),
|
||||
details("Charge starten").responseTime().mean().lt(20),
|
||||
details("Charge abschließen").responseTime().mean().lt(20),
|
||||
details("Produktionsauftrag anlegen").responseTime().mean().lt(20),
|
||||
details("Produktionsauftrag freigeben").responseTime().mean().lt(20),
|
||||
details("Produktionsauftrag abschließen").responseTime().mean().lt(20),
|
||||
details("Produktionsauftrag stornieren").responseTime().mean().lt(20),
|
||||
details("Produktionsauftrag umterminieren").responseTime().mean().lt(20),
|
||||
details("Bestandsbewegung erfassen").responseTime().mean().lt(20),
|
||||
details("Inventur anlegen").responseTime().mean().lt(20),
|
||||
details("Inventur starten").responseTime().mean().lt(20),
|
||||
details("Ist-Menge erfassen").responseTime().mean().lt(20),
|
||||
|
||||
// Tracing: BFS-Traversierung, mean < 50ms
|
||||
details("Charge vorwärts tracen").responseTime().mean().lt(50),
|
||||
details("Charge rückwärts tracen").responseTime().mean().lt(50),
|
||||
|
||||
// Produktionsaufträge-Listen: mean < 35ms
|
||||
details("Produktionsaufträge auflisten").responseTime().mean().lt(35),
|
||||
details("Produktionsaufträge nach Status").responseTime().mean().lt(35)
|
||||
// ── Tracing (BFS): mean < 15ms ─────────────────────
|
||||
details("Charge vorwärts tracen").responseTime().mean().lt(15),
|
||||
details("Charge rückwärts tracen").responseTime().mean().lt(15)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue