Optimisation de la vitesse de migration de PHPBB vers Discourse

Salut, je suis avec phpBB depuis 2004, donc je transfère un forum de taille respectable. Pour l’instant, cela fait deux jours que ça tourne : 303 139 / 2 167 314 (14 %), soit 446 par minute. Comment puis-je accélérer le processus ? Je suis sur DigitalOcean et n’utilise que 4 % du processeur. Redis continue de me donner des erreurs de délai d’attente. J’ai désactivé les pièces jointes.

Quelques détails utiles aideraient à affiner le diagnostic :

  • Taille du Droplet : RAM, nombre de vCPU, type/taille du disque
  • La commande d’import exacte que vous exécutez
  • Vos paramètres liés aux ressources dans app.yml, avec les secrets supprimés, en particulier :
    • UNICORN_WORKERS
    • db_shared_buffers
    • db_work_mem
  • Si l’import s’exécute dans le conteneur Docker standard de Discourse
  • Tout message d’expiration Redis ou extrait de journal
  • Si la base de données est locale (dans le même conteneur) ou externe

Une remarque importante : UNICORN_WORKERS n’accélérera probablement pas beaucoup l’import, car l’importateur phpBB n’est pas principalement géré par les workers web. L’augmenter pourrait simplement consommer plus de RAM. Les paramètres de mémoire PostgreSQL et les entrées/sorties du disque sont plus susceptibles d’être déterminants.

Puisque l’utilisation du CPU n’est que d’environ 4 %, le problème pourrait provenir des entrées/sorties, de la base de données ou d’une attente liée à Redis, plutôt que d’un manque de puissance CPU. L’expiration de Redis mérite d’être investiguée séparément.

image

