Lavoro Sidekiq in esecuzione prolungata che riavvia il codice interno

Sto attualmente creando un plugin relativo a meritmoot.com (che è in sviluppo e utilizza rilasci rolling) e, a causa dell’utilizzo di dati esterni, sto impiegando un job Sidekiq di lunga durata. Purtroppo, per qualche motivo, il job continua a riavviare il codice interno senza fallire né fornire output di errore. C’è qualcosa che sto trascurando in Sidekiq che potrebbe causare questo riavvio?

Nel mio ultimo job (che dovrebbe essere eseguito solo una volta al giorno) relativo ai Roll Calls, il job è stato riavviato a questi intervalli senza alcun errore:

0h-58m-42s (orario di avvio della prima iterazione)
1h-30m-12s (primo riavvio)
2h-1m-1s (secondo riavvio)
3h-46m-49s
4h-17m-11s
4h-47m-33s

Il job non è terminato e tutto il progresso è stato ripristinato dall’inizio. Vale la pena notare che ho un mio processo di logging che reindirizza stderr e stdout per il mio codice interno, anche se dubito che possa interferire con Sidekiq. (Chiedimi pure se vuoi dargli un’occhiata, è molto utile per lo sviluppo!)

Potrei salvare il progresso effettuato all’interno del codice, ma preferirei un processo più semplice, poiché ciò creerebbe un sovraccarico. C’è qualcosa che sto trascurando in Sidekiq che potrebbe causare il riavvio del mio codice?

Quindi vuoi un job Sidekiq che intenzionalmente richieda oltre un’ora per esecuzione? Puoi spiegare un po’ di più il motivo?

Sto raccogliendo molti dati esterni e li sto archiviando internamente sul mio sito web. I dati riguardano informazioni sul Congresso degli Stati Uniti

modifica: come disegni di legge pubblici, votazioni e informazioni sui membri

Stai finendo di memoria? Nel nostro hosting, abbiamo configurato Sidekiq per riavviarsi automaticamente se inizia a utilizzare troppa memoria.

Ecco alcune idee:

  • Invia i dati tramite l’API (ma raggiungerai i limiti di frequenza)
  • Esegui uno script esterno, specialmente se serve solo per recuperare il primo lotto di dati, mentre gli aggiornamenti richiederanno meno tempo

Memoria intesa come memoria del database o dell’hard disk? Ne sto usando molta, sì. Al momento sto valutando una versione leggermente modificata del suggerimento di @pfaffman, in cui forko il processo, permettendo al thread principale di terminare, ma generando un processo figlio che mantiene lo stesso contesto. (essenzialmente uno script esterno rispetto a Sidekiq)

Sto testando il pattern

pid = Process.fork
if pid.nil? then
  # Nel processo figlio
  exec "whatever --take-very-long"
else
  # Nel processo padre
  Process.detach(pid)
end

di https://stackoverflow.com/questions/806267/how-to-fire-and-forget-a-subprocess#806326 per risolvere il problema. È un po’ una situazione strana, ma l’API a cui mi sto collegando non ha funzionalità di aggiornamento, quindi sostanzialmente sto semplicemente aggiornando i dati riscaricando grandi porzioni dell’API ogni giorno :expressionless: :man_shrugging:

Ti farò sapere come va tra un paio d’ore

edit: si è bloccato di nuovo - penso che salverò il mio progresso periodicamente e cercherò modi per renderlo più efficiente

Invece di fare così, perché non impostare il tuo lavoro per operare in piccoli lotti? Ha davvero bisogno di richiedere 4 ore? Sincronizza 10 argomenti, poi altri 10… e così via.

Sidekiq non ha nulla che possa terminare i job a esecuzione prolungata; le ricostruzioni dell’applicazione lo faranno, così come gli aggiornamenti tramite l’interfaccia web.

Grazie per tutto l’aiuto,

Alla fine ho rimosso il codice del processo, non era efficace. Il job di riavvio stava semplicemente nascondendo un problema di fondo: l’inefficienza :sweat_smile:. Invece, ho adottato questo approccio:

  • Scrivere codice SQL in bulk (molto più veloce dell’esecuzione sequenziale), rilevando quando è realmente necessario aggiornare e permettendomi di evitare di usare la classe PostRevisor per rieseguire l’aggiornamento su elementi che non sono cambiati
  • Aumentare l’efficienza nel recupero dei dati via HTTP utilizzando connessioni persistenti e altri punti dati, inclusi elementi compressi (ove possibile)

