Реалізація черги імпорту товарів (фонова обробка)
Уявіть: завантажуєте прайс-лист із 50 000 товарів. Синхронний імпорт зависає на 2 хвилини та падає з 500-ю помилкою. В результаті — втрата даних і роздратування користувача. Ми розробляємо черги завдань, які не блокують інтерфейс і не втрачають дані. Файл приймається, завдання ставиться в чергу, і одразу повертається ID імпорту. Користувач бачить прогрес через WebSocket і не чекає.
Асинхронний імпорт на Laravel + Redis вирішує цю проблему: він обробляє файл без блокування, а ви бачите прогрес у реальному часі. Економія часу — до 70% порівняно з синхронними рішеннями.
Архітектура системи
HTTP Upload (файл/URL) ↓ Import Job (запис у чергу) ↓ Queue (Redis / SQS / RabbitMQ) ↓ Worker Process (окремий процес/контейнер) ↓ Chunk Processing (батчі по 500 товарів) ↓ Database (upsert) ↓ Progress Event (WebSocket / SSE → UI) Як реалізувати відмовостійкість черги?
Використовуємо Laravel Queues з retry-логікою: при збої воркер повторює обробку чанка до 3 разів із затримкою. Якщо всі спроби невдалі — завдання позначається як failed, і ми повідомляємо адміністратора. Дані не втрачаються — це підтверджує офіційна документація.
// Контролер — приймає файл і ставить завдання class ProductImportController extends Controller { public function upload(Request $request): JsonResponse { $path = $request->file('file')->store('imports'); $import = ImportJob::create([ 'file_path' => $path, 'status' => 'pending', 'total' => 0, 'processed' => 0, 'errors' => 0, ]); ProcessProductImport::dispatch($import->id); return response()->json(['import_id' => $import->id]); } } // Job — обробка у фоні class ProcessProductImport implements ShouldQueue { use Dispatchable, InteractsWithQueue; public int $timeout = 3600; // 1 година public int $tries = 3; public function handle(): void { $import = ImportJob::findOrFail($this->importId); $import->update(['status' => 'processing', 'started_at' => now()]); $reader = new CsvReader(storage_path('app/' . $import->file_path)); $total = $reader->count(); $import->update(['total' => $total]); foreach ($reader->chunk(500) as $chunkIndex => $rows) { try { DB::transaction(function () use ($rows) { foreach ($rows as $row) { Product::updateOrCreate( ['sku' => $row['sku']], $this->mapRow($row) ); } }); $processed = ($chunkIndex + 1) * 500; $import->update(['processed' => min($processed, $total)]); // Подія прогресу event(new ImportProgressUpdated($import->id, min($processed, $total), $total)); } catch (\Exception $e) { $import->increment('errors'); Log::error("Import chunk failed", ['chunk' => $chunkIndex, 'error' => $e->getMessage()]); } } $import->update(['status' => 'completed', 'finished_at' => now()]); } } Що робити при помилках імпорту?
Помилки рядків не переривають процес. Кожна помилка логується в таблицю import_errors:
CREATE TABLE import_errors ( id BIGSERIAL PRIMARY KEY, import_id BIGINT, row_number INT, row_data JSONB, error_msg TEXT, created_at TIMESTAMPTZ DEFAULT NOW() ); Після завершення користувач завантажує звіт із помилковими рядками. Ми також налаштовуємо алерти при перевищенні порогу помилок (наприклад, >5% від загальної кількості рядків).
WebSocket / SSE для прогресу
// Laravel Broadcasting: подія прогресу class ImportProgressUpdated implements ShouldBroadcast { public function broadcastOn(): Channel { return new PrivateChannel("import.{$this->importId}"); } public function broadcastWith(): array { return [ 'processed' => $this->processed, 'total' => $this->total, 'percent' => round($this->processed / $this->total * 100), ]; } } На фронтенді — підписка через Laravel Echo або нативний EventSource (SSE). Ми реалізуємо і те, й інше за вашим вибором.
Порівняння: синхронний vs асинхронний імпорт
| Параметр | Синхронний | Асинхронний (черга) |
|---|---|---|
| Макс. розмір файлу | ~500 рядків | не обмежений (чанки) |
| Час очікування користувача | до 30 сек | ~2 сек (завантаження) |
| Відмовостійкість | немає (одна помилка — все) | порядкова обробка + повтор |
| Прогрес | немає | WebSocket / SSE |
| Можливість паралельної обробки | немає | так (Laravel Batches) |
Асинхронний імпорт у 3-5 разів швидший за синхронний завдяки паралельній обробці та відсутності таймаутів.
Порівняння систем черг
| Характеристика | Redis | Amazon SQS | RabbitMQ |
|---|---|---|---|
| Швидкість | висока | середня | висока |
| Надійність | середня (без персистентності) | висока | висока |
| Складність налаштування | низька | середня | висока |
| Ціна | безкоштовно | плата за запити | безкоштовно (свій сервер) |
Для 90% проєктів достатньо Redis — він швидкий, простий і вбудований в Laravel. Для критичних даних або обсягів > 1 млн записів обираємо SQS або RabbitMQ. Якщо не впевнені у виборі — отримайте консультацію, ми допоможемо підібрати оптимальний драйвер.
Оптимальний розмір чанка
Розмір чанка — ключовий параметр продуктивності. Занадто маленький (50 рядків) створює багато накладних витрат на транзакції, занадто великий (5000) — ризикує перевищити ліміт пам'яті. Ми підбираємо чанк під ваш сервер: типічно 500–1000 рядків на один воркер. Для прискорення можна використовувати паралельні воркери з Laravel Batches.
Моніторинг черги за допомогою Laravel Horizon
Laravel Horizon надає гарний дашборд для відстеження черг: кількість завдань, час виконання, кількість помилок. Налаштовуємо алерти в Telegram або Slack при перевищенні порогу. Це дозволяє оперативно реагувати на збої та тримати руку на пульсі.
Паралельна обробка (Laravel Batches)
Для дуже великих файлів (100 000+ товарів) використовуємо розбивку на незалежні частини з паралельними воркерами:
class DispatchImportChunks implements ShouldQueue { public function handle(): void { $chunks = $this->splitFile($this->filePath, chunkSize: 1000); Bus::batch( array_map(fn($chunk) => new ProcessImportChunk($chunk), $chunks) ) ->then(fn(Batch $batch) => $this->onComplete($batch)) ->catch(fn(Batch $batch, Throwable $e) => $this->onError($batch, $e)) ->dispatch(); } } Це прискорює імпорт у 3–5 разів порівняно з послідовною обробкою. Скорочення часу простою серверів на 40% — типовий результат.
Як налаштувати воркери для черги?
- Встановіть Redis або інший драйвер черги.
- Налаштуйте supervisor для постійної роботи воркерів.
- Запустіть команду
php artisan queue:work redis --queue=import --tries=3 --timeout=3600. - Слідкуйте за логами через
php artisan queue:monitor.
Що входить у роботу
- Розробка архітектури черги (Redis / SQS / RabbitMQ)
- Реалізація завантаження файлу (CSV, Excel, XML, CommerceML)
- Налаштування воркерів з чанковою обробкою (розмір чанка підбираємо під ваш сервер)
- Система повторних спроб і обробки помилок (логування + алерти)
- Прогрес у реальному часі (WebSocket або SSE)
- Документація по запуску воркерів та моніторингу
- Інтеграція з вашим інтерфейсом (API для старту та отримання статусу)
Терміни
Базова реалізація (один формат файлу, один воркер) — 4–6 робочих днів. З паралельною обробкою та кількома форматами — 8–10 робочих днів. Оцінимо ваш проєкт безкоштовно після знайомства з файлом та вимогами.
Наш досвід
Більше 5 років на ринку, реалізовано 30+ проєктів з фоновими чергами для e-commerce та логістики. Використовуємо перевірені патерни: Repository, BFF, Event Sourcing. Гарантуємо відсутність втрат даних навіть при збої воркера.
Отримайте консультацію — ми запропонуємо архітектуру та точну оцінку. Зв'яжіться з нами, щоб обговорити імпорт ваших товарів.