Mon journal mis à jour
script/import_scripts/phpbb3.rb:15:in ‘’
302570 / 2167314 ( 14.0%) [448 éléments/min] Exception lors de la création du message 304683. Ignoré.
Attente de 1,0 seconde
/var/www/discourse/vendor/bundle/ruby/3.4.0/gems/redis-client-0.28.0/lib/redis_client/ruby_connection/buffered_io.rb:214:in ‘block in RedisClient::RubyConnection::BufferedIO#fill_buffer’
internal:kernel:168:in ‘Kernel#loop’
/var/www/discourse/vendor/bundle/ruby/3.4.0/gems/redis-client-0.28.0/lib/redis_client/ruby_connection/buffered_io.rb:197:in ‘RedisClient::RubyConnection::BufferedIO#fill_buffer’
/var/www/discourse/vendor/bundle/ruby/3.4.0/gems/redis-client-0.28.0/lib/redis_client/ruby_connection/buffered_io.rb:187:in ‘RedisClient::RubyConnection::BufferedIO#ensure_remaining’
/var/www/discourse/vendor/bundle/ruby/3.4.0/gems/redis-client-0.28.0/lib/redis_client/ruby_connection/buffered_io.rb:152:in ‘RedisClient::RubyConnection::BufferedIO#getbyte’
/var/www/discourse/vendor/bundle/ruby/3.4.0/gems/redis-client-0.28.0/lib/redis_client/ruby_connection/resp3.rb:113:in ‘RedisClient::RESP3.parse’
/var/www/discourse/vendor/bundle/ruby/3.4.0/gems/redis-client-0.28.0/lib/redis_client/ruby_connection/resp3.rb:50:in ‘RedisClient::RESP3.load’
/var/www/discourse/vendor/bundle/ruby/3.4.0/gems/redis-client-0.28.0/lib/redis_client/ruby_connection.rb:97:in ‘RedisClient::RubyConnection#read’
/var/www/discourse/vendor/bundle/ruby/3.4.0/gems/redis-client-0.28.0/lib/redis_client/connection_mixin.rb:37:in ‘RedisClient::ConnectionMixin#call’
/var/www/discourse/vendor/bundle/ruby/3.4.0/gems/redis-client-0.28.0/lib/redis_client.rb:374:in ‘block (2 levels) in RedisClient#call_v’
/var/www/discourse/vendor/bundle/ruby/3.4.0/gems/redis-client-0.28.0/lib/redis_client/middlewares.rb:16:in ‘RedisClient::BasicMiddleware#call’
/var/www/discourse/vendor/bundle/ruby/3.4.0/gems/redis-client-0.28.0/lib/redis_client.rb:373:in ‘block in RedisClient#call_v’
/var/www/discourse/vendor/bundle/ruby/3.4.0/gems/redis-client-0.28.0/lib/redis_client.rb:781:in ‘RedisClient#ensure_connected’
/var/www/discourse/vendor/bundle/ruby/3.4.0/gems/redis-client-0.28.0/lib/redis_client.rb:372:in ‘RedisClient#call_v’
/var/www/discourse/vendor/bundle/ruby/3.4.0/gems/redis-5.4.0/lib/redis/client.rb:90:in ‘Redis::Client#call_v’
/var/www/discourse/vendor/bundle/ruby/3.4.0/gems/rack-mini-profiler-4.0.1/lib/mini_profiler/profiling_methods.rb:90:in ‘block in Redis::Client#profile_method’
/var/www/discourse/vendor/bundle/ruby/3.4.0/gems/redis-5.4.0/lib/redis.rb:152:in ‘block in Redis#send_command’
/var/www/discourse/vendor/bundle/ruby/3.4.0/gems/redis-5.4.0/lib/redis.rb:151:in ‘Monitor#synchronize’
/var/www/discourse/vendor/bundle/ruby/3.4.0/gems/redis-5.4.0/lib/redis.rb:151:in ‘Redis#send_command’
/var/www/discourse/vendor/bundle/ruby/3.4.0/gems/redis-5.4.0/lib/redis/commands/scripting.rb:110:in ‘Redis::Commands::Scripting#_eval’
/var/www/discourse/vendor/bundle/ruby/3.4.0/gems/redis-5.4.0/lib/redis/commands/scripting.rb:97:in ‘Redis::Commands::Scripting#evalsha’
/var/www/discourse/lib/discourse_redis.rb:38:in ‘Kernel#public_send’
/var/www/discourse/lib/discourse_redis.rb:38:in ‘block in DiscourseRedis#method_missing’
/var/www/discourse/lib/discourse_redis.rb:29:in ‘DiscourseRedis.ignore_readonly’
/var/www/discourse/lib/discourse_redis.rb:38:in ‘DiscourseRedis#method_missing’
/var/www/discourse/lib/discourse_redis.rb:270:in ‘DiscourseRedis::EvalHelper#eval’
/var/www/discourse/lib/distributed_mutex.rb:82:in ‘DistributedMutex#get_lock’
/var/www/discourse/lib/distributed_mutex.rb:50:in ‘block in DistributedMutex#synchronize’
/var/www/discourse/lib/distributed_mutex.rb:49:in ‘Thread::Mutex#synchronize’
/var/www/discourse/lib/distributed_mutex.rb:49:in ‘DistributedMutex#synchronize’
/var/www/discourse/lib/distributed_mutex.rb:34:in ‘DistributedMutex.synchronize’
/var/www/discourse/lib/post_creator.rb:410:in ‘PostCreator#transaction’
/var/www/discourse/lib/post_creator.rb:200:in ‘PostCreator#create’
/var/www/discourse/script/import_scripts/base.rb:611:in ‘ImportScripts::Base#create_post’
/var/www/discourse/script/import_scripts/base.rb:555:in ‘block in ImportScripts::Base#create_posts’
/var/www/discourse/vendor/bundle/ruby/3.4.0/gems/rack-mini-profiler-4.0.1/lib/patches/db/mysql2/alias_method.rb:8:in ‘Mysql2::Result#each’
/var/www/discourse/vendor/bundle/ruby/3.4.0/gems/rack-mini-profiler-4.0.1/lib/patches/db/mysql2/alias_method.rb:8:in ‘Mysql2::Result#each’
/var/www/discourse/script/import_scripts/base.rb:542:in ‘ImportScripts::Base#create_posts’
/var/www/discourse/script/import_scripts/phpbb3/importer.rb:212:in ‘block in ImportScripts::PhpBB3::Importer#import_posts’
/var/www/discourse/script/import_scripts/base.rb:943:in ‘block in ImportScripts::Base#batches’
internal:kernel:168:in ‘Kernel#loop’
/var/www/discourse/script/import_scripts/base.rb:942:in ‘ImportScripts::Base#batches’
/var/www/discourse/script/import_scripts/phpbb3/importer.rb:293:in ‘ImportScripts::PhpBB3::Importer#batches’
/var/www/discourse/script/import_scripts/phpbb3/importer.rb:208:in ‘ImportScripts::PhpBB3::Importer#import_posts’
/var/www/discourse/script/import_scripts/phpbb3/importer.rb:38:in ‘ImportScripts::PhpBB3::Importer#execute’
/var/www/discourse/script/import_scripts/base.rb:47:in ‘ImportScripts::Base#perform’
/var/www/discourse/script/import_scripts/phpbb3/importer.rb:22:in ‘ImportScripts::PhpBB3::Importer#perform’
script/import_scripts/phpbb3.rb:35:in ‘module:PhpBB3
script/import_scripts/phpbb3.rb:16:in ‘module:ImportScripts
script/import_scripts/phpbb3.rb:15:in ‘’
333919 / 2167314 ( 15.4%) [441 éléments/min]

