tourze/json-rpc-lock-bundle
A Symfony bundle that adds distributed locking to JSON-RPC services, helping coordinate concurrent requests and prevent duplicate processing across workers. Useful for protecting shared resources in PHP apps.
This is the tourze/json-rpc-lock-bundle 2.0.0 release, a major version jump from 1.0.1. The release notes are effectively empty — only a compare link — so treat the diff itself, not the notes, as the source of truth before upgrading. Read the code in this order:
composer.json — confirm the PHP and framework constraints. The -bundle suffix signals a Symfony-style bundle; if you are on Laravel you will likely be wiring the service provider / config by hand rather than relying on auto-discovery.1.0.1...2.0.0 diff, specifically any renamed config keys, changed default TTLs, or changed exception types. In a major bump those are the things that break silently.Minimal first use case: pick one mutating JSON-RPC method that is not safe to run twice concurrently — a balance debit, an order creation, a "claim" endpoint — and guard it with the package's lock so that duplicate or racing client calls cannot both commit. Verify success by firing the same request twice in parallel (two terminals with curl, or a small forked PHP script) and confirming the second call is rejected or waits per your configuration rather than executing.
Guard the method, not the controller. The value of this package is per-method mutual exclusion keyed on request parameters. Mark the RPC method (or wrap it in the provided middleware) and derive the lock key from the parameters that define the resource — user_id, order_id, wallet_id — never from the raw payload or a timestamp, or the lock will never contend and therefore never protect anything.
Choose an atomic store deliberately. Redis (or another store with atomic SET NX semantics) is the only sane production choice. file and database cache drivers either do not enforce the lock across processes or serialize the whole application. Set this explicitly in config for every environment, including staging — a lock that works in prod and no-ops in staging produces bugs you only find in prod.
Decide the contention policy up front. There are two behaviors you will want, and they should not be the same everywhere:
-32000 with "operation in progress") so the client can retry or show a message.Map the lock-failure exception to a stable JSON-RPC error code in one place (an exception handler or the middleware) so clients can branch on it. Do not let it surface as a generic -32603 internal error.
Release in finally. Whatever the integration point, ensure the lock is released on exception, on timeout, and on validation failure inside the handler. A leaked lock on a hot key is an outage, not a bug.
Test with a real-enough store. array cache gives you a lock that is always uncontended, which is fine for testing the happy path but useless for testing contention. Use a Redis container in CI and write at least one test that asserts the second concurrent call fails.
Instrument it. Log lock acquisition failures with the key. That log line is how you discover hot keys, retry storms, and clients that double-submit.
The empty 2.0.0 changelog is itself the headline risk. A major bump on a package with no migration notes means the breaking change is undocumented. Diff the config schema and the public class signatures between 1.0.1 and 2.0.0 before you tag a release that depends on it. If you cannot determine what broke, pin ^1.0 until you can.
TTL must exceed worst-case handler duration, and be short enough to self-heal. If the lock TTL is shorter than a slow database call, two requests will run concurrently anyway — the lock silently stops protecting you under load, which is the worst failure mode because it only appears when traffic is high. Set TTL from measured p99 handler latency, not from a default, and add a metric on lock-held duration.
Lock key scope is shared across everything using the same cache. Prefix keys with the application and environment name. Two apps sharing one Redis instance with a generic lock: prefix will block each other in ways that are very hard to diagnose.
Nested and batch locks deadlock. If a single JSON-RPC batch acquires locks on multiple resources, order the acquisitions deterministically (sorted key order) across every code path, or you will get classic ABBA deadlocks under concurrency. Better: refuse to acquire more than one lock per request.
Do not lock read paths. The cost is a Redis round-trip on every call and a contention point on hot keys with no correctness benefit. Restrict the lock to methods that mutate state.
Verify the release actually applies the lock in the code path you think it does. Bundles of this shape often ship the lock infrastructure but require an explicit opt-in (attribute, tag, or middleware registration). A guard that is registered but never invoked is indistinguishable from a working one until the first race condition in production.
How can I help you explore Laravel packages today?