Laravel Read-Through Filesystem: як працює «лінива» міграція сховища

Перекладено ШІ 0 Laravel News 20 серпня, 2026

Забудьте про виснажливе перенесення мільйонів файлів між сховищами за допомогою складних ручних скриптів. Новий драйвер `read-through` у Laravel 13.26 самостійно мігрує дані на новий диск саме в момент звернення до них.

Перенесення мільйонів файлів з одного сховища в інше — це той етап міграції, на який зазвичай не закладають бюджет. Масштабний aws s3 sync між провайдерами триває днями, коштує чималих грошей за трафік і копіює все підряд: від застарілих аватарів до забутих експортів. Альтернатива — написання коду, який вручну перевіряє два диски, — засмічує проєкт конструкціями Storage::disk('old'), які потім важко видалити.

Laravel 13.26 представляє драйвер read-through, який бере це «жонлювання» дисками на себе. Ви вказуєте основний (primary) та резервний (fallback) диски: система спочатку шукає файл на основному, а якщо його там немає — звертається до старого сховища і автоматично копіює файл на нове під час читання. «Гарячі» файли мігрують самі при першому запиті, а «холодні» залишаються на місці, доки ви не вирішите, що з ними робити. Драйвер додав @taylorotwell у PR #61140.

# Налаштування диска

Диск read-through створюється на основі двох інших дисків у config/filesystems.php. Просто вкажіть їхні назви:

'disks' => [
 
    'r2' => [
        'driver' => 's3',
        // Cloudflare R2 credentials...
    ],
 
    'legacy-s3' => [
        'driver' => 's3',
        // бакет, з якого ви переходите...
    ],
 
    'assets' => [
        'driver' => 'read-through',
        'primary' => 'r2',
        'fallback' => 'legacy-s3',
    ],
 
],

У коді додатка ви використовуєте Storage::disk('assets') як будь-який інший диск. У цьому і полягає сенс: контролери та джоби навіть не знають про існування двох бакетів. Параметри primary та fallback також приймають масиви з конфігурацією, якщо ви не хочете реєструвати диски окремо. Менеджер валідує пару під час ініціалізації, тому відсутність диска або циклічне посилання на самого себе викличе InvalidArgumentException ще до початку роботи.

# Як розподіляються операції

Варто розібратися з правилами маршрутизації, перш ніж запускати це у продакшн:

  • Читання (get(), readStream()) перевіряє основний диск, а потім резервний. Якщо файл знайдено на резервному, він копіюється на основний, після чого повертається контент. Потокове читання використовує php://temp, щоб не завантажувати файл повністю в пам'ять.
  • Запис, видалення, переміщення та копіювання працюють тільки з primary.
  • Список файлів (files()) підтягується лише з primary, тому ви побачите лише ті об'єкти, що вже були перенесені або створені після перемикання.
  • Перевірка наявності та метадані (exists(), size(), mimeType(), lastModified(), url(), temporaryUrl()) звертаються до того диска, де лежить файл, не ініціюючи копіювання.

Два з цих правил мають свої нюанси. Списки файлів відображають лише основний диск, тому будь-яка логіка, що перебирає директорію, не побачить файлів на fallback. А видалення стосується лише primary: якщо ви видалите файл, який все ще є на fallback, він «воскресне» при наступному читанні. Під час міграції це зазвичай не проблема, оскільки старий диск згодом буде видалено, але поведінка, коли delete() повертає true, а наступний exists() теж true, може здивувати.

Перенесення файлів працює за принципом «найкращих зусиль». Якщо копіювання на основний диск не вдалося, читання з резервного все одно спрацює, а помилка буде пригнічена. Логіка в тому, що переповнений новий диск не має зупиняти роздачу файлів. Якщо ви хочете знати про такі помилки негайно, встановіть 'throw_on_promotion_failure' => true.

# Читання без копіювання

Іноді потрібно використовувати два диски без міграції. @jimbojsb у PR #61155 додав опцію copy саме для таких випадків:

'assets' => [
    'driver' => 'read-through',
    'primary' => 'local-assets',
    'fallback' => 'production-s3',
    'copy' => false,
],

З copy => false дані з резервного диска просто віддаються користувачу без копіювання. Це зручно для локальної розробки з дампом бази даних із продакшну: записи посилаються на файли, які існують лише в хмарі. Таке налаштування дозволяє відображати їх локально, не забиваючи пам'ять ноутбука. Також це підходить для першої фази міграції, щоб перевірити стабільність читання перед початком масового копіювання.

# План міграції

Підсумовуючи, переїзд зі старого S3 на R2 виглядає так:

  1. Створюєте новий бакет і додаєте його конфігурацію поряд зі старим.
  2. Змінюєте диск, який уже використовує ваш додаток (наприклад, assets), на пару read-through: новий бакет як primary, старий як fallback. Тепер нові завантаження йдуть у R2, а старі файли мігрують самі при зверненні.
  3. Коли основний трафік пройде через систему, більшість затребуваних файлів уже буде на новому місці. Решту можна перенести разовою синхронізацією або фоновим завданням.
  4. Замінюєте конфіг read-through на звичайний диск, що вказує на новий бакет, і видаляєте старий.

Між 2 та 4 кроками немає моменту ризикованого перемикання всього трафіку відразу. Відкат — це просто зміна конфігурації, адже дані на старому бакеті залишалися цілісними протягом усього процесу.

# Що ще почитати

Популярні

Інше, що варто прочитати

15 Оновлено 26 червня, 2026

Локальні моделі та їх скоупи в Laravel за допомогою атрибута Scope

В Laravel 12 ми отримали можливість використовувати новий підхід для визначення локальних скоупів у моделях Eloquent. Дізнайтеся, як новий атрибут #[Scope] спрощує цей процес і зберігає ваші назви методів незмінними

10 Оновлено 26 червня, 2026

Усе, що ми знаємо про Laravel 12

Laravel 12 обіцяє стати важливим оновленням, яке зосереджене на підтримці й оптимізації, забезпечуючи безболісний перехід для більшості застосунків. Чи готові ви дізнатися, які нові можливості та зміни чекають на вас у цій версії

20 Оновлено 26 червня, 2026

Оптимізація запитів до бази даних за допомогою скорочених методів Laravel

Laravel пропонує зручні методи для роботи з датами, які значно спрощують запити до бази даних. Досліджуйте, як ці інтуїтивно зрозумілі функції допомагають створювати чіткі та зрозумілі умови для роботи з часовими даними!