Skip to content

Commit 0202586

Browse files
author
Mo Kamioner
committed
feat: Add update_item_conditional example to Python samples.
1 parent 09c5f33 commit 0202586

File tree

1 file changed

+105
-2
lines changed

1 file changed

+105
-2
lines changed
Lines changed: 105 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,105 @@
1-
import boto3, json
2-
from boto3.dynamodb.conditions import Key
1+
from __future__ import print_function # Python 2/3 compatibility
2+
import boto3
3+
from boto3.dynamodb.conditions import Attr
4+
from botocore.exceptions import ClientError
5+
6+
dynamodb = boto3.resource("dynamodb", region_name="us-west-2")
7+
table = dynamodb.Table("RetailDatabase")
8+
9+
10+
class ConditionalCheckFailedError(Exception):
11+
"""
12+
An error indicating that a DynamoDB conditional check failed.
13+
Wrapped as a separate error for readability.
14+
"""
15+
16+
17+
def delete_item(email):
18+
response = table.delete_item(
19+
Key={
20+
"pk": email,
21+
"sk": "metadata",
22+
},
23+
)
24+
25+
26+
def create_user(email, name):
27+
initial_change_version = 0
28+
29+
try:
30+
table.put_item(
31+
Item={
32+
"pk": email,
33+
"sk": "metadata",
34+
"name": name,
35+
"change_version": initial_change_version,
36+
},
37+
ConditionExpression=Attr("pk").not_exists(),
38+
)
39+
except dynamodb.meta.client.exceptions.ConditionalCheckFailedException as e:
40+
raise ConditionalCheckFailedError() from e
41+
42+
return initial_change_version
43+
44+
45+
def update_name(email, name, last_change_version):
46+
try:
47+
response = table.update_item(
48+
Key={"pk": email, "sk": "metadata"},
49+
UpdateExpression="SET #n = :nm, #cv = #cv + :one",
50+
ExpressionAttributeNames={"#n": "name", "#cv": "change_version"},
51+
ExpressionAttributeValues={":nm": name, ":one": 1},
52+
ReturnValues="UPDATED_NEW",
53+
ConditionExpression=Attr("change_version").eq(last_change_version),
54+
)
55+
except dynamodb.meta.client.exceptions.ConditionalCheckFailedException as e:
56+
raise ConditionalCheckFailedError() from e
57+
58+
return int(response.get("Attributes", {}).get("change_version"))
59+
60+
61+
def get_user(email):
62+
response = table.get_item(
63+
Key={"pk": email, "sk": "metadata"},
64+
AttributesToGet=["name", "change_version"],
65+
)
66+
return {
67+
"email": email,
68+
"name": response["Item"]["name"],
69+
"change_version": int(response["Item"]["change_version"]),
70+
}
71+
72+
73+
def main():
74+
delete_item("[email protected]")
75+
76+
print("Creating an item with change_version = 0")
77+
last_change_version = create_user("[email protected]", "Jim Bob")
78+
current_user = get_user("[email protected]")
79+
print("current_user = ", current_user)
80+
81+
print("Change the user's name")
82+
last_change_version = update_name(
83+
"[email protected]", "Jim Roberts", last_change_version
84+
)
85+
current_user = get_user("[email protected]")
86+
print("current_user = ", current_user)
87+
88+
print(
89+
"Try to update the name with an old change_version imitating a race condition"
90+
)
91+
try:
92+
last_change_version = update_name(
93+
"[email protected]", "Jonathan Roberts", last_change_version - 1
94+
)
95+
except ConditionalCheckFailedError:
96+
print("Yup, this failed as expected, the user information did not change")
97+
else:
98+
raise RuntimeError("This should have failed")
99+
100+
current_user = get_user("[email protected]")
101+
print("current_user = ", current_user)
102+
103+
104+
if __name__ == "__main__":
105+
main()

0 commit comments

Comments
 (0)