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