Реалізація черги імпорту товарів (фонова обробка)
Уявіть: завантажуєте прайс-лист із 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. Гарантуємо відсутність втрат даних навіть при збої воркера.
Отримайте консультацію — ми запропонуємо архітектуру та точну оцінку. Зв'яжіться з нами, щоб обговорити імпорт ваших товарів.







