Далее у меня есть два вопроса: во-первых, лимит полезен, но он ограничивает только пространство поиска; во-вторых, я хочу иметь возможность указывать максимальное количество сообщений для изменения. Поэтому я планирую добавить ограничение на максимальное число сообщений для изменения, а также лимит для запроса.
Кроме того, при указании max имеет смысл подробно описывать, что именно обрабатывается, в целях отладки. Поэтому я хочу сделать вывод подробным, если max не равен nil — это позволит пользователям проверять процесс перед продолжением, так как это основной сценарий использования данной работы.
Я думаю, что буду передавать max как первый аргумент, а опциональный limit — как второй, потому что max действительно является наиболее важным параметром; limit же нужен лишь для того, чтобы сделать «один запрос» дешевле.
Второй вопрос касается не-загрузок: загрузок. Более года назад я пытался загрузить видео на сайт Discourse, к которому присоединился, пока путался в попытках разобраться, как написать миграцию с Google+ на Discourse, и увидел, что сгенерированная ссылка выглядела так: https://#{SiteSettings.absolute_base_url}/original/3X/b/a/ba9e06ebc2f4397f26793bb5cd4e169308dd371d.mp4.
Сегодня же при загрузке видео я получаю что-то вроде: .
По крайней мере, в моём последнем тесте migrate_from_s3 полностью исказил эти URL-адреса, превратив их даже не в URL, так что это определённо нужно исправить. Затем, я считаю, что на практике эта задача вряд ли столкнётся с , поэтому на первом этапе я хочу просто добавить разметку ссылки вокруг магического протокола upload, вместо того чтобы заставлять регулярное выражение обрабатывать оба случая, что ещё больше усложнило бы его чтение. Хотя я могу передумать.
Похоже, что тег video или audio добавляется в JavaScript через соответствие регулярному выражению, поэтому мне придётся скопировать регулярные выражения из app/assets/javascripts/discourse/app/lib/uploads.js в задачу, чтобы правильно их идентифицировать. Я включу исходный код регулярных выражений, чтобы следующий человек, который их найдёт, знал, откуда их обновлять. ![]()
…
Сегодня вечером я выделил время на эту работу и начал работу над черновиком PR. Он ещё не завершён; я знаю, что в нём остались ошибки. На данный момент я не изменил поведение для URL-адресов псевдопротокола upload: (обычно для изображений), хотя добавил проверку на корректность.
Благодаря изменениям в этом PR я успешно мигрировал как обычные загрузки псевдопротокола upload:, так и видео, которые в настоящее время явно ссылаются на S3 (в моём случае — на DigitalOcean Spaces). Я использую это для изменения только одного сообщения за раз с помощью следующей команды:
bin/rake uploads:batch_migrate_from_s3[1,1000]
Обратите внимание, что эта команда не перейдёт дальше первых 1000 сообщений, возвращаемых somewhat случайно из запроса к базе данных; низкий лимит установлен лишь для ускорения выполнения запроса при миграции одного сообщения за раз, проверке корректности поведения этого сообщения и последующем повторном запуске для поиска следующего. Эта команда работает таким образом только с моим текущим PR!
…
Я продолжаю добавлять диагностический вывод по мере работы над этим и начинаю думать, что это важно не только для разработки. Я наблюдаю множество временных сбоев загрузки из DigitalOcean Spaces, когда часть файлов в сообщении мигрируется, а часть — нет. В оригинале это просто печатает . и продолжает работу, затем говорит Done, но на самом деле задача не выполнена. Мне пришлось сделать около пяти-шести проходов по одному сообщению, прежде чем все файлы были мигрированы. (Я не считал, потому что сначала думал, что отлаживаю локальную ошибку.) Я ожидаю, что мне придётся запускать эту миграцию неоднократно с тем же лимитом, пока диагностические данные не станут чистыми. Поэтому я делаю подробный вывод прогресса только если установлен max, но вывожу полезные предупреждающие сообщения в любом случае.
В настоящее время я использую следующий обходной путь для периодических сбоев загрузки в Discourse Spaces, который на практике значительно повысил мой процент успеха (трёх повторных попыток оказалось вполне достаточно для сотен мигрированных сообщений).
https://github.com/johnsonm/discourse/commit/7dfac12a2ea6ec04ba4e0616b4e0dbd1d806cff7
Также я обнаружил, что somehow у нас оказались видео больше установленного мной лимита при импорте с Google+. Мне пришлось временно увеличить SiteSettings.max_image_size_kb и SiteSettings.max_attachment_size_kb во время одноразовой миграции нескольких видео с избыточным размером, хотя мне неясно, как они вообще оказались на сайте. Я не хочу ломать их сейчас… Не знаю, была ли ошибка, позволявшая загружать файлы избыточного размера, в моём скрипте импорта, в Discourse или просто в моей памяти о том, какие изменения я вносил в настройки со временем. ![]()
Поскольку значительная часть того, что я мигрирую, была импортирована с Google+, некоторые мои сообщения не прошли текущие проверки валидации. Я получил несколько ошибок Unhandled failure: Validation failed: Sorry, new users can only put one image in a post и сначала не понял, почему они не повторяются. Оказалось, что загрузки успешно перемещены на локальное хранилище, и все они использовали псевдопротокол upload:, поэтому сырое содержимое не изменилось. Однако post.save! всё равно завершался ошибкой валидации, что предотвращало вызов post.rebake!, поэтому у меня есть несколько сообщений из 30 тысяч с изображениями, которые требуют повторной обработки; к сожалению, у меня нет записи о том, какие именно это сообщения. Теперь я перешёл на post.save!(validate: false) как ещё одно исправление, так что эта конкретная проблема больше не должна возникать. Я очень рад, что миграция начала прерываться при необработанных ошибках, иначе это могло бы нанести гораздо больший ущерб, чем несколько сообщений.
…
Чтобы мой сайт оставался работоспособным, включая отправку уведомлений, во время выполнения миграции, я не хочу перегружать очереди Sidekiq. Я знаю, что именование — одна из двух самых сложных задач в информатике, наряду с инвалидацией кэша и ошибками off-by-one, но я предлагаю использовать переменную окружения DISCOURSE_MIGRATION_MAX_ENQUEUED для ограничения общего числа заполненных слотов очереди (не слотов задач), которые могут быть заняты при переходе к миграции следующего элемента после rebake во время миграции, чтобы избежать перегрузки очередей и обеспечить продолжение работы сайта. У меня есть патч, добавляющий это значение по умолчанию равным нулю для всех операций повторной обработки сообщений в lib/tasks/uploads.rake. Я использую это в своей производственной миграции.