Lange laufende Sidekiq-Jobs starten internen Code neu

Ich erstelle derzeit ein Plugin im Zusammenhang mit meritmoot.com (das sich in der Entwicklung befindet und Rolling Releases nutzt). Aufgrund der Nutzung externer Daten verwende ich einen langlaufenden Sidekiq-Job. Leider startet der Job aus irgendeinem Grund den internen Job-Code immer wieder neu, ohne dass der Job fehlschlägt oder eine Fehlerausgabe erfolgt. Übersehe ich etwas in Sidekiq, das einen Neustart auslösen könnte?

Bei meinem letzten Job (der nur einmal täglich ausgeführt werden soll), der sich auf Roll Calls bezieht, wurde der Job in folgenden Intervallen ohne Fehler neu gestartet:

0h-58m-42s (Startzeit der ersten Iteration)
1h-30m-12s (erster Neustart)
2h-1m-1s (zweiter Neustart)
3h-46m-49s
4h-17m-11s
4h-47m-33s

Der Job wurde nicht abgeschlossen, und der gesamte Fortschritt wurde neu gestartet. Es ist erwähnenswert, dass ich einen eigenen Logging-Prozess habe, der stderr und stdout für meinen internen Code umleitet, obwohl ich bezweifle, dass dies Sidekiq beeinträchtigen würde. (Fragen Sie mich gerne, falls Sie einen Blick darauf werfen möchten – es ist sehr nützlich für die Entwicklung!)

Es wäre möglich, den im Code erzielten Fortschritt zu speichern, aber ich bevorzuge einen einfacheren Prozess, da dies Overhead erzeugt. Übersehe ich etwas in Sidekiq, das dazu führen könnte, dass mein Code einen Neustart auslöst?

Du möchtest also einen Sidekiq-Job, der absichtlich länger als eine Stunde pro Ausführung benötigt? Kannst du etwas genauer erklären, warum?

2 „Gefällt mir“

Ich verarbeite eine große Menge externer Daten und speichere sie intern auf meiner Website. Die Daten betreffen Informationen zum US-Kongress.

edit: wie öffentlich zugängliche Gesetzesentwürfe, Abstimmungsergebnisse und Mitgliederinformationen

1 „Gefällt mir“

Läuft dir der Speicher aus? Bei unserem Hosting ist Sidekiq so konfiguriert, dass er sich automatisch neu startet, wenn er zu viel Speicher verbraucht.

2 „Gefällt mir“

Hier sind einige Ideen:

  • Schieben Sie die Daten über die API (aber dann stoßen Sie auf Ratenbegrenzungen)
  • Führen Sie ein externes Skript aus, insbesondere wenn es nur darum geht, den ersten Datenbatch zu ziehen und Updates weniger Zeit in Anspruch nehmen werden

Meinst du Speicher im Sinne von Datenbank- oder Festplattenspeicher? Ja, ich verbrauche davon aktuell sehr viel. Ich prüfe derzeit eine leicht abgewandelte Variante des Vorschlags von @pfaffman, bei der ich den Prozess fork, sodass der Hauptthread beendet werden kann, während ein Kindprozess mit demselben Kontext gestartet wird (im Wesentlichen ein externes Skript in Bezug auf Sidekiq).

1 „Gefällt mir“

Ich teste das Muster von https://stackoverflow.com/questions/806267/how-to-fire-and-forget-a-subprocess#806326, um das Problem zu lösen.

pid = Process.fork
if pid.nil? then
  # Im Kindprozess
  exec "whatever --take-very-long"
else
  # Im Elternprozess
  Process.detach(pid)
end

Es ist ein etwas seltsames Problem, aber die API, mit der ich verbinde, bietet keine Aktualisierungsfunktion. Deshalb lade ich im Grunde täglich große Teile der API erneut herunter, um die Daten zu aktualisieren :expressionless: :man_shrugging:

Ich gebe dir in ein paar Stunden Bescheid, wie es läuft.

Edit: Es ist wieder gestoppt – ich denke, ich werde meinen Fortschritt regelmäßig speichern und nach Möglichkeiten suchen, es effizienter zu gestalten.

1 „Gefällt mir“

Anstatt dies zu tun, warum nicht deinen Job so anpassen, dass er in kleineren Schritten arbeitet? Muss er wirklich vier Stunden in Anspruch nehmen? Synchronisiere 10 Themen, dann weitere 10 … und so weiter.

Sidekiq verfügt über keine Funktion, die langlaufende Jobs beendet. Neuinstallationen der App führen dies durch, ebenso wie Upgrades über die Web-Oberfläche.

4 „Gefällt mir“

Vielen Dank für all die Hilfe!

Ich habe am Ende den Prozesscode entfernt, da er nicht effektiv war. Der Neustart-Job hat lediglich ein zugrunde liegendes Problem der Ineffizienz überdeckt :sweat_smile:. Stattdessen habe ich Folgendes umgesetzt:

  • Schreiben von Bulk-SQL-Code (der deutlich schneller ist als sequenzielle Verarbeitung), um zu erkennen, wann ein Update wirklich erforderlich ist, und um die Verwendung der PostRevisor-Klasse für das erneute Aktualisieren unveränderter Elemente zu überspringen
  • Steigerung der Effizienz bei der Datenabfrage über HTTP durch persistente Verbindungen und andere Datenpunkte, einschließlich komprimierter Elemente (wo möglich)

Das Schreiben von Bulk-SQL-Befehlen hat eine enorme Geschwindigkeitssteigerung gebracht. Was ich aktualisiere, sind:

post-Tabelle: cooked, last_updated-Spalten
topic-Tabelle: title, last_updated-Spalten

Meine nächste Idee ist es, PostRevisor vollständig zu überspringen, indem ich etwas in der Art mache:

1 - Daten in eine Temporärtabelle verschieben

2 - UPDATE topics FROM temp_table
      SET topics.title = temp_table.title, topics.last_updated = temp_table.last_updated
      WHERE topics.id = temp_table.id AND topics.title != temp_table.title

3 - UPDATE posts FROM temp_table
        SET posts.raw = temp_table.raw, posts.last_updated = temp_table.last_updated
        WHERE posts.id = temp_table.id AND posts.raw != temp_table.raw

4 - Anschließend den Such-Neuindexierungsjob auslösen, da sich Titel und Inhalt geändert haben.

Übersehe ich etwas? Discourse ist komplex, und wenn ich PostRevisor überspringe, befürchte ich, dass ich Tabellen berühren könnte, mit denen ich keine Erfahrung habe (post_stats, post_timings, post_uploads, quoted_posts sind einige, die ich in der Datenbank sehe). Andererseits benötige ich nicht alle Validierungen, die PostRevisor bietet, da das System diese Revisionen aus einer vertrauenswürdigen, vorhersehbaren Quelle erhält. Die Lösung scheint ziemlich ungewiss zu sein.

Was denkst du?

1 „Gefällt mir“

Update – Ich habe einige Code-Checks durchgeführt, da es im Laufe der Zeit eine ungewöhnlich hohe Anzahl von Updates gab, und festgestellt, dass etwas nicht gerechtfertigte Updates bei Datenpunkten verursacht, deren rohes JSON-Format sich tatsächlich nicht geändert hat. Sobald dieser Fehler behoben ist, wird das oben Genannte wahrscheinlich nicht mehr notwendig sein :tipping_hand_man:. Ich hätte testen sollen … das hätte mir eine Menge Zeit gespart. Ich werde das oben Genannte trotzdem vielleicht noch ausprobieren. Es wird aber keine Priorität haben. Es wird jedoch bei schnellen Updates helfen, wenn ich das Format der Datendarstellung ändere. Außerdem ist es bereits geschrieben, nur noch nicht getestet.

1 „Gefällt mir“

Der Code für das Massen-Update ist fertig. Wären Sie daran interessiert, diesen in einen bestimmten Branch zu pushen, sobald er stabiler ist? Der Anwendungsfall ist zwar recht spezifisch, aber er kann Tausende von Datensätzen schnell aktualisieren, einschließlich Tags. Er wurde so entwickelt, dass er TopicsBulkAction erweitert. Hier ist das Readme, das ich geschrieben habe, falls Sie detailliertere Informationen wünschen:

  # Eingabe
  #   - Liste von Hashes, die cooked, post_id, topic_id, title, updated_at und tags enthalten (raw verweist auf cooked)
  #   [{post_id: #, cooked: "", topic_id: #, title: "", updated_at: date_time, tags: [{tag: "", tagGroup: ""}, ... ] }, ... ]
  #   - category_name, der Name der Kategorie, die aktualisiert wird. Dies wird für die Suchindexierung verwendet.

  # Optionale Hash-Attribute, die in den Listenelementen enthalten sein können:
  #   - raw: Falls nicht enthalten, entspricht dies cooked.
  #   - fancy_title: Falls nicht enthalten, entspricht dies title.
  #   - slug: Falls nicht enthalten, wird aus title abgeleitet (dies ist mit der URL verknüpft).

  # Anwendungsfall: Effiziente regelmäßige Aktualisierung von Topics aus einer sich ändernden Nicht-Discourse-Datenquelle, um Informationen zu spiegeln. Beachten Sie, dass dies nicht für das allgemeine Erstellen von Beiträgen oder Topics gedacht ist, sondern ausschließlich für die Aktualisierung des Topic-Titels und des HAUPT-Beitrags des Topics. Für allgemeine Beitragskorrekturen wenden Sie sich an PostRevisor in lib/post_revisor.rb.

  # - Geht von vorab zubereitetem (pre-cooked), benutzerdefiniert zubereitetem oder unverändertem Inhalt aus. Die Daten werden nicht validiert.
  # - Beiträge sollten bei der Erstellung cook_methods: Post.cook_methods[:raw_html] gesetzt haben, wenn raw == cooked ist.
  #     Dies ist erforderlich, wenn Sie benutzerdefiniertes HTML zur Anzeige innerhalb des Beitrags schreiben.
  #     Andernfalls könnte Discourse dies in Zukunft erneut zubereiten, was problematisch wäre. Stellen Sie sicher, dass die Informationsquelle vertrauenswürdig ist und deren Inhalt escapet wurde.
  # - Falls das oben Genannte nicht ideal ist, stellen Sie sicher, dass Sie raw enthalten, den korrekten cook_method bei der Erstellung des Beitrags festlegen (falls das System es erneut zubereitet), raw durch Ihre gewählte cook_method verarbeiten und sowohl raw als auch das ausgegebene cooked in Ihren Hashes enthalten.
  # - Verfolgt die Wortanzahl, indem es Unterschiede zwischen der Wortanzahl vor und nach der Aktualisierung des Beitrags ermittelt und diese an das Topic weitergibt.
  # - Verfolgt die Tag-Anzahl auf ähnliche Weise.
1 „Gefällt mir“