Problemi con i caricamenti dopo il cambio del datacenter DO Spaces

Gentili tutti,
dopo aver cercato nel forum con le migliori mie capacità senza trovare una risposta risolutiva, chiedo supporto per una situazione strana emersa dopo un recente cambio di datacenter di Digital Ocean.
Quindi, avevamo tutti i nostri caricamenti archiviati in un bucket Digital Ocean Spaces, nel datacenter ams3.
Dopo due grossi problemi hardware e la conseguente interruzione del servizio in poco più di un mese, lo scorso weekend abbiamo deciso di spostare tutti i nostri file nel datacenter fra1.

Ecco i passaggi che ho seguito:

  1. In preparazione al trasferimento, ho caricato tutti i file presenti su ams3 (le 3 classiche cartelle originals, optimized e tombstone) nel nuovo bucket su fra1 utilizzando s3cmd.
  2. Sono andato nelle impostazioni del forum e ho impostato il nuovo endpoint per gli allegati, cdnl e il bucket di backup.
  3. Ho lanciato un completo “re-bake” dei post, aspettandomi che sistemasse tutto in un colpo solo.

Purtroppo non è stato così. La maggior parte degli allegati è stata “trasferita” correttamente, ma alcune centinaia no. Non è chiaro cosa sia successo, ma questi allegati mancanti sono stati spostati nella directory tombstone.

Pensavo che lanciare il task rake rake uploads:recover_from_tombstone avrebbe risolto il problema, ma non è stato così. I file vengono rilevati, ma alla fine del task nessun allegato viene recuperato e le immagini non sono ancora visibili nei post.

Ho iniziato a scavare un po’ più a fondo e ho scoperto che eseguendo UploadRecovery.new(dry_run: true).recover (trovato esplorando meta) nella console di rails mi forniva informazioni preziose, come l’URL del post nonché l’URL breve o lungo dell’immagine problematica.

Per gli URL restituiti nella forma breve, ho quindi scritto un po’ di codice Python per “tradurre” il nome breve del file caricato nella forma lunga, in modo da poter verificare la presenza del file nel bucket.
L’ho fatto e posso confermare che tutti i file mancanti sono lì, sia nel nuovo bucket che in quello vecchio. Parte dei caricamenti mancanti li ho trovati nella directory tombstone, come previsto, ma alcuni altri si trovano stranamente ancora nella directory original. I file non sono corrotti. Se vi accedo tramite URL si aprono correttamente in entrambi i datacenter, e se li scarico localmente sulla mia macchina Linux posso aprirli senza errori.

In qualche modo il processo di recupero dei caricamenti non riesce a individuarli e a sistemare ciò che è andato storto nel DB. :man_shrugging:

Quindi le mie domande sono:

  • c’è un modo per capire perché, anche se i file caricati sono in tombstone (o in original), il task rake non riesce a recuperarli?
  • qual è la sequenza corretta di passaggi per garantire che, in caso di cambio di bucket o anche di transizione da DO a un altro ambiente compatibile con AWS, tutti gli allegati vengano spostati e preparati correttamente per lo scambio? Più in generale, cosa si dovrebbe fare, passo dopo passo, in questi casi? Chiaramente un semplice re-bake non è sufficiente. :confused:
  • cosa fa il task posts:invalidate_broken_images? Cioè, cosa significa invalidate?

Grazie in anticipo, sto lottando con questo problema da una settimana e ho davvero bisogno di risolverlo o diventerò pazzo :smiley: :stuck_out_tongue:
Per vostra informazione, il suggerimento di ricaricare a mano tutti gli 800+ allegati non è considerato una risposta valida. Deve esserci una ragione algoritmica… :laughing:

2 Mi Piace

Penso che tu abbia saltato un DbHelper.remap('oldbucketurl', 'newbucketurl') tra i passaggi 2 e 3.

4 Mi Piace

Ciao @falco, grazie per la risposta.
Sì, all’inizio me lo sono dimenticato.
L’ho eseguito dopo averlo trovato cercando qui su meta. :confused: e ha aiutato a recuperare alcuni file.
Tra l’altro, dopo averlo eseguito, ho fatto un rebake completo.

Cosa altro potrei provare?

1 Mi Piace

Quindi, potrei avere un’idea di cosa stia succedendo qui.
Non ho pensato di menzionare un fatto relativo all’output del task rake rake uploads:recover_from_tombstone, che potrebbe fornire un indizio interessante.

Sembra che il task stia effettivamente trovando i file degli upload nel tombstone, ma mi lancia un avviso riguardo a qualcosa (il nome completo del file di upload) che risulta errato. Ad esempio:

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

Eseguendo un comando find sulla mia copia locale delle directory degli upload, risulta che ho effettivamente un file chiamato 487b613752a0c338646fecc942512e5de9afeb3f.jpeg.

Il shortlink appartenente a questo specifico upload è upload://alcIv6jVlmjiEOEBh8fNDJyRms7.jpeg, e applicando l’algoritmo base62 che calcola il nome completo del file corrispondente, risulta che il valore è 487b613752a0c338646fecc942512e5de9afeb3f, esattamente il nome del file con cui il task rake recover_from_tombstone mi avvisa che c’è un errore. :thinking:

Perché lo strumento afferma che è errato e dovrebbe essere c87c4f08d1a9aac3f43d19722cfd5a94f2544272 invece?

In ogni caso, ho eseguito il task rake uploads:fix_relative_upload_links diverse volte, e poi ho rieseguito rake uploads:recover_from_tombstone, ma nulla sembra cambiare.

Modifica:
Cercando 487b613752a0c338646fecc942512e5de9afeb3f in un backup del database fatto prima di cambiare bucket, vedo che il record nella tabella uploads relativo a questa immagine mostrava esattamente questo nome esadecimale, quindi tanto meno riesco a capire perché il task rake si lamenti di ciò.

image

Questo è uno dei fraintendimenti più antichi su Meta.
Non è necessario eseguire un rebake dopo un remap ben mirato.

2 Mi Piace

Potresti avere ragione, ma il fatto è che è difficile sapere esattamente cosa fare e cosa non fare in questi casi senza un tutorial o una guida da parte degli sviluppatori.
Si ha sempre la sensazione di aver dovuto fare qualcosa di più o in un ordine diverso, come se si dovesse scoprire una ricetta che funziona, estraendola da decine di post scritti negli ultimi 3/4 anni. :stuck_out_tongue:
Il rebaking sembra essere la panacea per molte cose e innocuo per i post esistenti.

È un modo complicato per dire che, dato quante volte le persone hanno incappato in problemi con la gestione dei caricamenti e simili, una bella guida ufficiale dello Staff sarebbe un importante punto di riferimento. :wink:

1 Mi Piace

Scusa, devo riprendere questo argomento.

Durante la scorsa settimana ho dedicato del tempo a leggere il codice dei task Rake per gli upload, cercando di capire cosa succede sotto il cofano di recover_from_tombstone e recover. È una cosa complessa a causa dell’incapsulamento delle classi, quindi direi che ho fallito in gran parte.

Tuttavia, ho capito questo (per favore @Falco correggimi se sbaglio): il nome del file su disco di un upload viene creato combinando il suo SHA1 e la sua estensione originale. Viene poi archiviato su disco/AWS in una directory il cui percorso dipende dalla prima e talvolta dalla seconda lettera del nome, all’interno di cartelle 1X, 2X, 3X… (non ho capito come vengono determinate queste).
Infine, lo SHA1 e il nome del file vengono archiviati, tra le altre cose, nei record della tabella uploads in PostgreSQL.

Tornando a quanto accaduto durante il nostro spostamento del datacenter di Digital Ocean, ecco cosa è successo, per quanto ho capito:

  1. Abbiamo copiato tutti i file da ams3 a fra1.
  2. Non siamo riusciti a eseguire DbHelper.remap('oldbucketurl', 'newbucketurl') come suggerito da @falco, ma non era chiaro per noi che fosse necessario in questo caso.
  3. Abbiamo avviato un rebake globale. A questo punto, migliaia di immagini si sono “rotte” e molte sono state spostate nel tombstone. Non mi è del tutto chiaro il motivo.
  4. Mi sono reso conto che qualcosa non andava, ho interrotto il rebake in corso e ho scoperto il comando remap cercando qui in meta. Abbiamo quindi avviato il task DbHelper.remap('oldbucketurl', 'newbucketurl').
  5. Per recuperare le immagini spostate nel tombstone al punto 3, abbiamo lanciato rake uploads:recover_from_tombstone, che ne ha recuperate alcune, ma ne ha lasciate centinaia senza recupero, mostrando errori relativi allo SHA1 dei file, come: Warning /t/eclisse-parziale-di-sole-04-01-2011/14456/50 had an incorrect 3f5a1c136b97aebac4a188432c8e3ab7487f3bca should be ec88ee9eea18f3b8424bfef796345c68582911b5 storing in custom field 'rake uploads:fix_relative_upload_links' can fix this, come se il file fosse stato in qualche modo modificato e quindi lo SHA1 è ora diverso. Il recupero di tali file fallisce.

Non abbiamo mai modificato i file mentre li spostavamo tra i due datacenter. Usando s3cmd, li stavamo letteralmente scaricando localmente dal vecchio bucket e ricaricandoli immediatamente nel nuovo.

Perché lo SHA1 calcolato da Discourse dovrebbe essere diverso?

Sarebbe possibile forzare il task recover a ignorare la discrepanza dello SHA1 e semplicemente adattare l’import nel DB a ciò che è presente, oppure rinominare i file esistenti con il nuovo SHA1 durante il recupero?

Sto trascurando qualcosa di ovvio? Grazie a tutti per l’aiuto.

Quindi, solo per dare a questo thread una chiusura che potrebbe essere utile a qualcun altro, ecco come abbiamo risolto la situazione.

Essenzialmente, dato che era impossibile recuperare gli allegati mancanti tramite i vari task Rake di recupero degli upload, ho preparato uno script Ruby (scusate in anticipo, non sono assolutamente uno sviluppatore Ruby o Rails, quindi scommetto che il codice è inefficiente e brutto, ma questo è un altro discorso :stuck_out_tongue: ) che:

  1. Trova tutti i post contenenti la stringa upload://
  2. Estrae il shortlink di ogni upload e lo trasforma nel suo hash sha1 a lunghezza estesa
  3. Interroga la tabella Uploads
  4. Se viene trovato un allegato con quell’hash sha1 in Uploads, quell’upload viene saltato; altrimenti, viene controllato l’URL di quell’upload nel vecchio bucket/spaces di Digital Ocean.
  5. Se l’upload viene trovato nel vecchio bucket/spaces, allora lo shortlink viene sostituito con l’URL dello stesso upload nel vecchio bucket.
  6. Se modificato, viene attivata una ricottura (rebake) del post originale, per permettere a Discourse di fare il lavoro pesante di riscaricare localmente l’upload “perso” e ricreare tutto ciò che serve nel DB.

Per evitare il blacklisting e ridurre il carico sul server, viene introdotto un intervallo di 20 secondi ogni volta che viene richiesta una ricottura.


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} non trovato nel DB. Recupero da 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 "AGGIORNAMENTO IN CORSO!"
        # goahead = gets
        p.raw = new_raw
        p.cooked = ''
        p.save
        p.rebake!(invalidate_broken_images: true);
        puts "*******************************************"
        sleep 30
    else
        puts "SALTA!"
        puts "*******************************************"
        sleep 1
    end
end

1 Mi Piace