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

Symfony Lock Doctrine Migrations Bundle Laravel Package

marein/symfony-lock-doctrine-migrations-bundle

A Symfony bundle that integrates the Lock component with Doctrine Migrations, using locks to prevent concurrent migration runs. Keeps deployments safe across multiple servers or containers by ensuring only one process executes migrations at a time.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit: The framing matters more than the code here. This is marein/symfony-lock-doctrine-migrations-bundle — a Symfony bundle that decorates the Doctrine Migrations executor with a symfony/lock lock. It is not a Laravel package: there is no service provider, no config/migrations.php publish tag, no Artisan hook, and Laravel's own migrator never passes through Doctrine's migration lifecycle. So if the target is Laravel, the architectural fit is zero as a dependency. What is reusable is the pattern: an atomic, store-backed lock serializing migrate across deploy replicas. In Laravel that is Cache::lock(...) around Artisan::call('migrate', ['--force' => true]) in the release job — roughly 30 lines, no third-party code, no supply-chain surface. If the target is Symfony 6/7 on Doctrine Migrations, the fit is excellent and the bundle is the correct, low-blast-radius insertion point.

Integration feasibility: Symfony: high. composer require, bundle registration (automatic under Flex), one config block naming a lock store, and the existing bin/console doctrine:migrations:migrate --no-interaction invocation becomes lock-aware without touching migration classes. Laravel: reimplement natively. No Symfony class, config key, or framework.lock stanza is portable, and a port is not worth the engineering time given the alternative is trivial and better supported.

Technical risk: Low on code. The 1.0.4 delta is maintenance-only — PHP 8.4 added to CI, dependency constraints widened and tested, a Compose file for local dev. No breaking changes, no deprecations, no config surface change. Two real risks, both non-code. First, widened dependency constraints: "allow and test new dependency versions" means Composer may resolve symfony/lock / doctrine/migrations combinations the maintainer's CI has not exercised, so composer.lock becomes the contract and any bump of those two packages needs the concurrency test re-run. Second, governance: 8 stars, a single maintainer, modest release cadence, and a narrow user base. That is acceptable for a small, well-understood decorator on a deploy path we can replace same-day, and unacceptable as an unexamined critical-path dependency — vendor, fork, or keep the native fallback documented.

Key questions:

  • Is the target stack Symfony or Laravel? If Laravel, the decision is "build the 30-line lock, do not adopt," and this assessment ends there.
  • Which lock store, and is it the same HA store the app already depends on? The lock becomes a hard dependency of every deploy — if the store is down, migrations do not ship.
  • What lock TTL versus slowest-migration duration? A TTL shorter than a long DDL means the lock expires mid-run and a second replica starts migrating concurrently — the exact failure the bundle exists to prevent.
  • Does the platform already guarantee single-runner migration execution (dedicated Kubernetes Job, one-off ECS task)? If yes, this is defense in depth, not a requirement, and should be prioritized accordingly.
  • What is the rollback? (Answer: revert to the bare migrate command. Cheap, which is why the risk is acceptable.)

Integration Approach

Stack fit: Adopt directly for Symfony + Doctrine Migrations deployed as more than one replica, or where CI runners and rolling deploys can overlap. Do not adopt for Laravel, Lumen, or Phinx-based stacks. Lock store selection is the integration decision that actually matters: flock and semaphore stores are host-local and provide no protection across containers — this is the #1 silent failure mode in Docker/Kubernetes. Production must point at Redis, Memcached, Postgres advisory locks, or the DB store, and must avoid a DB-store lock if any migration drops or recreates the database that holds the lock.

Migration path: No data migration. The change is confined to the deployment entrypoint. For Laravel, the equivalent entrypoint change is:

$lock = Cache::lock('doctrine-migrations:'.config('database.default'), 300);

if (! $lock->get()) {
    $this->error('Another deployment is migrating.');
    return 1;
}

try {
    Artisan::call('migrate', ['--force' => true]);
} finally {
    $lock->release();
}

