01
Identify the condition that actually failed
ConditionalCheckFailedException can come from many business conditions, not only version checks. Inspect the request’s ConditionExpression and attribute values. Check whether the item exists, the key is correct, and the intended condition still describes acceptable state.
Our experiment used a version attribute initially set to 1. A first conditional update changed the state and incremented it to 2. A second request still expecting 1 was rejected, leaving the first writer’s state intact.
02
Make the version check part of the write
A read followed by an unconditional update can overwrite a change made between those operations. In a manual versioning design, the update must atomically require the version that was read and advance that version with the change.
This resource-style Boto3 fragment assumes table, key, expected_version, and desired_status were deliberately selected by the application. It is one conditional update, not a generic concurrency mechanism for every table design.
response = table.update_item(
Key=key,
UpdateExpression="SET #v = #v + :one, #s = :status",
ConditionExpression="#v = :expected",
ExpressionAttributeNames={"#v": "version", "#s": "status"},
ExpressionAttributeValues={
":one": 1, ":status": desired_status, ":expected": expected_version
},
ReturnValues="ALL_NEW",
)03
Handle conflicts separately from transport failures
Classify the service error code and route a conditional conflict to application logic. Retrying the same stale expected version does not make its condition true. Removing the condition to suppress the error would remove the protection you intended.
In the local test, we read the item after the rejected write and confirmed that the competing status and version were preserved. That readback established the failure’s effect on data rather than relying only on the exception name.
from botocore.exceptions import ClientError
try:
response = attempt_conditional_update()
except ClientError as error:
if error.response["Error"]["Code"] != "ConditionalCheckFailedException":
raise
# Report or reevaluate the conflict using current state.
# Do not blindly retry the same expected version.04
Reevaluate before a bounded retry
If the business operation can still apply to the current state, read the latest item and recompute or merge the intended change. Choose whether to retry, report a conflict to a user, or abandon an operation whose preconditions no longer hold.
Our test’s reevaluated update used expected version 2 and produced version 3. That verified the condition mechanics only. The application remains responsible for deciding whether its desired business change is still valid after another writer acts.
05
Keep the concurrency model explicit
Preserve the failed expected version, the resulting version, and non-sensitive operation context in diagnostics. Do not log full items that may contain private data. A small two-writer regression test can verify that stale writes remain rejected.
Global tables, transactions, SDK-managed version annotations, and application-level idempotency have additional semantics. Validate those separately if used. The local single-table experiment should not be described as proof of a multi-Region concurrency strategy.
Sınırları bil
Validated with DynamoDB Local 3.3.2 and Boto3 1.42.97. A manual version condition accepted version 1 once, rejected the stale second write, and accepted a reevaluated version-2 update. No managed-service global-table or distributed multi-Region behavior was tested.
Kaynaklar ve ileri okuma
CoreTrailKit tarafından hazırlanan teknik yayın.
Bütün yazıları keşfet