Je suis actuellement en train de créer un plugin lié à meritmoot.com (qui est en développement et en versions progressives). En raison de son utilisation de données externes, j’utilise un job Sidekiq de longue durée. Malheureusement, pour une raison quelconque, ce job redémarre le code interne sans échouer ni produire de sortie d’erreur. Y a-t-il quelque chose que je néglige dans Sidekiq qui pourrait provoquer ce redémarrage ?
Sur mon dernier job (qui est censé ne s’exécuter qu’une fois par jour) lié aux Roll Calls, le job a redémarré aux intervalles suivants sans erreur :
0h-58m-42s (début de la première itération)
1h-30m-12s (premier redémarrage)
2h-1m-1s (deuxième redémarrage)
3h-46m-49s
4h-17m-11s
4h-47m-33s
Le job n’a pas terminé et toute la progression a été réinitialisée. Il est à noter que j’ai mis en place mon propre processus de journalisation qui redirige stderr et stdout pour mon code interne, bien que je doute que cela interfère avec Sidekiq. (demandez-moi si vous souhaitez y jeter un coup d’œil, c’est très utile en développement !)
Il m’est possible de sauvegarder la progression effectuée dans le code, mais je préférerais un processus plus simple, car cela crée une surcharge. Y a-t-il quelque chose que je néglige dans Sidekiq qui pourrait provoquer le redémarrage de mon code ?
Vous voulez donc un travail Sidekiq qui prend intentionnellement plus d’une heure par exécution ? Pouvez-vous nous en dire un peu plus sur les raisons ?
Par mémoire, parlez-vous de la mémoire de la base de données ou du disque dur ? J’en utilise beaucoup, en effet. Je suis actuellement en train d’examiner une suggestion légèrement modifiée de @pfaffman, où je duplique le processus, permettant au thread principal de se terminer, tout en lançant un processus fils qui conserve le même contexte. (essentiellement un script externe par rapport à Sidekiq)
pid = Process.fork
if pid.nil? then
# Dans l'enfant
exec "whatever --take-very-long"
else
# Dans le parent
Process.detach(pid)
end
provenant de https://stackoverflow.com/questions/806267/how-to-fire-and-forget-a-subprocess#806326 pour résoudre le problème. C’est un peu étrange, mais l’API à laquelle je me connecte ne dispose pas de fonctionnalité de mise à jour, je rafraîchis donc essentiellement les données en téléchargeant à nouveau de grandes parties de l’API chaque jour
Je vous tiendrai informé de l’évolution dans quelques heures
edit : arrêté à nouveau — je pense que je vais sauvegarder mon progrès périodiquement et examiner des moyens de le rendre plus efficace
Au lieu de faire cela, pourquoi ne pas configurer votre tâche pour qu’elle fonctionne par petits lots ? Est-il vraiment nécessaire qu’elle prenne 4 heures ? Synchronisez 10 sujets, puis 10 autres… et ainsi de suite.
Sidekiq ne dispose d’aucun mécanisme pour interrompre les tâches de longue durée ; les reconstructions de l’application le font, tout comme les mises à niveau via l’interface web.
J’ai fini par supprimer le code du processus, car il n’était pas efficace. Le travail de redémarrage masquait simplement un problème sous-jacent de très faible efficacité . J’ai plutôt opté pour :
L’écriture de code SQL en lots (beaucoup plus rapide que le traitement séquentiel), en détectant quand une mise à jour est vraiment nécessaire, et en me permettant de sauter l’utilisation de la classe PostRevisor pour mettre à jour à nouveau des éléments qui n’ont pas changé
L’augmentation de l’efficacité lors de la récupération des données via HTTP en utilisant des connexions persistantes et d’autres points de données, y compris des éléments compressés (lorsque cela est possible)
J’ai constaté que l’écriture de commandes SQL en lots offre un gain de vitesse considérable. Ce que je mets à jour :
Ma prochaine idée est de completely éviter PostRevisor en procédant quelque chose comme suit :
1 - déplacer les données vers une table temporaire
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 - puis déclencher le travail de réindexation de la recherche, car le titre et le contenu ont changé.
Y a-t-il quelque chose que j’oublie ? Discourse est complexe, et en contournant PostRevisor, j’ai l’impression de pouvoir toucher à des tables avec lesquelles je n’ai pas d’expérience (post_stats, post_timings, post_uploads, quoted_posts sont quelques-unes que je vois dans la base de données). Cependant, je n’ai pas non plus besoin de toute la validation que fournit PostRevisor, car le système reçoit ces révisions à partir d’une source fiable et prévisible. Cela semble être une solution assez aléatoire.
Mise à jour : j’ai effectué quelques vérifications de code car il y avait un nombre étrange de mises à jour au fil du temps et j’ai découvert qu’un problème provoquait des mises à jour injustifiées sur des éléments de données qui n’avaient pas réellement changé dans leur format JSON brut. Une fois cette erreur résolue, ce qui précède ne sera probablement plus nécessaire j’aurais dû faire des tests… cela m’aurait fait gagner beaucoup de temps. Je pense que je vais tout de même essayer la solution ci-dessus, mais elle ne sera pas prioritaire. Cela aidera pour des mises à jour rapides lorsque je modifie le format de présentation des données. De plus, le code est déjà écrit, il n’a juste pas été testé.
Code de mise à jour par lots terminé : seriez-vous intéressé à ce que je le pousse sur une branche spécifique une fois qu’il sera plus stable ? Son cas d’utilisation est assez spécifique, mais il permet de mettre à jour des milliers d’enregistrements rapidement, y compris les balises. Il est conçu pour étendre TopicsBulkAction. Voici le fichier README que j’ai rédigé si vous souhaitez des informations plus détaillées :
# Entrée
# - Liste de hachages contenant cooked, post_id, topic_id, title, updated_at, tags (raw pointera vers cooked)
# [{post_id: #, cooked: "", topic_id: #, title: "", updated_at: date_time, tags: [{tag: "", tagGroup: ""}, ... ] } , ... ]
# - category_name, le nom de la catégorie mise à jour. Cela est utilisé pour l'indexation de recherche.
# Attributs de hachage optionnels à inclure dans les éléments de la liste :
# - raw, s'il n'est pas inclus, sera égal à cooked.
# - fancy_title, s'il n'est pas inclus, sera égal à title.
# - slug, s'il n'est pas inclus, sera généré à partir de title (cela est lié à son URL).
# Cas d'utilisation : mise à jour régulière des topics à partir d'une source de données backend non-Discourse changeante, de manière efficace
# pour refléter les informations mises à jour. Notez que cela n'est pas conçu pour la publication générale de posts ou de topics, mais pour la mise à jour
# du titre du topic et du POST PRINCIPAL du topic. Pour la révision générale des posts, consultez PostRevisor dans lib/post_revisor.rb.
# - Suppose des données pré-cuites, des données cuites personnalisées ou affichées telles quelles. Les données ne sont pas validées.
# - Les posts doivent avoir (cook_methods: Post.cook_methods[:raw_html]) défini lors de la création si votre raw == cooked.
# Faites cela si vous écrivez du HTML personnalisé à afficher dans le post.
# Sinon, Discourse pourrait le recuire à l'avenir, ce qui serait problématique. Assurez-vous que la source d'information
# est fiable et que son contenu est échappé.
# - Si ce qui précède n'est pas idéal, assurez-vous d'inclure raw, définissez la méthode de cuisson correcte lors de la création du post
# (au cas où le système le recuirait), passez raw à travers votre méthode de cuisson choisie, et incluez à la fois raw et le résultat cooked
# dans vos hachages.
# - Garde une trace du word_count en notant les différences entre les comptes de mots avant et après du post, et en transmettant cela
# au topic.
# - Garde une trace du nombre de balises de manière similaire.