Symfony 8.2 to Add Declarative Controller Locking With #[Lock] Attribute
Starting with Symfony 8.2, developers will be able to apply concurrency locks directly to controller methods using a new PHP attribute, reducing the code needed to coordinate requests that act on the same resource. Symfony core team member Javier Eguiluz detailed the feature in an official blog post published on 16 October 2026.
The addition addresses a common problem in web applications: two or more requests attempting to modify the same data before an earlier operation has finished. Such conflicts can occur during catalogue imports, ticket purchases, payment processing and inventory updates. Symfony already provides a Lock component for coordinating these operations, but developers have generally needed to write code to create, acquire and release locks themselves.
The new #[Lock] attribute moves much of that work into Symfony's controller-handling process. For example, adding #[Lock('catalog', methods: 'POST')] to a controller method allows Symfony to coordinate requests using the same lock key while leaving other HTTP methods unaffected. The feature requires Symfony's Lock component and an appropriately configured locking service.
When a request encounters an existing exclusive lock, Symfony returns an HTTP 409 Conflict response by default. Developers can instead configure blocking: true, causing the competing request to wait until the lock becomes available. Blocking does not have a separate timeout, making the duration of the protected operation an important consideration.
Symfony contributor Adrian Brajković developed the original implementation through pull request #66515. The attribute supports several configuration options, including custom lock factories, expiration periods, HTTP method restrictions and lock keys derived from expressions.
Another addition allows developers to construct lock keys from route parameters. A controller handling ticket purchases, for example, can use #[Lock('ticket-sale-{conference}', blocking: true, ttl: 10)] to create a separate lock for each conference. Requests affecting the same conference share a lock, while requests for different conferences can proceed independently. Symfony core contributor Nicolas Grekas added this placeholder support through pull request #66784, with the same mechanism also becoming available to Symfony's #[RateLimit] attribute.
Symfony 8.2 also introduces shared controller locks through the read: true option. Shared locks allow multiple read operations to run concurrently while coordinating access with operations that require an exclusive lock, provided the configured lock store supports that behaviour. A catalogue could therefore support simultaneous downloads while preventing an import from modifying it during those operations.
Lock lifecycle management remains important despite the simpler interface. Symfony releases the acquired controller lock when execution finishes and retains it until content delivery completes for a streamed response. The default lock lifetime is 30 seconds, and Symfony does not automatically renew it. A lock that expires before the protected operation finishes can allow another request to acquire the same resource.
Developers can address longer operations by receiving the acquired lock through a controller argument typed as LockInterface or SharedLockInterface. The controller can then refresh the lock or release it once the critical operation is complete, allowing unrelated work such as notifications to continue without holding the resource. Brajković added this capability through pull request #66697. Symfony recommends making the argument nullable because a request may not always acquire its own lock.
The implementation also has an access-control consideration. Symfony normally acquires controller locks before resolving arguments and before evaluating its #[IsGranted] attributes, although lock keys depending on controller arguments affect that ordering. Consequently, an unauthorised request may receive HTTP 409 instead of HTTP 403, or wait for a blocking lock before permission checks reject it. Applications using the feature must account for this execution sequence.
For Drupal developers, the development is relevant because Drupal uses Symfony components and already provides a separate mechanism for concurrency control. Drupal core's LockBackendInterface supports acquiring, releasing and waiting for locks, allowing application code to coordinate operations affecting shared resources.
The distinction is that Symfony's new attribute provides declarative controller-level integration, rather than introducing locking itself. Drupal 12's September 2026 alpha release notes identify Symfony 8.1 as an updated dependency. Neither that dependency update nor the introduction of Symfony 8.2's attribute establishes automatic support for controller locking in Drupal. Adopting the feature would require additional integration with Drupal's controller execution and service architecture.
Concurrency is already a documented concern within the Drupal Commerce ecosystem. A Commerce Stock issue opened in February 2021 described two customers completing purchases for the final available item almost simultaneously, allowing both orders to proceed. The issue was closed as working as designed in January 2022, with the maintainer identifying serialised order completion and advance inventory reservations as possible approaches. The historical report does not establish that the same behaviour remains reproducible in current releases.
A more recent Commerce Stock Reserve proposal, submitted on 2 September 2026, takes a different approach. Its author proposed database-serialised reservations, using database row locking to enforce inventory guarantees while retaining Drupal's lock service for contention management. The proposal remains under review, and its reported results have not been independently established.
These examples illustrate both the relevance and the limits of controller locking. A lock scoped to an individual order may prevent duplicate processing of that order without protecting inventory shared by several orders. Reliable stock reservations and payment workflows can also require database transactions, durable reservation records and idempotent processing across separate requests or background operations. Symfony's attribute reduces controller-level implementation work but does not replace those safeguards.
Controller locking also addresses a different problem from asynchronous transaction coordination. The DropTimes previously covered Symfony 8.2's transactional outbox and claim check additions to Messenger, which concern reliable message delivery and handling large message payloads. Unlike those messaging mechanisms, the new #[Lock] attribute coordinates competing HTTP controller executions.
Symfony 8.2 remains under development, with its stable release scheduled for November 2026. Developers can review the attribute's options, implementation examples and operational limitations in the official Symfony announcement, alongside the project's Lock component documentation.