Ça semble fonctionner en tout cas, pas de plaintes.
Pour l’instant

Arrêt d’Unicorn

Désactivation des pièces jointes et des avatars

La taille de ce droplet semble raisonnablement adaptée pour ce type d’importation, je ne m’attendrais donc pas à ce que UNICORN_WORKERS soit le principal facteur limitant ici.

Quelques observations :

  • Arrêter Unicorn est probablement acceptable pour un conteneur dédié uniquement à l’importation ou à la préproduction, mais cela ne devrait pas accélérer massivement l’importateur lui-même.
  • La désactivation des pièces jointes et des avatars devrait éliminer deux des étapes les plus lentes. Si vous n’obtenez toujours qu’environ 440 publications par minute, le goulot d’étranglement peut provenir des E/S de la base de données, du côté des requêtes MySQL de phpBB source, ou des attentes liées à Redis/PostgreSQL.
  • Il vaut la peine de surveiller attentivement la ligne de journal suivante :

Exception while creating post 304683. Skipping.

Même si l’importation se poursuit, cela signifie qu’au moins cette publication a été ignorée. Si cela se produit à plusieurs reprises, la migration finale risque de manquer des publications.

La partie Redis semble indiquer un délai d’attente de 1 seconde du client Redis lors du verrouillage distribué de Discourse, pendant que PostCreator crée une publication. Je vérifierais si Redis est réellement surchargé ou si le délai d’attente est simplement trop agressif lors d’une longue importation.

Les vérifications utiles suivantes seraient :

free -h
df -h
iostat -xz 1
vmstat 1
redis-cli INFO memory
redis-cli INFO stats
redis-cli SLOWLOG GET 20

De plus, la base de données MySQL de phpBB est-elle locale sur le même droplet, ou est-elle lue via le réseau ? Étant donné que la pile d’appels itère à travers mysql2, une base de données source lente ou un disque lent peut maintenir l’utilisation du CPU basse pendant que l’importateur attend.

Au rythme actuel, avec 333 919 / 2 167 314 à environ 441 éléments par minute, il vous reste encore environ 69 heures, donc cela pourrait se terminer, mais je serais principalement inquiet de savoir si ces exceptions de délai d’attente Redis provoquent l’ignorance de publications.

Je déconseille fortement d’augmenter UNICORN_WORKERS. Lors d’une exécution dédiée à l’importation, Unicorn est largement sans importance. L’essentiel est que l’importation se poursuive mais ignore des publications, ce qui est le point que je soulignerais.