Probleme beim Hochladen nach der Änderung des DO Spaces Rechenzentrums

Liebe Community,

nachdem ich das Forum nach bestem Wissen und Gewissen durchsucht habe, ohne eine Lösung zu finden, wende ich mich mit einer ungewöhnlichen Situation an den Support, die nach einem kürzlichen Wechsel des DigitalOcean-Rechenzentrums aufgetreten ist.

Wir hatten alle unsere Uploads in einem DigitalOcean Spaces Bucket im Rechenzentrum ams3 gespeichert. Nach zwei schweren Hardware-Problemen und den daraus resultierenden Dienstunterbrechungen innerhalb von etwas mehr als einem Monat haben wir uns letztes Wochenende entschlossen, alle unsere Dateien in das Rechenzentrum fra1 zu verschieben.

Hier sind die Schritte, die ich befolgt habe:

  1. Als Vorbereitung auf den Transfer habe ich alle Dateien, die wir in ams3 hatten (die drei klassischen Verzeichnisse: originals, optimized und tombstone), mit s3cmd in den neuen Bucket in fra1 hochgeladen.
  2. Ich bin in die Forumseinstellungen gegangen und habe den neuen Endpunkt für Anhänge, cdnl und den Sicherungs-Bucket eingestellt.
  3. Ich habe eine vollständige Neubearbeitung aller Beiträge (full post rebake) gestartet, in der Erwartung, dass damit alle Probleme auf einmal behoben werden.

Leider war das nicht der Fall. Die meisten Anhänge wurden korrekt „überführt“, aber einige hundert nicht. Es ist mir nicht klar, was passiert ist, aber diese fehlenden Anhänge wurden in das tombstone-Verzeichnis verschoben.

Ich dachte, dass die Ausführung des Rake-Tasks rake uploads:recover_from_tombstone das Problem beheben würde, aber nein. Die Dateien werden zwar erkannt, aber am Ende des Tasks werden keine Anhänge wiederhergestellt; Bilder sind nach wie vor in den Beiträgen nicht sichtbar.

Ich habe mich etwas tiefer in die Materie eingearbeitet und festgestellt, dass die Ausführung von UploadRecovery.new(dry_run: true).recover (gefunden durch Stöbern im Meta-Bereich) in der Rails-Konsole wertvolle Informationen liefert, wie z. B. die URL des Beitrags sowie die kurze oder lange URL des problematischen Bildes.

Für die in Kurzform zurückgegebenen URLs habe ich ein kleines Python-Skript geschrieben, um den kurzen Upload-Dateinamen in die Langform zu „übersetzen“, damit ich prüfen konnte, ob die Datei im Bucket vorhanden ist.
Ich habe es getan und ich kann bestätigen, dass alle fehlenden Dateien vorhanden sind, sowohl im neuen als auch im alten Bucket. Ein Teil der fehlenden Uploads befand sich erwartungsgemäß im tombstone-Verzeichnis, aber einige andere befinden sich seltsamerweise immer noch im original-Verzeichnis. Die Dateien sind nicht beschädigt. Wenn ich über die URL darauf zugreife, öffnen sie sich in beiden Rechenzentren korrekt, und wenn ich sie lokal auf meinem Linux-Rechner speichere, kann ich sie ohne Fehler öffnen.

Irgendwie scheitert der Upload-Wiederherstellungsprozess daran, diese Dateien zu erfassen und zu beheben, was immer auch in der Datenbank schiefgelaufen ist. :man_shrugging:

Meine Fragen sind also:

  • Gibt es eine Möglichkeit zu verstehen, warum der Rake-Task trotz der Anwesenheit der Upload-Dateien in tombstone (oder original) scheitert, sie wiederherzustellen?
  • Was wäre die richtige Abfolge von Schritten, um sicherzustellen, dass im Falle eines Bucket-Wechsels oder sogar beim Übergang von DO zu einer anderen AWS-kompatiblen Umgebung alle Anhänge korrekt verschoben und für den Wechsel vorbereitet werden? Allgemeiner gefragt: Was sollte man in einem solchen Fall Schritt für Schritt tun? Offensichtlich reicht eine einfache Neubearbeitung (rebake) nicht aus. :confused:
  • Was bewirkt der Task posts:invalidate_broken_images? Was bedeutet invalidate (ungültig machen) genau?

Vielen Dank im Voraus. Ich kämpfe seit einer Woche damit und muss das unbedingt abschließen, sonst werde ich noch verrückt :smiley: :stuck_out_tongue:

Zur Info: Der Vorschlag, alle 800+ Anhänge manuell neu zu laden, wird nicht als gültige Antwort akzeptiert. Es muss einen algorithmischen Grund geben… :laughing:

2 „Gefällt mir“

Ich denke, du hast zwischen Schritt 2 und 3 ein DbHelper.remap('oldbucketurl', 'newbucketurl') übersehen.

4 „Gefällt mir“

Hallo @falco, danke für deine Antwort.
Ja, anfangs habe ich es tatsächlich vergessen.
Ich habe das ausgeführt, nachdem ich es hier auf Meta gefunden habe. :confused: Es hat geholfen, einige Dateien wiederherzustellen.
Ich habe übrigens nach dem Ausführen eine vollständige Neubearbeitung durchgeführt.

Was könnte ich noch versuchen?

1 „Gefällt mir“

Also, ich habe vielleicht einen Hinweis darauf, was hier vor sich geht.
Mir ist nicht eingefallen, einen Fakt bezüglich der Ausgabe der Rake-Task rake uploads:recover_from_tombstone zu erwähnen, der auf einen interessanten Hinweis hindeuten könnte.

Es scheint, als würde die Task die Upload-Dateien im Tombstone tatsächlich finden, warnt mich aber vor etwas (dem vollständigen Dateinamen des Uploads), das falsch zu sein scheint. So ähnlich:

Warning /t/i-miei-modellini-volanti/28272/212 had an incorrect 487b613752a0c338646fecc942512e5de9afeb3f should be c87c4f08d1a9aac3f43d19722cfd5a94f2544272 storing in custom field 'rake uploads:fix_relative_upload_links' can fix this

Wenn ich auf meiner lokalen Kopie der Upload-Verzeichnisse einen find-Befehl ausführe, stelle ich fest, dass ich tatsächlich eine Datei namens 487b613752a0c338646fecc942512e5de9afeb3f.jpeg habe.

Der Shortlink, der zu diesem spezifischen Upload gehört, ist upload://alcIv6jVlmjiEOEBh8fNDJyRms7.jpeg. Wendet man den Base62-Algorithmus an, der den entsprechenden vollständigen Dateinamen berechnet, ergibt sich der Wert 487b613752a0c338646fecc942512e5de9afeb3f – genau der Dateiname, vor dem die Rake-Task recover_from_tombstone warnt, dass er falsch sei. :thinking:

Warum behauptet das Tool, er sei falsch, und sollte stattdessen c87c4f08d1a9aac3f43d19722cfd5a94f2544272 lauten?

Immerhin habe ich die Task rake uploads:fix_relative_upload_links mehrmals ausgeführt und danach erneut rake uploads:recover_from_tombstone gestartet, aber es scheint sich nichts zu ändern.

Edit:
Wenn ich in einem Datenbank-Backup, das ich vor dem Wechsel des Buckets erstellt habe, nach 487b613752a0c338646fecc942512e5de9afeb3f suche, sehe ich, dass der Datensatz in der Tabelle uploads, der zu diesem Bild gehört, exakt diesen Hex-Dateinamen anzeigte. Daher verstehe ich umso weniger, warum die Rake-Task sich darüber beschwert.

image

Dies ist eines der ältesten Missverständnisse auf Meta.
Sie müssen nach einer gut abgestimmten Remap nicht neu berechnen lassen.

2 „Gefällt mir“

Du magst recht haben, aber die Sache ist die: Es ist schwierig, genau zu wissen, was man in solchen Fällen tun und was man lassen sollte, ohne ein Tutorial oder eine Anleitung von den Entwicklern. Man hat immer das Gefühl, etwas übersehen oder in einer anderen Reihenfolge gemacht zu haben – so als müsste man ein funktionierendes Rezept herausfinden, indem man Dutzende von Beiträgen der letzten drei bis vier Jahre durchforstet. :stuck_out_tongue:
Das Neuberechnen (Rebaking) scheint für viele Probleme die Allzwecklösung zu sein und ist für bestehende Beiträge harmlos.

Das ist eine komplizierte Art zu sagen, dass angesichts der vielen Fälle, in denen Nutzer bei der Verwaltung von Uploads und Ähnlichem ins Stolpern gerieten, ein ordentlicher offizieller Leitfaden vom Team eine wichtige Referenz wäre. :wink:

1 „Gefällt mir“

Entschuldigung, ich muss diesen Beitrag wiederherstellen.

Letzte Woche habe ich einige Zeit damit verbracht, den Code der Rake-Tasks für Uploads zu lesen, um zu verstehen, was im Inneren von recover_from_tombstone und recover vor sich geht. Das ist aufgrund der Kapselung der Klassen schwierig, und ich muss sagen, dass ich größtenteils gescheitert bin.

Was ich jedoch verstanden habe (bitte @Falco korrigiere mich, falls ich falsch liege), ist, dass der Dateiname auf der Festplatte eines Uploads durch die Kombination seiner SHA1-Prüfsumme und seiner ursprünglichen Dateierweiterung erstellt wird. Anschließend wird er auf der Festplatte bzw. in AWS in einem Verzeichnis gespeichert, dessen Pfad vom ersten und manchmal zweiten Buchstaben des Namens abhängt, innerhalb von 1X, 2X, 3X usw. (wie diese bestimmt werden, habe ich nicht verstanden).

Schließlich werden die SHA1-Prüfsumme und der Dateiname unter anderem in den Datensätzen der Tabelle uploads in PostgreSQL gespeichert.

Zurück zu dem, was während unseres Wechsels des Digital Ocean-Rechenzentrums passiert ist: Das ist es, was meines besten Wissens nach geschehen ist:

  1. Wir haben alle Dateien von ams3 nach fra1 kopiert.
  2. Wir haben es versäumt, DbHelper.remap('oldbucketurl', 'newbucketurl') durchzuführen, wie von @falco empfohlen, aber es war uns nicht klar, dass wir dies in diesem Fall tun mussten.
  3. Wir haben eine globale Neugenerierung (Rebake) gestartet. In diesem Stadium „brachen

Also, nur um diesen Thread abzuschließen, was vielleicht für jemand anderen nützlich sein könnte: So haben wir die Situation gelöst.

Im Wesentlichen habe ich, da es unmöglich war, die fehlenden Anhänge über die verschiedenen Rake-Tasks zur Wiederherstellung von Uploads wiederherzustellen, ein Ruby-Skript erstellt (Entschuldigung im Voraus, ich bin definitiv KEIN Ruby- oder Rails-Entwickler, also ist der Code wahrscheinlich ineffizient und hässlich, aber das ist nebensächlich :P), das:

  1. Alle Beiträge findet, die den String upload:// enthalten
  2. Den Shortlink jedes Uploads extrahiert und in seine lange SHA1-Hash-Form umwandelt
  3. Die Tabelle „Uploads“ abfragt
  4. Wenn ein Anhang mit dem SHA1-Hash in „Uploads“ gefunden wird, wird dieser Upload übersprungen; andernfalls wird die URL dieses Uploads im alten Digital Ocean Bucket/Spaces überprüft.
  5. Wenn der Link im alten Bucket/Spaces gefunden wird, wird der Shortlink durch die URL zum gleichen Upload im alten Bucket ersetzt.
  6. Falls geändert, wird eine Neubearbeitung (Rebake) des ursprünglichen Beitrags ausgelöst, damit Discourse die schwere Arbeit übernimmt, den „verlorenen“ Upload lokal neu herunterzuladen und alles Notwendige in der Datenbank neu zu erstellen.

Um eine Blacklistierung zu vermeiden und die Last auf dem Server zu reduzieren, wird bei jeder angeforderten Neubearbeitung ein Intervall von 20 Sekunden eingefügt.


def remoteFileExist(url, retries=3)
    puts "Requesting #{url} ..."
    uri = URI(url)
    response = nil
    res = Net::HTTP.get_response(uri)
    puts res['content-type']
    if res.code[0,1] == "2" and res['content-type'].include? 'image'
        return true
    else
        return false
    end
    rescue Net::ReadTimeout => e
        puts "TRY #{retries}/n ERROR: timed out while trying to connect #{e}"
        if retries <= 1
            raise
        end
        remoteFileExist(url, retries - 1)
    end
end


####################################################################


posts=Post.where("raw like '%upload://%' " ).order('topic_id ASC, post_number DESC');
idx = 0;
posts.each do |p|
    idx = idx + 1;
    puts ""

    matches = p.raw.scan(/(!\[(.)*\]\(upload:\/\/([a-zA-Z0-9]+)\.(jpeg|jpg|png|gif|pdf|mp3|mp4|mov)\))/)

    new_raw = p.raw

    matches.each do |m|  
        short_url = m[0];
        short_sha = m[2];
        ext = m[3];
        long_sha = Base62.decode(short_sha).to_s(16).rjust(40,"0")

        upload = Upload.where('sha1 = ?', long_sha)

        puts "#{short_url} -> #{long_sha}\n"

        if upload.all.count == 0
            puts "#{long_sha} not found in DB. Recovering from ams3...\n"

            subdir1 = long_sha[0]
            subdir2 = long_sha[1]

            new_url1 = "https://discourse-data.ams3.digitaloceanspaces.com/original/3X/#{subdir1}/#{subdir2}/#{long_sha}.#{ext}"
            test1 = remoteFileExist(new_url1)
            if test1
                new_raw = new_raw.gsub(short_url, "\n#{new_url1}")
            else
                new_url2 = "https://discourse-data.ams3.digitaloceanspaces.com/original/2X/#{subdir1}/#{long_sha}.#{ext}"
                if remoteFileExist(new_url2)
                    new_raw = new_raw.gsub(short_url, "\n#{new_url2}")
                end
            end
            puts ""
            sleep 5
        end
    end

    if p.raw != new_raw
        puts "OLD\n"
        puts p.raw
        puts "-----------"
        puts "NEW\n"
        puts new_raw
        puts "-----------"
        puts "UPDATING!"
        # goahead = gets
        p.raw = new_raw
        p.cooked = ''
        p.save
        p.rebake!(invalidate_broken_images: true);
        puts "*******************************************"
        sleep 30
    else
        puts "SKIP!"
        puts "*******************************************"
        sleep 1
    end
end

1 „Gefällt mir“