Locking concurrent calls

← Locking concurrent calls

Ashterix

docs: rewrite JsonRpcBundle docs, add SDK docs, move them under docs/api

en/docs/api/json-rpc-bundle/add_rpc_service/lock.md new file +50 −0

Summary

Classname Lock
Namespace Ufo\RpcObject\RPC
Target method
public function __construct(
    public ?string $paramName = null,
    public ?int $ttl = null,
)

Locking concurrent calls

Some methods must not run in parallel: paying for a single order, generating a report, syncing with an external system. The #[RPC\Lock] attribute prevents a method from starting while a previous call is still running.

use Ufo\RpcObject\RPC;

#[RPC\Lock(paramName: 'orderId', ttl: RPC\Lock::T_10_SECOND)]
public function pay(string $orderId): bool
{
    // ...
}
  • paramName — a separate lock for each value of the parameter: two pay calls with different orderId values run concurrently, with the same one they do not. Without paramName the whole method is locked, regardless of the arguments.
  • ttl — the lock lifetime in seconds, so that it does not remain forever if the process crashes. Constants: T_SECOND, T_2_SECOND, T_5_SECOND, T_10_SECOND, T_20_SECOND, T_30_SECOND.

The lock is acquired before the method runs (the rpc.pre_execute event) and released after it, including on error. Acquisition is blocking (acquire(true)): a parallel call with the same key waits until the lock is released. If the lock store reports a conflict, the call fails with an error:

{
    "jsonrpc": "2.0",
    "id": 1,
    "error": {
        "code": -32400,
        "message": "Method 'Order.pay' is locked."
    }
}
Known inconsistency

In the current version, a conflict is not turned into an error: the parallel call waits for the lock to be released. Details and a workaround are in Open issues.

Locking is implemented with Symfony Lock; it requires the ext-sysvsem extension.