Files
search/README.md
T
2026-09-13 18:01:58 +02:00

3.8 KiB
Raw Blame History

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
  • 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):

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:

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