Key the lock on the connection name so parallel migrations against different databases do not block each other. Note Laravel's migrator already takes an internal cache lock in recent versions — verify whether the extra guard is needed before adding it, so we are not stacking redundant locks and confused failure modes.

Compatibility: PHP 8.4 is now tested and safe; 8.1–8.3 support is unchanged. The widened symfony/lock and doctrine/migrations ranges are the one item requiring a deliberate decision rather than an automatic composer update. Pin resolved versions in composer.lock, confirm they match what we actually run, and re-run the two-replica staging test after any bump of those two packages. The added compose.yaml is developer experience only, with no production implication.

Sequencing:

  1. Confirm stack and whether a single-runner guarantee already exists. If Laravel → switch to the native lock and close this out.
  2. Verify composer.lock resolution against the widened constraints; upgrade in staging first, never straight to production.
  3. Add the bundle (Symfony) or the cache-lock wrapper (Laravel); configure the store per environment, production on the existing HA store.
  4. Run a two-replica concurrent deploy in staging; assert exactly one acquires, the other blocks or exits non-zero, and no duplicate DDL lands.
  5. Roll to production behind a normal release; keep the bare-command path documented as rollback.
  6. Add a check that the lock is never left held after a failed deploy (stale key usually means a SIGKILLed process, not a bundle bug).

Operational Impact

Maintenance: Symfony path: near-zero code maintenance — one dependency, one registration, one config block. The real recurring cost shifts to the lock store, which becomes a hard dependency of every deploy and must be covered by the same on-call and HA posture as the database. Budget a standing review of the relaxed dependency constraints: automated dependency PRs touching symfony/lock or doctrine/migrations should require the staging concurrency test as a blocking check, not a nice-to-have. Laravel path: the native implementation is our code, so we own it outright — cheaper to reason about, marginally more to maintain, and immune to upstream abandonment.

Support: Single maintainer, 8 stars, low cadence. Fine for a small decorator on a replaceable path; document the ~30-line native alternative so the team is never blocked by upstream, record the exact pinned version in the dependency inventory, and pre-agree that "remove the bundle and wrap the command ourselves" is a same-day change requiring no architecture review. Internal support load should be near zero either way.

Scaling: Scales the way we need it to — the store absorbs contention, so dozens of simultaneously booting pods serialize correctly instead of racing on the migrations table. Two scaling-adjacent caveats to own: lock TTL must exceed the slowest migration (long DDL on a large table is the realistic worst case), and the deploy timeout plus readiness probes must tolerate a replica that is blocked waiting on the lock rather than killing it mid-wait. If a blocked replica is killed by a probe, verify the lock is released on process termination, or you trade a race condition for a stale lock and a wedged deploy.

Failure modes to instrument: store unavailable (migrations halt — alert on deploy failures, not just app health); lock held past TTL mid-migration (duplicate DDL — alert on concurrent migrate invocations); stale lock after SIGKILL (deploy wedged — document redis-cli keys '*migration*' triage and a manual clear runbook); and host-local store misconfiguration in a containerized environment (no protection at all, silent — assert the configured store is non-local as a config test in CI).

Ramp-up: Low. An engineer familiar with Symfony bundles or Laravel's cache can be productive in under a day; the conceptual load is "one lock around one command." The ramp-up cost that matters is organizational, not individual: the deploy pipeline owner must understand that migrations now depend on the lock store, and the on-call runbook must say what to do when a deploy hangs on a held lock. Write that down before rollout, not after the first stuck release.

TPM recommendation: For a Symfony/Doctrine estate with multi-replica deploys, adopt — the pattern is correct, the code surface is small, and the exit is cheap. For a Laravel estate, do not adopt; implement Cache::lock around Artisan::call('migrate'), which delivers the same guarantee with no single-maintainer dependency, and reuse the operational runbook above unchanged.

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.
mehedimi/laravel-amp-mail
happones/laravel-vue-i18n-generator
jwerd/inertia-table
siberfx/flysystem-wasabi
hollyit/token-replace
ippanelcom/laravel-sdk
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel