Ошибка миграции в S3 — но проверка не прошла

За годы мы столкнулись с множеством проблем при миграции на S3, включая неявные миграции при восстановлении резервной копии.

EXCEPTION: 8 posts are not remapped to new S3 upload URL. S3 migration failed for db 'default'.

Вот лишь некоторые примеры:

Сегодня я столкнулся с ещё одним таким случаем, и, поскольку был пятница, решил разобраться в проблеме, вместо того чтобы просто закомментировать проверку.

Итак, у нас следующая ситуация:

  • у нас мульти-сайт
  • предположим, что имя базы данных — dbname
  • у нас установлен параметр S3_CDN_URL
  • у нас не установлен параметр CDN_URL

Вот что происходит в файле /lib/file_store/to_s3_migration.rb.

Сначала определяется префикс:

prefix = @migrate_to_multisite ? "uploads/#{@current_db}/original/" : "original/"

Затем файлы загружаются в S3, и после этого выполняется переназначение ссылок, что по сути выглядит так (с некоторыми вариациями):

        from = "/uploads/#{@current_db}/original/"
        to = "#{SiteSetting.Upload.s3_base_url}/#{prefix}"

Таким образом, в случае мульти-сайта переназначение происходит:

  • с /uploads/dbname/original/
  • на https://bucket-location-url.com/uploads/dbname/original/

И, наконец, выполняется проверка:

      cdn_path = SiteSetting.cdn_path("/uploads/#{@current_db}/original").sub(/https?:/, "")
      count = Post.where("cooked LIKE '%#{cdn_path}%'").count
      if count > 0
        error_message = "#{count} posts are not remapped to new S3 upload URL. #{failure_message}"
        raise_or_log(error_message, should_raise)
        success = false
      end

Теперь SiteSetting.cdn_path берётся из lib/global_path.rb и выглядит так:

  def cdn_path(p)
    GlobalSetting.cdn_url.blank? ? p : "#{GlobalSetting.cdn_url}#{path(p)}"
  end

Итак, если у нас есть CDN для S3, но нет обычного CDN, то SiteSetting.cdn_path("/uploads/#{@current_db}/original") будет просто /uploads/dbname/original.

А согласно нашему переназначению, новые пути — https://bucket-location-url.com/uploads/dbname/original/.

Это означает:

  1. cdn_path является подстрокой нового целевого пути;
  2. следовательно, Post.where("cooked LIKE '%#{cdn_path}%'").count всегда будет находить посты;
  3. система ложно сработает и прервёт процесс :scream: :scream: :scream:
3 лайка