Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Doctrine Async Insert Bundle Laravel Package

tourze/doctrine-async-insert-bundle

A Symfony bundle that adds asynchronous insert support to Doctrine ORM, letting you queue entity inserts and flush them in batches to reduce database round trips and speed up bulk writes in your applications.

View on GitHub
Deep Wiki
Context7

Getting Started

tourze/doctrine-async-insert-bundle is a Symfony bundle that buffers Doctrine INSERT statements and flushes them as a batch instead of issuing one round-trip per row. There is no Laravel service provider, no facade, and no config/ publish tag — so treat it as either (a) something you wire manually into a Laravel app that already runs Doctrine DBAL alongside Eloquent, or (b) a reference implementation for the buffered-insert pattern you reimplement with the Laravel queue.

Install and inspect before trusting it:

composer require tourze/doctrine-async-insert-bundle:^1.0

Look at these first, in order:

  1. src/DependencyInjection/Configuration.php — the real contract. Config key, default buffer size, flush strategy, whether a transport/queue is required.
  2. The class decorating Doctrine\DBAL\Connection (usually an AsyncInsertConnection or a middleware) — this tells you exactly which calls are deferred and which still pass through synchronously.
  3. The kernel.terminate / event subscriber that triggers the flush — that is your durability boundary.

First use case: append-only, high-volume writes where read-after-write is not required — request logs, audit trails, metrics samples, feed fan-out rows. Anything where a user hits "save" and immediately reads the row back is a bad first candidate.

Version 1.0.3 ships with no changelog beyond a compare link, so treat it as a drop-in patch on 1.0.x and verify by diffing the tag rather than reading notes.

Implementation Patterns

Buffer, then flush at a known boundary. The bundle's core loop is: intercept insert → append to in-memory buffer → flush on terminate (or when the buffer hits a size threshold). In a Laravel app the equivalent boundary is terminate() middleware, App\Console\Kernel command completion, or a queued job's handle() tail. Never leave a flush dependent on something that might not run.

Pair it with a transaction, not against it. If a deferred insert is part of a unit of work that can roll back, the buffer must be drained inside the same transaction or dropped on rollback. Confirm which the bundle does before relying on it; if it flushes at terminate, you have decoupled the insert from the transaction and lost atomicity.

Chunk your flushes. A single INSERT ... VALUES (...), (...), ... with 10k rows will blow past max_allowed_packet / Postgres's 65535 parameter limit. Set the buffer size to something like 100–500 rows and let the bundle issue multiple statements rather than one giant one.

Laravel-native analogue if you skip the bundle:

// deferred, durable, and observable — unlike a bare in-memory buffer
LogEventJob::dispatch($rows)->onQueue('inserts')->afterCommit();

// inside the job
collect($rows)->chunk(500)->each(fn ($chunk) => DB::table('events')->insert($chunk->all()));

This gives you retries, backoff, dead-lettering, and a dashboard for free. The bundle gives you lower latency and no queue infrastructure. Choose deliberately.

Testing pattern. Because inserts are deferred, the assertion you want is on the buffer, not the table:

// flush deterministically in tests instead of relying on terminate
$this->app->make(InsertBuffer::class)->flush();
$this->assertDatabaseCount('events', 3);

If the bundle exposes no flush entry point, that is a red flag for testability and a reason to wrap it behind your own repository method.

Integration tip. Isolate deferred writes behind a repository or dedicated model so the rest of the codebase never calls a deferred insert by accident. One ->insert() call that silently buffers is the kind of bug that shows up in production as "the row appeared 40 seconds later."

Gotchas and Tips

  • The 1.0.3 release notes are empty. No description, no CHANGELOG.md entry — only a compare URL. Pin to 1.0.2 or 1.0.3 explicitly and run composer diff / review the GitHub compare before upgrading a production app. Silent patch releases on a package that mutates your write path deserve paranoia.
  • Read-after-write breaks. DB::getLastInsertId() and any insert()-then-select() sequence are unreliable once buffering is on. Auto-increment IDs may not exist when your code returns.
  • Constraint violations surface late — or not at all. A unique-key violation that would have thrown at the call site now throws (or is swallowed) at flush time, far from the code that caused it. Log aggressively in the flush path and monitor it as a real failure mode.
  • Foreign keys and ordering. A parent insert and child insert in the same buffer may not flush in a safe order. Verify ordering guarantees, or flush parent tables synchronously.
  • Long-lived processes. Under Laravel Octane or a queue:work daemon the container is not rebuilt per request, so an in-memory buffer is not cleared by a fresh request lifecycle. Flush at the end of every job/request loop iteration or the buffer grows unbounded and leaks into the next unit of work.
  • Failure handling at shutdown. A flush on terminate/shutdown that throws will often be suppressed by the runtime. Wrap it, log it, and consider a fallback persistence path so rows are never silently dropped.
  • No Laravel auto-discovery. There is no extra.laravel.providers, so composer require alone will do nothing in a Laravel app. You must register the connection decorator and config yourself — and the Symfony config schema (tourze_doctrine_async_insert.*) won't be loaded by Laravel's config system at all.
  • Extension points. The useful seams are the buffer implementation (swap in a queue-backed or Redis-backed one) and the flush trigger. If neither is bound behind an interface, fork the pattern rather than patching a vendor tree.
  • Debugging. If writes seem to vanish, dump the buffer contents before flush; if writes appear duplicated, check whether an exception during flush causes a retry of an already-partially-applied batch — batching without idempotency is how you get double inserts.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels