Table of Contents
- Performance & Optimierungen (Code-getrieben)
- Leitgedanke
- 1) SQLite: Connection-Pooling + PRAGMAs
- 2) Schema-Init nur einmal pro PDO
- 3) SQL-Datei-Caching
- 4) Config-Caching (TTL)
- 5) Registrierungsnummern: weniger Daten laden + atomare Vergabe
- 6) Mailversand: Queue + Rate-Limit (Token-Bucket)
- 7) API-Caching: Event-Datum
- 8) Build: Copy-Optimierungen / Robustheit
- 9) Kleine aber wichtige Sicherheits-/Stabilitätsoptimierungen
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
Performance & Optimierungen (Code-getrieben)
Diese Seite erklärt die wichtigsten Performance-Optimierungen im Projekt: was sie tun, warum sie helfen und wo sie im Code umgesetzt sind.
Begriffe (WAL, PRAGMA, PRG, etc.): siehe Glossar.
Leitgedanke
Das System ist “einfach”, aber unter Last entstehen Engpässe vor allem durch:
- SQLite-Overhead (viele Verbindungen, Locking),
- unnötige Queries / File-I/O,
- synchronen Mailversand,
- Caches (fehlend oder zu aggressiv).
1) SQLite: Connection-Pooling + PRAGMAs
Wo: zgb-backend/Repository/Repository.php (Repository::CreatePDO()).
Connection-Pooling (Reuse)
- Pro DB-Pfad wird eine PDO-Verbindung in einem statischen Pool wiederverwendet.
- Vor Reuse wird
SELECT 1geprüft (falls Verbindung kaputt ist, wird sie neu erstellt).
Warum das hilft: SQLite ist dateibasiert. Viele kurzlebige Connections erzeugen unnötigen Overhead und können Locking-Probleme verstärken.
PRAGMA-Einstellungen
Im Code werden (konservativ) diese PRAGMAs gesetzt:
PRAGMA journal_mode = WAL- Warum: WAL erlaubt parallele Reads/Writes besser (wichtig bei gleichzeitigen Zugriffen).
PRAGMA synchronous = NORMAL- Warum: weniger fsync-Overhead → schneller (mit akzeptablem Risiko).
PRAGMA cache_size = 10000- Warum: mehr SQLite-Cache → weniger Disk-I/O.
PRAGMA temp_store = MEMORY- Warum: temporäre Daten im RAM → schneller.
2) Schema-Init nur einmal pro PDO
Wo: RegistrationRepository, ConfigRepository, MailQueueRepository.
- Jede Repo-Klasse führt
CREATE TABLE IF NOT EXISTS ...nur einmal pro PDO-Instanz aus (statisches Flagspl_object_hash($pdo)).
Warum: CREATE TABLE IF NOT EXISTS ist zwar idempotent, aber unnötiger Overhead, wenn es bei jeder Instanziierung läuft.
3) SQL-Datei-Caching
Wo: zgb-backend/Repository/Repository.php (getSQL()).
- SQL-Dateien aus
Repository/Schema/*werden einmal gelesen und dann aus einem statischen Cache geliefert.
Warum: spart File-I/O, besonders bei vielen Requests/Queries.
4) Config-Caching (TTL)
Wo: zgb-backend/Repository/ConfigRepository.php.
loadConfig()verwendet einen statischen Cache pro PDO-Instanz (TTL: 60s).- Bei
setConfig()/saveConfig()wird der Cache invalidiert.
Warum: loadConfig() würde sonst viele einzelne getConfig()-Reads erzeugen. Mit Cache werden DB-Reads stark reduziert.
5) Registrierungsnummern: weniger Daten laden + atomare Vergabe
Wo: zgb-backend/Repository/RegistrationRepository.php und static/registration/index.php.
Effizientere “freie Nummer” Suche
getFirstFreeNumber(start, end)lädt nur die belegtennumberim Bereich und sucht dann in PHP mit O(1)-Lookups.
Warum: reduziert Datenmenge und CPU gegenüber “alle Registrierungen laden”.
Atomare Vergabe beim POST (Race-Fix)
- Nummern werden erst beim POST vergeben und in SQLite mit
BEGIN IMMEDIATEserialisiert.
Warum: verhindert Race-Conditions, wenn viele Nutzer gleichzeitig anmelden.
6) Mailversand: Queue + Rate-Limit (Token-Bucket)
Wo: zgb-backend/Service/MailQueueWorker.php, static/registration/index.php.
- Statt synchroner
mail()-Calls im User-Request werden Jobs enqueued. - Cron-Worker sendet kontrolliert nach, mit Token-Bucket Rate-Limit.
Warum: stabilere Requests, Einhaltung von Provider-Limits, Retry/Backoff, weniger Lastspitzen.
Siehe Details: Mail-Queue
7) API-Caching: Event-Datum
Wo: static/api/event-date.php + static/assets/script.js.
- Browser-Cache: 30 Sekunden
- Server-Cache: 60 Sekunden (file-basiert in
public/data/.event-date-cache.json) - JS nutzt einen 30s Cache-Buster (damit das Frontend zeitnah aktualisiert)
Warum: statische HTML-Seiten können das Event-Datum nicht aus der DB lesen; die API ist eine kleine Brücke, die trotzdem performant bleibt.
8) Build: Copy-Optimierungen / Robustheit
Wo: scripts/build.mjs.
- Statische Dateien werden rekursiv kopiert.
- Einige unnötige Duplikate werden übersprungen (
Original/, doppeltelogo-svg.svg). - Kopieren von Dotfiles ist robust gegen Deploy-Setups, die Dotfiles verlieren (warnen statt Build hart zu brechen).
Warum: Build-Zeit/Artefakte klein halten und Deploy-Fehler vermeiden.
9) Kleine aber wichtige Sicherheits-/Stabilitätsoptimierungen
- Mail Header Injection Schutz (CR/LF Checks) + RFC2047-Betreff-Encoding:
- Wo:
zgb-backend/Service/MailService.php
- Wo:
- Session-Härtung (Cookie-Flags, SameSite):
- Wo:
zgb-backend/Service/SessionService.php
- Wo:
Warum: verhindert “billige” Angriffe und reduziert Support-Fälle – indirekt auch Performance (weniger Failures/Noise).
Einstieg
Architektur
- Architektur-Übersicht
- Frontend-Build
- Backend
- Datenbank
- Mail-Queue
- Performance & Optimierungen
- Entscheidungen & Historie
Betrieb
Projektarbeit
- Code:
ssh://forgejo@home.schumbi.de/ralf/zgb_www.git - Wiki:
ssh://forgejo@home.schumbi.de/ralf/zgb_www.wiki.git