Background Product Import Queue (Async Processing)
Imagine uploading a price list with 50,000 products. A synchronous import hangs for 2 minutes and fails with a 500 error. Result: data loss and user frustration. We design task queues that don't block the interface and don't lose data. The file is accepted, the task is queued, and an import ID is returned immediately. The user sees progress via WebSocket and doesn't wait.
Async import on Laravel + Redis solves this: it processes the file without blocking, and you see progress in real time. Time savings—up to 70% compared to synchronous solutions.
Architecture Overview
HTTP Upload (file/URL)
↓
Import Job (write to queue)
↓
Queue (Redis / SQS / RabbitMQ)
↓
Worker Process (separate process/container)
↓
Chunk Processing (batches of 500 products)
↓
Database (upsert)
↓
Progress Event (WebSocket / SSE → UI)
How to Achieve Queue Fault Tolerance?
We use Laravel Queues with retry logic: on failure the worker retries the chunk up to 3 times with delay. If all attempts fail, the job is marked as failed, and we notify the admin. Data is never lost—confirmed by the official documentation.
// Controller—accepts file and queues job
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—background processing
class ProcessProductImport implements ShouldQueue
{
use Dispatchable, InteractsWithQueue;
public int $timeout = 3600; // 1 hour
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)]);
// Progress event
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()]);
}
}
Handling Import Errors
Row errors do not stop the process. Each error is logged in import_errors table:
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()
);
After completion, the user downloads a report with erroneous rows. We also set up alerts when error threshold is exceeded (e.g., >5% of total rows).
WebSocket / SSE for Progress
// Laravel Broadcasting: progress event
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),
];
}
}
On the frontend—subscribe via Laravel Echo or native EventSource (SSE). We implement both based on your choice.
Comparison: Synchronous vs Async Import
| Parameter | Synchronous | Async (Queue) |
|---|---|---|
| Max file size | ~500 rows | unlimited (chunks) |
| User wait time | up to 30 sec | ~2 sec (upload) |
| Fault tolerance | none (one error breaks all) | row-by-row processing + retry |
| Progress | none | WebSocket / SSE |
| Parallel processing possible | no | yes (Laravel Batches) |
Async import is 3–5 times faster than synchronous due to parallel processing and no timeouts.
Queue System Comparison
| Characteristic | Redis | Amazon SQS | RabbitMQ |
|---|---|---|---|
| Speed | high | medium | high |
| Reliability | medium (without persistence) | high | high |
| Setup complexity | low | medium | high |
| Cost | free | per request | free (self-hosted) |
For 90% of projects, Redis is sufficient—fast, simple, and built into Laravel. For critical data or volumes > 1 million records, choose SQS or RabbitMQ. If unsure about choice, get a consultation—we'll help select the optimal driver.
Optimal Chunk Size
Chunk size is a key performance parameter. Too small (50 rows) creates high transaction overhead, too large (5000) risks memory limits. We tailor chunk size to your server: typically 500–1000 rows per worker. For acceleration, use parallel workers with Laravel Batches.
Queue Monitoring with Laravel Horizon
Laravel Horizon provides a beautiful dashboard for queue monitoring: job count, execution time, error count. We set up alerts in Telegram or Slack on threshold breach. This enables quick reaction to failures and keeps you in control.
Parallel Processing (Laravel Batches)
For very large files (100,000+ products), we split into independent parts with parallel workers:
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();
}
}
This speeds up import 3–5 times compared to sequential processing. Typical result: server downtime reduced by 40%.
How to set up queue workers?
- Install Redis or another queue driver.
- Configure supervisor for persistent worker operation.
- Run command
php artisan queue:work redis --queue=import --tries=3 --timeout=3600. - Monitor logs via
php artisan queue:monitor.
What's Included
- Queue architecture design (Redis / SQS / RabbitMQ)
- File upload implementation (CSV, Excel, XML, CommerceML)
- Worker setup with chunked processing (chunk size tuned to your server)
- Retry and error handling system (logging + alerts)
- Real-time progress (WebSocket or SSE)
- Documentation on starting workers and monitoring
- Integration with your interface (API to start and get status)
Timeline
Basic implementation (one file format, one worker)—4–6 working days. With parallel processing and multiple formats—8–10 working days. We'll estimate your project for free after reviewing your file and requirements.
Our Experience
Our team has extensive experience in e-commerce and logistics with background queues, having completed 30+ projects. We use proven patterns: Repository, BFF, Event Sourcing. We guarantee no data loss even in case of worker failure.
Get a consultation—we'll propose architecture and precise estimate. Contact us to discuss your import needs.







