Probleme bei der Migration von S3

When migrating from S3 to local storage, we see a number of issues.

The main issue is that the migrate_from_s3 rake task is not taking the Uploads table as a starting point, but the posts. This causes it to skip a lot of uploads which are being left on S3.

  • uploads used as logo’s because they are not referenced in a post
  • uploads for avatars because they are not referenced in a post
  • uploads that are (for some reason) referenced by their CDN URL in raw because they do not match the regex that is being used to identify the uploads
  • uploads that are on non-AWS S3 storage because they do not match the regex because it requires amazonaws in the URL
  • uploads that for some reason do not match the second, different regex (we’re seeing this with non-image uploads like mp3 files, not sure why this is happening)
2 „Gefällt mir“

We’re working on improving the way we associate uploads to posts (by using the upload:// scheme) that will make these storage migrations much more bulletproof. There’s little point to fixing these rake tasks before that’s done.

7 „Gefällt mir“

Ich habe kürzlich einen neuen PR eröffnet, um die Migration von weniger Beiträgen gleichzeitig zu ermöglichen (z. B. zum Testen eines Beitrags nach dem anderen), und Tests für diese Aufgabe hinzugefügt.

Es scheint sinnvoll, mit Beiträgen zu beginnen, da man nach jeder Migration die Beiträge neu rendern muss, um die URL im gerenderten Beitrag zu aktualisieren. Wenn man zuerst alle Uploads migriert und dann mit den Neubearbeitungen beginnt, könnte die Website für potenziell sehr lange Zeit stark beschädigt sein – besonders bei großen Seiten (ich muss etwa 100 GB von S3 auf lokalen Speicher migrieren, daher ist mir das wichtig). Aber mein Ansatz könnte helfen, eine migrate_uploads_from_s3-Aufgabe zu entwickeln, die nach migrate_from_s3 ausgeführt wird, um Uploads zu bereinigen, die nicht zu Beiträgen gehören.

@zogstrip Wie ist der aktuelle Stand von „Wir arbeiten an …“ – ist das noch im Fluss, oder lohnt es sich jetzt, diese Migration genauer zu betrachten?

5 „Gefällt mir“

Wir haben viel an der Upload-Seite gearbeitet, um sicherzustellen, dass wir bessere Verknüpfungen zwischen Beiträgen <=> Uploads haben.

Diese Migrationsaufgaben können jederzeit verbessert werden :+1:

2 „Gefällt mir“

Ich werde herausfinden, ob es sich lohnt, mich nach der Migration meiner Seite in den Nicht-Arbeitszeiten mit Batch-Uploads zu beschäftigen! :smiling_face: Danke für das PR-Review!

1 „Gefällt mir“

Ich habe als Nächstes zwei Punkte anzusprechen: Der erste ist, dass das Limit hilfreich ist, aber nur eine Begrenzung des Suchraums darstellt; als Nächstes möchte ich in der Lage sein, eine maximale Anzahl von zu ändernden Beiträgen anzugeben. Ich werde also sowohl eine maximale Anzahl von zu ändernden Beiträgen als auch ein Limit für die Abfrage hinzufügen.

Außerdem macht es bei der Angabe von max aus Debugging-Gründen Sinn, ausführlich zu beschreiben, was verarbeitet wird. Daher möchte ich die Ausgabe ausführlich gestalten, wenn max nicht nil ist. Dies ermöglicht es den Nutzern, den Prozess vor dem Fortsetzen zu validieren, da dies der Hauptanwendungsfall dieser Arbeit ist.

Ich denke, ich werde max als erstes Argument und ein optionales limit als zweites Argument angeben, da max tatsächlich das Wichtigste ist; das limit dient nur dazu, „einen Ping nur

4 „Gefällt mir“

@zogstrip Hast du kurz Zeit, diesen PR zu prüfen, da du Kontext von deiner letzten Prüfung in diesem Bereich hast? FIX: Make migrations from S3 more robust; fix bare URL migration by johnsonm · Pull Request #10093 · discourse/discourse · GitHub

Ich habe darin die Fixes enthalten, die ich während dieser relativ großen Migration durchgeführt habe. Ich habe nicht versucht, für jeden Fix Tests hinzuzufügen; ich bin mir nicht sicher, wie man jede Form von Fehler injizieren kann. Aber zumindest ist die neue Funktionalität getestet.

@RGJ Ich denke, dass mein PR in seinem aktuellen Zustand alle deine ersten beiden Punkte außer dem CDN-Aspekt abdecken könnte. Ich bin mir jedoch beim CDN nicht sicher. Meine Seite nutzte ein CDN und migrierte Videos, die CDN-URLs hatten, aber das könnte ein Nebeneffekt der Namensgebung mit Discourse Spaces gewesen sein. Falls du weitere Fälle hast, wird mein PR hoffentlich eine einfache Grundstruktur bieten, um die Regex und Testfälle für zusätzliche Varianten hinzuzufügen.

Ich denke, es ist richtig, zuerst nach Posts zu migrieren, da nach dem Migrieren der Uploads in einem Post dieser neu gerendert (rebaked) werden muss, damit der gerenderte Post die richtigen URLs enthält. Nachdem ich meine Posts migriert habe (was jetzt, da ich meine Rate-Limiting-Logik geändert habe, um direkt die Queue-Länge zu prüfen, weniger als zwei Wochen dauern könnte), werde ich mich um die verbleibenden Aufräumarbeiten kümmern.

Da mehrere Posts Referenzen auf denselben Inhalt teilen können, falls mehr als eine Person dieselbe Datei hochlädt, ist ein zweiter Durchgang erforderlich, der die gerenderten Daten auf alte URLs prüft und diese Posts neu rendert, um die neuen Speicherorte zu übernehmen. Dabei kann dieselbe Rate-Limiting-Logik verwendet werden, um Queues nicht zu überlasten.

Bei makerforums werde ich wahrscheinlich keine defekten Logos sehen, da wir das Branding angepasst haben, nachdem wir keine neuen Inhalte mehr in „s3

5 „Gefällt mir“

Nun, das ist schon eine ganze Weile her…

Ich bin gespannt, wie das gelöst wird. Momentan habe ich mehrere Seiten in einem Multisite-Setup, die Bilder von S3 abrufen müssen (derzeit sind meiner Meinung nach einige Bilder lokal gespeichert und andere auf S3) und diese dann auf S3 hochladen.

1 „Gefällt mir“

@pfaffman Es ist wahrscheinlich gut, dass ich zu Beginn meines Migrationsprojekts noch nicht wusste, was ich mittlerweile herausgefunden habe. Es sieht so aus, als wäre alles in wenigen Stunden migriert worden, wenn ich den Minio-Client verwendet hätte, um alle S3-Bucket-Inhalte in den lokalen Upload-Ordner zu kopieren, alle Upload.url-Felder in der Rails-Konsole auf nil gesetzt und die Seite neu gebaut hätte – ohne dass alle Bilder neu generiert werden mussten. (Stattdessen bin ich durch die erneute Ausführung aller Bildkonvertierungen limitiert, als wäre die lokale CPU günstiger als das einfache Kopieren der bereits verarbeiteten Bilder aus S3.)

Und wenn es so einfach gewesen wäre, hätte ich keine dieser Arbeiten investiert, um Migrationen im Allgemeinen zuverlässiger zu machen, und niemand sonst hätte davon profitiert. :smiling_face:

4 „Gefällt mir“

Ach so. Das klingt genau nach dem, was ich wissen wollte. Vielleicht probiere ich das mal aus.

1 „Gefällt mir“

Ich würde… auf jeden Fall ein Backup erstellen, bevor du diesen Weg ausprobierst, da ich nur spekuliere. Ich möchte dich nicht in die Irre führen.

Ach ja, noch etwas: Das wäre bei Audio- und Video-Uploads komplett gescheitert, und ich hätte es erst später bemerkt. Dann hätte ich versucht, das Problem zu lösen und benutzerdefinierten Code zu schreiben. Wenn du also Audio- und Video-Uploads hast, solltest du unbedingt dort anfangen. Ohne den offenen PR, der erst nach dem Release von 2.5 gemergt wird, funktioniert es nicht richtig, da @sam ihn mit 2.6 markiert hat.

2 „Gefällt mir“

Entschuldige bitte, dass ich so spät in das Gespräch einsteige. Ich habe mir gerade deinen PR angesehen und frage mich, warum du das Upload-Modell neu erstellst, anstatt einfach dessen URL zu ändern? Und warum nicht einfach durch die OptimizedImages iterieren und dasselbe tun?

2 „Gefällt mir“

@RGJ Ich habe das überhaupt nicht geändert; das Neu-Erstellen von Upload war eine Fehlerbehebung von @zogstrip unter Cannot execute the rake uploads:migrate_from_s3 - #11

Ich versuche lediglich, den bereits vorhandenen Code zum Laufen zu bringen, und kenne die Discourse-Interna nicht gut; ich habe mich hier oft im Dunkeln herumgetastet. Meine einzige Ruby-Erfahrung sind die wenigen PRs, die ich für Discourse eingereicht habe. Dem Muster des bestehenden Codes zu folgen scheint nicht der effizienteste Weg zu sein (siehe meine Diskussion mit @pfaffman oben zum Thema Short-Circuiting); dem stimme ich voll zu. Wie du an der Tatsache erkennen kannst, dass ich heute Morgen noch nicht einmal bemerkt habe, dass OptimizedImages.url ebenfalls auf einen /uploads-Pfad geändert und etag auf nil gesetzt werden muss (und was noch alles), bin ich immer noch am Blindflug.

Ich muss weiterhin zumindest zuerst durch die Beiträge iterieren, um alte, direkte URLs in Beiträgen zu korrigieren. Weitere Korrekturen sind noch nötig, wie etwa das Nicht-Neu-Validieren von Beiträgen und das Nicht-schweigende Unterdrücken von Fehlern. Ich möchte weiterhin ein Rate-Limit einsetzen, um die Auswirkungen auf Live-Systeme zu begrenzen.

Zu deinen ersten beiden Bedenken im Zusammenhang mit Nicht-Post-Updates: Hier ist meine Arbeit im Gange, noch nicht auf einem Live-System getestet (Commit), die vielleicht hilfreich sein könnte. Ich werde sie jedoch erst auf meinem Live-System testen, wenn die Migration der Post-Uploads abgeschlossen ist.

Das Anpassen von etwas, das früher funktionierte, war alles, wozu ich gerade Lust hatte. Wenn du eine schnellere Migration durchführen möchtest, bin ich dafür; es könnte Sinn ergeben, meine langsamere – aber zumindest bessere – Arbeit als Pareto-Verbesserung zu mergen, und dann könntest du sie komplett durch etwas deutlich Besseres ersetzen. Ich wäre der Erste, der das feiert, selbst wenn ich zu diesem Zeitpunkt nicht mehr in der Lage sein sollte, es zu nutzen.

1 „Gefällt mir“

@mcdanlj Vielen Dank für deine Erklärung. Ich habe zwar eine ehrliche Frage gestellt, wollte aber nicht andeuten, dass es aktuell Probleme gibt. Ich weiß nicht, ob die von mir vorgeschlagene Art überhaupt „besser

3 „Gefällt mir“

@RGJ gleichermaßen, ich bin aufrichtig der Meinung: Wenn jemand mit Sicherheit weiß, wie man das Fiasko des Neuaufbaus aller Bilder vermeidet, bin ich dafür. Und wenn ich ein erfahrener Discourse-Entwickler wäre und wüsste, wie man das einfach umsetzt, hätte ich das wahrscheinlich von Anfang an so gemacht.

Meine Vermutung ist, dass die Migration weg von S3 für diejenigen mit vielen Bildern kein häufiger Fall ist, sodass es sich im Verhältnis zu allgemein nützlicheren Features für Discourse kaum lohnt, die Zeit dafür aufzuwenden. Ich hatte beim Import von Google+ nach MakerForums etwa 100 GB hochgeladene Dateien. Wir entschieden uns für S3 mit der Idee, dass viele Nutzer aus diesen Communities zu MakerForums wechseln könnten. Am Ende zogen jedoch nur wenige Communities aktiv mit, sodass das weitere Wachstum den Verbleib bei S3 letztlich nicht mehr rechtfertigte. Ich gehe davon aus, dass dies ein eher extremer Ausreißer ist und die Neubearbeitung der Bilder nur einmalige Kosten verursacht.

Schließlich bin ich Ihnen sehr dankbar, dass Sie dieses Thema überhaupt eröffnet haben, denn sonst hätte ich nicht-postbezogene Uploads sehr leicht übersehen.

@RGJ Sie haben eindeutig recht, dass das Zerstören und Neuerstellen keine gute Lösung ist. Ich habe eine Prüfung hinzugefügt: raise "Error: upload url #{url} changed to #{new_upload.short_url}, should be unchanged." if url != new_upload.short_url, die zumindest Probleme mit beschädigten Quellen meldet. Der heutige Ausfall von Digital Ocean Spaces hat dazu geführt, dass mir beschädigte Daten gesendet wurden, wodurch dieser Fehler bei vielen Uploads ausgelöst wurde – ich weiß nicht, wie viele. Da jedoch der alte Upload bereits zerstört war, habe ich nun einige beschädigte Seiten. Mir ist das erst aufgefallen, nachdem ich den Scrollback mit den Logs verloren hatte, die mir mitteilten, welche Seiten beschädigt sind. Daher kann ich sie nicht einmal manuell aus einem Backup reparieren.

Meine Arbeit ist also zwar eine Verbesserung gegenüber dem alten Weg, da sie zumindest einige Fehler meldet, aber es wäre viel besser, die Dateien herunterzuladen, die SHA1-Prüfsummen der heruntergeladenen Dateien zu prüfen und sie dann in lokale Dateien zu schreiben. In der Zwischenzeit habe ich meinen PR so angepasst, dass die Migration bei jedem unbehandelten Fehler gestoppt wird, um Datenverlust zumindest zu begrenzen.

Ich bin immer noch der Meinung, dass meine Arbeit als Pareto-Verbesserung gemergt werden sollte, da sie die Situation nicht verschlechtert, sondern verbessert. Es wäre jedoch deutlich besser, es einfach richtig zu machen. Ich denke, das wäre die Erstellung von lib/file_store/from_s3_migration.rb, die parallel zu lib/file_store/to_s3_migration.rb arbeitet, aber das übersteigt derzeit meine Möglichkeiten.

Da mein PR noch nicht überprüft wurde, habe ich noch mehr darauf aufgesetzt. Ich habe eine Aufgabe uploads:report_missing_uploads hinzugefügt, die in raw nach Vorkommen von upload://... sucht, bei denen das Upload-Objekt nicht vorhanden ist. Zumindest in meinem Fall kann ich dank Backups durch diese suchen, die verwaisten Dateien finden und sie auf die Site wiederherstellen, um dann diese Beiträge neu zu rendern und die fehlenden Bilder wiederherzustellen. Ich habe 678 solcher Fälle gefunden, von denen ich bisher alle außer 10 in den Backups wiedergefunden habe. Daher bin ich froh, dass ich diesen Test geschrieben habe! Ich muss das noch erledigen, bevor ich in die Phase übergehen kann, in der ich die restlichen Beiträge neu rendern werde.

Ich habe meine erste Migrationsphase abgeschlossen, ohne die betroffenen Beiträge mit geteilten Uploads neu zu rendern und ohne nicht-postbezogene Uploads. Nach Abschluss eines weiteren Remote-Backups plane ich, diese Funktionen ebenfalls zu testen und sie meinem PR hinzuzufügen.

Ich habe nun mit dem Testen der nächsten Migrationsphasen begonnen: Neu-Rendering und Migration nicht-postbezogener Uploads. Meine erste Lektion war, dass das Neu-Rendering als nächster Schritt aufgrund von Avatar-Bildern in Zitaten, die nicht-postbezogene Uploads betreffen, fehlschlägt. Daher muss das Neu-Rendering der Beiträge der letzte Schritt sein.

Nächste Entdeckung: Danke für die Fremdschlüsselbeschränkungen! Während der Migration nicht-postbezogener Uploads stellte ich fest, dass ich mindestens Profile vor der allgemeinen Migration nicht-postbezogener Uploads migrieren muss.

ActiveRecord::InvalidForeignKey: PG::ForeignKeyViolation: ERROR:  update or delete on table "uploads" violates foreign key constraint "fk_rails_1d362f2e97" on table "user_profiles"

Die Profil-Migration war knifflig, da ich zwei mit dem Profil verbundene Uploads migrieren musste. In einigen Fällen verwendeten die Benutzer jedoch dasselbe Bild für beide, sodass ich diese Arbeit in drei Schritten erledigen musste. Ich habe alle Profile auf meiner Site mit S3-URLs erfolgreich migriert.

Ich habe alle nicht-postbezogenen, nicht-profilbezogenen Uploads migriert. @RGJ, wenn Sie den von mir geposteten Branch testen möchten, ist er mit meiner Arbeit auf dem neuesten Stand. Alles darin wurde bereits für eine abgeschlossene Site-Migration verwendet.

@cvx Ich weiß nicht, wann Sie Zeit für eine Überprüfung haben werden, aber ohne meinen PR ist migrate_from_s3 definitiv voller schweigsamer Datenvernichtungsfehler. Was ich getan habe, ist nicht perfekt, aber es schützt vor vielen Fehlern, auf die ich in der Praxis gestoßen bin.

Ich habe hier all die Arbeit erledigt, die ich derzeit vorhatte. Ich habe meine Site migriert, was bedeutet, dass ich selbst keine effektiv bewerteten Änderungen mehr vornehmen kann. Wenn Sie diesen PR ablehnen, schlage ich dringend vor, stattdessen die bestehende Migration zu löschen, da sie für Benutzer Daten stillschweigend zerstören wird.

Bezüglich des PR: Manchmal treten in CI (in der aktuellen Version) Testfehler auf, ähnlich wie hier:

7867 examples, 0 failures, 11 pending, 1 error occurred outside of examples

##[error]Process completed with exit code 1.

Dies lässt sich bei mir lokal (bisher) nicht reproduzieren, und ich sehe in den CI-Logs keine Informationen darüber, was in CI falsch läuft. Daher werde ich meine Zeit nicht weiter damit verschwenden, CI-Ausgaben nach versteckten Informationen zu durchsuchen.

Eine letzte Anmerkung: Nach Abschluss der Migration stellten wir fest, dass die Profilbilder einiger Benutzer bei der Migration verloren gingen und sie diese manuell wiederherstellen. Ich vermute, dass zusätzlicher Code erforderlich gewesen wäre, um dies erfolgreich abzuschließen. Da ich das jedoch erst zu spät herausfand, habe ich keinen Testfall, um dies zu validieren. Das ist also ein verbleibender Fehler, der in einigen Konfigurationen Probleme verursachen könnte, aber ich bin nicht mehr in der Lage, diese zusätzliche Korrektur zu schreiben.

5 „Gefällt mir“

Da mein PR, der die stille Datenkorruption behebt, die migrate_from_s3 betrifft, noch nicht einmal einen kurzen Blick erhalten hat, kann migrate_to_s3 dann zumindest als Einbahnstraße dokumentiert werden?

Derzeit ist es ehrlich gesagt eine Falle für Anfänger.

@cvx @zogstrip Was ist eure Präferenz? Den PR prüfen oder migrate_from_s3 einfach komplett entfernen und offen zugeben, dass es eine Einbahnstraße ist?

5 „Gefällt mir“

Entschuldigung, hier liegt mein Fehler. Ich werde den PR diese Woche prüfen.

7 „Gefällt mir“

Der Review-Inhalt besagte im Wesentlichen, dass der aktuelle Ansatz so völlig falsch ist, dass er von Grund auf neu und auf eine andere Art und Weise aufgebaut werden muss; dass der Versuch, ihn so zu beheben, wie er zuvor geschrieben wurde, nicht in Ordnung ist. Ich bin nicht bereit, das zu tun, daher bin ich der festen Überzeugung, dass migrate_from_s3 vollständig entfernt werden sollte und migrate_to_s3 als Einbahnstraße gekennzeichnet werden muss, bei der man wirklich sicher sein sollte, dass man sich dauerhaft darauf festlegt. Aktuell ist das Vorhandensein von migrate_from_s3 im Quellcode überhaupt ein Fehler der Datenintegrität in Discourse.

Ich habe während meiner Arbeit am Migrationsprozess kein zeitnahes Review erhalten, und da ich die Migration längst durchgeführt habe (mit einigen fehlerhaften Ergebnissen, wie etwa beschädigten Avataren), verfüge ich über keine sinnvolle Testumgebung mehr. Ich habe null Vertrauen, dass ich es richtig machen würde, daher habe ich mich aus diesem Streit zurückgezogen. Es liegt nun am nächsten Pechvogel, der dachte, es wäre eine gute Idee, Bilder in einem S3-ähnlichen Objektspeicherdienst abzulegen, um das Problem zu lösen. Entschuldigung!

2 „Gefällt mir“

@CxV Da du das vorherige Migration als vollständig falsch durchgeführt und von vorne zu beginnen abgelehnt hast, bitte prüfe und merge diese Entfernung des Datenkorruptions-Fehlers:

2 „Gefällt mir“

Wie bereits erwähnt, bin ich bei der von mir durchgeführten Migration auf einige Probleme gestoßen. Dies stützt @cvxs Aussage, dass ich die Migration völlig falsch geschrieben habe, und solange niemand sie korrekt durchführt, sollte die Entfernung der bestehenden Migration, die voller stiller Datenkorruptionen ist, wirklich zusammengeführt werden. (Es ist ein sehr einfacher Patch, die Überprüfung sollte trivial sein. Je schneller er zusammengeführt wird, desto unwahrscheinlicher ist es, dass jemand seine Website zerstört, indem er versucht, durch eine Einwegtür rückwärts zu gehen. Ich weiß nicht, wie ich noch eindringlicher für eine Zusammenführung plädieren könnte; wenn ich es könnte, würde ich es tun. Edit: Danke @cvx für die Genehmigung, die Zusammenführung und das Follow-up, um sicherzustellen, dass die Massenmigration als irreversibel markiert ist.)

Hier sind die bekannten Probleme, auf die ich gestoßen bin:

  • Zumindest der Discobot-Avatar wurde nicht angezeigt; alle Discobot-Teilnahmen zeigten den generischen Personen-Avatar (hellgrauer Kopf und Schultern auf weißem Hintergrund).
  • Die Avatare einiger anderer Benutzer wurden nicht korrekt migriert. Viele wurden manuell behoben.
  • Während die Website konfiguriert wurde, experimentierte der ursprüngliche Administrator mit Änderungen an der „S3“-Konfiguration (in unserem Fall Digital Ocean Spaces, mit und ohne CDN), was zu einigen verwaisten optimierten Bildern führte, die bei der Migration nicht behoben wurden.

Für alle, die meinen Branch ausprobiert haben und dabei Bilder verloren haben, habe ich einen Hack in der Rails-Konsole ausgeführt, der einige Dinge behoben hat. Dies ist nicht der richtige Weg, dies zu tun; ich kenne das Modell einfach nicht gut genug, um es richtig zu machen. Mein Ruby-Wissen ist sehr schwach; ich berühre Ruby nicht außerhalb meiner kleinen Beiträge zu Discourse.

So wie es ist, hat dieser schreckliche Hack jedoch defekte Bilder (durch die anfänglichen Konfigurationsfehler) repariert und zumindest den Discobot-Avatar wiederhergestellt (und möglicherweise andere, aber alle, von denen ich zuvor wusste, waren bereits repariert).

Was hier nicht zu sehen ist, ist alles, was ich zwischen den Schritten getan habe, wobei ich jede Gruppe, während ich weiterging, betrachtet habe. Dies war Teil einer Session zum „Erkunden, was in der Rails-Konsole vorhanden ist“ und kein Skript, das ich geschrieben und ausgeführt habe. Es gibt keine Fehlerbehandlung, und ich habe vor Beginn dieser Arbeiten ein vollständiges Website-Backup erstellt.

Ich weiß nicht, wie ich deutlicher sagen soll, dass dieser Code nicht die idiomatische Ruby-on-Rails-Methode zur Reparatur ist. Ich wollte nur teilen, wie ich zumindest einen Teil des Schadens behoben habe, den ich mir bei meinem vorherigen Versuch zugefügt habe, meine Website von Digital Ocean Spaces zu migrieren.

require 'set'

uploadids = Set.new
optimizedimages = Set.new
OptimizedImage.where("url like '%UNIQUEPARTOFS3URL%'").each do |oi|
  uploadids.add(oi.upload_id)
  optimizedimages.add(oi)
end

postids = Set.new
uploadids.each do |u|
  PostUpload.where(upload_id: u).each do |pu|
    postids.add(pu.post_id)
  end
end

optimizedimages.each do |oi|
  oi.delete
end

postids.each do |pid|
  Post.where(id: pid).each do |p|
    p.rebake!
  end
end
2 „Gefällt mir“