Skip to content

rebuild_indices scheiterte seit Januar jede Nacht an sechs Indizes #447

Description

@zdavatz

jobs/rebuild_indices meldete jede Nacht Built 29 indices. Failed building 6 indices und beendete sich mit exit 1 — an allen 225 Nächten, die das Debug-Protokoll noch abdeckt (01.01.2026 bis 26.08.2026; weiter reicht es nicht zurück).

Unbemerkt, weil die Cron-Zeilen bis zum 24.08.2026 auf >/dev/null 2>&1 endeten. substance_index_atc hatte gar keine Tabelle mehr — der Index wird vor dem Neuaufbau gelöscht, und der Aufbau kam nie durch.

Drei Ursachen, die nichts miteinander zu tun haben

1. Latin-1 in searchterms.rb — vier Indizes

substance_index, substance_soundex_index, doctor_index, hospital_index

incompatible encoding regexp match (UTF-8 regexp with ISO-8859-1 string)
  src/util/searchterms.rb:85:in 'String#gsub'

Der Schutz prüfte auf ASCII_8BIT und auf valid_encoding?. Bei ISO-8859-1 sagen beide „in Ordnung":

s = "Bef\xFCllung".force_encoding("ISO-8859-1")
s.encoding == Encoding::ASCII_8BIT   # => false
s.valid_encoding?                    # => true   <- in Latin-1 ist jede Bytefolge gueltig
s.gsub(/[[:punct:]]/u, "")           # => Encoding::CompatibilityError

Hier muss umgerechnet werden, nicht umetikettiert — force_encoding ist nur bei ASCII_8BIT richtig, wo UTF-8-Bytes ohne Etikett liegen. Der neue to_utf8 behandelt zusätzlich eingefrorene Strings, die der alte Schutz komplett übersprang (unless term.frozen?, weil force_encoding in place mutiert hätte).

Ein fünfter Index hing am selben Ort: indication_index scheiterte an "\xC3" from ASCII-8BIT to UTF-8.

2. Ein einziges kaputtes Objekt — zwei Indizes

substance_index_atc, substance_index_sequence

undefined method 'substance' for an instance of Array
  odba-1.1.9/lib/odba/stub.rb:115:in 'ODBA::Stub#method_missing'

Composition 58333159 von Dermophil MED Lippenbalsam (25790/02) hielt ein leeres Array dort, wo ein ActiveAgent hingehört. Beide Definitionen rufen .substance auf jedem Element von active_agents — ein kaputtes Element kostet also den ganzen Index, nicht bloss den einen Eintrag.

Eines von 41 801. Und es tarnt sich gut:

odba_id=58333163
  is_a? ActiveAgent      : true     <- Stub antwortet aus der Deklaration, ohne aufzuloesen
  respond_to? :substance : false    <- respond_to? loest auf und sagt die Wahrheit
  odba_instance          : Array (leer)

Der Composition-Text nennt, was fehlte:

Quelle : balsamum peruvianum 5 mg, levomenolum 2 mg, salolum 10 mg, ...
heil   : 1:Levomenolum, 2:Salolum
kaputt : 0:58333163 (Array)

jobs/repair_broken_active_agents stellt solche Elemente aus dem Text wieder her — über die Position, nicht per Regex-Rateverfahren, und es fasst nichts an, wenn die heilen Nachbarn nicht dort stehen, wo der Text sie erwartet. Balsamum Peruvianum 5 mg ist wieder da.

Eine Falle dabei: Array#delete vergleicht mit ==, das geht bei einem Stub durch ActiveAgentCommon#== und ruft .substance auf dem Gegenüber — also genau auf dem kaputten Element. Man kann es nicht per Wert löschen. object_id ist der Weg; es steht auf ODBAs no_override-Liste (stub.rb:83), equal? nicht und würde auflösen.

3. Eine Methode, die es auf der Klasse nicht gibt

index_name: 'substance_index_sequence'
origin_klass: 'ODDB::Substance'
resolve_target: 'active_sequences'      # <- gibt es nur auf OddbPrevalence
init_source:    'self.active_sequences' # <- hier richtig, self ist die Anwendung

Substance hat sequences, kein active_sequences. Der Aufruf lief über SimpleLanguage#method_missing ins Leere — und zwar auch beim normalen Speichern einer Substanz, nicht nur beim Neuaufbau.

Behoben

Beide resolve_origin-Ausdrücke sind jetzt abgedichtet, damit ein einzelnes kaputtes Element nie wieder einen ganzen Index kostet.

Alle sieben bauen durch, null Fehler:

Index Dauer
substance_index_atc 313 s
substance_index 86 s
substance_index_sequence 366 s
substance_soundex_index 98 s
indication_index 32 s
doctor_index 439 s
hospital_index 5 s

Regressionstests: test/test_util/searchterms.rb (9 runs).

Die Lehre

Ein Job, der jede Nacht mit exit 1 endet und dessen Ausgabe niemand liest, ist genau so gut wie kein Job. Dieselbe Sorte Befund wie die drei toten Rack-Backends im August und der SL-Import, der zwölf Tage lang nichts tat und exit 0 meldete. Seit dem 24.08. schreibt bin/oddb_cron nach log/cron/<job>-<YYYY-MM>.log — deshalb ist das hier überhaupt aufgefallen.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions