81 lines
3.8 KiB
Markdown
81 lines
3.8 KiB
Markdown
# search
|
||
|
||
das komplette serverjonas search
|
||
|
||
- `crawler.py` – PostgreSQL-Crawler, baut den Suchindex auf
|
||
- `launcher.py` – startet/überwacht den Crawler und rollt Updates aus (siehe unten)
|
||
- `search/` – Suchmaschine darüber (CLI + HTTP-API), siehe [search/README.md](search/README.md)
|
||
- `web/` – Statistik-Webseite
|
||
- `fix_pages_search_vector_trigger.sql` – einmalig laufen lassen: kappelt den Text,
|
||
der im DB-Trigger `pages_search_vector_update()` in `to_tsvector()` einfließt
|
||
(Postgres hartes Limit: 1.048.575 Bytes pro `tsvector`)
|
||
|
||
## Sehr große Seiten / tsvector-Limit
|
||
|
||
PostgreSQL begrenzt einen einzelnen `tsvector`-Wert hart auf 1.048.575 Bytes
|
||
(nicht konfigurierbar). Der Trigger `pages_search_vector_update()` baut diesen
|
||
aus der `content`-Spalte – Seiten mit sehr viel Text (beobachtet: ~2 MB, z. B.
|
||
`pkg.go.dev/...?tab=importedby`) haben den Insert dadurch mit
|
||
`ProgramLimitExceeded` abgebrochen und alle Worker crashen lassen.
|
||
|
||
Dagegen wirken drei Maßnahmen zusammen:
|
||
|
||
1. **DB-Trigger (empfohlen, einmalig einspielen):**
|
||
`fix_pages_search_vector_trigger.sql` ersetzt die Trigger-Funktion so, dass
|
||
nur die ersten 200.000 Zeichen in `to_tsvector()` einfließen. Der volle Text
|
||
bleibt in `content` gespeichert, nur der Suchindex ist gekürzt. Vor dem
|
||
Einspielen die eigene Funktions-Version prüfen:
|
||
`SELECT prosrc FROM pg_proc WHERE proname = 'pages_search_vector_update';`
|
||
2. **Im Crawler:** `save_page()` kappelt den Inhalt vor dem Insert zusätzlich
|
||
auf `CONTENT_INDEX_MAX_CHARS` (200.000 Zeichen) – wirkt sofort, auch ohne
|
||
Trigger-Fix. Überlange Seiten werden dann normal gespeichert.
|
||
3. **Fehlerbehandlung:** Sollte der Insert trotzdem an einem Datenbank-Limit
|
||
scheitern, fängt der Worker `ProgramLimitExceeded` ab, loggt die URL als
|
||
Warnung, rollt die Transaktion zurück, markiert den Queue-Eintrag als
|
||
`failed` (kein endloses Wiederholen) und läuft weiter, statt zu sterben.
|
||
|
||
Zusätzlich werden bekannt wertlose URLs (`pkg.go.dev/...?tab=importedby` –
|
||
Megabyte-große reine Rückverweis-Listen) über `SKIP_URL_PATTERNS` schon vor dem
|
||
Fetch übersprungen und als `done` markiert.
|
||
|
||
## Rolling Updates (Generation-Mechanismus)
|
||
|
||
Zentrale Tabelle (eine Zeile, `id = 1`):
|
||
|
||
```sql
|
||
CREATE TABLE IF NOT EXISTS deployment_state (
|
||
id INTEGER PRIMARY KEY,
|
||
generation BIGINT NOT NULL,
|
||
required_version INTEGER NOT NULL
|
||
);
|
||
```
|
||
|
||
**Ablauf:**
|
||
|
||
1. Einmalig initialisieren: `python3 crawler.py --init-deployment`
|
||
2. Launcher starten: `python3 launcher.py` (startet + überwacht den Crawler)
|
||
3. Neue Version verteilen:
|
||
- Neue `crawler.py` mit erhöhter `JONASSEARCH_CRAWLER_VERSION` bereitstellen
|
||
(Update-Quelle im `.env` setzen oder Datei nach `releases/<VERSION>/crawler.py` legen)
|
||
- `python3 launcher.py --check-update`
|
||
→ neue Version wird gestaget → geprüft (`--self-update`) → aktiviert
|
||
→ **erst dann** wird die Generation in PostgreSQL erhöht (`--update-deployment`)
|
||
4. Alle laufenden alten Crawler merken das beim nächsten sicheren Punkt
|
||
(vor jedem relevanten Commit bzw. spätestens nach
|
||
`JONASSEARCH_UPDATE_CHECK_SECONDS` im Hintergrund-Check),
|
||
rollen ihre offene Transaktion sauber zurück und beenden sich mit
|
||
**Exit-Code 42** – nie mitten in einer Transaktion, kein hartes Killen.
|
||
5. Der jeweilige Launcher erkennt 42 und startet die bereits bereitgestellte
|
||
neue Version, die die neue Generation beim Start liest.
|
||
|
||
Exit-Codes des Crawlers: `0` = normal beendet, `1` = Fehler/Crash,
|
||
`42` = ausschließlich „Update erforderlich".
|
||
|
||
Nützliche Befehle:
|
||
|
||
```bash
|
||
python3 crawler.py --version # installierte Version
|
||
python3 launcher.py --status # Generation + Versionen anzeigen
|
||
python3 crawler.py --self-update 2 # gestagte Release prüfen (ohne zu aktivieren)
|
||
python3 crawler.py --update-deployment 2 # Generation+1 und required_version=2 setzen
|
||
``` |