Bisher lief pro Seitenaufruf ein UPDATE press_releases SET hits = hits + 1 im Request. Auf der über 1 GB großen Tabelle blockiert dieser Schreibzugriff, sobald dort eine Schemaänderung läuft — die wartenden Requests belegten dabei PHP-FPM-Worker und DB-Verbindungen, bis die ganze Seite stand. Der Request schreibt jetzt nur noch in die kleine Append-Tabelle press_release_events, und selbst dabei wird jeder Fehler geschluckt und gemeldet: Ein Aufruf-Zähler darf eine Seite niemals langsamer oder kaputt machen. Die Summierung in press_releases.hits übernimmt gebündelt der Scheduler. Der Fortschritt läuft über eine Wasserstandsmarke (höchste bereits verbuchte Event-ID) statt über einen Zwischenspeicher, damit bei einem Neustart kein Aufruf verloren geht. Zähler und Marke werden je Durchgang gemeinsam committet — sonst wäre nur der Durchgang wiederanlaufsicher, nicht der Abbruch darin, und der nächste Lauf verbuchte dieselben Aufrufe doppelt. Eine Migration setzt die Marke beim Deploy auf den Ist-Stand; ohne sie würde der erste Lauf den gesamten Altbestand ein zweites Mal aufaddieren. Weil das Log damit mit dem Traffic wächst und als einzige Log-Tabelle des Systems keine Bereinigung hatte, begrenzt press-releases:purge-events es auf einen Aufbewahrungszeitraum (Default 395 Tage = 13 Monate, damit der Vorjahresmonats-Vergleich möglich bleibt). Gelöscht wird ausschließlich unterhalb der Wasserstandsmarke — alles darüber ist noch nicht in hits verbucht und wäre sonst dauerhaft verloren. Fehlt die Marke, bricht der Lauf ab, statt zu raten. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
59 lines
1.6 KiB
PHP
59 lines
1.6 KiB
PHP
<?php
|
|
|
|
namespace App\Models;
|
|
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Support\Facades\Cache;
|
|
|
|
/**
|
|
* Laufzeit-Einstellungen mit Admin-UI (Key-Value, JSON-Wert, gecacht).
|
|
* Erster Nutzer: Schalter „KI-Prüfung Legacy-API" (10.07.2026).
|
|
*/
|
|
class AppSetting extends Model
|
|
{
|
|
protected $primaryKey = 'key';
|
|
|
|
protected $keyType = 'string';
|
|
|
|
public $incrementing = false;
|
|
|
|
protected $fillable = ['key', 'value'];
|
|
|
|
/**
|
|
* @return array<string, string>
|
|
*/
|
|
protected function casts(): array
|
|
{
|
|
return ['value' => 'json'];
|
|
}
|
|
|
|
public static function getValue(string $key, mixed $default = null): mixed
|
|
{
|
|
return Cache::rememberForever(
|
|
"app-setting.{$key}",
|
|
fn (): mixed => static::query()->find($key)?->value,
|
|
) ?? $default;
|
|
}
|
|
|
|
public static function getBool(string $key, bool $default = false): bool
|
|
{
|
|
return (bool) static::getValue($key, $default);
|
|
}
|
|
|
|
public static function setValue(string $key, mixed $value): void
|
|
{
|
|
static::query()->updateOrCreate(['key' => $key], ['value' => $value]);
|
|
static::forgetCached($key);
|
|
}
|
|
|
|
/**
|
|
* Verwirft den zwischengespeicherten Wert. Nötig überall dort, wo an
|
|
* `app_settings` vorbei geschrieben wird — insbesondere in Migrationen,
|
|
* die den Query Builder benutzen: `getValue()` cacht dauerhaft
|
|
* (`rememberForever`), ein Direktschreiber bliebe sonst unsichtbar.
|
|
*/
|
|
public static function forgetCached(string $key): void
|
|
{
|
|
Cache::forget("app-setting.{$key}");
|
|
}
|
|
}
|