[ADP-362] Concurrency Control
reviewing phase 1
Add references
Overview
This specification covers two primary concurrency control mechanisms: Optimistic Locking and Pessimistic Locking.
Guidance
- If necessary, MUST ensure data consistency and integrity during concurrent operations.
- SHOULD use an appropriate concurrency control mechanism based on the application’s requirements and characteristics.
- MUST handle conflicts gracefully and inform clients appropriately.
Optimistic Locking
Concept: Optimistic locking assumes that conflicts are rare and checks for conflicts only at the time of committing the transaction. It uses ETags to detect conflicts.
Implementation Guidelines:
Resource Versioning with ETags:
- MUST include an ETag header in the resource representation to act as a version identifier.
- SHOULD use a strong ETag for resources that require precise control.
Example Resource Representation:
httpGET /users/123 HTTP/1.1 Host: example.com HTTP/1.1 200 OK Content-Type: application/json ETag: "abc123" { "id": 123, "email": "user@example.com", "phone": "+1234567890" }
Including ETag in Update Requests:
- MUST require clients to include the ETag in update requests using the
If-Match
header. - SHOULD reject the update if the
If-Match
header is missing.
Example Update Request with ETag:
httpPATCH /users/123 HTTP/1.1 Host: example.com Content-Type: application/json-patch+json If-Match: "abc123" [ { "op": "replace", "path": "/email", "value": "newemail@example.com" } ]
- MUST require clients to include the ETag in update requests using the
Conflict Detection:
- MUST compare the provided ETag with the current ETag of the resource before applying the update.
- MUST reject the update if the ETags do not match, indicating that the resource has been modified by another client.
Error Responses:
- MUST return a
412 Precondition Failed
status code when a conflict is detected. - SHOULD use the HTTP Problem Details format for error responses.
Example Conflict Response:
httpHTTP/1.1 412 Precondition Failed Content-Type: application/problem+json { "type": "https://example.com/problems/concurrent-update", "title": "Precondition Failed", "status": 412, "detail": "The resource has been modified by another client.", "instance": "/users/123", "currentETag": "def456" }
- MUST return a
Updating the ETag:
- MUST generate a new ETag each time the resource is successfully updated.
- SHOULD include the updated ETag in the response headers.
Example Successful Update Response:
httpHTTP/1.1 200 OK Content-Type: application/json ETag: "def456" { "id": 123, "email": "newemail@example.com", "phone": "+1234567890" }
Pessimistic Locking
Concept: Pessimistic locking assumes that conflicts are common and locks the resource for the duration of the operation to prevent other clients from modifying it.
Implementation Guidelines:
Lock Acquisition:
- MUST lock the resource before performing any modifications.
- SHOULD use a locking mechanism; we introduce a WebDAV-like mechanism here.
- SHOULD return
Lock-Token
in the response header when the lock request succeeds. - SHOULD specify
Timeout
in the response header.
Operation during lock:
- SHOULD specify
Lock-Token
in the response header.
- SHOULD specify
Lock Release:
- MUST release the lock once the operation is completed.
- SHOULD ensure that locks are released even if the operation fails, typically using a try-finally block, or a server timeout mechanism.
- SHOULD specify
Lock-Token
in the request header
Handling Lock Failures:
- MUST return a
423 Locked
status code if the resource is already locked by another client. - SHOULD use the HTTP Problem Details format for error responses.
Example Lock Failure Response:
httpHTTP/1.1 423 Locked Content-Type: application/problem+json { "type": "https://example.com/problems/locked", "title": "Locked", "status": 423, "detail": "The resource is currently locked by another operation.", "instance": "/users/123" }
- MUST return a
Long-Running Operations:
- SHOULD minimize the duration of locks to avoid long wait times for other clients.
- MAY use a lock timeout mechanism to release locks that have been held for too long.
Best Practices
- Clear Documentation:
- SHOULD clearly document the concurrency control mechanism used, including how to include ETags in requests and handle conflicts.
- Client Handling:
- SHOULD advise clients on how to handle
412 Precondition Failed
and423 Locked
responses, including retry strategies.
- SHOULD advise clients on how to handle
- Consistency:
- MUST ensure that the ETag is consistently updated and checked across all instances of the resource.
- Idempotent Operations:
- SHOULD design update operations to be idempotent, so that retrying an operation with the same ETag does not result in inconsistent states.
Example Workflow for Optimistic Locking
Client Fetches Resource:
- Client retrieves the resource and gets the current ETag.
httpGET /users/123 HTTP/1.1 Host: example.com
Response:
httpHTTP/1.1 200 OK Content-Type: application/json ETag: "abc123" { "id": 123, "email": "user@example.com", "phone": "+1234567890" }
Client Sends Update Request:
- Client includes the ETag in the update request using the
If-Match
header.
httpPATCH /users/123 HTTP/1.1 Host: example.com Content-Type: application/json-patch+json If-Match: "abc123" [ { "op": "replace", "path": "/email", "value": "newemail@example.com" } ]
- Client includes the ETag in the update request using the
Server Checks ETag:
- Server checks if the provided ETag matches the current ETag of the resource.
- If ETags match, the server applies the update and generates a new ETag.
- If ETags do not match, the server returns a
412 Precondition Failed
response.
Successful Update Response:
- If the update is successful, the server responds with the updated resource and new ETag.
httpHTTP/1.1 200 OK Content-Type: application/json ETag: "def456" { "id": 123, "email": "newemail@example.com", "phone": "+1234567890" }
Conflict Response:
- If a conflict is detected, the server responds with a
412 Precondition Failed
status code and the current ETag of the resource.
httpHTTP/1.1 412 Precondition Failed Content-Type: application/problem+json { "type": "https://example.com/problems/concurrent-update", "title": "Precondition Failed", "status": 412, "detail": "The resource has been modified by another client.", "instance": "/users/123", "currentETag": "def456" }
- If a conflict is detected, the server responds with a
Example Workflow for Pessimistic Locking
Client Requests Lock:
- Client requests a lock on the resource before performing the update.
httpPOST /users/123/lock HTTP/1.1 Host: example.com Timeout: Second-60
Response:
httpHTTP/1.1 200 OK Content-Type: application/json Lock-Token: abc123 { "lockId": "abc123", "resource": "/users/123", "locked": true }
Client Sends Update Request:
- Client sends the update request, including the lock identifier.
httpPATCH /users/123 HTTP/1.1 Host: example.com Content-Type: application/json-patch+json Lock-Id: "abc123" [ { "op": "replace", "path": "/email", "value": "newemail@example.com" } ]
Server Applies Update:
- Server verifies the lock identifier, applies the update, and then releases the lock.
- If the lock identifier is invalid or the resource is locked by another client, the server returns a
423 Locked
response.
Successful Update Response:
- If the update is successful, the server responds with the updated resource.
httpHTTP/1.1 200 OK Content-Type: application/json { "id": 123, "email": "newemail@example.com", "phone": "+1234567890" }
Lock Failure Response:
- If the resource is already locked by another client, the server responds with a
423 Locked
status code.
httpHTTP/1.1 423 Locked Content-Type: application/problem+json { "type": "https://example.com/problems/locked", "title": "Locked", "status": 423, "detail": "The resource is currently locked by another operation.", "instance": "/users/123" }
- If the resource is already locked by another client, the server responds with a
Client Unlocks the resource
httpPOST /users/123/lock HTTP/1.1 Host: example.com Lock-Token: abc123
This specification provides a comprehensive framework for implementing concurrency control in RESTful APIs using both optimistic and pessimistic locking mechanisms. The use of ETags ensures standardized version control, and the HTTP Problem Details format ensures standardized and informative error responses.