# Der Tick

> Wie 'tickt' das Modul

---

Der **Tick** ist das Herz des Moduls: einmal pro Minute prüft er alle aktivierten Jobs, entscheidet, was fällig ist, und führt es aus. Weder das webtrees-Core noch das Betriebssystem planen selbst — sie starten nur <a href="https://github.com/bschwede/wt-cronjob/tree/main/cli/tick.php" target="_blank" rel="noopener"><code>tick.php</code></a>
.




<pre class="mermaid">flowchart TD
    subgraph Triggers[&#34;`⏱ Auslöser *(once per minute)*`&#34;]
        CRON[&#34;`🕑 OS cron / systemd&lt;br/&gt;*every minute*`&#34;]
        WATCH[&#34;`👁 watch-Daemon&lt;br/&gt;*(in-module)*`&#34;]
    end

    subgraph Guard[&#34;🛡 Guard&#34;]
        TP[&#34;`📄 cli/tick.php · **cron:tick**&lt;br/&gt;*guard(): 403 over HTTP*`&#34;]
        OFF{&#34;`⚠ site offline?&lt;br/&gt;*data/offline.txt*`&#34;}
        BOOT[&#34;`🚀 boot()&lt;br/&gt;*webtrees &#43; DB*`&#34;]
    end

    subgraph Selection[&#34;🔎 Arbeit auswählen&#34;]
        TIME[&#34;`📅 time-based jobs&lt;br/&gt;**next_run_at due** *(UTC)*`&#34;]
        CE[(&#34;`📦 cj_event&lt;br/&gt;*Event-Warteschlange*`&#34;)]
        DRAIN[&#34;`🧮 drain queue&lt;br/&gt;*coalesced · 60 s budget*`&#34;]
    end

    subgraph Execution[&#34;`⚙ Ausführung *(isolated child)*`&#34;]
        RUN[&#34;`🧪 JobRunner&lt;br/&gt;**proc_open**`&#34;]
        LIM[&#34;`⏲ timeout 30–3600 s&lt;br/&gt;last **64 KB** · exit code`&#34;]
    end

    subgraph Storage[&#34;💾 Speicherung&#34;]
        CJRUN[(&#34;`cj_run&lt;br/&gt;*Laufverlauf*`&#34;)]
        MARK[&#34;`🏷 tick.last&lt;br/&gt;*stale &gt; 300 s → tick_stale*`&#34;]
    end

    CRON --&gt; Guard
    WATCH --&gt; Guard
    TP --&gt; OFF
    OFF --&gt;|&#34;offline&#34;| SKIP[&#34;⏭ Tick überspringen&#34;]
    OFF --&gt;|&#34;online&#34;| BOOT
    BOOT --&gt; TIME
    BOOT --&gt; DRAIN
    CE --&gt; DRAIN
    TIME --&gt; Execution
    DRAIN --&gt; Execution
    RUN --&gt; LIM
    LIM --&gt; CJRUN
    BOOT --&gt; MARK

    classDef sourceStyle fill:#e3f2fd,stroke:#1565C0,stroke-width:2px
    classDef queueStyle fill:#fff3e0,stroke:#E65100,stroke-width:2px
    classDef tickStyle fill:#e8f5e9,stroke:#2E7D32,stroke-width:2px
    classDef execStyle fill:#fce4ec,stroke:#C2185B,stroke-width:2px
    classDef storageStyle fill:#f5f5f5,stroke:#616161,stroke-width:1px

    class CRON,WATCH sourceStyle
    class OFF,CE queueStyle
    class TP,TIME,DRAIN tickStyle
    class RUN,LIM execStyle
    class CJRUN,MARK,SKIP storageStyle
</pre>

## Was einen Tick startet

Einen Trigger wählen (einmalig):

- **OS-Cron** — `* * * * *` vom webtrees-Root aus (siehe [Installation](setup/installation)).
- **systemd-Timer** — die generierte Unit von der Modul-Admin-Seite.
- **watch-Daemon** — der residente Prozess im Modul, der <a href="https://github.com/bschwede/wt-cronjob/tree/main/cli/tick.php" target="_blank" rel="noopener"><code>tick.php</code></a>
 alle 60 s neu startet und FPM-Worker-Recycling überlebt ([Installation](setup/installation), die auch das Lifecycle-Diagramm des Daemons trägt).

Alle drei führen denselben Befehl aus: `php modules_v4/cronjob/cli/tick.php cron:tick`.

## Was ein Tick tut

1. **Guard** — `CliBootstrap::guard()` antwortet `403`, falls das Skript jemals über HTTP erreicht wird; danach lädt `CliBootstrap::boot()` webtrees + die Datenbank.
2. **Offline-Check** — wenn `data/offline.txt` existiert, wird der Tick komplett übersprungen, vor jedem Datenbankzugriff — z. B. während eines webtrees-Updates, wenn Schema und Code im Fluss sind.
3. **Arbeit auswählen** — zwei Quellen, in einem Durchlauf kombiniert:
   - **zeitbasierte Jobs**, deren `next_run_at` verstrichen ist (Cron-Zeitpläne, **UTC**); die fälligen Trigger werden relativ zum letzten Lauf berechnet (bzw. zur Erzeugung bei nie gelaufenen Jobs).
   - **Event-Jobs** — die `cj_event`-Warteschlange wird von der ältesten zuerst geleert, **gekoppelt auf einen Lauf pro Ereignisnamen**, innerhalb eines **60-Sekunden-Budgets**. Event-Jobs erhalten den Ereignisnamen und das Payload über die Umgebungsvariablen `CRONJOB_EVENT` / `CRONJOB_EVENT_PAYLOAD`. Siehe [Event-Pipeline](events).
4. **Jeden Job isoliert ausführen** — `JobRunner` validiert den Befehl (ein `modules_v4/<module>/cli/<script>.php`-Pfad oder einer der vier erlaubten Core-Befehle; höchstens 10 Argumente) und startet ihn als **Kindprozess** mit hartem **Timeout** (30–3600 s). Es werden nur die **letzten 64 KB** der Ausgabe erfasst; ein Job, der seinen Timeout erreicht, wird beendet ([`proc_terminate`](https://www.php.net/manual/de/function.proc-terminate.php)) und als Exit `-9` gemeldet.
5. **Protokollieren** — jeder Lauf wird in `cj_run` geschrieben (Status, Exit-Code, Dauer, der auslösende Trigger, erfasste Ausgabe) — das ist die [Ausführungshistorie](usage). Event-gesteuerte Läufe werden zusätzlich über `cj_event_run` verknüpft.
6. **Marker** — der Tick schreibt `data/cronjob/tick.last`. Ist dieser Marker älter als 300 s, meldet das Modul sich im Admin-Status als `tick_stale`.

## Manuelle Ausführung

Den Tick kann man auch manuell ausführen — `--dry-run`, `--job=<name>`, `--strict` und `--full-output` sind auf der Seite [CLI-Skripte](cli-scripts) beschrieben. Achtung: `--full-output` kann personenbezogene Daten offenlegen — nur verwenden, wenn der Ausgabeort vertrauenswürdig ist.