Ho scoperto che scrivere comandi SQL in bulk offre un enorme aumento di velocità. Ciò che sto aggiornando è:

tabella post: colonne cooked, last_updated
tabella topic: colonne title, last_updated

La mia prossima idea è saltare completamente PostRevisor facendo qualcosa del genere:

1 - spostare i dati in una tabella temporanea

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 - e quindi attivare il job di re-indicizzazione della ricerca poiché titolo e contenuto sono cambiati.

C’è qualcosa che sto trascurando? Discourse è complesso, e saltando PostRevisor ho la sensazione di poter toccare tabelle con cui non ho esperienza (post_stats, post_timings, post_uploads, quoted_posts sono alcune che vedo nel database). Tuttavia, non ho bisogno di tutta la validazione che PostRevisor fornisce, dato che il sistema riceve queste revisioni da una fonte attendibile e prevedibile. Sembra una soluzione piuttosto incerta.

Cosa ne pensi?

Aggiornamento: stavo eseguendo dei controlli sul codice poiché c’era un numero insolito di aggiornamenti nel tempo e ho scoperto che c’è qualcosa che causa aggiornamenti non necessari su elementi di dati che in realtà non sono cambiati nel loro formato JSON grezzo. Una volta risolto questo errore, quanto sopra probabilmente non sarà più necessario :tipping_hand_man: avrei dovuto fare i test… mi avrebbe fatto risparmiare un sacco di tempo. Penso che potrei comunque provare quanto sopra, anche se non sarà una priorità. Mi aiuterà per aggiornamenti rapidi quando modifico il formato in cui i dati vengono presentati. Inoltre è già scritto, solo non testato.

Ho completato il codice per l’aggiornamento in blocco: saresti interessato a vederlo spinto su un ramo specifico una volta che sarà più stabile? Il suo caso d’uso è piuttosto specifico, ma per ciò che fa può aggiornare migliaia di record rapidamente, inclusi i tag. È progettato per estendere TopicsBulkAction. Ecco il readme che ho scritto se desideri informazioni più approfondite:

  # input
  #   - lista di hash contenenti cooked, post_id, topic_id, title, updated_at, tags (raw punterà a cooked)
  #   [{post_id: #, cooked: "", topic_id: #, title: "", updated_at: date_time, tags: [{tag: "", tagGroup: ""}, ... ] } ,  ... ]
  #   - category_name, il nome della categoria che viene aggiornata. Questo viene utilizzato per l'indicizzazione della ricerca.

  # attributi hash opzionali da includere negli elementi della lista:
  #   - raw, se non incluso sarà uguale a cooked.
  #   - fancy_title, se non incluso sarà uguale a title
  #   - slug, se non incluso verrà elaborato da title (questo è correlato al suo URL)

  # caso d'uso: aggiornamento regolare dei topic da una fonte dati backend non-Discourse in cambiamento, in modo efficiente
  # per rispecchiare l'aggiornamento delle informazioni. Nota che questo non è pensato per la creazione generale di post o topic, ma per l'aggiornamento
  # del titolo del topic e del post PRINCIPALE del topic. Per la revisione generale dei post, vai a PostRevisor in lib/post_revisor.rb

  # - Assume contenuto già cotto (cooked), custom cooked o visualizzato così com'è. I dati non sono validati.
  # - i post dovrebbero avere (cook_methods: Post.cook_methods[:raw_html]) impostato alla creazione se il tuo raw == cooked.
  #     Dovresti farlo se stai scrivendo HTML personalizzato da visualizzare all'interno del post.
  #     Altrimenti Discourse potrebbe ricucirlo in futuro, il che sarebbe negativo. Assicurati che la fonte delle informazioni
  #     sia attendibile e che i contenuti siano escapati.
  # - Se quanto sopra non è ideale, assicurati di includere raw, imposta il metodo di cottura corretto durante la creazione del post
  #     (nel caso in cui il sistema lo ricucisca), esegui raw attraverso il metodo di cottura scelto e includi sia raw che il cooked risultante
  #     nei tuoi hash.
  # - Tiene traccia della word_count annotando le differenze tra il conteggio delle parole prima e dopo del post e passando tale valore
  #     al topic.
  # - Tiene traccia del conteggio dei tag in modo simile