-
Notifications
You must be signed in to change notification settings - Fork 16
Description
Summary
Locks prevent concurrent modification of entity resources by multiple clients. Before a client can modify configurations, execute operations, or change modes on a locked entity, it must acquire a lock. This is important when multiple diagnostic tools or dashboards are connected simultaneously.
Locks have an expiration timer and support scoping (lock only specific resource collections).
Proposed solution
1. POST /api/v1/{entity-path}/locks
Acquire a lock on an entity (or on specific scopes within it).
Applies to entity types: Components, Apps
Path parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
{entity-path} |
URL segment | Yes | e.g., components/engine_ecu |
Request body:
{
"lock_expiration": 600,
"break_lock": false,
"scopes": ["configurations", "operations"]
}| Field | Type | Required | Description |
|---|---|---|---|
lock_expiration |
integer |
Yes | Seconds until auto-release. Must be > 0. |
break_lock |
boolean |
No | If true and entity is already locked by another client, break the existing lock (requires sufficient privileges). Default: false. |
scopes |
string[] |
No | Specific resource collections to lock (e.g., ["configurations"]). If empty/omitted, locks all resource collections on the entity. |
Valid scope values: data, operations, configurations, faults, modes, scripts, bulk-data
Response 201 Created:
{
"id": "lock_001",
"owned": true,
"scopes": ["configurations", "operations"],
"lock_expiration": "2026-02-14T11:00:00Z"
}| Field | Type | Required | Description |
|---|---|---|---|
id |
string |
Yes | Lock identifier |
owned |
boolean |
Yes | true if the requesting client owns this lock |
scopes |
string[] |
Conditional | Locked scopes (present when scoped lock) |
lock_expiration |
string (ISO 8601) |
Yes | When the lock expires |
Error responses:
| Status | Error Code | When |
|---|---|---|
400 |
invalid-parameter |
Unknown scope, invalid expiration (≤ 0) |
404 |
entity-not-found |
Entity doesn't exist |
409 |
invalid-request |
Entity already locked by another client and break_lock is false or lock is not breakable |
409 response when already locked:
{
"error_code": "invalid-request",
"message": "Entity is already locked by another client",
"parameters": {
"existing_lock_id": "lock_999",
"owned": "false"
}
}2. GET /api/v1/{entity-path}/locks
List all active locks on an entity.
Response 200 OK:
{
"items": [
{
"id": "lock_001",
"owned": true,
"scopes": ["configurations", "operations"],
"lock_expiration": "2026-02-14T11:00:00Z"
}
]
}Typically there's 0 or 1 active lock. The owned field is relative to the requesting client.
3. GET /api/v1/{entity-path}/locks/{lock-id}
Read details of a specific lock.
Path parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
{lock-id} |
string |
Yes | Lock identifier |
Response 200 OK: Returns a single LockInfo object.
Error: 404 if lock doesn't exist (expired or never created).
4. PUT /api/v1/{entity-path}/locks/{lock-id}
Extend (or modify) the lock's expiration. Only the lock owner can extend it.
Request body:
{
"lock_expiration": 1200
}| Field | Type | Required | Description |
|---|---|---|---|
lock_expiration |
integer |
Yes | New duration in seconds from now |
Response 204 No Content
Error responses:
| Status | Error Code | When |
|---|---|---|
403 |
forbidden |
Client does not own this lock |
404 |
resource-not-found |
Lock doesn't exist |
5. DELETE /api/v1/{entity-path}/locks/{lock-id}
Release a lock. Only the lock owner (or an admin with break_lock privileges) can release it.
Response 204 No Content
Error responses:
| Status | Error Code | When |
|---|---|---|
403 |
forbidden |
Client does not own this lock and doesn't have admin privileges |
404 |
resource-not-found |
Lock doesn't exist |
Business Rules
Locking behavior
- Parent lock propagates to children: Locking a Component also locks its hosted Apps with the same scope. Locking an Area locks contained Components.
- Expired lock cleanup: When a lock expires, the server:
- Terminates all in-scope temporary resources (subscriptions, triggers)
- Removes the lock
- Frees the entity for other clients
- Unlocked resources still accessible: Resources that don't have a lock requirement (per capability description) remain accessible even when the entity is locked.
- Lock breaking: If
break_lock: trueis sent and the client has sufficient privileges:- The existing lock is terminated
- The original lock holder receives
409with error codelock-brokenon their next locked request - A new lock is created for the requesting client
Client identification
Lock ownership requires client identification. Options:
- JWT token claim - if auth is enabled, the client is identified by their JWT token
- Client session ID - a session cookie or custom header
- For initial implementation: use a server-generated client ID returned at lock creation time, passed via custom header on subsequent requests
Lock enforcement in handlers
Any handler that modifies a locked resource collection must check:
- Is the entity locked?
- Does the lock scope include this resource collection?
- Does the requesting client own the lock?
If locked and client doesn't own it → return 409 with "lock-broken" or similar error.
Additional context
Architecture
- Create a
LockManagerclass with in-memory lock storage - Each lock:
id,entity_id,client_id,scopes,expiration_time,created_at - Use a ROS 2 timer for periodic expiration checks
- Lock enforcement: middleware-style check in
HandlerContextor individual handlers
Route registration
srv->Post((api_path("/components") + R"(/([^/]+)/locks$)"), handler);
srv->Get((api_path("/components") + R"(/([^/]+)/locks$)"), handler);
srv->Get((api_path("/components") + R"(/([^/]+)/locks/([^/]+)$)"), handler);
srv->Put((api_path("/components") + R"(/([^/]+)/locks/([^/]+)$)"), handler);
srv->Delete((api_path("/components") + R"(/([^/]+)/locks/([^/]+)$)"), handler);
// Same for /apps/Tests
- Unit test: acquire lock → 201
- Unit test: acquire while already locked → 409
- Unit test: acquire with break_lock → new lock created
- Unit test: extend lock → 204
- Unit test: release lock → 204
- Unit test: lock expiration auto-releases
- Unit test: modify locked resource without owning lock → 409
- Unit test: scoped lock only affects listed collections